Merge pull request #263 from rathena/cleanup/bonus_script
Bonus Script clean up * Removed fixed array size (MAX_PC_BONUS_SCRIPT) of bonus_script on players, changed to link list. * Changed fixed script string on player's bonus_script to StringBuf, reduce memory usage for each connected player. * Added new flags to expand duplicated script and flag to allow duplicate script. * Reduced buf length from 1024 to 512 on bonus_script_data (used for saving/loading bonus_script from char-server). Still, MAX_PC_BONUS_SCRIPT is used to gives limit data can be loaded. * Merged 'pc_bonus_script_clear_all' to 'pc_bonus_script_clear' * Now bonus script will be saved on auto-save timer or other save request, prevent losing the bonus when "something" happen. But, saving when player is quitting will removes the bonus that won't be saved on logout. * Fixed Superstar_Snack (22843) and Sealed_Dracula_Card (22846) fields NOTE: Please import 'upgrade_20150131.sql' for `bonus_script` table changes Signed-off-by: Cydh Ramdh <house.bad@gmail.com>
This commit is contained in:
commit
bf6d2f0197
@ -4643,6 +4643,8 @@ BSF_REM_ON_LUXANIMA 0x040
|
||||
BSF_REM_ON_MADOGEAR 0x080
|
||||
BSF_REM_ON_DAMAGED 0x100
|
||||
BSF_PERMANENT 0x200
|
||||
BSF_FORCE_REPLACE 0x400
|
||||
BSF_FORCE_DUPLICATE 0x800
|
||||
|
||||
false 0
|
||||
true 1
|
||||
|
@ -9878,10 +9878,10 @@
|
||||
22837,Integer_Time,Integer Time,2,10,,100,,,,,,,,,,,,,{ getitem 673,5; },{},{}
|
||||
22838,Something_Present_Candy_Holders,Something Present Candy Holders,2,10,,100,,,,,,,,,,,,,{},{},{}
|
||||
22842,Sealed_Dracula_Scroll_II,Sealed Dracula Scroll II,2,10,,10,,,,,,,,,,,,,{ getitem callfunc("F_Rand",22846,6228,6232,24223,24227,17474/*, Enchant Letter*/),1; },{},{}
|
||||
22843,Superstar_Snack,Superstar Snack,2,10,,10,,,,,,,,,,,,,{ bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300; },{},{}
|
||||
22843,Superstar_Snack,Superstar Snack,2,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300; },{},{}
|
||||
22844,Sealed_Dracula_Card_Album,Sealed Dracula Card Album,2,10,,10,,,,,,,,,,,,,{/*No Info*/},{},{}
|
||||
22845,Sealed_Fortune_Egg,Sealed Fortune Egg,2,10,,10,,,,,,,,,,,,,{ getitem callfunc("F_Rand",4488,4497,4486,4480,4485,4539,4487,4494,4538,4489,4490,4482,4503,22846),1; },{},{}
|
||||
22846,Sealed_Dracula_Card,Sealed Dracula Card,2,10,,10,,,,,,,,,,,,,{ bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300; },{},{}
|
||||
22846,Sealed_Dracula_Card,Sealed Dracula Card,2,10,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300; },{},{}
|
||||
// Shadow Equip
|
||||
24000,T_STR1_Armor_Shadow,T STR1 Armor Shadow,12,10,,0,,,,0,0xFFFFFFFF,63,2,65536,,1,,,{ bonus bStr,1; },{},{}
|
||||
24001,T_DEX1_Weapon_Shadow,T DEX1 Weapon Shadow,12,10,,0,,,,0,0xFFFFFFFF,63,2,131072,,1,,,{ bonus bDex,1; },{},{}
|
||||
|
@ -2491,9 +2491,9 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
|
||||
0x2b2f
|
||||
Type: AZ
|
||||
Structure: <cmd>.W <len>.W <cid>.L <count>.W
|
||||
Structure: <cmd>.W <len>.W <cid>.L <count>.B { <bonus_script_data>.?B }
|
||||
index: 0,2,4,8
|
||||
len: variable: 10+50*bonus_script_data
|
||||
len: variable: 9+count*bonus_script_data
|
||||
parameter:
|
||||
- cmd : packet identification (0x2b2f)
|
||||
desc:
|
||||
@ -2840,9 +2840,9 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
|
||||
0x2b2e
|
||||
Type: ZA
|
||||
Structure: <cmd>.W <len>.W <char_id>.L <count>.W
|
||||
Structure: <cmd>.W <len>.W <char_id>.L <count>.B { <bonus_script_data>.?B }
|
||||
index: 0,2,4,8
|
||||
len: variable: 10+MAX_PC_BONUS_SCRIPT*bonus_script_data
|
||||
len: variable: 9+count*bonus_script_data
|
||||
parameter:
|
||||
- cmd : packet identification (0x2b2e)
|
||||
- len
|
||||
|
@ -5350,9 +5350,6 @@ After that time, the script will automatically expire. The same bonus cannot be
|
||||
stacked. By default, this bonus will be stored on `bonus_script` table when player
|
||||
logs out.
|
||||
|
||||
Note that the maximum number of 'bonus_script' commands that can run simultaneously
|
||||
for a player is 10 (MAX_PC_BONUS_SCRIPT in 'src/map/pc.h').
|
||||
|
||||
Flags (bitmask):
|
||||
1 : Remove when dead.
|
||||
2 : Removable by Dispell.
|
||||
@ -5360,10 +5357,13 @@ Flags (bitmask):
|
||||
8 : Remove when player logs out.
|
||||
16 : Removeable by Banishing Buster.
|
||||
32 : Removable by Refresh.
|
||||
128 : Removable by Luxanima.
|
||||
256 : Remove when Madogear is activated or deactivated.
|
||||
512 : Remove when receive damage.
|
||||
1024: Script is permanent, cannot be cleared by bonus_script_clear
|
||||
64 : Removable by Luxanima.
|
||||
128 : Remove when Madogear is activated or deactivated.
|
||||
256 : Remove when receive damage.
|
||||
512 : Script is permanent, cannot be cleared by bonus_script_clear.
|
||||
1024: Force to replace duplicated script by expanding the duration.
|
||||
2048: Force to add duplicated script. This flag cannot be stacked with 1024,
|
||||
if both are defined, 1024 will be checked first and ignore this flag.
|
||||
|
||||
Types:
|
||||
This will be used to decide negative or positive buff for 'debuff_on_logout'.
|
||||
|
@ -9909,10 +9909,10 @@ REPLACE INTO `item_db_re` VALUES (22829,'Sealed_Card_Album','Sealed Card Album',
|
||||
REPLACE INTO `item_db_re` VALUES (22837,'Integer_Time','Integer Time',2,10,NULL,100,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem 673,5;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22838,'Something_Present_Candy_Holders','Something Present Candy Holders',2,10,NULL,100,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22842,'Sealed_Dracula_Scroll_II','Sealed Dracula Scroll II',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem callfunc("F_Rand",22846,6228,6232,24223,24227,17474/*, Enchant Letter*/),1;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22843,'Superstar_Snack','Superstar Snack',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22843,'Superstar_Snack','Superstar Snack',2,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus bAtk2,50; bonus bMatk,50; }",300;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22844,'Sealed_Dracula_Card_Album','Sealed Dracula Card Album',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'/*No Info*/',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22845,'Sealed_Fortune_Egg','Sealed Fortune Egg',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'getitem callfunc("F_Rand",4488,4497,4486,4480,4485,4539,4487,4494,4538,4489,4490,4482,4503,22846),1;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22846,'Sealed_Dracula_Card','Sealed Dracula Card',2,10,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (22846,'Sealed_Dracula_Card','Sealed Dracula Card',2,10,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,63,2,NULL,NULL,NULL,NULL,NULL,'bonus_script "{ bonus3 bSPDrainRate,50,5,0; }",300;',NULL,NULL);
|
||||
# Shadow Equip
|
||||
REPLACE INTO `item_db_re` VALUES (24000,'T_STR1_Armor_Shadow','T STR1 Armor Shadow',12,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,65536,NULL,'1',NULL,NULL,'bonus bStr,1;',NULL,NULL);
|
||||
REPLACE INTO `item_db_re` VALUES (24001,'T_DEX1_Weapon_Shadow','T DEX1 Weapon Shadow',12,10,NULL,0,NULL,NULL,NULL,0,0xFFFFFFFF,63,2,131072,NULL,'1',NULL,NULL,'bonus bDex,1;',NULL,NULL);
|
||||
|
@ -706,12 +706,12 @@ CREATE TABLE IF NOT EXISTS `interreg` (
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `bonus_script` (
|
||||
`char_id` varchar(11) NOT NULL,
|
||||
`script` varchar(1024) NOT NULL,
|
||||
`tick` varchar(11) NOT NULL DEFAULT '0',
|
||||
`flag` varchar(3) NOT NULL DEFAULT '0',
|
||||
`type` char(1) NOT NULL DEFAULT '0',
|
||||
`icon` varchar(3) NOT NULL DEFAULT '-1'
|
||||
`char_id` INT(11) UNSIGNED NOT NULL,
|
||||
`script` TEXT NOT NULL,
|
||||
`tick` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`flag` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`type` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`icon` SMALLINT(3) NOT NULL DEFAULT '-1'
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `vending_items` (
|
||||
|
6
sql-files/upgrades/upgrade_20150131.sql
Normal file
6
sql-files/upgrades/upgrade_20150131.sql
Normal file
@ -0,0 +1,6 @@
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `char_id` INT(11) UNSIGNED NOT NULL;
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `script` TEXT NOT NULL;
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `tick` INT(11) UNSIGNED NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `flag` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `type` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `bonus_script` MODIFY COLUMN `icon` SMALLINT(3) NOT NULL DEFAULT '-1';
|
@ -1242,91 +1242,116 @@ int chmapif_parse_reqcharunban(int fd){
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Get bonus_script data(s) from table to load
|
||||
* @param fd
|
||||
*/
|
||||
/**
|
||||
* ZA 0x2b2d
|
||||
* <cmd>.W <char_id>.L
|
||||
* AZ 0x2b2f
|
||||
* <cmd>.W <len>.W <cid>.L <count>.B { <bonus_script_data>.?B }
|
||||
* Get bonus_script data(s) from table to load then send to player
|
||||
* @param fd
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int chmapif_bonus_script_get(int fd) {
|
||||
if (RFIFOREST(fd) < 6)
|
||||
return 0;
|
||||
else {
|
||||
int cid;
|
||||
cid = RFIFOL(fd,2);
|
||||
uint8 num_rows = 0;
|
||||
uint32 cid = RFIFOL(fd,2);
|
||||
struct bonus_script_data tmp_bsdata;
|
||||
SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
|
||||
|
||||
RFIFOSKIP(fd,6);
|
||||
|
||||
if (SQL_ERROR == Sql_Query(sql_handle,"SELECT `script`, `tick`, `flag`, `type`, `icon` FROM `%s` WHERE `char_id`='%d'",
|
||||
schema_config.bonus_script_db,cid))
|
||||
if (SQL_ERROR == SqlStmt_Prepare(stmt,
|
||||
"SELECT `script`, `tick`, `flag`, `type`, `icon` FROM `%s` WHERE `char_id` = '%d' LIMIT %d",
|
||||
schema_config.bonus_script_db, cid, MAX_PC_BONUS_SCRIPT) ||
|
||||
SQL_ERROR == SqlStmt_Execute(stmt) ||
|
||||
SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &tmp_bsdata.script_str, sizeof(tmp_bsdata.script_str), NULL, NULL) ||
|
||||
SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UINT32, &tmp_bsdata.tick, 0, NULL, NULL) ||
|
||||
SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT16, &tmp_bsdata.flag, 0, NULL, NULL) ||
|
||||
SQL_ERROR == SqlStmt_BindColumn(stmt, 3, SQLDT_UINT8, &tmp_bsdata.type, 0, NULL, NULL) ||
|
||||
SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_INT16, &tmp_bsdata.icon, 0, NULL, NULL)
|
||||
)
|
||||
{
|
||||
Sql_ShowDebug(sql_handle);
|
||||
SqlStmt_ShowDebug(stmt);
|
||||
SqlStmt_Free(stmt);
|
||||
return 1;
|
||||
}
|
||||
if (Sql_NumRows(sql_handle) > 0) {
|
||||
struct bonus_script_data bsdata;
|
||||
int count;
|
||||
char *data;
|
||||
|
||||
WFIFOHEAD(fd,10+50*sizeof(struct bonus_script_data));
|
||||
WFIFOW(fd,0) = 0x2b2f;
|
||||
WFIFOL(fd,4) = cid;
|
||||
for (count = 0; count < MAX_PC_BONUS_SCRIPT && SQL_SUCCESS == Sql_NextRow(sql_handle); ++count) {
|
||||
Sql_GetData(sql_handle,0,&data,NULL); memcpy(bsdata.script,data,strlen(data)+1);
|
||||
Sql_GetData(sql_handle,1,&data,NULL); bsdata.tick = atoi(data);
|
||||
Sql_GetData(sql_handle,2,&data,NULL); bsdata.flag = atoi(data);
|
||||
Sql_GetData(sql_handle,3,&data,NULL); bsdata.type = atoi(data);
|
||||
Sql_GetData(sql_handle,4,&data,NULL); bsdata.icon = atoi(data);
|
||||
memcpy(WFIFOP(fd,10+count*sizeof(struct bonus_script_data)),&bsdata,sizeof(struct bonus_script_data));
|
||||
}
|
||||
if (count >= MAX_PC_BONUS_SCRIPT)
|
||||
ShowWarning("Too many bonus_script for %d, some of them were not loaded.\n",cid);
|
||||
if (count > 0) {
|
||||
WFIFOW(fd,2) = 10 + count*sizeof(struct bonus_script_data);
|
||||
WFIFOW(fd,8) = count;
|
||||
WFIFOSET(fd,WFIFOW(fd,2));
|
||||
if ((num_rows = (uint8)SqlStmt_NumRows(stmt)) > 0) {
|
||||
uint8 i;
|
||||
uint32 size = 9 + num_rows * sizeof(struct bonus_script_data);
|
||||
|
||||
//Clear the data once loaded.
|
||||
if (SQL_ERROR == Sql_Query(sql_handle,"DELETE FROM `%s` WHERE `char_id`='%d'",schema_config.bonus_script_db,cid))
|
||||
Sql_ShowDebug(sql_handle);
|
||||
ShowInfo("Loaded %d bonus_script for char_id: %d\n",count,cid);
|
||||
WFIFOHEAD(fd, size);
|
||||
WFIFOW(fd, 0) = 0x2b2f;
|
||||
WFIFOW(fd, 2) = size;
|
||||
WFIFOL(fd, 4) = cid;
|
||||
WFIFOB(fd, 8) = num_rows;
|
||||
|
||||
for (i = 0; i < num_rows && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++) {
|
||||
struct bonus_script_data bsdata;
|
||||
memset(&bsdata, 0, sizeof(bsdata));
|
||||
memset(bsdata.script_str, '\0', sizeof(bsdata.script_str));
|
||||
|
||||
safestrncpy(bsdata.script_str, tmp_bsdata.script_str, strlen(tmp_bsdata.script_str)+1);
|
||||
bsdata.tick = tmp_bsdata.tick;
|
||||
bsdata.flag = tmp_bsdata.flag;
|
||||
bsdata.type = tmp_bsdata.type;
|
||||
bsdata.icon = tmp_bsdata.icon;
|
||||
memcpy(WFIFOP(fd, 9 + i * sizeof(struct bonus_script_data)), &bsdata, sizeof(struct bonus_script_data));
|
||||
}
|
||||
|
||||
WFIFOSET(fd, size);
|
||||
|
||||
ShowInfo("Bonus Script loaded for CID=%d. Total: %d.\n", cid, i);
|
||||
|
||||
if (SQL_ERROR == SqlStmt_Prepare(stmt,"DELETE FROM `%s` WHERE `char_id`='%d'",schema_config.bonus_script_db,cid) ||
|
||||
SQL_ERROR == SqlStmt_Execute(stmt))
|
||||
SqlStmt_ShowDebug(stmt);
|
||||
}
|
||||
Sql_FreeResult(sql_handle);
|
||||
SqlStmt_Free(stmt);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Save bonus_script data(s) to the table
|
||||
* @param fd
|
||||
*/
|
||||
/**
|
||||
* ZA 0x2b2e
|
||||
* <cmd>.W <len>.W <char_id>.L <count>.B { <bonus_script>.?B }
|
||||
* Save bonus_script data(s) to the table
|
||||
* @param fd
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int chmapif_bonus_script_save(int fd) {
|
||||
if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
|
||||
return 0;
|
||||
else {
|
||||
int count, cid;
|
||||
uint32 cid = RFIFOL(fd,4);
|
||||
uint8 count = RFIFOB(fd,8);
|
||||
|
||||
cid = RFIFOL(fd,4);
|
||||
count = RFIFOW(fd,8);
|
||||
if (SQL_ERROR == Sql_Query(sql_handle,"DELETE FROM `%s` WHERE `char_id` = '%d'", schema_config.bonus_script_db, cid))
|
||||
Sql_ShowDebug(sql_handle);
|
||||
|
||||
if (count > 0) {
|
||||
struct bonus_script_data bs;
|
||||
char esc_script[MAX_BONUS_SCRIPT_LENGTH*2];
|
||||
struct bonus_script_data bsdata;
|
||||
StringBuf buf;
|
||||
int i;
|
||||
char esc_script[MAX_BONUS_SCRIPT_LENGTH] = "";
|
||||
uint8 i;
|
||||
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_Printf(&buf,"INSERT INTO `%s` (`char_id`, `script`, `tick`, `flag`, `type`, `icon`) VALUES ",schema_config.bonus_script_db);
|
||||
StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `script`, `tick`, `flag`, `type`, `icon`) VALUES ", schema_config.bonus_script_db);
|
||||
for (i = 0; i < count; ++i) {
|
||||
memcpy(&bs,RFIFOP(fd,10+i*sizeof(struct bonus_script_data)),sizeof(struct bonus_script_data));
|
||||
Sql_EscapeString(sql_handle,esc_script,bs.script);
|
||||
memcpy(&bsdata, RFIFOP(fd, 9 + i*sizeof(struct bonus_script_data)), sizeof(struct bonus_script_data));
|
||||
Sql_EscapeString(sql_handle, esc_script, bsdata.script_str);
|
||||
if (i > 0)
|
||||
StringBuf_AppendStr(&buf,", ");
|
||||
StringBuf_Printf(&buf,"('%d','%s','%d','%d','%d','%d')",cid,esc_script,bs.tick,bs.flag,bs.type,bs.icon);
|
||||
StringBuf_Printf(&buf, "('%d','%s','%d','%d','%d','%d')", cid, esc_script, bsdata.tick, bsdata.flag, bsdata.type, bsdata.icon);
|
||||
}
|
||||
if (SQL_ERROR == Sql_QueryStr(sql_handle,StringBuf_Value(&buf)))
|
||||
Sql_ShowDebug(sql_handle);
|
||||
|
||||
StringBuf_Destroy(&buf);
|
||||
ShowInfo("Saved %d bonus_script for char_id: %d\n",count,cid);
|
||||
}
|
||||
ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count);
|
||||
RFIFOSKIP(fd,RFIFOW(fd,2));
|
||||
}
|
||||
return 1;
|
||||
|
@ -2717,18 +2717,29 @@ void linkdb_insert( struct linkdb_node** head, void *key, void* data)
|
||||
node->data = data;
|
||||
}
|
||||
|
||||
void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... )
|
||||
{
|
||||
int linkdb_vforeach( struct linkdb_node** head, LinkDBFunc func, va_list ap) {
|
||||
struct linkdb_node *node;
|
||||
if( head == NULL ) return;
|
||||
int retCount = 0;
|
||||
if( head == NULL )
|
||||
return 0;
|
||||
node = *head;
|
||||
while ( node ) {
|
||||
va_list args;
|
||||
va_start(args, func);
|
||||
func( node->key, node->data, args );
|
||||
va_end(args);
|
||||
va_list argscopy;
|
||||
va_copy(argscopy, ap);
|
||||
retCount += func(node->key, node->data, argscopy);
|
||||
va_end(argscopy);
|
||||
node = node->next;
|
||||
}
|
||||
return retCount;
|
||||
}
|
||||
|
||||
int linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... ) {
|
||||
va_list ap;
|
||||
int retCount = 0;
|
||||
va_start(ap, func);
|
||||
retCount = linkdb_vforeach(head, func, ap);
|
||||
va_end(ap);
|
||||
return retCount;
|
||||
}
|
||||
|
||||
void* linkdb_search( struct linkdb_node** head, void *key)
|
||||
|
@ -875,14 +875,15 @@ struct linkdb_node {
|
||||
void *data;
|
||||
};
|
||||
|
||||
typedef void (*LinkDBFunc)(void* key, void* data, va_list args);
|
||||
typedef int (*LinkDBFunc)(void* key, void* data, va_list args);
|
||||
|
||||
void linkdb_insert ( struct linkdb_node** head, void *key, void* data); // 重複を考慮しない
|
||||
void linkdb_replace( struct linkdb_node** head, void *key, void* data); // 重複を考慮する
|
||||
void* linkdb_search ( struct linkdb_node** head, void *key);
|
||||
void* linkdb_erase ( struct linkdb_node** head, void *key);
|
||||
void linkdb_final ( struct linkdb_node** head );
|
||||
void linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
|
||||
int linkdb_vforeach(struct linkdb_node** head, LinkDBFunc func, va_list ap);
|
||||
int linkdb_foreach( struct linkdb_node** head, LinkDBFunc func, ... );
|
||||
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "cbasetypes.h"
|
||||
#include "../common/db.h"
|
||||
#include <time.h>
|
||||
#include "../common/strlib.h"// StringBuf
|
||||
|
||||
// server->client protocol version
|
||||
// 0 - pre-?
|
||||
@ -73,7 +74,7 @@
|
||||
#define MAX_GUILDLEVEL 50 ///Max Guild level
|
||||
#define MAX_GUARDIANS 8 ///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
|
||||
#define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
|
||||
#define MAX_PC_BONUS_SCRIPT 20 ///Max bonus script
|
||||
#define MAX_PC_BONUS_SCRIPT 50 ///Max bonus script can be fetched from `bonus_script` table on player load [Cydh]
|
||||
|
||||
// for produce
|
||||
#define MIN_ATTRIBUTE 0
|
||||
@ -259,12 +260,13 @@ struct status_change_data {
|
||||
long val1, val2, val3, val4, tick; //Remaining duration.
|
||||
};
|
||||
|
||||
#define MAX_BONUS_SCRIPT_LENGTH 1024
|
||||
#define MAX_BONUS_SCRIPT_LENGTH 512
|
||||
struct bonus_script_data {
|
||||
char script[MAX_BONUS_SCRIPT_LENGTH];
|
||||
long tick;
|
||||
char type;
|
||||
short flag, icon;
|
||||
char script_str[MAX_BONUS_SCRIPT_LENGTH]; //< Script string
|
||||
uint32 tick; ///< Tick
|
||||
uint16 flag; ///< Flags @see enum e_bonus_script_flags
|
||||
int16 icon; ///< Icon SI
|
||||
uint8 type; ///< 0 - None, 1 - Buff, 2 - Debuff
|
||||
};
|
||||
|
||||
struct skill_cooldown_data {
|
||||
|
255
src/map/chrif.c
255
src/map/chrif.c
@ -97,8 +97,8 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
|
||||
//2b2b: Incoming, chrif_parse_ack_vipActive -> vip info result
|
||||
//2b2c: FREE
|
||||
//2b2d: Outgoing, chrif_bsdata_request -> request bonus_script for pc_authok'ed char.
|
||||
//2b2e: Outgoing, chrif_save_bsdata -> Send bonus_script of player for saving.
|
||||
//2b2f: Incoming, chrif_load_bsdata -> received bonus_script of player for loading.
|
||||
//2b2e: Outgoing, chrif_bsdata_save -> Send bonus_script of player for saving.
|
||||
//2b2f: Incoming, chrif_bsdata_received -> received bonus_script of player for loading.
|
||||
|
||||
int chrif_connected = 0;
|
||||
int char_fd = -1;
|
||||
@ -282,7 +282,6 @@ int chrif_save(struct map_session_data *sd, int flag) {
|
||||
if (chrif_isconnected()) {
|
||||
chrif_save_scdata(sd);
|
||||
chrif_skillcooldown_save(sd);
|
||||
chrif_save_bsdata(sd);
|
||||
chrif_req_login_operation(sd->status.account_id, sd->status.name, CHRIF_OP_LOGIN_BANK, 0, 2, sd->status.bank_vault); //save Bank data
|
||||
}
|
||||
if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
|
||||
@ -291,6 +290,8 @@ int chrif_save(struct map_session_data *sd, int flag) {
|
||||
|
||||
chrif_check(-1); //Character is saved on reconnect.
|
||||
|
||||
chrif_bsdata_save(sd, (flag && (flag != 3)));
|
||||
|
||||
//For data sync
|
||||
if (sd->state.storage_flag == 2)
|
||||
gstorage_storagesave(sd->status.account_id, sd->status.guild_id, flag);
|
||||
@ -1598,6 +1599,130 @@ void chrif_parse_ack_vipActive(int fd) {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ZA 0x2b2d
|
||||
* <cmd>.W <char_id>.L
|
||||
* Requets bonus_script datas
|
||||
* @param char_id
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int chrif_bsdata_request(uint32 char_id) {
|
||||
chrif_check(-1);
|
||||
WFIFOHEAD(char_fd,6);
|
||||
WFIFOW(char_fd,0) = 0x2b2d;
|
||||
WFIFOL(char_fd,2) = char_id;
|
||||
WFIFOSET(char_fd,6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZA 0x2b2e
|
||||
* <cmd>.W <len>.W <char_id>.L <count>.B { <bonus_script>.?B }
|
||||
* Stores bonus_script data(s) to the table
|
||||
* @param sd
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int chrif_bsdata_save(struct map_session_data *sd, bool quit) {
|
||||
uint8 i = 0;
|
||||
|
||||
chrif_check(-1);
|
||||
|
||||
if (!sd)
|
||||
return 0;
|
||||
|
||||
// Removing...
|
||||
if (quit && sd->bonus_script.head) {
|
||||
uint16 flag = BSF_REM_ON_LOGOUT; //Remove bonus when logout
|
||||
if (battle_config.debuff_on_logout&1) //Remove negative buffs
|
||||
flag |= BSF_REM_DEBUFF;
|
||||
if (battle_config.debuff_on_logout&2) //Remove positive buffs
|
||||
flag |= BSF_REM_BUFF;
|
||||
pc_bonus_script_clear(sd, flag);
|
||||
}
|
||||
|
||||
//ShowInfo("Saving %d bonus script for CID=%d\n", sd->bonus_script.count, sd->status.char_id);
|
||||
|
||||
WFIFOHEAD(char_fd, 9 + sd->bonus_script.count * sizeof(struct bonus_script_data));
|
||||
WFIFOW(char_fd, 0) = 0x2b2e;
|
||||
WFIFOL(char_fd, 4) = sd->status.char_id;
|
||||
|
||||
if (sd->bonus_script.count) {
|
||||
unsigned int tick = gettick();
|
||||
struct linkdb_node *node = NULL;
|
||||
|
||||
for (node = sd->bonus_script.head; node && i < MAX_PC_BONUS_SCRIPT; node = node->next) {
|
||||
const struct TimerData *timer = NULL;
|
||||
struct bonus_script_data bs;
|
||||
struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data;
|
||||
|
||||
if (!entry || !(timer = get_timer(entry->tid)) || DIFF_TICK(timer->tick,tick) < 0)
|
||||
continue;
|
||||
|
||||
memset(&bs, 0, sizeof(bs));
|
||||
safestrncpy(bs.script_str, StringBuf_Value(entry->script_buf), StringBuf_Length(entry->script_buf)+1);
|
||||
bs.tick = DIFF_TICK(timer->tick, tick);
|
||||
bs.flag = entry->flag;
|
||||
bs.type = entry->type;
|
||||
bs.icon = entry->icon;
|
||||
memcpy(WFIFOP(char_fd, 9 + i * sizeof(struct bonus_script_data)), &bs, sizeof(struct bonus_script_data));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != sd->bonus_script.count && sd->bonus_script.count > MAX_PC_BONUS_SCRIPT)
|
||||
ShowWarning("Only allowed to save %d (mmo.h::MAX_PC_BONUS_SCRIPT) bonus script each player.\n", MAX_PC_BONUS_SCRIPT);
|
||||
}
|
||||
|
||||
WFIFOB(char_fd, 8) = i;
|
||||
WFIFOW(char_fd, 2) = 9 + sd->bonus_script.count * sizeof(struct bonus_script_data);
|
||||
WFIFOSET(char_fd, WFIFOW(char_fd, 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* AZ 0x2b2f
|
||||
* <cmd>.W <len>.W <cid>.L <count>.B { <bonus_script_data>.?B }
|
||||
* Bonus script received, set to player
|
||||
* @param fd
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int chrif_bsdata_received(int fd) {
|
||||
struct map_session_data *sd;
|
||||
uint32 cid = RFIFOL(fd,4);
|
||||
uint8 count = 0;
|
||||
|
||||
sd = map_charid2sd(cid);
|
||||
|
||||
if (!sd) {
|
||||
ShowError("chrif_bsdata_received: Player with CID %d not found!\n",cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((count = RFIFOB(fd,8))) {
|
||||
uint8 i = 0;
|
||||
|
||||
//ShowInfo("Loaded %d bonus script for CID=%d\n", count, sd->status.char_id);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,9 + i*sizeof(struct bonus_script_data));
|
||||
struct s_bonus_script_entry *entry = NULL;
|
||||
|
||||
if (bs->script_str[0] == '\0' || !bs->tick)
|
||||
continue;
|
||||
|
||||
if (!(entry = pc_bonus_script_add(sd, bs->script_str, bs->tick, (enum si_type)bs->icon, bs->flag, bs->type)))
|
||||
continue;
|
||||
|
||||
linkdb_insert(&sd->bonus_script.head, (void *)((intptr_t)entry), entry);
|
||||
}
|
||||
|
||||
if (sd->bonus_script.head)
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
*
|
||||
*------------------------------------------*/
|
||||
@ -1676,7 +1801,7 @@ int chrif_parse(int fd) {
|
||||
case 0x2b27: chrif_authfail(fd); break;
|
||||
case 0x2b29: chrif_load_bankdata(fd); break;
|
||||
case 0x2b2b: chrif_parse_ack_vipActive(fd); break;
|
||||
case 0x2b2f: chrif_load_bsdata(fd); break;
|
||||
case 0x2b2f: chrif_bsdata_received(fd); break;
|
||||
default:
|
||||
ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
|
||||
set_eof(fd);
|
||||
@ -1798,121 +1923,6 @@ int chrif_send_report(char* buf, int len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Requets bonus_script datas
|
||||
* @param char_id
|
||||
*/
|
||||
int chrif_bsdata_request(uint32 char_id) {
|
||||
chrif_check(-1);
|
||||
WFIFOHEAD(char_fd,6);
|
||||
WFIFOW(char_fd,0) = 0x2b2d;
|
||||
WFIFOL(char_fd,2) = char_id;
|
||||
WFIFOSET(char_fd,6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Stores bonus_script data(s) to the table
|
||||
* @param sd
|
||||
*/
|
||||
int chrif_save_bsdata(struct map_session_data *sd) {
|
||||
int i;
|
||||
uint8 count = 0;
|
||||
unsigned int tick;
|
||||
struct bonus_script_data bs;
|
||||
const struct TimerData *timer;
|
||||
|
||||
chrif_check(-1);
|
||||
tick = gettick();
|
||||
|
||||
WFIFOHEAD(char_fd,10+MAX_PC_BONUS_SCRIPT*sizeof(struct bonus_script_data));
|
||||
WFIFOW(char_fd,0) = 0x2b2e;
|
||||
WFIFOL(char_fd,4) = sd->status.char_id;
|
||||
|
||||
i = BSF_REM_ON_LOGOUT; //Remove bonus with this flag
|
||||
if (battle_config.debuff_on_logout&1) //Remove negative buffs
|
||||
i |= BSF_REM_DEBUFF;
|
||||
if (battle_config.debuff_on_logout&2) //Remove positive buffs
|
||||
i |= BSF_REM_BUFF;
|
||||
|
||||
//Clear data that won't be stored
|
||||
pc_bonus_script_clear(sd,i);
|
||||
|
||||
for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) {
|
||||
if (!(&sd->bonus_script[i]) || !sd->bonus_script[i].script || sd->bonus_script[i].script_str[0] == '\0')
|
||||
continue;
|
||||
|
||||
timer = get_timer(sd->bonus_script[i].tid);
|
||||
if (timer == NULL || DIFF_TICK(timer->tick,tick) < 0)
|
||||
continue;
|
||||
|
||||
memcpy(bs.script,sd->bonus_script[i].script_str,strlen(sd->bonus_script[i].script_str)+1);
|
||||
bs.tick = DIFF_TICK(timer->tick,tick);
|
||||
bs.flag = sd->bonus_script[i].flag;
|
||||
bs.type = sd->bonus_script[i].type;
|
||||
bs.icon = sd->bonus_script[i].icon;
|
||||
|
||||
memcpy(WFIFOP(char_fd,10+count*sizeof(struct bonus_script_data)),&bs,sizeof(struct bonus_script_data));
|
||||
pc_bonus_script_remove(&sd->bonus_script[i]);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
WFIFOW(char_fd,8) = count;
|
||||
WFIFOW(char_fd,2) = 10+count*sizeof(struct bonus_script_data);
|
||||
WFIFOSET(char_fd,WFIFOW(char_fd,2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Loads bonus_script datas
|
||||
* @param fd
|
||||
*/
|
||||
int chrif_load_bsdata(int fd) {
|
||||
struct map_session_data *sd;
|
||||
int cid, count;
|
||||
uint8 i;
|
||||
bool calc = false;
|
||||
|
||||
cid = RFIFOL(fd,4);
|
||||
sd = map_charid2sd(cid);
|
||||
|
||||
if (!sd) {
|
||||
ShowError("chrif_load_bsdata: Player with CID %d not found!\n",cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sd->status.char_id != cid) {
|
||||
ShowError("chrif_load_bsdata: Receiving data for char id does not matches (%d != %d)!\n",sd->status.char_id,cid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
count = RFIFOW(fd,8);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
struct script_code *script;
|
||||
struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,10 + i*sizeof(struct bonus_script_data));
|
||||
|
||||
if (bs->script[0] == '\0' || !(script = parse_script(bs->script,"chrif_load_bsdata",1,1)))
|
||||
continue;
|
||||
|
||||
memcpy(sd->bonus_script[i].script_str,bs->script,strlen(bs->script));
|
||||
sd->bonus_script[i].script = script;
|
||||
sd->bonus_script[i].tick = gettick() + bs->tick;
|
||||
sd->bonus_script[i].flag = (uint8)bs->flag;
|
||||
sd->bonus_script[i].type = bs->type;
|
||||
sd->bonus_script[i].icon = bs->icon;
|
||||
if (bs->icon != SI_BLANK) //Gives status icon if exist
|
||||
clif_status_change(&sd->bl,sd->bonus_script[i].icon,1,bs->tick,1,0,0);
|
||||
calc = true;
|
||||
}
|
||||
if (calc)
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DBApply
|
||||
*/
|
||||
@ -1954,6 +1964,13 @@ void do_init_chrif(void) {
|
||||
sizeof(struct mmo_charstatus));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if((sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT) > 0xFFFF){
|
||||
ShowError("bonus_script_data size = %d is too big, please reduce MAX_PC_BONUS_SCRIPT (%d) size. (must be below 0xFFFF).\n",
|
||||
(sizeof(struct bonus_script_data) * MAX_PC_BONUS_SCRIPT), MAX_PC_BONUS_SCRIPT);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auth_db = idb_alloc(DB_OPT_BASE);
|
||||
auth_db_ers = ers_new(sizeof(struct auth_node),"chrif.c::auth_db_ers",ERS_OPT_NONE);
|
||||
|
||||
|
@ -89,8 +89,7 @@ int chrif_req_charunban(int aid, const char* character_name);
|
||||
int chrif_load_bankdata(int fd);
|
||||
|
||||
int chrif_bsdata_request(uint32 char_id);
|
||||
int chrif_save_bsdata(struct map_session_data *sd);
|
||||
int chrif_load_bsdata(int fd);
|
||||
int chrif_bsdata_save(struct map_session_data *sd, bool quit);
|
||||
|
||||
void do_final_chrif(void);
|
||||
void do_init_chrif(void);
|
||||
|
@ -1945,11 +1945,12 @@ int guild_castledatasave(int castle_id, int index, int value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void guild_castle_reconnect_sub(void *key, void *data, va_list ap) {
|
||||
int guild_castle_reconnect_sub(void *key, void *data, va_list ap) {
|
||||
int castle_id = GetWord((int)__64BPRTSIZE(key), 0);
|
||||
int index = GetWord((int)__64BPRTSIZE(key), 1);
|
||||
intif_guild_castle_datasave(castle_id, index, *(int *)data);
|
||||
aFree(data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
260
src/map/pc.c
260
src/map/pc.c
@ -1143,8 +1143,6 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
|
||||
sd->autobonus2[i].active = INVALID_TIMER;
|
||||
for(i = 0; i < ARRAYLENGTH(sd->autobonus3); i++)
|
||||
sd->autobonus3[i].active = INVALID_TIMER;
|
||||
for(i = 0; i < ARRAYLENGTH(sd->bonus_script); i++)
|
||||
sd->bonus_script[i].tid = INVALID_TIMER;
|
||||
|
||||
if (battle_config.item_auto_get)
|
||||
sd->state.autoloot = 10000;
|
||||
@ -1265,6 +1263,9 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
|
||||
sd->status.cashshop_sent = false;
|
||||
|
||||
sd->last_addeditem_index = -1;
|
||||
|
||||
sd->bonus_script.head = NULL;
|
||||
sd->bonus_script.count = 0;
|
||||
|
||||
// Request all registries (auth is considered completed whence they arrive)
|
||||
intif_request_registry(sd,7);
|
||||
@ -11038,80 +11039,208 @@ void pc_show_version(struct map_session_data *sd) {
|
||||
clif_displaymessage(sd->fd,buf);
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
/**
|
||||
* Run bonus_script on player
|
||||
* @param sd
|
||||
* @author [Cydh]
|
||||
**/
|
||||
void pc_bonus_script(struct map_session_data *sd) {
|
||||
int now = gettick();
|
||||
struct linkdb_node *node = NULL, *next = NULL;
|
||||
|
||||
if (!sd || !(node = sd->bonus_script.head))
|
||||
return;
|
||||
|
||||
while (node) {
|
||||
struct s_bonus_script_entry *entry = NULL;
|
||||
next = node->next;
|
||||
|
||||
if ((entry = (struct s_bonus_script_entry *)node->data)) {
|
||||
// Only start timer for new bonus_script
|
||||
if (entry->tid == INVALID_TIMER) {
|
||||
if (entry->icon != SI_BLANK) // Gives status icon if exist
|
||||
clif_status_change(&sd->bl, entry->icon, 1, entry->tick, 1, 0, 0);
|
||||
|
||||
entry->tick += now;
|
||||
entry->tid = add_timer(entry->tick, pc_bonus_script_timer, sd->bl.id, (intptr_t)entry);
|
||||
}
|
||||
|
||||
if (entry->script)
|
||||
run_script(entry->script, 0, sd->bl.id, 0);
|
||||
else
|
||||
ShowError("pc_bonus_script: The script has been removed somewhere. \"%s\"\n", StringBuf_Value(entry->script_buf));
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add bonus_script to player
|
||||
* @param sd Player
|
||||
* @param script_str Script string
|
||||
* @param dur Duration in ms
|
||||
* @param icon SI
|
||||
* @param flag Flags @see enum e_bonus_script_flags
|
||||
* @param type 0 - None, 1 - Buff, 2 - Debuff
|
||||
* @return New created entry pointer or NULL if failed or NULL if duplicate fail
|
||||
* @author [Cydh]
|
||||
**/
|
||||
struct s_bonus_script_entry *pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type) {
|
||||
struct script_code *script = NULL;
|
||||
struct linkdb_node *node = NULL;
|
||||
struct s_bonus_script_entry *entry = NULL;
|
||||
|
||||
if (!sd)
|
||||
return NULL;
|
||||
|
||||
if (!(script = parse_script(script_str, "bonus_script", 0, SCRIPT_IGNORE_EXTERNAL_BRACKETS))) {
|
||||
ShowError("pc_bonus_script_add: Failed to parse script '%s' (CID:%d).\n", script_str, sd->status.char_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Duplication checks
|
||||
if ((node = sd->bonus_script.head)) {
|
||||
while (node) {
|
||||
entry = (struct s_bonus_script_entry *)node->data;
|
||||
if (strcmpi(script_str, StringBuf_Value(entry->script_buf)) == 0) {
|
||||
int newdur = gettick() + dur;
|
||||
if (flag&BSF_FORCE_REPLACE && entry->tick < newdur) { // Change duration
|
||||
settick_timer(entry->tid, newdur);
|
||||
script_free_code(script);
|
||||
return NULL;
|
||||
}
|
||||
else if (flag&BSF_FORCE_DUPLICATE) // Allow duplicate
|
||||
break;
|
||||
else { // No duplicate bonus
|
||||
script_free_code(script);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
CREATE(entry, struct s_bonus_script_entry, 1);
|
||||
|
||||
entry->script_buf = StringBuf_Malloc();
|
||||
StringBuf_AppendStr(entry->script_buf, script_str);
|
||||
entry->tid = INVALID_TIMER;
|
||||
entry->flag = flag;
|
||||
entry->icon = icon;
|
||||
entry->tick = dur; // Use duration first, on run change to expire time
|
||||
entry->type = type;
|
||||
entry->script = script;
|
||||
sd->bonus_script.count++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove bonus_script data from player
|
||||
* @param sd: Target player
|
||||
* @param list: Bonus script entry from player
|
||||
* @author [Cydh]
|
||||
**/
|
||||
void pc_bonus_script_free_entry(struct map_session_data *sd, struct s_bonus_script_entry *entry) {
|
||||
if (entry->tid != INVALID_TIMER)
|
||||
delete_timer(entry->tid, pc_bonus_script_timer);
|
||||
|
||||
if (entry->script)
|
||||
script_free_code(entry->script);
|
||||
|
||||
if (entry->script_buf)
|
||||
StringBuf_Free(entry->script_buf);
|
||||
|
||||
if (sd) {
|
||||
if (entry->icon != SI_BLANK)
|
||||
clif_status_load(&sd->bl, entry->icon, 0);
|
||||
if (sd->bonus_script.count > 0)
|
||||
sd->bonus_script.count--;
|
||||
}
|
||||
aFree(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do final process if no entry left
|
||||
* @param sd
|
||||
**/
|
||||
static void inline pc_bonus_script_check_final(struct map_session_data *sd) {
|
||||
if (sd->bonus_script.count == 0) {
|
||||
if (sd->bonus_script.head && sd->bonus_script.head->data)
|
||||
pc_bonus_script_free_entry(sd, (struct s_bonus_script_entry *)sd->bonus_script.head->data);
|
||||
linkdb_final(&sd->bonus_script.head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timer for bonus_script
|
||||
* @param tid
|
||||
* @param tick
|
||||
* @param id
|
||||
* @param data
|
||||
* @author [Cydh]
|
||||
**/
|
||||
int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) {
|
||||
uint8 i = (uint8)data;
|
||||
struct map_session_data *sd;
|
||||
struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data;
|
||||
|
||||
sd = map_id2sd(id);
|
||||
if (!sd) {
|
||||
ShowDebug("pc_bonus_script_timer: Null pointer id: %d data: %d\n",id,data);
|
||||
ShowError("pc_bonus_script_timer: Null pointer id: %d tid: %d\n", id, tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i >= MAX_PC_BONUS_SCRIPT || !(&sd->bonus_script[i]) || !sd->bonus_script[i].script) {
|
||||
ShowDebug("pc_bonus_script_timer: Invalid index %d\n",i);
|
||||
if (tid == INVALID_TIMER)
|
||||
return 0;
|
||||
|
||||
if (!sd->bonus_script.head || entry == NULL) {
|
||||
ShowError("pc_bonus_script_timer: Invalid entry pointer 0x%08X!\n", entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sd->bonus_script[i].icon != SI_BLANK)
|
||||
clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0);
|
||||
pc_bonus_script_remove(&sd->bonus_script[i]);
|
||||
linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry));
|
||||
pc_bonus_script_free_entry(sd, entry);
|
||||
pc_bonus_script_check_final(sd);
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
* Remove bonus_script data from player
|
||||
* @param sd: Target player
|
||||
* @param i: Bonus script index
|
||||
**/
|
||||
void pc_bonus_script_remove(struct s_bonus_script *bscript) {
|
||||
if (!bscript)
|
||||
return;
|
||||
|
||||
if (bscript->script)
|
||||
script_free_code(bscript->script);
|
||||
bscript->script = NULL;
|
||||
memset(bscript->script_str, '\0', sizeof(bscript->script_str));
|
||||
bscript->tick = 0;
|
||||
bscript->flag = 0;
|
||||
bscript->icon = SI_BLANK;
|
||||
if (bscript->tid != INVALID_TIMER)
|
||||
delete_timer(bscript->tid,pc_bonus_script_timer);
|
||||
bscript->tid = INVALID_TIMER;
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
/**
|
||||
* Check then clear all active timer(s) of bonus_script data from player based on reason
|
||||
* @param sd: Target player
|
||||
* @param flag: Reason to remove the bonus_script. e_bonus_script_flags or e_bonus_script_types
|
||||
* @author [Cydh]
|
||||
**/
|
||||
void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) {
|
||||
uint8 i, count = 0;
|
||||
if (!sd)
|
||||
struct linkdb_node *node = NULL;
|
||||
uint16 count = 0;
|
||||
|
||||
if (!sd || !(node = sd->bonus_script.head))
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) {
|
||||
if (&sd->bonus_script[i] && sd->bonus_script[i].script &&
|
||||
(sd->bonus_script[i].flag&flag || //Remove bonus script based on e_bonus_script_flags
|
||||
(sd->bonus_script[i].type && (
|
||||
(flag&BSF_REM_BUFF && sd->bonus_script[i].type == 1) || //Remove bonus script based on buff type
|
||||
(flag&BSF_REM_DEBUFF && sd->bonus_script[i].type == 2)) //Remove bonus script based on debuff type
|
||||
)))
|
||||
while (node) {
|
||||
struct linkdb_node *next = node->next;
|
||||
struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data;
|
||||
|
||||
if (entry && (
|
||||
(flag == BSF_PERMANENT) || // Remove all with permanent bonus
|
||||
(!flag && !(entry->flag&BSF_PERMANENT)) || // Remove all WITHOUT permanent bonus
|
||||
(flag&entry->flag) || // Matched flag
|
||||
(flag&BSF_REM_BUFF && entry->type == 1) || // Remove buff
|
||||
(flag&BSF_REM_DEBUFF && entry->type == 2) // Remove debuff
|
||||
)
|
||||
)
|
||||
{
|
||||
if (sd->bonus_script[i].icon != SI_BLANK)
|
||||
clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0);
|
||||
pc_bonus_script_remove(&sd->bonus_script[i]);
|
||||
linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry));
|
||||
pc_bonus_script_free_entry(sd, entry);
|
||||
count++;
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
pc_bonus_script_check_final(sd);
|
||||
|
||||
if (count && !(flag&BSF_REM_ON_LOGOUT)) //Don't need to do this if log out
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
}
|
||||
@ -11119,25 +11248,38 @@ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) {
|
||||
/**
|
||||
* Clear all bonus script from player
|
||||
* @param sd
|
||||
* @param permanent If true, will removes permanent bonus script.
|
||||
* @param flag &1 - Remove permanent bonus_script, &2 - Logout
|
||||
* @author [Cydh]
|
||||
*/
|
||||
void pc_bonus_script_clear_all(struct map_session_data *sd, bool permanent) {
|
||||
uint8 i, count = 0;
|
||||
if (!sd)
|
||||
**/
|
||||
void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag) {
|
||||
struct linkdb_node *node = NULL;
|
||||
uint16 count = 0;
|
||||
|
||||
if (!sd || !(node = sd->bonus_script.head))
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) {
|
||||
if (!&sd->bonus_script[i] && !sd->bonus_script[i].script)
|
||||
continue;
|
||||
if (!permanent && sd->bonus_script[i].flag&BSF_PERMANENT)
|
||||
continue;
|
||||
if (sd->bonus_script[i].icon != SI_BLANK)
|
||||
clif_status_load(&sd->bl, sd->bonus_script[i].icon, 0);
|
||||
pc_bonus_script_remove(&sd->bonus_script[i]);
|
||||
count++;
|
||||
while (node) {
|
||||
struct linkdb_node *next = node->next;
|
||||
struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)node->data;
|
||||
|
||||
if (entry && (
|
||||
!(entry->flag&BSF_PERMANENT) ||
|
||||
((flag&1) && entry->flag&BSF_PERMANENT)
|
||||
)
|
||||
)
|
||||
{
|
||||
linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry));
|
||||
pc_bonus_script_free_entry(sd, entry);
|
||||
count++;
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
|
||||
pc_bonus_script_check_final(sd);
|
||||
|
||||
if (count && !(flag&2))
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
}
|
||||
|
||||
/** [Cydh]
|
||||
|
24
src/map/pc.h
24
src/map/pc.h
@ -7,6 +7,7 @@
|
||||
#include "../common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus
|
||||
#include "../common/ers.h"
|
||||
#include "../common/timer.h" // INVALID_TIMER
|
||||
#include "../common/strlib.h"// StringBuf
|
||||
#include "map.h" // RC_ALL
|
||||
#include "atcommand.h" // AtCommandType
|
||||
#include "battle.h" // battle_config
|
||||
@ -157,13 +158,13 @@ struct s_pc_itemgrouphealrate {
|
||||
};
|
||||
|
||||
///Timed bonus 'bonus_script' struct [Cydh]
|
||||
struct s_bonus_script {
|
||||
struct s_bonus_script_entry {
|
||||
struct script_code *script;
|
||||
char script_str[MAX_BONUS_SCRIPT_LENGTH]; //Used for comparing and storing on table
|
||||
StringBuf *script_buf; //Used for comparing and storing on table
|
||||
uint32 tick;
|
||||
uint8 flag;
|
||||
char type; //0 - Ignore; 1 - Buff; 2 - Debuff
|
||||
int16 icon;
|
||||
uint16 flag;
|
||||
enum si_type icon;
|
||||
uint8 type; //0 - Ignore; 1 - Buff; 2 - Debuff
|
||||
int tid;
|
||||
};
|
||||
|
||||
@ -603,8 +604,13 @@ struct map_session_data {
|
||||
struct vip_info vip;
|
||||
bool disableshowrate; //State to disable clif_display_pinfo(). [Cydh]
|
||||
#endif
|
||||
struct s_bonus_script bonus_script[MAX_PC_BONUS_SCRIPT]; ///Bonus Script [Cydh]
|
||||
|
||||
|
||||
/// Bonus Script [Cydh]
|
||||
struct s_bonus_script_list {
|
||||
struct linkdb_node *head; ///< Bonus script head node. data: struct s_bonus_script_entry *entry, key: (intptr_t)entry
|
||||
uint16 count;
|
||||
} bonus_script;
|
||||
|
||||
struct s_pc_itemgrouphealrate **itemgrouphealrate; /// List of Item Group Heal rate bonus
|
||||
uint8 itemgrouphealrate_count; /// Number of rate bonuses
|
||||
|
||||
@ -1118,9 +1124,9 @@ void pc_crimson_marker_clear(struct map_session_data *sd);
|
||||
void pc_show_version(struct map_session_data *sd);
|
||||
|
||||
int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data);
|
||||
void pc_bonus_script_remove(struct s_bonus_script *bscript);
|
||||
void pc_bonus_script(struct map_session_data *sd);
|
||||
struct s_bonus_script_entry *pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type);
|
||||
void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag);
|
||||
void pc_bonus_script_clear_all(struct map_session_data *sd, bool permanent);
|
||||
|
||||
void pc_cell_basilica(struct map_session_data *sd);
|
||||
|
||||
|
@ -2352,12 +2352,6 @@ void script_hardcoded_constants(void) {
|
||||
script_set_constant("Option_Dragon",OPTION_DRAGON,false);
|
||||
script_set_constant("Option_Costume",OPTION_COSTUME,false);
|
||||
|
||||
/* bonus_script commands */
|
||||
script_set_constant("BSF_REM_BUFF",BSF_REM_BUFF,false);
|
||||
script_set_constant("BSF_REM_DEBUFF",BSF_REM_DEBUFF,false);
|
||||
script_set_constant("BSF_ALL",BSF_ALL,false);
|
||||
script_set_constant("BSF_CLEARALL",BSF_CLEARALL,false);
|
||||
|
||||
/* sc_start flags */
|
||||
script_set_constant("SCSTART_NONE",SCSTART_NONE,false);
|
||||
script_set_constant("SCSTART_NOAVOID",SCSTART_NOAVOID,false);
|
||||
@ -18909,16 +18903,20 @@ BUILDIN_FUNC(montransform) {
|
||||
* @author [Cydh]
|
||||
**/
|
||||
BUILDIN_FUNC(bonus_script) {
|
||||
uint8 i, flag = 0;
|
||||
uint16 flag = 0;
|
||||
int16 icon = SI_BLANK;
|
||||
uint32 dur;
|
||||
char type = 0;
|
||||
uint8 type = 0;
|
||||
TBL_PC* sd;
|
||||
const char *script_str = NULL;
|
||||
struct script_code *script = NULL;
|
||||
struct s_bonus_script_entry *entry = NULL;
|
||||
|
||||
if (script_hasdata(st,7))
|
||||
sd = map_charid2sd(script_getnum(st,7));
|
||||
if (script_hasdata(st,7)) {
|
||||
if (!(sd = map_charid2sd(script_getnum(st,7)))) {
|
||||
ShowError("buildin_bonus_script: Player CID=%d is not found.\n", script_getnum(st,7));
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
sd = script_rid2sd(st);
|
||||
|
||||
@ -18927,46 +18925,28 @@ BUILDIN_FUNC(bonus_script) {
|
||||
|
||||
script_str = script_getstr(st,2);
|
||||
dur = 1000 * abs(script_getnum(st,3));
|
||||
FETCH(4,flag);
|
||||
FETCH(5,type);
|
||||
FETCH(6,icon);
|
||||
FETCH(4, flag);
|
||||
FETCH(5, type);
|
||||
FETCH(6, icon);
|
||||
|
||||
// No Script string, No Duration!
|
||||
if (script_str[0] == '\0' || !dur) {
|
||||
//ShowWarning("buildin_bonus_script: Invalid script. Skipping...\n");
|
||||
ShowError("buildin_bonus_script: Invalid! Script: \"%s\". Duration: %d\n", script_str, dur);
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
}
|
||||
|
||||
//Skip duplicate entry
|
||||
ARR_FIND(0,MAX_PC_BONUS_SCRIPT,i,&sd->bonus_script[i] && sd->bonus_script[i].script_str && strcmp(sd->bonus_script[i].script_str,script_str) == 0);
|
||||
if (i < MAX_PC_BONUS_SCRIPT) {
|
||||
//ShowWarning("buildin_bonus_script: Duplicate entry with bonus '%d'. Skipping...\n",i);
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
if (!(script = parse_script(script_str,"bonus_script",0,1))) {
|
||||
ShowWarning("buildin_bonus_script: Failed to parse script '%s' (cid:%d). Skipping...\n",script_str,sd->status.char_id);
|
||||
if (strlen(script_str) >= MAX_BONUS_SCRIPT_LENGTH) {
|
||||
ShowError("buildin_bonus_script: Script string to long: \"%s\".\n", script_str);
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
}
|
||||
|
||||
//Find the empty slot
|
||||
ARR_FIND(0,MAX_PC_BONUS_SCRIPT,i,!sd->bonus_script[i].script);
|
||||
if (i >= MAX_PC_BONUS_SCRIPT) {
|
||||
ShowWarning("buildin_itemscript: Maximum script_bonus is reached (cid:%d max: %d). Skipping...\n",sd->status.char_id,MAX_PC_BONUS_SCRIPT);
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
if (icon <= SI_BLANK || icon >= SI_MAX)
|
||||
icon = SI_BLANK;
|
||||
|
||||
if ((entry = pc_bonus_script_add(sd, script_str, dur, (enum si_type)icon, flag, type))) {
|
||||
linkdb_insert(&sd->bonus_script.head, (void *)((intptr_t)entry), entry);
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
}
|
||||
|
||||
//Add the script data
|
||||
memcpy(sd->bonus_script[i].script_str,script_str,strlen(script_str)+1);
|
||||
sd->bonus_script[i].script = script;
|
||||
sd->bonus_script[i].tick = gettick() + dur;
|
||||
sd->bonus_script[i].flag = flag;
|
||||
sd->bonus_script[i].type = type;
|
||||
sd->bonus_script[i].icon = icon;
|
||||
|
||||
if (sd->bonus_script[i].icon != SI_BLANK) //Gives status icon if exist
|
||||
clif_status_change(&sd->bl,sd->bonus_script[i].icon,1,dur,1,0,0);
|
||||
|
||||
status_calc_pc(sd,SCO_NONE);
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -18979,7 +18959,7 @@ BUILDIN_FUNC(bonus_script) {
|
||||
*/
|
||||
BUILDIN_FUNC(bonus_script_clear) {
|
||||
TBL_PC* sd;
|
||||
bool flag = 0;
|
||||
bool flag = false;
|
||||
|
||||
if (script_hasdata(st,2))
|
||||
flag = script_getnum(st,2);
|
||||
@ -18992,7 +18972,7 @@ BUILDIN_FUNC(bonus_script_clear) {
|
||||
if (sd == NULL)
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
|
||||
pc_bonus_script_clear_all(sd,flag); /// Don't remove permanent script
|
||||
pc_bonus_script_clear(sd,(flag ? BSF_PERMANENT : BSF_REM_ALL));
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -3171,13 +3171,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
|
||||
run_script(data->script,0,sd->bl.id,0);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { //Process script Bonus [Cydh]
|
||||
if (!(&sd->bonus_script[i]) || !sd->bonus_script[i].script)
|
||||
continue;
|
||||
if (sd->bonus_script[i].tid == INVALID_TIMER) //Just add timer only for new attached script
|
||||
sd->bonus_script[i].tid = add_timer(sd->bonus_script[i].tick,pc_bonus_script_timer,sd->bl.id,i);
|
||||
run_script(sd->bonus_script[i].script,0,sd->bl.id,0);
|
||||
}
|
||||
pc_bonus_script(sd);
|
||||
|
||||
if( sd->pd ) { // Pet Bonus
|
||||
struct pet_data *pd = sd->pd;
|
||||
|
@ -1746,23 +1746,25 @@ enum e_status_change_start_flags {
|
||||
|
||||
///Enum for bonus_script's flag [Cydh]
|
||||
enum e_bonus_script_flags {
|
||||
BSF_REM_ON_DEAD = 0x001, ///Removed when dead
|
||||
BSF_REM_ON_DISPELL = 0x002, ///Removed by Dispell
|
||||
BSF_REM_ON_CLEARANCE = 0x004, ///Removed by Clearance
|
||||
BSF_REM_ON_LOGOUT = 0x008, ///Removed when player logged out
|
||||
BSF_REM_ON_BANISHING_BUSTER = 0x010, ///Removed by Banishing Buster
|
||||
BSF_REM_ON_REFRESH = 0x020, ///Removed by Refresh
|
||||
BSF_REM_ON_LUXANIMA = 0x040, ///Removed by Luxanima
|
||||
BSF_REM_ON_MADOGEAR = 0x080, ///Removed when Madogear is activated or deactivated
|
||||
BSF_REM_ON_DAMAGED = 0x100, ///Removed when receive damage
|
||||
BSF_PERMANENT = 0x200, ///Cannot be removed by sc_end SC_ALL
|
||||
BSF_REM_ON_DEAD = 0x001, ///< Removed when dead
|
||||
BSF_REM_ON_DISPELL = 0x002, ///< Removed by Dispell
|
||||
BSF_REM_ON_CLEARANCE = 0x004, ///< Removed by Clearance
|
||||
BSF_REM_ON_LOGOUT = 0x008, ///< Removed when player logged out
|
||||
BSF_REM_ON_BANISHING_BUSTER = 0x010, ///< Removed by Banishing Buster
|
||||
BSF_REM_ON_REFRESH = 0x020, ///< Removed by Refresh
|
||||
BSF_REM_ON_LUXANIMA = 0x040, ///< Removed by Luxanima
|
||||
BSF_REM_ON_MADOGEAR = 0x080, ///< Removed when Madogear is activated or deactivated
|
||||
BSF_REM_ON_DAMAGED = 0x100, ///< Removed when receive damage
|
||||
BSF_PERMANENT = 0x200, ///< Cannot be removed by sc_end SC_ALL
|
||||
|
||||
// These flags better in the last of everything
|
||||
BSF_REM_BUFF = 0x1000, ///Remove positive buff
|
||||
BSF_REM_DEBUFF = 0x2000, ///Remove negative buff
|
||||
// These flags cannot be stacked, BSF_FORCE_REPLACE has highest priority to check if YOU force to add both
|
||||
BSF_FORCE_REPLACE = 0x400, ///< Force to replace duplicated script by expanding the duration
|
||||
BSF_FORCE_DUPLICATE = 0x800, ///< Force to add duplicated script
|
||||
|
||||
BSF_ALL = 0x0FFF|BSF_REM_BUFF|BSF_REM_DEBUFF,
|
||||
BSF_CLEARALL = BSF_ALL&~BSF_PERMANENT,
|
||||
// These flags aren't part of 'bonus_script' scripting flags
|
||||
BSF_REM_ALL = 0x0, ///< Remove all bonus script
|
||||
BSF_REM_BUFF = 0x4000, ///< Remove positive buff if battle_config.debuff_on_logout&1
|
||||
BSF_REM_DEBUFF = 0x8000, ///< Remove negative buff if battle_config.debuff_on_logout&2
|
||||
};
|
||||
|
||||
///Enum for status_get_hpbonus and status_get_spbonus
|
||||
|
@ -3169,6 +3169,10 @@ int unit_free(struct block_list *bl, clr_type clrtype)
|
||||
sd->num_quests = sd->avail_quests = 0;
|
||||
}
|
||||
|
||||
// Clearing...
|
||||
if (sd->bonus_script.head)
|
||||
pc_bonus_script_clear(sd, BSF_REM_ALL);
|
||||
|
||||
pc_itemgrouphealrate_clear(sd);
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user