Compare commits

...

10 Commits

Author SHA1 Message Date
Aleos
1e01f6448f
Merge branch 'master' into hotfix/issue4984 2023-01-09 13:05:01 -05:00
Aleos
42eccbb1bf
Merge branch 'master' into hotfix/issue4984 2022-12-24 00:35:02 -05:00
aleos
50bd9cdfbe Fixes statuses with tick intervals
* Adds a new storage location for these tick types.
* Reverts some of the tick_total variable assignments.
* Adjusts sc_start to account for 64 bit tick values.
Thanks to @eppc0330!
2022-12-23 16:22:35 -05:00
aleos
5676a45908 Resolves compile errors 2022-12-14 15:08:50 -05:00
Aleos
1c1bb9892c
Merge branch 'master' into hotfix/issue4984 2022-12-14 14:41:56 -05:00
Aleos
c951f68250
Merge branch 'master' into hotfix/issue4984 2022-12-09 14:54:37 -05:00
aleos
9440ba8929 Minor clif fix
* tick_total is not a timer ID and thus a lookup is unnecessary.
2022-12-08 08:11:08 -05:00
aleos
9ff252d2b1 Merge branch 'master' into hotfix/issue4984 2022-12-07 13:12:16 -05:00
aleos
7a345b85cb Cleanups
Thanks to @Lemongrass3110!
2020-08-13 10:37:43 -04:00
aleos
da8d77bfc1 Corrects status icon timer after relog
* Fixes #4984.
* Resolves status icon timers not properly showing the difference of remaining/total time when a player relogs.
* Fixes status icon timers going into negative values.
Thanks to @attackjom!
2020-08-03 10:20:07 -04:00
12 changed files with 275 additions and 140 deletions

View File

@ -940,6 +940,8 @@ CREATE TABLE IF NOT EXISTS `sc_data` (
`char_id` int(11) unsigned NOT NULL, `char_id` int(11) unsigned NOT NULL,
`type` smallint(11) unsigned NOT NULL, `type` smallint(11) unsigned NOT NULL,
`tick` bigint(20) NOT NULL, `tick` bigint(20) NOT NULL,
`tick_total` bigint(20) NOT NULL,
`tick_time` bigint(20) NOT NULL,
`val1` int(11) NOT NULL default '0', `val1` int(11) NOT NULL default '0',
`val2` int(11) NOT NULL default '0', `val2` int(11) NOT NULL default '0',
`val3` int(11) NOT NULL default '0', `val3` int(11) NOT NULL default '0',

View File

@ -0,0 +1,5 @@
ALTER TABLE `sc_data` ADD COLUMN `tick_total` BIGINT(20) NOT NULL AFTER `tick`;
UPDATE `sc_data` SET `tick_total` = `tick`;
ALTER TABLE `sc_data` ADD COLUMN `tick_time` BIGINT(20) NOT NULL AFTER `tick_total`;
UPDATE `sc_data` SET `tick_time` = `tick`;

View File

@ -2385,7 +2385,7 @@ bool char_checkdb(void){
return false; return false;
} }
//checking scdata_db //checking scdata_db
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`type`,`tick`,`val1`,`val2`,`val3`,`val4`" if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`char_id`,`type`,`tick`,`tick_total`,`tick_time`,`val1`,`val2`,`val3`,`val4`"
" FROM `%s` LIMIT 1;", schema_config.scdata_db) ){ " FROM `%s` LIMIT 1;", schema_config.scdata_db) ){
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);
return false; return false;

View File

@ -284,7 +284,7 @@ int chmapif_parse_askscdata(int fd){
int aid, cid; int aid, cid;
aid = RFIFOL(fd,2); aid = RFIFOL(fd,2);
cid = RFIFOL(fd,6); cid = RFIFOL(fd,6);
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", if( SQL_ERROR == Sql_Query(sql_handle, "SELECT type, tick, val1, val2, val3, val4, tick_total, tick_time from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'",
schema_config.scdata_db, aid, cid) ) schema_config.scdata_db, aid, cid) )
{ {
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);
@ -308,6 +308,8 @@ int chmapif_parse_askscdata(int fd){
Sql_GetData(sql_handle, 3, &data, NULL); scdata.val2 = atoi(data); Sql_GetData(sql_handle, 3, &data, NULL); scdata.val2 = atoi(data);
Sql_GetData(sql_handle, 4, &data, NULL); scdata.val3 = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); scdata.val3 = atoi(data);
Sql_GetData(sql_handle, 5, &data, NULL); scdata.val4 = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); scdata.val4 = atoi(data);
Sql_GetData(sql_handle, 6, &data, NULL); scdata.tick_total = strtoll(data, nullptr, 10);
Sql_GetData(sql_handle, 7, &data, NULL); scdata.tick_time = strtoll(data, nullptr, 10);
memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &scdata, sizeof(struct status_change_data)); memcpy(WFIFOP(fd, 14+count*sizeof(struct status_change_data)), &scdata, sizeof(struct status_change_data));
} }
if (count >= 50) if (count >= 50)
@ -958,14 +960,14 @@ int chmapif_parse_save_scdata(int fd){
int i; int i;
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", schema_config.scdata_db); StringBuf_Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `tick_total`, `tick_time`, `val1`, `val2`, `val3`, `val4`) VALUES ", schema_config.scdata_db);
for( i = 0; i < count; ++i ) for( i = 0; i < count; ++i )
{ {
memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data)); memcpy (&data, RFIFOP(fd, 14+i*sizeof(struct status_change_data)), sizeof(struct status_change_data));
if( i > 0 ) if( i > 0 )
StringBuf_AppendStr(&buf, ", "); StringBuf_AppendStr(&buf, ", ");
StringBuf_Printf(&buf, "('%d','%d','%hu','%" PRtf "','%ld','%ld','%ld','%ld')", aid, cid, StringBuf_Printf(&buf, "('%d','%d','%hu','%" PRtf "','%" PRtf "','%" PRtf "','%ld','%ld','%ld','%ld')", aid, cid,
data.type, data.tick, data.val1, data.val2, data.val3, data.val4); data.type, data.tick, data.tick_total, data.tick_time, data.val1, data.val2, data.val3, data.val4);
} }
if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);

