瀏覽代碼

Follow up 95705d41bee8d15d50d34c484617790e4aae9b21
* Changed bonus script list to link list
* 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 quiting, will removes the bonus that won't be saved on logout.

Signed-off-by: Cydh Ramdh <house.bad@gmail.com>

Cydh Ramdh 10 年之前
父節點
當前提交
148353eed9
共有 11 個文件被更改,包括 261 次插入220 次删除
  1. 4 1
      src/char/char_mapif.c
  2. 18 7
      src/common/db.c
  3. 3 2
      src/common/db.h
  4. 60 52
      src/map/chrif.c
  5. 1 1
      src/map/chrif.h
  6. 2 1
      src/map/guild.c
  7. 153 147
      src/map/pc.c
  8. 10 6
      src/map/pc.h
  9. 5 2
      src/map/script.c
  10. 1 1
      src/map/status.h
  11. 4 0
      src/map/unit.c

+ 4 - 1
src/char/char_mapif.c

@@ -1328,6 +1328,9 @@ int chmapif_bonus_script_save(int fd) {
 		uint32 cid = RFIFOL(fd,4);
 		uint8 count = RFIFOB(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) {
 			char esc_script[MAX_BONUS_SCRIPT_LENGTH*2];
 			struct bonus_script_data bsdata;
@@ -1346,9 +1349,9 @@ int chmapif_bonus_script_save(int fd) {
 			if (SQL_ERROR == Sql_QueryStr(sql_handle,StringBuf_Value(&buf)))
 				Sql_ShowDebug(sql_handle);
 
-			ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count);
 			StringBuf_Destroy(&buf);
 		}
+		ShowInfo("Bonus Script saved for CID=%d. Total: %d.\n", cid, count);
 		RFIFOSKIP(fd,RFIFOW(fd,2));
 	}
 	return 1;

+ 18 - 7
src/common/db.c

@@ -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)

+ 3 - 2
src/common/db.h

@@ -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, ...  );
 
 
 

+ 60 - 52
src/map/chrif.c

@@ -282,8 +282,6 @@ int chrif_save(struct map_session_data *sd, int flag) {
 		if (chrif_isconnected()) {
 			chrif_save_scdata(sd);
 			chrif_skillcooldown_save(sd);
-			if (flag != 3)
-				chrif_bsdata_save(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) )
@@ -292,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);
@@ -1619,63 +1619,63 @@ int chrif_bsdata_request(uint32 char_id) {
 /**
  * ZA 0x2b2e
  * <cmd>.W <len>.W <char_id>.L <count>.B { <bonus_script>.?B }
- * Stores bonus_script data(s) to the table when player log out
+ * Stores bonus_script data(s) to the table
  * @param sd
  * @author [Cydh]
  **/
-int chrif_bsdata_save(struct map_session_data *sd) {
-	uint16 i;
-	uint8 count = 0;
-	unsigned int tick;
+int chrif_bsdata_save(struct map_session_data *sd, bool quit) {
+	uint8 i = 0;
 
 	chrif_check(-1);
 
-	if (!sd || !sd->bonus_script_num)
+	if (!sd)
 		return 0;
 
-	tick = gettick();
+	// 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_num * sizeof(struct bonus_script_data));
+	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;
 
-	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;
+	if (sd->bonus_script.count) {
+		unsigned int tick = gettick();
+		struct linkdb_node *node = NULL;
 
-	//Clear data that won't be stored
-	pc_bonus_script_clear(sd, i);
+		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 (!sd->bonus_script_num)
-		return 0;
-
-	for (i = 0; i < sd->bonus_script_num; i++) {
-		const struct TimerData *timer = get_timer(sd->bonus_script[i]->tid);
-		struct bonus_script_data bs;
-
-		if (timer == NULL || DIFF_TICK(timer->tick,tick) < 0)
-			continue;
+			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(sd->bonus_script[i]->script_buf), StringBuf_Length(sd->bonus_script[i]->script_buf)+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, 9 + count * sizeof(struct bonus_script_data)), &bs, sizeof(struct bonus_script_data));
-		count++;
-	}
+			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 (count > 0) {
-		WFIFOB(char_fd, 8) = count;
-		WFIFOW(char_fd, 2) = 9 + sd->bonus_script_num * sizeof(struct bonus_script_data);
-		WFIFOSET(char_fd, WFIFOW(char_fd, 2));
+		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);
 	}
 
-	// Clear All
-	pc_bonus_script_clear_all(sd,3);
+	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;
 }
