From 4b16e4b0202f49aba70464146c43d2ee7631a9a7 Mon Sep 17 00:00:00 2001 From: Lemongrass3110 Date: Mon, 7 Nov 2022 01:50:44 +0100 Subject: [PATCH] Initial implementation of the goldpc timer Fixes #6912 Thanks to @Balferian --- conf/battle/feature.conf | 16 +++++++++++++ npc/other/goldpc.txt | 12 ++++++++++ npc/scripts_athena.conf | 1 + src/map/battle.cpp | 5 ++++ src/map/battle.hpp | 5 ++++ src/map/chrif.cpp | 18 +++++++++++++- src/map/clif.cpp | 44 +++++++++++++++++++++++++++++++++++ src/map/clif.hpp | 2 ++ src/map/clif_packetdb.hpp | 4 ++++ src/map/packets.hpp | 19 +++++++++++++++ src/map/pc.cpp | 49 +++++++++++++++++++++++++++++++++++++++ src/map/pc.hpp | 4 ++++ 12 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 npc/other/goldpc.txt diff --git a/conf/battle/feature.conf b/conf/battle/feature.conf index aef58b5519..cd30a98ab3 100644 --- a/conf/battle/feature.conf +++ b/conf/battle/feature.conf @@ -135,3 +135,19 @@ feature.dynamicnpc_rangey: 2 // Should the dynamic NPCs look into the direction of the player? (Note 1) // Default: no feature.dynamicnpc_direction: no + +// Enable the Gold PC timer? (Note 1) +// Default: yes +feature.goldpc_active: yes + +// How many seconds does a player have to be online to receive a point? +// Default: 3600s (1h) +feature.goldpc_time: 3600 + +// How many points can a player have at maximum? +// Default: 300 +feature.goldpc_max_points: 300 + +// Should being a VIP player double the points a player gets? (Note 1) +// Default: yes +feature.goldpc_vip: yes diff --git a/npc/other/goldpc.txt b/npc/other/goldpc.txt new file mode 100644 index 0000000000..c6ed149c38 --- /dev/null +++ b/npc/other/goldpc.txt @@ -0,0 +1,12 @@ +//===== rAthena Script ======================================= +//= Gold PC Bonus NPC +//===== Description: ========================================= +//= NPC that can be spawned via the Gold PC Timer Button. +//===== Changelog: =========================================== +//= 1.0 Initial release [Lemongrass] +//============================================================ + +prontera,0,0,5 script Goldpoint Manager::GOLDPCCAFE 4_F_02,{ + // TODO: Implement + end; +} diff --git a/npc/scripts_athena.conf b/npc/scripts_athena.conf index c16cf9ab55..8ff28c6eb3 100644 --- a/npc/scripts_athena.conf +++ b/npc/scripts_athena.conf @@ -179,6 +179,7 @@ npc: npc/other/comodo_gambling.txt npc: npc/other/divorce.txt npc: npc/other/fortune.txt npc: npc/other/gm_npcs.txt +npc: npc/other/goldpc.txt npc: npc/other/guildpvp.txt npc: npc/other/gympass.txt npc: npc/other/hugel_bingo.txt diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 84528f82cb..d9360aba33 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -10276,6 +10276,11 @@ static const struct _battle_data { { "feature.dynamicnpc_rangey", &battle_config.feature_dynamicnpc_rangey, 2, 0, INT_MAX, }, { "feature.dynamicnpc_direction", &battle_config.feature_dynamicnpc_direction, 0, 0, 1, }, + { "feature.goldpc_active", &battle_config.feature_goldpc_active, 1, 0, 1, }, + { "feature.goldpc_time", &battle_config.feature_goldpc_time, 3600, 0, 3600, }, + { "feature.goldpc_max_points", &battle_config.feature_goldpc_max_points, 300, 0, 300, }, + { "feature.goldpc_vip", &battle_config.feature_goldpc_vip, 1, 0, 1, }, + #include "../custom/battle_config_init.inc" }; diff --git a/src/map/battle.hpp b/src/map/battle.hpp index 15bd21a493..bcd3e78bd2 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -719,6 +719,11 @@ struct Battle_Config int feature_dynamicnpc_rangey; int feature_dynamicnpc_direction; + int feature_goldpc_active; + int feature_goldpc_time; + int feature_goldpc_max_points; + int feature_goldpc_vip; + #include "../custom/battle_config_struct.inc" }; diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index 7e6c251353..ab9e8ba01f 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -324,9 +324,25 @@ int chrif_save(struct map_session_data *sd, int flag) { if (sd->premiumStorage.dirty) storage_premiumStorage_save(sd); - if (flag&CSAVE_QUITTING) + if( flag&CSAVE_QUITTING ){ sd->state.storage_flag = 0; //Force close it. + if( sd->goldpc_tid != INVALID_TIMER ){ + const struct TimerData* td = get_timer( sd->goldpc_tid ); + + if( td != nullptr ){ + // Get the remaining milliseconds until the next reward + t_tick remaining = td->tick - gettick(); + + // Always round up to full second and a little safety delay + remaining += ( remaining % 1000 ) + 2000; + + // Store the seconds that already fully passed + pc_setreg2( sd, GOLDPC_SECONDS_VAR, battle_config.feature_goldpc_time - remaining / 1000 ); + } + } + } + //Saving of registry values. if (sd->vars_dirty) intif_saveregistry(sd); diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 768f479bd4..f12b9ace2e 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -24901,6 +24901,50 @@ void clif_macro_reporter_status(map_session_data &sd, e_macro_report_status styp #endif } +void clif_goldpc_info( struct map_session_data& sd ){ +#if PACKETVER_MAIN_NUM >= 20140508 || PACKETVER_RE_NUM >= 20140508 || defined(PACKETVER_ZERO) + if( battle_config.feature_goldpc_active ){ + struct PACKET_ZC_GOLDPCCAFE_POINT p = {}; + + p.packetType = HEADER_ZC_GOLDPCCAFE_POINT; + p.active = sd.goldpc_tid != INVALID_TIMER; + if( battle_config.feature_goldpc_vip && pc_isvip( &sd ) ){ + p.unitPoint = 2; + }else{ + p.unitPoint = 1; + } + p.point = (int32)pc_readreg2( &sd, GOLDPC_POINT_VAR ); + // TODO: check if we should send max value, if disabled/max reached + p.accumulatePlaySecond = (int32)( 3600 - battle_config.feature_goldpc_time + pc_readreg2( &sd, GOLDPC_SECONDS_VAR ) ); + + clif_send( &p, sizeof( p ), &sd.bl, SELF ); + } +#endif +} + +void clif_parse_dynamic_npc( int fd, struct map_session_data* sd ){ +#if PACKETVER_MAIN_NUM >= 20140430 || PACKETVER_RE_NUM >= 20140430 || defined(PACKETVER_ZERO) + struct PACKET_CZ_DYNAMICNPC_CREATE_REQUEST* p = (struct PACKET_CZ_DYNAMICNPC_CREATE_REQUEST*)RFIFOP( fd, 0 ); + + char npcname[NPC_NAME_LENGTH + 1]; + + if( strncasecmp( "GOLDPCCAFE", p->nickname, sizeof( p->nickname ) ) == 0 ){ + safestrncpy( npcname, p->nickname, sizeof( npcname ) ); + }else{ + return; + } + + struct npc_data* nd = npc_name2id( npcname ); + + if( nd == nullptr ){ + ShowError( "clif_parse_dynamic_npc: Original NPC \"%s\" was not found.\n", npcname ); + return; + } + + npc_duplicate_npc_for_player( *nd, *sd ); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ diff --git a/src/map/clif.hpp b/src/map/clif.hpp index e548eb4427..cfdeb8be8e 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -1243,4 +1243,6 @@ void clif_macro_detector_status(map_session_data &sd, e_macro_detect_status styp void clif_macro_reporter_select(map_session_data &sd, const std::vector &aid_list); void clif_macro_reporter_status(map_session_data &sd, e_macro_report_status stype); +void clif_goldpc_info( struct map_session_data& sd ); + #endif /* CLIF_HPP */ diff --git a/src/map/clif_packetdb.hpp b/src/map/clif_packetdb.hpp index e861cfbf6b..b00e816780 100644 --- a/src/map/clif_packetdb.hpp +++ b/src/map/clif_packetdb.hpp @@ -2136,6 +2136,10 @@ packet(0x09DA,-1); #endif +#if PACKETVER_MAIN_NUM >= 20140430 || PACKETVER_RE_NUM >= 20140430 || defined(PACKETVER_ZERO) + parseable_packet( HEADER_CZ_DYNAMICNPC_CREATE_REQUEST, sizeof( PACKET_CZ_DYNAMICNPC_CREATE_REQUEST ), clif_parse_dynamic_npc, 0 ); +#endif + // 2014-10-08Ragexe #if PACKETVER >= 20141008 parseable_packet(0x9FB, -1, clif_parse_pet_evolution, 2, 4); // CZ_PET_EVOLUTION diff --git a/src/map/packets.hpp b/src/map/packets.hpp index e7dc30bc71..b5a695ccd6 100644 --- a/src/map/packets.hpp +++ b/src/map/packets.hpp @@ -367,6 +367,23 @@ struct PACKET_CZ_REQ_CHANGE_MEMBERPOS{ struct PACKET_CZ_REQ_CHANGE_MEMBERPOS_sub list[]; } __attribute__((packed)); +#if PACKETVER_MAIN_NUM >= 20140508 || PACKETVER_RE_NUM >= 20140508 || defined(PACKETVER_ZERO) +struct PACKET_ZC_GOLDPCCAFE_POINT{ + int16 packetType; + int8 active; + int8 unitPoint; + int32 point; + int32 accumulatePlaySecond; +} __attribute__((packed)); +#elif PACKETVER_MAIN_NUM >= 20140430 || PACKETVER_RE_NUM >= 20140430 + // TODO: find difference (1byte) priority low... +#endif + +struct PACKET_CZ_DYNAMICNPC_CREATE_REQUEST{ + int16 packetType; + char nickname[NAME_LENGTH]; +} __attribute__((packed)); + // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute #if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 ) #pragma pack( pop ) @@ -409,6 +426,8 @@ DEFINE_PACKET_HEADER(ZC_NOTIFY_BARGAIN_SALE_SELLING, 0x9b2) DEFINE_PACKET_HEADER(ZC_NOTIFY_BARGAIN_SALE_CLOSE, 0x9b3) DEFINE_PACKET_HEADER(ZC_ACK_COUNT_BARGAIN_SALE_ITEM, 0x9c4) DEFINE_PACKET_HEADER(ZC_ACK_GUILDSTORAGE_LOG, 0x9da) +DEFINE_PACKET_HEADER(ZC_GOLDPCCAFE_POINT, 0xa15) +DEFINE_PACKET_HEADER(CZ_DYNAMICNPC_CREATE_REQUEST, 0xa16) DEFINE_PACKET_HEADER(CZ_REQ_APPLY_BARGAIN_SALE_ITEM2, 0xa3d) DEFINE_PACKET_HEADER(CZ_REQ_STYLE_CHANGE, 0xa46) DEFINE_PACKET_HEADER(ZC_STYLE_CHANGE_RES, 0xa47) diff --git a/src/map/pc.cpp b/src/map/pc.cpp index af89e5c506..4757e76618 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -1972,6 +1972,47 @@ bool pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl return true; } +TIMER_FUNC(pc_goldpc_update){ + struct map_session_data* sd = map_id2sd( id ); + + if( sd == nullptr ){ + return 0; + } + + sd->goldpc_tid = INVALID_TIMER; + + // Check if feature is still active + if( !battle_config.feature_goldpc_active ){ + return 0; + } + + // TODO: add mapflag to disable? + + int32 points = (int32)pc_readreg2( sd, GOLDPC_POINT_VAR ); + + if( points < battle_config.feature_goldpc_max_points ){ + if( battle_config.feature_goldpc_vip && pc_isvip( sd ) ){ + points += 2; + }else{ + points += 1; + } + + points = std::min( points, battle_config.feature_goldpc_max_points ); + + pc_setreg2( sd, GOLDPC_POINT_VAR, points ); + pc_setreg2( sd, GOLDPC_SECONDS_VAR, 0 ); + + if( points < battle_config.feature_goldpc_max_points ){ + sd->goldpc_tid = add_timer( gettick() + battle_config.feature_goldpc_time * 1000, pc_goldpc_update, sd->bl.id, NULL ); + } + + // Update the client + clif_goldpc_info( *sd ); + } + + return 0; +} + /*========================================== * Invoked once after the char/account/account2 registry variables are received. [Skotlex] * We didn't receive item information at this point so DO NOT attempt to do item operations here. @@ -2080,6 +2121,13 @@ void pc_reg_received(struct map_session_data *sd) clif_instance_info( *sd ); #endif + if( battle_config.feature_goldpc_active ){ + sd->goldpc_tid = add_timer( gettick() + ( battle_config.feature_goldpc_time - pc_readreg2( sd, GOLDPC_SECONDS_VAR ) ) * 1000, pc_goldpc_update, sd->bl.id, NULL ); + clif_goldpc_info( *sd ); + }else{ + sd->goldpc_tid = INVALID_TIMER; + } + // pet if (sd->status.pet_id > 0) intif_request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); @@ -15452,6 +15500,7 @@ void do_init_pc(void) { add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer"); add_timer_func_list(pc_on_expire_active, "pc_on_expire_active"); add_timer_func_list(pc_macro_detector_timeout, "pc_macro_detector_timeout"); + add_timer_func_list( pc_goldpc_update, "pc_goldpc_update" ); add_timer(gettick() + autosave_interval, pc_autosave, 0, 0); diff --git a/src/map/pc.hpp b/src/map/pc.hpp index 5f026dad65..1bb4760ab4 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -63,6 +63,8 @@ enum sc_type : int16; #define ATTENDANCE_DATE_VAR "#AttendanceDate" #define ATTENDANCE_COUNT_VAR "#AttendanceCounter" #define ACHIEVEMENTLEVEL "AchievementLevel" +#define GOLDPC_POINT_VAR "Goldpc_Points" +#define GOLDPC_SECONDS_VAR "Goldpc_Seconds" //Total number of classes (for data storage) #define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC) @@ -931,6 +933,8 @@ struct map_session_data { } captcha_upload; s_macro_detect macro_detect; + + int goldpc_tid; }; extern struct eri *pc_sc_display_ers; /// Player's SC display table