View File

@ -408,6 +408,8 @@ struct status_change_data {
unsigned short type; //SC_type unsigned short type; //SC_type
long val1, val2, val3, val4; long val1, val2, val3, val4;
t_tick tick; //Remaining duration. t_tick tick; //Remaining duration.
t_tick tick_total; // Total duration
t_tick tick_time; // Interval duration
}; };
#define MAX_BONUS_SCRIPT_LENGTH 512 #define MAX_BONUS_SCRIPT_LENGTH 512

View File

@ -1318,6 +1318,15 @@ int chrif_save_scdata(map_session_data *sd) { //parses the sc_data of the player
data.tick = 0; //Negative tick does not necessarily mean that sc has expired data.tick = 0; //Negative tick does not necessarily mean that sc has expired
} else } else
data.tick = INFINITE_TICK; //Infinite duration data.tick = INFINITE_TICK; //Infinite duration
if (sce->tick_timer != INVALID_TIMER) {
timer = get_timer(sce->tick_timer);
if (timer == nullptr || timer->func != status_change_tick_timer)
continue;
if (DIFF_TICK(timer->tick, tick) >= 0)
data.tick_time = DIFF_TICK(timer->tick, tick);
} else
data.tick_time = 0;
data.tick_total = sce->tick_total;
data.type = i; data.type = i;
data.val1 = sce->val1; data.val1 = sce->val1;
data.val2 = sce->val2; data.val2 = sce->val2;
@ -1401,7 +1410,7 @@ int chrif_load_scdata(int fd) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
struct status_change_data *data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data)); struct status_change_data *data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data));
status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_LOADED|SCSTART_NORATEDEF); status_change_start_sub(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, data->tick_total, data->tick_time, SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_LOADED|SCSTART_NORATEDEF);
} }
pc_scdata_received(sd); pc_scdata_received(sd);

View File