@@ -1690,8 +1690,7 @@ int chrif_bsdata_save(struct map_session_data *sd) {
 int chrif_bsdata_received(int fd) {
 	struct map_session_data *sd;
 	uint32 cid = RFIFOL(fd,4);
-	uint8 i, count = 0;
-	bool calc = false;
+	uint8 count = 0;
 
 	sd = map_charid2sd(cid);
 
@@ -1700,19 +1699,28 @@ int chrif_bsdata_received(int fd) {
 		return -1;
 	}
 
-	count = RFIFOB(fd,8);
+	if ((count = RFIFOB(fd,8))) {
+		struct s_bonus_script *list = NULL;
+		uint8 i = 0;
 
-	for (i = 0; i < count; i++) {
-		struct bonus_script_data *bs = (struct bonus_script_data*)RFIFOP(fd,9 + i*sizeof(struct bonus_script_data));
+		//ShowInfo("Loaded %d bonus script for CID=%d\n", count, sd->status.char_id);
 
-		if (bs->script_str[0] == '\0' || !bs->tick)
-			continue;
+		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 (pc_bonus_script_add(sd, bs->script_str, bs->tick, (enum si_type)bs->icon, bs->flag, bs->type))
-			calc = true;
+		if (sd->bonus_script.head)
+			status_calc_pc(sd,SCO_NONE);
 	}
-	if (calc)
-		status_calc_pc(sd,SCO_NONE);
 	return 0;
 }
 

+ 1 - 1
src/map/chrif.h

@@ -89,7 +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_bsdata_save(struct map_session_data *sd);
+int chrif_bsdata_save(struct map_session_data *sd, bool quit);
 
 void do_final_chrif(void);
 void do_init_chrif(void);

+ 2 - 1
src/map/guild.c

@@ -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;
 }
 
 /**

+ 153 - 147
src/map/pc.c

@@ -1263,9 +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 = NULL;
-	sd->bonus_script_num = 0;
+	
+	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);
@@ -11039,6 +11039,16 @@ void pc_show_version(struct map_session_data *sd) {
 	clif_displaymessage(sd->fd,buf);
 }
 
+int pc_bonus_script_list(void *key, void *data, va_list ap) {
+	struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data;
+	struct map_session_data *sd = va_arg(ap, struct map_session_data *);
+	struct linkdb_node *node = (struct linkdb_node *)key;
+	if (sd)
+		ShowDebug("  cid=%d aid=%d\n",sd->status.char_id, sd->status.account_id);
+	ShowDebug("    key:%d e:0x%08X n:0x%08X nn:0x%08X np:0x%08X\n",(intptr_t)key, entry, key, node->next, node->prev);
+	return 1;
+}
+
 /**
  * Run bonus_script on player
  * @param sd
@@ -11047,22 +11057,32 @@ void pc_show_version(struct map_session_data *sd) {
 void pc_bonus_script(struct map_session_data *sd) {
 	uint8 i = 0;
 	int now = gettick();
+	struct linkdb_node *node = NULL, *next = NULL;
 
-	if (!sd || !sd->bonus_script_num)
+	if (!sd || !(node = sd->bonus_script.head))
 		return;
 
-	for (i = 0; i < sd->bonus_script_num; i++) {
-		if (sd->bonus_script[i]->tid == INVALID_TIMER) { // Start new timer for new bonus_script
-			sd->bonus_script[i]->tick += now;
-			sd->bonus_script[i]->tid = add_timer(sd->bonus_script[i]->tick, pc_bonus_script_timer, sd->bl.id, 0);
-			if (sd->bonus_script[i]->icon != SI_BLANK) //Gives status icon if exist
-				clif_status_change(&sd->bl, sd->bonus_script[i]->icon, 1, sd->bonus_script[i]->tick, 1, 0, 0);
-		}
-		if (!sd->bonus_script[i]->script) {
-			ShowError("pc_bonus_script: The script has been removed somewhere. \"%s\"\n", StringBuf_Value(sd->bonus_script[i]->script_buf));
-			continue;
+	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));
 		}
-		run_script(sd->bonus_script[i]->script, 0, sd->bl.id, 0);
+
+		node = next;
 	}
 }
 
@@ -11074,126 +11094,93 @@ void pc_bonus_script(struct map_session_data *sd) {
  * @param icon SI
  * @param flag Flags @see enum e_bonus_script_flags
  * @param type 0 - None, 1 - Buff, 2 - Debuff
- * @return True if added, False if cannot
+ * @return New created entry pointer or NULL if failed or NULL if duplicate fail
  * @author [Cydh]
  **/
-bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type) {
+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 false;
+		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 false;
+		return NULL;
 	}
 