@ -6464,87 +6464,26 @@ void clif_cooking_list( map_session_data *sd, int trigger, uint16 skill_id, int
/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)] /// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs] /// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
/// 0983 <index>.W <id>.L <state>.B <total msec>.L <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE3) (PACKETVER >= 20120618) /// 0983 <index>.W <id>.L <state>.B <total msec>.L <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE3) (PACKETVER >= 20120618)
/// @param bl Sends packet to clients around this object /// @param bl: Sends packet to clients around this object
/// @param id ID of object that has this effect /// @param id: ID of object that has this effect
/// @param type Status icon see enum efst_type /// @param type: Status icon (see efst_types)
/// @param flag 1:Active, 0:Deactive /// @param flag: 1:Active, 0:Inactive
/// @param tick Duration in ms /// @param tick_total: Total duration in ms
/// @param val1 /// @param tick: Remaining duration in ms
/// @param val2 /// @param val1: Value 1
/// @param val3 /// @param val2: Value 2
void clif_status_change_sub(struct block_list *bl, int id, int type, int flag, t_tick tick, int val1, int val2, int val3, enum send_target target_type) /// @param val3: Value 3
void clif_status_change_sub(struct block_list *bl, int id, int type, int flag, t_tick tick_total, t_tick tick, int val1, int val2, int val3)
{ {
unsigned char buf[32];
if (type == EFST_BLANK) //It shows nothing on the client...
return;
if (type == EFST_POSTDELAY && tick == 0)
return;
nullpo_retv(bl); nullpo_retv(bl);
// Statuses with an infinite duration, but still needs a duration sent to display properly.
if (type == EFST_LUNARSTANCE || type == EFST_UNIVERSESTANCE || type == EFST_SUNSTANCE || type == EFST_STARSTANCE)
tick = 200;
#if PACKETVER >= 20120618
if (flag && battle_config.display_status_timers)
WBUFW(buf,0) = 0x983;
else
#elif PACKETVER >= 20090121
if (flag && battle_config.display_status_timers)
WBUFW(buf,0) = 0x43f;
else
#endif
WBUFW(buf,0) = 0x196;
WBUFW(buf,2) = type;
WBUFL(buf,4) = id;
WBUFB(buf,8) = flag;
#if PACKETVER >= 20120618
if (flag && battle_config.display_status_timers) {
if (tick <= 0)
tick = 9999; // this is indeed what official servers do
WBUFL(buf,9) = client_tick(tick);/* at this stage remain and total are the same value I believe */
WBUFL(buf,13) = client_tick(tick);
WBUFL(buf,17) = val1;
WBUFL(buf,21) = val2;
WBUFL(buf,25) = val3;
}
#elif PACKETVER >= 20090121
if (flag && battle_config.display_status_timers) {
if (tick <= 0)
tick = 9999; // this is indeed what official servers do
WBUFL(buf,9) = client_tick(tick);
WBUFL(buf,13) = val1;
WBUFL(buf,17) = val2;
WBUFL(buf,21) = val3;
}
#endif
clif_send(buf, packet_len(WBUFW(buf,0)), bl, target_type);
}
/* Sends status effect to clients around the bl
* @param bl Object that has the effect
* @param type Status icon see enum efst_type
* @param flag 1:Active, 0:Deactive
* @param tick Duration in ms
* @param val1
* @param val2
* @param val3
*/
void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick, int val1, int val2, int val3) {
map_session_data *sd = NULL;
if (type == EFST_BLANK) //It shows nothing on the client... if (type == EFST_BLANK) //It shows nothing on the client...
return; return;
if (type == EFST_POSTDELAY && tick == 0) if (type == EFST_POSTDELAY && tick == 0)
return; return;
if (type == EFST_ILLUSION && !battle_config.display_hallucination) // Disable Hallucination. if (!(status_efst_get_bl_type((efst_type)type) & bl->type)) // only send status changes that actually matter to the client
return; return;
#if !( PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 ) #if !( PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 )
@ -6553,14 +6492,56 @@ void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick,
type = EFST_RIDING; type = EFST_RIDING;
#endif #endif
nullpo_retv(bl); packet_status_change p = { 0 };
map_session_data *sd = BL_CAST(BL_PC, bl);
sd = BL_CAST(BL_PC, bl); if (battle_config.display_status_timers > 0) {
// Statuses with an infinite duration, but still needs a duration sent to display properly.
switch (type) {
case EFST_LUNARSTANCE:
case EFST_UNIVERSESTANCE:
case EFST_SUNSTANCE:
case EFST_STARSTANCE:
tick = 200;
break;
default:
if (tick < 0)
tick = 9999; // this is indeed what official servers do
break;
}
}
if (!(status_efst_get_bl_type((efst_type)type)&bl->type)) // only send status changes that actually matter to the client p.PacketType = status_changeType;
return; p.index = type;
p.AID = id;
p.state = (uint8)flag;
clif_status_change_sub(bl, bl->id, type, flag, tick, val1, val2, val3, ((sd ? (pc_isinvisible(sd) ? SELF : AREA) : AREA_WOS))); #if PACKETVER >= 20090121
if (battle_config.display_status_timers > 0) {
#if PACKETVER >= 20120618
p.Total = client_tick(tick);
#endif
p.Left = client_tick(tick_total);
p.val1 = val1;
p.val2 = val2;
p.val3 = val3;
}
#endif
clif_send(&p, sizeof(p), bl, pc_isinvisible(sd) ? SELF : AREA);
}
/* Sends status effect to clients around the bl
* @param bl: Object that has the effect
* @param type: Status icon (see efst_types)
* @param flag: 1:Active, 0:Inactive
* @param tick_total: Total duration in ms
* @param val1: Value 1
* @param val2: Value 2
* @param val3: Value 3
*/
void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick_total, int val1, int val2, int val3) {
clif_status_change_sub(bl, bl->id, type, flag, tick_total, tick_total, val1, val2, val3);
} }
/** /**
@ -6570,13 +6551,12 @@ void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick,
* @param target: Client send type * @param target: Client send type
*/ */
void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl, enum send_target target) { void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl, enum send_target target) {
unsigned char i; nullpo_retv(bl);
struct sc_display_entry **sc_display; struct sc_display_entry **sc_display;
unsigned char sc_display_count; unsigned char sc_display_count;
bool spheres_sent; bool spheres_sent;
nullpo_retv(bl);
switch( bl->type ){ switch( bl->type ){
case BL_PC: { case BL_PC: {
map_session_data* sd = (map_session_data*)bl; map_session_data* sd = (map_session_data*)bl;
@ -6598,14 +6578,14 @@ void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl,
return; return;
} }
for (i = 0; i < sc_display_count; i++) { for (unsigned char i = 0; i < sc_display_count; i++) {
enum sc_type type = sc_display[i]->type; enum sc_type type = sc_display[i]->type;
status_change *sc = status_get_sc(bl); status_change *sc = status_get_sc(bl);
const struct TimerData *td = (sc && sc->getSCE(type) ? get_timer(sc->getSCE(type)->timer) : NULL); const TimerData *td = (sc && sc->getSCE(type) ? get_timer(sc->getSCE(type)->timer) : nullptr);
t_tick tick = 0; t_tick tick = 0, cur_tick = gettick();
if (td) if (td != nullptr)
tick = DIFF_TICK(td->tick, gettick()); tick = DIFF_TICK(td->tick, cur_tick);
// Status changes that need special handling // Status changes that need special handling
switch( type ){ switch( type ){
@ -6626,9 +6606,9 @@ void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl,
} }
#if PACKETVER > 20120418 #if PACKETVER > 20120418
clif_efst_status_change(tbl, bl->id, target, status_db.getIcon(type), tick, sc_display[i]->val1, sc_display[i]->val2, sc_display[i]->val3); clif_efst_status_change(tbl, bl->id, target, status_db.getIcon(type), sc->getSCE(type)->tick_total, tick, sc_display[i]->val1, sc_display[i]->val2, sc_display[i]->val3);
#else #else
clif_status_change_sub(tbl, bl->id, status_db.getIcon(type), 1, tick, sc_display[i]->val1, sc_display[i]->val2, sc_display[i]->val3, target); clif_status_change_sub(tbl, bl->id, status_db.getIcon(type), 1, tick, tick sc_display[i]->val1, sc_display[i]->val2, sc_display[i]->val3, target);
#endif #endif
} }
} }
@ -6636,38 +6616,30 @@ void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl,
/// Notifies the client when a player enters the screen with an active EFST. /// Notifies the client when a player enters the screen with an active EFST.
/// 08ff <id>.L <index>.W <remain msec>.L { <val>.L }*3 (ZC_EFST_SET_ENTER) (PACKETVER >= 20111108) /// 08ff <id>.L <index>.W <remain msec>.L { <val>.L }*3 (ZC_EFST_SET_ENTER) (PACKETVER >= 20111108)
/// 0984 <id>.L <index>.W <total msec>.L <remain msec>.L { <val>.L }*3 (ZC_EFST_SET_ENTER2) (PACKETVER >= 20120618) /// 0984 <id>.L <index>.W <total msec>.L <remain msec>.L { <val>.L }*3 (ZC_EFST_SET_ENTER2) (PACKETVER >= 20120618)
void clif_efst_status_change(struct block_list *bl, int tid, enum send_target target, int type, t_tick tick, int val1, int val2, int val3) { void clif_efst_status_change(struct block_list *bl, int tid, enum send_target target, int type, t_tick tick_total, t_tick tick, int val1, int val2, int val3) {
#if PACKETVER >= 20111108 #if PACKETVER >= 20111108
unsigned char buf[32]; nullpo_retv(bl);
#if PACKETVER >= 20120618
const int cmd = 0x984;
#elif PACKETVER >= 20111108
const int cmd = 0x8ff;
#endif
int offset = 0;
if (type == EFST_BLANK) if (type == EFST_BLANK)
return; return;
nullpo_retv(bl); if (tick < 0)
if (tick <= 0)
tick = 9999; tick = 9999;
WBUFW(buf,offset + 0) = cmd; PACKET_EFST_SET_ENTER p = { 0 };
WBUFL(buf,offset + 2) = tid;
WBUFW(buf,offset + 6) = type; p.PacketType = HEADER_ZC_EFST_SET_ENTER;
#if PACKETVER >= 20111108 p.GID = tid;
WBUFL(buf,offset + 8) = client_tick(tick); // Set remaining status duration [exneval] p.type = type;
p.remaining = client_tick(tick);
#if PACKETVER >= 20120618 #if PACKETVER >= 20120618
WBUFL(buf,offset + 12) = client_tick(tick); p.total = client_tick(tick_total);
offset += 4;
#endif #endif
WBUFL(buf,offset + 12) = val1; p.val1 = val1;
WBUFL(buf,offset + 16) = val2; p.val2 = val2;
WBUFL(buf,offset + 20) = val3; p.val3 = val3;
#endif
clif_send(buf,packet_len(cmd),bl,target); clif_send(&p, sizeof(p), bl, target);
#endif #endif
} }

View File

@ -771,8 +771,9 @@ void clif_bladestop(struct block_list *src, int dst_id, int active);
void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target); void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target);
#define clif_status_load(bl, type, flag) clif_status_change((bl), (type), (flag), 0, 0, 0, 0) #define clif_status_load(bl, type, flag) clif_status_change((bl), (type), (flag), 0, 0, 0, 0)
void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick, int val1, int val2, int val3); void clif_status_change(struct block_list *bl, int type, int flag, t_tick tick_total, int val1, int val2, int val3);
void clif_efst_status_change(struct block_list *bl, int tid, enum send_target target, int type, t_tick tick, int val1, int val2, int val3); void clif_status_change_sub(struct block_list *bl, int id, int type, int flag, t_tick tick_total, t_tick tick, int val1, int val2, int val3);
void clif_efst_status_change(struct block_list *bl, int tid, enum send_target target, int type, t_tick tick_total, t_tick tick, int val1, int val2, int val3);
void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl, enum send_target target); void clif_efst_status_change_sub(struct block_list *tbl, struct block_list *bl, enum send_target target);
void clif_wis_message(map_session_data* sd, const char* nick, const char* mes, int mes_len, int gmlvl); void clif_wis_message(map_session_data* sd, const char* nick, const char* mes, int mes_len, int gmlvl);