-	if (!sd->bonus_script_num)
-		CREATE(sd->bonus_script, struct s_bonus_script *, 1);
-	else {
-		uint8 i = 0;
-		for (i = 0; i < sd->bonus_script_num; i++) {
-			if (strcmpi(script_str, StringBuf_Value(sd->bonus_script[i]->script_buf)) == 0)
-				break;
-		}
-
-		// Duplication checks
-		if (i < sd->bonus_script_num) {
-			int newdur = gettick() + dur;
-			if (flag&BSF_FORCE_REPLACE && sd->bonus_script[i]->tick < newdur) {
-				settick_timer(sd->bonus_script[i]->tid, newdur);
-				script_free_code(script);
-				return true;
-			}
-			else if (flag&BSF_FORCE_DUPLICATE) {
-				;
-			}
-			else {
-				// No duplicate bonus
-				script_free_code(script);
-				return false;
+	// 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;
 		}
-
-		if (i >= UINT8_MAX) {
-			ShowError("pc_bonus_script_add: Reached max (%d) possible bonuses for this player %d\n", UINT8_MAX);
-			script_free_code(script);
-			return false;
-		}
-
-		RECREATE(sd->bonus_script, struct s_bonus_script *, sd->bonus_script_num+1);
 	}
 
-	CREATE(sd->bonus_script[sd->bonus_script_num], struct s_bonus_script, 1);
-
-	sd->bonus_script[sd->bonus_script_num]->script_buf = StringBuf_Malloc();
-	StringBuf_AppendStr(sd->bonus_script[sd->bonus_script_num]->script_buf, script_str);
-	sd->bonus_script[sd->bonus_script_num]->tid = INVALID_TIMER;
-	sd->bonus_script[sd->bonus_script_num]->flag = flag;
-	sd->bonus_script[sd->bonus_script_num]->icon = icon;
-	sd->bonus_script[sd->bonus_script_num]->tick = dur; // Use duration first, on run change to expire time
-	sd->bonus_script[sd->bonus_script_num]->type = type;
-	sd->bonus_script[sd->bonus_script_num]->script = script;
-
-	sd->bonus_script_num++;
-	return true;
-}
-
-/**
- * Move bonus_script allocation to empty space
- * @param sd
- * @author [Cydh]
- **/
-static void pc_bonus_script_move(struct map_session_data *sd) {
-	if (sd && sd->bonus_script_num) {
-		uint8 i, cur;
-
-		for (i = 0, cur = 0; i < sd->bonus_script_num; i++) {
-			if (sd->bonus_script[i] == NULL)
-				continue;
-
-			if (i != cur)
-				sd->bonus_script[cur] = sd->bonus_script[i];
+	CREATE(entry, struct s_bonus_script_entry, 1);
 
-			cur++;
-		}
-
-		if (!(sd->bonus_script_num = cur)) {
-			aFree(sd->bonus_script);
-			sd->bonus_script = NULL;
-			sd->bonus_script_num = 0;
-		}
-	}
+	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 idx: Bonus script idx in player array
+* @param list: Bonus script entry from player
 * @author [Cydh]
 **/
-static void pc_bonus_script_remove(struct map_session_data *sd, uint8 idx) {
-	uint8 i = 0, cursor = 0;
+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 (!sd || !sd->bonus_script_num)
-		return;
+	if (entry->script)
+		script_free_code(entry->script);
 
-	if (idx >= sd->bonus_script_num) {
-		ShowError("pc_bonus_script_remove: Invalid index: %d\n", idx);
-		return;
-	}
-
-	if (sd->bonus_script[idx]->tid != INVALID_TIMER)
-		delete_timer(sd->bonus_script[idx]->tid, pc_bonus_script_timer);
-
-	if (sd->bonus_script[idx]->icon != SI_BLANK)
-		clif_status_load(&sd->bl, sd->bonus_script[idx]->icon, 0);
+	if (entry->script_buf)
+		StringBuf_Free(entry->script_buf);
 
-	script_free_code(sd->bonus_script[idx]->script);
-	StringBuf_Free(sd->bonus_script[idx]->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);
+}
 
-	aFree(sd->bonus_script[idx]);
-	sd->bonus_script[idx] = NULL;
+/**
+ * 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);
+	}
 }
 
 /**
@@ -11205,8 +11192,8 @@ static void pc_bonus_script_remove(struct map_session_data *sd, uint8 idx) {
 * @author [Cydh]
 **/
 int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) {
-	uint8 i;
 	struct map_session_data *sd;
+	struct s_bonus_script_entry *entry = (struct s_bonus_script_entry *)data;
 
 	sd = map_id2sd(id);
 	if (!sd) {
@@ -11214,21 +11201,17 @@ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) {
 		return 0;
 	}
 
-	if (tid == INVALID_TIMER || !sd->bonus_script_num)
+	if (tid == INVALID_TIMER)
 		return 0;
 
-	for (i = 0; i < sd->bonus_script_num; i++) {
-		if (sd->bonus_script[i]->tid == tid)
-			break;
-	}
-
-	if (i == sd->bonus_script_num) {
-		ShowError("pc_bonus_script_timer: Timer %d is not found.\n", tid);
+	if (!sd->bonus_script.head || entry == NULL) {
+		ShowError("pc_bonus_script_timer: Invalid entry pointer 0x%08X!\n", entry);
 		return 0;
 	}
 
-	pc_bonus_script_remove(sd, i);
-	pc_bonus_script_move(sd);
+	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;
 }
@@ -11240,27 +11223,37 @@ int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) {
 * @author [Cydh]
 **/
 void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) {
-	uint8 i, count = 0;
-	if (!sd || !sd->bonus_script_num)
+	struct linkdb_node *node = NULL;
+	uint16 count = 0;
+
+	if (!sd || !(node = sd->bonus_script.head))
 		return;
 
-	for (i = 0; i < sd->bonus_script_num; i++) {
-		if ((flag&sd->bonus_script[i]->flag) || // Matched flag
-			(sd->bonus_script[i]->type && (
-				(flag&BSF_REM_BUFF   && sd->bonus_script[i]->type == 1) || // Buff type
-				(flag&BSF_REM_DEBUFF && sd->bonus_script[i]->type == 2))   // 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
+				)
+			)
 		{
-			pc_bonus_script_remove(sd, i);
+			linkdb_erase(&sd->bonus_script.head, (void *)((intptr_t)entry));
+			pc_bonus_script_free_entry(sd, entry);
 			count++;
 		}
+
+		node = next;
 	}
-	if (count) {
-		pc_bonus_script_move(sd);
-		if (!(flag&BSF_REM_ON_LOGOUT)) { //Don't need to do this if log out
-			status_calc_pc(sd,SCO_NONE);
-		}
-	}
+
+	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);
 }
 
 /**
@@ -11270,21 +11263,34 @@ void pc_bonus_script_clear(struct map_session_data *sd, uint16 flag) {
 * @author [Cydh]
 **/
 void pc_bonus_script_clear_all(struct map_session_data *sd, uint8 flag) {
-	uint8 i, count = 0;
-	if (!sd || !sd->bonus_script_num)
+	struct linkdb_node *node = NULL;
+	uint16 count = 0;
+
+	if (!sd || !(node = sd->bonus_script.head))
 		return;
 
-	for (i = 0; i < sd->bonus_script_num; i++) {
-		if (!(flag&1) && (sd->bonus_script[i]->flag&BSF_PERMANENT))
-			continue;
-		pc_bonus_script_remove(sd, i);
-		count++;
-	}
-	if (count) {
-		pc_bonus_script_move(sd);
-		if (!(flag&2))
-			status_calc_pc(sd,SCO_NONE);
+	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;
 	}
+
+	pc_bonus_script_check_final(sd);
+
+	if (count && !(flag&2))
+		status_calc_pc(sd,SCO_NONE);
 }
 
 /** [Cydh]

+ 10 - 6
src/map/pc.h

@@ -158,7 +158,7 @@ struct s_pc_itemgrouphealrate {
 };
 
 ///Timed bonus 'bonus_script' struct [Cydh]
-struct s_bonus_script {
+struct s_bonus_script_entry {
 	struct script_code *script;
 	StringBuf *script_buf; //Used for comparing and storing on table
 	uint32 tick;
@@ -604,9 +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; ///Bonus Script [Cydh]
-	uint8 bonus_script_num;
-	
+
+	/// 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
 
@@ -1121,9 +1125,9 @@ 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(struct map_session_data *sd);
-bool pc_bonus_script_add(struct map_session_data *sd, const char *script_str, uint32 dur, enum si_type icon, uint16 flag, uint8 type);
+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, uint8 flag);
+int pc_bonus_script_list(void *key, void *data, va_list ap);
 
 void pc_cell_basilica(struct map_session_data *sd);
 

+ 5 - 2
src/map/script.c

@@ -18909,6 +18909,7 @@ BUILDIN_FUNC(bonus_script) {
 	uint8 type = 0;
 	TBL_PC* sd;
 	const char *script_str = NULL;
+	struct s_bonus_script_entry *entry = NULL;
 
 	if (script_hasdata(st,7)) {
 		if (!(sd = map_charid2sd(script_getnum(st,7)))) {
@@ -18942,8 +18943,10 @@ BUILDIN_FUNC(bonus_script) {
 	if (icon <= SI_BLANK || icon >= SI_MAX)
 		icon = SI_BLANK;
 
-	if (pc_bonus_script_add(sd, script_str, dur, (enum si_type)icon, flag, type))
+	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);
+	}
 	return SCRIPT_CMD_SUCCESS;
 }
 
@@ -18969,7 +18972,7 @@ BUILDIN_FUNC(bonus_script_clear) {
 	if (sd == NULL)
 		return SCRIPT_CMD_FAILURE;
 
-	pc_bonus_script_clear_all(sd,(flag ? 1 : 0));
+	pc_bonus_script_clear(sd,(flag ? BSF_PERMANENT : BSF_REM_ALL));
 	return SCRIPT_CMD_SUCCESS;
 }
 

+ 1 - 1
src/map/status.h

@@ -1762,7 +1762,7 @@ enum e_bonus_script_flags {
 	BSF_FORCE_DUPLICATE			= 0x800, ///< Force to add duplicated script
 
 	// 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
 };

+ 4 - 0
src/map/unit.c

@@ -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;
 		}