View File

@ -475,6 +475,19 @@ struct PACKET_ZC_ACK_SE_CASH_ITEM_LIST2{
struct PACKET_ZC_ACK_SE_CASH_ITEM_LIST2_sub items[]; struct PACKET_ZC_ACK_SE_CASH_ITEM_LIST2_sub items[];
} __attribute__((packed)); } __attribute__((packed));
struct PACKET_EFST_SET_ENTER {
int16 PacketType;
uint32 GID;
uint16 type;
uint32 remaining;
#if PACKETVER >= 20120618
uint32 total;
#endif
int32 val1;
int32 val2;
int32 val3;
};
// NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 ) #if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 )
#pragma pack( pop ) #pragma pack( pop )
@ -515,7 +528,13 @@ DEFINE_PACKET_HEADER(CZ_REQ_SE_CASH_TAB_CODE, 0x846)
DEFINE_PACKET_HEADER(ZC_ACK_SE_CASH_ITEM_LIST2, 0x8c0) DEFINE_PACKET_HEADER(ZC_ACK_SE_CASH_ITEM_LIST2, 0x8c0)
DEFINE_PACKET_HEADER(ZC_ACK_SCHEDULER_CASHITEM, 0x8ca) DEFINE_PACKET_HEADER(ZC_ACK_SCHEDULER_CASHITEM, 0x8ca)
DEFINE_PACKET_HEADER(ZC_CLEAR_DIALOG, 0x8d6) DEFINE_PACKET_HEADER(ZC_CLEAR_DIALOG, 0x8d6)
#if PACKETVER >= 20111108 && PACKETVER < 20120618
DEFINE_PACKET_HEADER(ZC_EFST_SET_ENTER, 0x8ff)
#endif
DEFINE_PACKET_HEADER(ZC_ENTRY_QUEUE_INIT, 0x90e); DEFINE_PACKET_HEADER(ZC_ENTRY_QUEUE_INIT, 0x90e);
#if PACKETVER >= 20120618
DEFINE_PACKET_HEADER(ZC_EFST_SET_ENTER, 0x984)
#endif
DEFINE_PACKET_HEADER(ZC_BANKING_CHECK, 0x9a6) DEFINE_PACKET_HEADER(ZC_BANKING_CHECK, 0x9a6)
DEFINE_PACKET_HEADER(ZC_ACK_BANKING_DEPOSIT, 0x9a8) DEFINE_PACKET_HEADER(ZC_ACK_BANKING_DEPOSIT, 0x9a8)
DEFINE_PACKET_HEADER(ZC_ACK_BANKING_WITHDRAW, 0x9aa) DEFINE_PACKET_HEADER(ZC_ACK_BANKING_WITHDRAW, 0x9aa)

View File

@ -12246,7 +12246,8 @@ BUILDIN_FUNC(sc_start)
TBL_NPC * nd = map_id2nd(st->oid); TBL_NPC * nd = map_id2nd(st->oid);
struct block_list* bl; struct block_list* bl;
enum sc_type type; enum sc_type type;
int tick, val1, val2, val3, val4=0, rate, flag; int val1, val2, val3, val4=0, rate, flag;
t_tick tick;
char start_type; char start_type;
const char* command = script_getfuncname(st); const char* command = script_getfuncname(st);
@ -12258,7 +12259,7 @@ BUILDIN_FUNC(sc_start)
start_type = 1; start_type = 1;
type = (sc_type)script_getnum(st,2); type = (sc_type)script_getnum(st,2);
tick = script_getnum(st,3); tick = script_getnum64(st,3);
val1 = script_getnum(st,4); val1 = script_getnum(st,4);
//If from NPC we make default flag 1 to be unavoidable //If from NPC we make default flag 1 to be unavoidable

View File

@ -9846,19 +9846,22 @@ void status_display_remove(struct block_list *bl, enum sc_type type) {
} }
/** /**
* Applies SC defense to a given status change * Applies a Status Change for a given amount of time
* This function also determines whether or not the status change will be applied
* @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC] * @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
* @param bl: Target of the status change (See: enum sc_type) * @param bl: Target of the status change (See: enum sc_type)
* @param type: Status change (SC_*) * @param type: Status change (SC_*)
* @param rate: Initial percentage rate of affecting bl (0~10000) * @param rate: Initial percentage rate of affecting bl (0~10000)
* @param val1~4: Depends on type of status change * @param val1: Depends on type of status change
* @param duration: Initial duration that the status change affects bl * @param val2: Depends on type of status change
* @param val3: Depends on type of status change
* @param val4: Depends on type of status change
* @param duration: Remaining duration
* @param duration_total: Initial duration
* @param flag: Value which determines what parts to calculate. See e_status_change_start_flags * @param flag: Value which determines what parts to calculate. See e_status_change_start_flags
* @param delay: Delay in milliseconds before the SC is applied * @param delay: Delay in milliseconds before the SC is applied
* @return adjusted duration based on flag values * @return adjusted duration based on flag values
*/ */
int status_change_start(struct block_list* src, struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,t_tick duration,unsigned char flag, int32 delay) { int status_change_start_sub(struct block_list* src, struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,t_tick duration,t_tick duration_total,t_tick duration_tick,unsigned char flag, int32 delay) {
map_session_data *sd = NULL; map_session_data *sd = NULL;
status_change* sc; status_change* sc;
struct status_change_entry* sce; struct status_change_entry* sce;
@ -9932,14 +9935,14 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
} }
} }
// Adjust tick according to status resistances // Adjust duration_total according to status resistances
if( !(flag&(SCSTART_NOAVOID|SCSTART_LOADED)) ) { if( !(flag&(SCSTART_NOAVOID|SCSTART_LOADED)) ) {
duration = status_get_sc_def(src, bl, type, rate, duration, flag); duration_total = status_get_sc_def(src, bl, type, rate, duration_total, flag);
if( !duration ) if( duration_total == 0 )
return 0; return 0;
} }
int tick = (int)duration; int tick = static_cast<int>(duration_total);
sd = BL_CAST(BL_PC, bl); sd = BL_CAST(BL_PC, bl);
vd = status_get_viewdata(bl); vd = status_get_viewdata(bl);
@ -12758,6 +12761,33 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
calc_flag.reset(SCB_BODY); calc_flag.reset(SCB_BODY);
}*/ }*/
t_tick totaltick, subtick, subticktime = (intptr_t)nullptr;
bool has_tick_interval = false;
//if (tick_time)
// tick += 1;
if (duration_total > INT_MAX)
totaltick = duration_total;
else
totaltick = tick;
if (!(flag & SCSTART_LOADED)) {
subtick = totaltick; // When starting a new SC (not loading), its remaining duration is the same as the total
if (tick_time) {
subticktime = tick_time;
has_tick_interval = true;
}
} else {
subtick = duration;
if (duration_tick > 0) {
subticktime = duration_tick;
has_tick_interval = true;
}
}
if (!(flag&SCSTART_NOICON) && !(flag&SCSTART_LOADED && scdb->flag[SCF_DISPLAYPC] || scdb->flag[SCF_DISPLAYNPC])) { if (!(flag&SCSTART_NOICON) && !(flag&SCSTART_LOADED && scdb->flag[SCF_DISPLAYPC] || scdb->flag[SCF_DISPLAYNPC])) {
int status_icon = scdb->icon; int status_icon = scdb->icon;
@ -12766,15 +12796,15 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
status_icon = EFST_ATTACK_PROPERTY_NOTHING + val1; // Assign status icon for older clients status_icon = EFST_ATTACK_PROPERTY_NOTHING + val1; // Assign status icon for older clients
#endif #endif
clif_status_change(bl, status_icon, 1, tick, scdb->flag[SCF_SENDVAL1] ? val1 : 1, scdb->flag[SCF_SENDVAL2] ? val2 : 0, scdb->flag[SCF_SENDVAL3] ? val3 : 0); if (sc->getSCE(type))
clif_status_change(bl, status_icon, 0, 0, 0, 0, 0);
clif_status_change_sub(bl, bl->id, status_icon, 1, totaltick, subtick, scdb->flag[SCF_SENDVAL1] ? val1 : 1, scdb->flag[SCF_SENDVAL2] ? val2 : 0, scdb->flag[SCF_SENDVAL3] ? val3 : 0);
} }
// Used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first.
if( tick_time )
tick = tick_time;
// Don't trust the previous sce assignment, in case the SC ended somewhere between there and here. // Don't trust the previous sce assignment, in case the SC ended somewhere between there and here.
if((sce=sc->getSCE(type))) { // reuse old sc if((sce=sc->getSCE(type))) { // reuse old sc
if (has_tick_interval && sce->tick_timer != INVALID_TIMER)
delete_timer(sce->tick_timer, status_change_tick_timer);
if( sce->timer != INVALID_TIMER ) if( sce->timer != INVALID_TIMER )
delete_timer(sce->timer, status_change_timer); delete_timer(sce->timer, status_change_timer);
sc_isnew = false; sc_isnew = false;
@ -12786,10 +12816,15 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
sce->val2 = val2; sce->val2 = val2;
sce->val3 = val3; sce->val3 = val3;
sce->val4 = val4; sce->val4 = val4;
if (tick >= 0) if (subtick >= 0)
sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type); sce->timer = add_timer(gettick() + subtick, status_change_timer, bl->id, type);
else else
sce->timer = INVALID_TIMER; // Infinite duration sce->timer = INVALID_TIMER; // Infinite duration
if (has_tick_interval && subticktime >= 0)
sce->tick_timer = add_timer(gettick() + subticktime, status_change_tick_timer, bl->id, type);
else
sce->tick_timer = INVALID_TIMER; // Infinite duration
sce->tick_total = totaltick;
if (calc_flag.any()) { if (calc_flag.any()) {
if (sd != nullptr) { if (sd != nullptr) {
@ -12905,6 +12940,24 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
return 1; return 1;
} }
/**
* Applies a Status Change for its full time
* @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
* @param bl: Target of the status change (See: enum sc_type)
* @param type: Status change (SC_*)
* @param rate: Initial percentage rate of affecting bl (0~10000)
* @param val1: Depends on type of status change
* @param val2: Depends on type of status change
* @param val3: Depends on type of status change
* @param val4: Depends on type of status change
* @param tick: Remaining duration
* @param flag: Value which determines what parts to calculate. See e_status_change_start_flags
* @return adjusted duration based on flag values
*/
int status_change_start(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, t_tick tick, unsigned char flag, int32 delay) {
return status_change_start_sub(src, bl, type, rate, val1, val2, val3, val4, 0, tick, flag, delay);
}
/** /**
* End all statuses except those listed * End all statuses except those listed
* TODO: May be useful for dispel instead resetting a list there * TODO: May be useful for dispel instead resetting a list there
@ -12958,6 +13011,8 @@ int status_change_clear(struct block_list* bl, int type)
status_change_end(bl, status); status_change_end(bl, status);
if( type == 1 && sc->getSCE(status) ) { // If for some reason status_change_end decides to still keep the status when quitting. [Skotlex] if( type == 1 && sc->getSCE(status) ) { // If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
(sc->count)--; (sc->count)--;
if (sc->getSCE(status)->tick_timer != INVALID_TIMER)
delete_timer(sc->getSCE(status)->tick_timer, status_change_tick_timer);
if (sc->getSCE(status)->timer != INVALID_TIMER) if (sc->getSCE(status)->timer != INVALID_TIMER)
delete_timer(sc->getSCE(status)->timer, status_change_timer); delete_timer(sc->getSCE(status)->timer, status_change_timer);
sc->deleteSCE(status); sc->deleteSCE(status);
@ -13044,6 +13099,8 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
if (!status_isdead(bl) && (sce->val2 || sce->val3 || sce->val4)) if (!status_isdead(bl) && (sce->val2 || sce->val3 || sce->val4))
return 0; //Don't end the status change yet as there are still unit groups associated with it return 0; //Don't end the status change yet as there are still unit groups associated with it
} }
if (sce->tick_timer != INVALID_TIMER)
delete_timer(sce->tick_timer, status_change_tick_timer);
if (sce->timer != INVALID_TIMER) // Could be a SC with infinite duration if (sce->timer != INVALID_TIMER) // Could be a SC with infinite duration
delete_timer(sce->timer,status_change_timer); delete_timer(sce->timer,status_change_timer);
} }
@ -13691,6 +13748,67 @@ TIMER_FUNC(status_change_timer){
sce->timer = add_timer(t, status_change_timer, bl->id, data); sce->timer = add_timer(t, status_change_timer, bl->id, data);
}; };
// If status has an interval and there is at least 100ms remaining time, wait for next interval
if (interval > 0 && sc->getSCE(type) && sce->val4 >= 100) {
sc_timer_next(min(sce->val4, interval) + tick);
sce->val4 -= interval;
if (dounlock)
map_freeblock_unlock();
return 0;
}
if (dounlock)
map_freeblock_unlock();
// Default for all non-handled control paths is to end the status
return status_change_end(bl, type, tid);
}
/**
* Resets timers for statuses that have an interval
* Used with reoccurring status effects, such as dropping SP every 5 seconds
* @param tid: Timer ID
* @param tick: How long before next call
* @param id: ID of character
* @param data: Information passed through the timer call
* @return 1: Success 0: Fail
*/
TIMER_FUNC(status_change_tick_timer) {
block_list *bl = map_id2bl(id);
if (!bl) {
ShowDebug("status_change_tick_timer: Null pointer id: %d data: %" PRIdPTR "\n", id, data);
return 0;
}
status_change *const sc = status_get_sc(bl);
status_data *const status = status_get_status_data(bl);
if (!sc) {
ShowDebug("status_change_tick_timer: Null pointer id: %d data: %" PRIdPTR " bl-type: %d\n", id, data, bl->type);
return 0;
}
sc_type type = (sc_type)data;
status_change_entry *const sce = sc->getSCE(type);
if (!sce) {
ShowDebug("status_change_tick_timer: Null pointer id: %d data: %" PRIdPTR " bl-type: %d\n", id, data, bl->type);
return 0;
}
if (sce->tick_timer != tid) {
ShowError("status_change_tick_timer: Mismatch for type %d: %d != %d (bl id %d)\n", type, tid, sce->tick_timer, bl->id);
return 0;
}
int interval = status_get_sc_interval(type);
bool dounlock = false;
map_session_data *sd = BL_CAST(BL_PC, bl);
std::function<void(t_tick)> sc_timer_next = [&sce, &bl, &data](t_tick t) {
sce->tick_timer = add_timer(t, status_change_tick_timer, bl->id, data);
};
switch(type) { switch(type) {
case SC_MAXIMIZEPOWER: case SC_MAXIMIZEPOWER:
case SC_CLOAKING: case SC_CLOAKING:
@ -13887,7 +14005,7 @@ TIMER_FUNC(status_change_timer){
bl->m == sd->feel_map[1].m || bl->m == sd->feel_map[1].m ||
bl->m == sd->feel_map[2].m) bl->m == sd->feel_map[2].m)
{ // Timeout will be handled by pc_setpos { // Timeout will be handled by pc_setpos
sce->timer = INVALID_TIMER; sce->tick_timer = INVALID_TIMER;
return 0; return 0;
} }
break; break;
@ -14698,8 +14816,7 @@ TIMER_FUNC(status_change_timer){
if (dounlock) if (dounlock)
map_freeblock_unlock(); map_freeblock_unlock();
// Default for all non-handled control paths is to end the status return 1;
return status_change_end( bl,type,tid );
} }
/** /**
@ -15869,6 +15986,7 @@ void do_init_status(void) {
memset(SCDisabled, 0, sizeof(SCDisabled)); memset(SCDisabled, 0, sizeof(SCDisabled));
add_timer_func_list(status_change_timer,"status_change_timer"); add_timer_func_list(status_change_timer,"status_change_timer");
add_timer_func_list(status_change_tick_timer, "status_change_tick_timer");
add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer"); add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
add_timer_func_list(status_clear_lastEffect_timer, "status_clear_lastEffect_timer"); add_timer_func_list(status_clear_lastEffect_timer, "status_clear_lastEffect_timer");
initDummyData(); initDummyData();

View File

@ -3200,6 +3200,8 @@ struct sc_display_entry {
struct status_change_entry { struct status_change_entry {
int timer; int timer;
int val1,val2,val3,val4; int val1,val2,val3,val4;
int tick_timer; // Timer for the next interval based timer execution
t_tick tick_total; // Total amount of time the status is active for
}; };
///Status change ///Status change
@ -3383,6 +3385,7 @@ int status_isimmune(struct block_list *bl);
t_tick status_get_sc_def(struct block_list *src,struct block_list *bl, enum sc_type type, int rate, t_tick tick, unsigned char flag); t_tick status_get_sc_def(struct block_list *src,struct block_list *bl, enum sc_type type, int rate, t_tick tick, unsigned char flag);
int status_change_start(struct block_list* src, struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,t_tick duration,unsigned char flag, int32 delay = 0); int status_change_start(struct block_list* src, struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,t_tick duration,unsigned char flag, int32 delay = 0);
int status_change_start_sub(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, t_tick duration, t_tick duration_total, t_tick duration_tick, unsigned char flag, int32 delay = 0);
//Short version, receives rate in 1->100 range, and does not uses a flag setting. //Short version, receives rate in 1->100 range, and does not uses a flag setting.
static int sc_start(block_list *src, block_list *bl, sc_type type, int32 rate, int32 val1, t_tick duration, int32 delay = 0) { static int sc_start(block_list *src, block_list *bl, sc_type type, int32 rate, int32 val1, t_tick duration, int32 delay = 0) {
return status_change_start(src, bl, type, 100 * rate, val1, 0, 0, 0, duration, SCSTART_NONE, delay); return status_change_start(src, bl, type, 100 * rate, val1, 0, 0, 0, duration, SCSTART_NONE, delay);
@ -3395,6 +3398,7 @@ static int sc_start4(block_list *src, block_list *bl, sc_type type, int32 rate,
} }
int status_change_end(struct block_list* bl, enum sc_type type, int tid = INVALID_TIMER); int status_change_end(struct block_list* bl, enum sc_type type, int tid = INVALID_TIMER);
TIMER_FUNC(status_change_timer); TIMER_FUNC(status_change_timer);
TIMER_FUNC(status_change_tick_timer);
int status_change_timer_sub(struct block_list* bl, va_list ap); int status_change_timer_sub(struct block_list* bl, va_list ap);
int status_change_clear(struct block_list* bl, int type); int status_change_clear(struct block_list* bl, int type);
void status_change_clear_buffs(struct block_list* bl, uint8 type); void status_change_clear_buffs(struct block_list* bl, uint8 type);