ソースを参照

Added new script_command for 'timed bonus/script' idea: http://rathena.org/board/topic/72463-suggest-to-add-a-script-command-run-item-script-inside-npc-script/
-- bonus_script "<script code>",<duration>{,<flag>{,<type>{,<char_id>}}};
-- see 'doc/script_commands.txt' for more details.
-- Thank everyone who discussed it

Signed-off-by: Cahyadi Ramadhan Togihon <house.bad@gmail.com>

Cahyadi Ramadhan Togihon 11 年 前
コミット
99098c19bc
9 ファイル変更280 行追加33 行削除
  1. 24 0
      doc/script_commands.txt
  2. 2 0
      src/map/clif.c
  3. 1 1
      src/map/map.c
  4. 127 21
      src/map/pc.c
  5. 16 1
      src/map/pc.h
  6. 68 6
      src/map/script.c
  7. 20 4
      src/map/skill.c
  8. 8 0
      src/map/status.c
  9. 14 0
      src/map/status.h

+ 24 - 0
doc/script_commands.txt

@@ -5059,6 +5059,30 @@ autobonus and autobonus3).
 
 ---------------------------------------
 
+*bonus_script "<script code>",<duration>{,<flag>{,<type>{,<char_id>}}};
+
+This command attachs script to player within specified 'duration' in second.
+
+flag:
+&1: Remove when dead
+&2: Removable by dispell
+&4: Removable by clearance
+[TODO] &8: Save script when player logout and continue when player login back
+
+type:
+0 = Buff
+1 = Debuff
+
+NOTE:
+- Cannot stack same bonus
+- Max. bonus_script for a player is 10 (pc.h MAX_PC_BONUS_SCRIPT)
+
+Example:
+- Apple gives you +5 Str bonus for 1 minute when it's consumed.
+  512,Apple,Apple,0,15,,20,,,,,0xFFFFFFFF,63,2,,,,,,{ bonus_script "{ bonus bStr,5; }",60; },{},{}
+
+---------------------------------------
+
 *skill <skill id>,<level>{,<flag>};
 *skill "<skill name>",<level>{,<flag>};
 *addtoskill <skill id>,<level>{,<flag>};

+ 2 - 0
src/map/clif.c

@@ -9877,6 +9877,7 @@ void clif_parse_QuitGame(int fd, struct map_session_data *sd)
 	{
 		set_eof(fd);
 		pc_damage_log_clear(sd,0);
+		pc_bonus_script_clear(sd);
 		clif_disconnect_ack(sd, 0);
 	} else {
 		clif_disconnect_ack(sd, 1);
@@ -10247,6 +10248,7 @@ void clif_parse_Restart(int fd, struct map_session_data *sd)
 			(!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
 		{	//Send to char-server for character selection.
 			pc_damage_log_clear(sd,0);
+			pc_bonus_script_clear(sd);
 			chrif_charselectreq(sd, session[fd]->client_addr);
 		} else {
 			clif_disconnect_ack(sd, 1);

+ 1 - 1
src/map/map.c

@@ -1716,7 +1716,7 @@ int map_quit(struct map_session_data *sd) {
 		elemental_clean_effect(sd->ed);
 		unit_remove_map(&sd->ed->bl,CLR_RESPAWN);
 	}
-
+	
 	unit_remove_map_pc(sd,CLR_RESPAWN);
 
 	if( map[sd->bl.m].instance_id ) { // Avoid map conflicts and warnings on next login

+ 127 - 21
src/map/pc.c

@@ -6920,6 +6920,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 		item_tmp.card[3]=GetWord(sd->status.char_id,1);
 		map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
 	}
+	
+	//Remove bonus_script when dead
+	pc_bonus_script_check(sd,BSF_REM_ON_DEAD);
 
 	// changed penalty options, added death by player if pk_mode [Valaris]
 	if(battle_config.death_penalty_type
@@ -7044,7 +7047,6 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 		}
 	}
 
-
 	//Reset "can log out" tick.
 	if( battle_config.prevent_logout )
 		sd->canlog_tick = gettick() - battle_config.prevent_logout;
@@ -10224,65 +10226,79 @@ void pc_itemcd_do(struct map_session_data *sd, bool load) {
 	return;
 }
 
+/**
+* Clear the dmglog data from player
+* @param sd
+* @param md
+**/
 void pc_clear_log_damage_sub(int char_id, struct mob_data *md)
 {
-	int i;
+	uint8 i;
 	ARR_FIND(0,DAMAGELOG_SIZE,i,md->dmglog[i].id == char_id);
-	if( i < DAMAGELOG_SIZE )
-	{
+	if (i < DAMAGELOG_SIZE) {
 		md->dmglog[i].id=0;
 		md->dmglog[i].dmg=0;
 		md->dmglog[i].flag=0;
 	}
 }
 
+/**
+* Add log to player's dmglog
+* @param sd
+* @param id Monster's id
+**/
 void pc_damage_log_add(struct map_session_data *sd, int id)
 {
-	int i = 0;
+	uint8 i = 0;
 
-	if( !sd )
+	if (!sd)
 		return;
 
-	for(i = 0; i < DAMAGELOG_SIZE_PC && sd->dmglog[i].id != id; i++)
-		if( !sd->dmglog[i].id )
-		{
+	for (i = 0; i < DAMAGELOG_SIZE_PC && sd->dmglog[i].id != id; i++)
+		if (!sd->dmglog[i].id) {
 			sd->dmglog[i].id = id;
 			break;
 		}
 	return;
 }
 
+/**
+* Clear dmglog data from player
+* @param sd
+* @param id Monster's id
+**/
 void pc_damage_log_clear(struct map_session_data *sd, int id)
 {
-	int i;
+	uint8 i;
 	struct mob_data *md = NULL;
-	if( !sd )
+	if (!sd)
 		return;
 
-	if( !id )
-	{
-		for(i = 0; i < DAMAGELOG_SIZE_PC; i++)	// track every id
-		{
+	if (!id) {
+		for (i = 0; i < DAMAGELOG_SIZE_PC; i++) {
 			if( !sd->dmglog[i].id )	//skip the empty value
 				continue;
 
-			if( (md = map_id2md(sd->dmglog[i].id)) )
+			if ((md = map_id2md(sd->dmglog[i].id)))
 				pc_clear_log_damage_sub(sd->status.char_id,md);
 		}
 		memset(sd->dmglog,0,sizeof(sd->dmglog));	// clear all
 	}
-	else
-	{
-		if( (md = map_id2md(id)) )
+	else {
+		if ((md = map_id2md(id)))
 			pc_clear_log_damage_sub(sd->status.char_id,md);
 
 		ARR_FIND(0,DAMAGELOG_SIZE_PC,i,sd->dmglog[i].id == id);	// find the id position
-		if( i < DAMAGELOG_SIZE_PC )
+		if (i < DAMAGELOG_SIZE_PC)
 			sd->dmglog[i].id = 0;
 	}
 }
 
-
+/**
+* Deposit some money to bank
+* @param sd
+* @param money Amount of money to deposit
+**/
 enum e_BANKING_DEPOSIT_ACK pc_bank_deposit(struct map_session_data *sd, int money) {
 	unsigned int limit_check = money+sd->status.bank_vault;
 	
@@ -10301,6 +10317,11 @@ enum e_BANKING_DEPOSIT_ACK pc_bank_deposit(struct map_session_data *sd, int mone
 	return BDA_SUCCESS;
 }
 
+/**
+* Withdraw money from bank
+* @param sd
+* @param money Amount of money that will be withdrawn
+**/
 enum e_BANKING_WITHDRAW_ACK pc_bank_withdraw(struct map_session_data *sd, int money) {
 	unsigned int limit_check = money+sd->status.zeny;
 	
@@ -10323,6 +10344,10 @@ enum e_BANKING_WITHDRAW_ACK pc_bank_withdraw(struct map_session_data *sd, int mo
 	return BWA_SUCCESS;
 }
 
+/**
+* Clear Cirmson Marker data from caster
+* @param sd
+**/
 void pc_crimson_marker_clear(struct map_session_data *sd) {
 	uint8 i;
 
@@ -10336,6 +10361,10 @@ void pc_crimson_marker_clear(struct map_session_data *sd) {
 	}
 }
 
+/**
+* Show version to player
+* @param sd
+**/
 void pc_show_version(struct map_session_data *sd) {
 	const char* svn = get_svn_revision();
 	const char* git = get_git_hash();
@@ -10350,6 +10379,83 @@ void pc_show_version(struct map_session_data *sd) {
 	clif_displaymessage(sd->fd,buf);
 }
 
+/** [Cydh]
+* Timer for bonus_script
+* @param tid
+* @param tick
+* @param id
+* @param data
+**/
+int pc_bonus_script_timer(int tid, unsigned int tick, int id, intptr_t data) {
+	uint8 i = (uint8)data;
+	struct map_session_data *sd;
+
+	sd = map_id2sd(id);
+	if (!sd) {
+		ShowDebug("pc_bonus_script_timer: Null pointer id: %d data: %d\n",id,data);
+		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);
+		return 0;
+	}
+
+	pc_bonus_script_remove(sd,i);
+	status_calc_pc(sd,false);
+	return 0;
+}
+
+/** [Cydh]
+* Remove bonus_script data from sd
+* @param sd target
+* @param i script index
+**/
+void pc_bonus_script_remove(struct map_session_data *sd, uint8 i) {
+	if (!sd || i < 0 || i >= MAX_PC_BONUS_SCRIPT)
+		return;
+
+	memset(&sd->bonus_script[i].script,0,sizeof(sd->bonus_script[i].script));
+	sd->bonus_script[i].script_str = NULL;
+	sd->bonus_script[i].tick = 0;
+	sd->bonus_script[i].tid = 0;
+	sd->bonus_script[i].flag = 0;
+}
+
+/** [Cydh]
+* Clear all active timer(s) of bonus_script data from sd
+* @param sd target
+* @param flag reason to remove the bonus_script
+**/
+void pc_bonus_script_check(struct map_session_data *sd, enum e_bonus_script_flag flag) {
+	uint8 i, count = 0;
+	if (!sd)
+		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) {
+			delete_timer(sd->bonus_script[i].tid,pc_bonus_script_timer);
+			pc_bonus_script_remove(sd,i);
+			count++;
+		}
+	}
+	if (count)
+		status_calc_pc(sd,false);
+}
+
+/** [Cydh]
+* Clear all active timer(s) of bonus_script data from sd
+* @param sd target
+**/
+void pc_bonus_script_clear(struct map_session_data *sd) {
+	uint8 i;
+	if (!sd)
+		return;
+	for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++)
+		if (&sd->bonus_script[i] && sd->bonus_script[i].tid)
+			delete_timer(sd->bonus_script[i].tid,pc_bonus_script_timer);
+}
+
 /*==========================================
  * pc Init/Terminate
  *------------------------------------------*/

+ 16 - 1
src/map/pc.h

@@ -24,6 +24,7 @@
 #define MAX_PC_SKILL_REQUIRE 5
 #define MAX_PC_FEELHATE 3
 #define DAMAGELOG_SIZE_PC 100	// Any idea for this value?
+#define MAX_PC_BONUS_SCRIPT 10
 
 //Update this max as necessary. 55 is the value needed for Super Baby currently
 //Raised to 84 since Expanded Super Novice needs it.
@@ -551,8 +552,17 @@ struct map_session_data {
 		uint8 count; //Count of target for skill like RL_D_TAIL
 	} c_marker;
 	bool flicker;
-};
 
+	//Timed bonus 'bonus_script' struct [Cydh]
+	struct s_script {
+		struct script_code *script;
+		const char *script_str; //Used for comparing and storing on table
+		uint32 tick;
+		uint8 flag;
+		bool isBuff; //Can be used for deciding which bonus that buff or debuff
+		int tid;
+	} bonus_script[MAX_PC_BONUS_SCRIPT];
+};
 
 enum weapon_type {
 	W_FIST,	//Bare hands
@@ -1025,6 +1035,11 @@ 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 map_session_data *sd, uint8 i);
+void pc_bonus_script_check(struct map_session_data *sd, enum e_bonus_script_flag flag);
+void pc_bonus_script_clear(struct map_session_data *sd);
+
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
 int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_race, uint32 mob_mode, int type);
 #endif

+ 68 - 6
src/map/script.c

@@ -17966,7 +17966,7 @@ BUILDIN_FUNC(party_destroy)
 *  1 - check by date
 * @return true/false
  *------------------------------------------*/
-BUILDIN_FUNC(is_clientver){
+BUILDIN_FUNC(is_clientver) {
 	TBL_PC *sd = NULL;
 	int type = script_getnum(st,2);
 	int data = script_getnum(st,3);
@@ -17997,7 +17997,7 @@ BUILDIN_FUNC(is_clientver){
 * Retrieves server definitions.
 * (see @type in const.txt)
  *------------------------------------------*/
-BUILDIN_FUNC(getserverdef){
+BUILDIN_FUNC(getserverdef) {
 	int type = script_getnum(st,2);
 	switch(type){
 		case 0: script_pushint(st,PACKETVER); break;
@@ -18094,6 +18094,70 @@ BUILDIN_FUNC(montransform) {
 	return 0;
 }
 
+/** [Cydh]
+ * bonus_script "<script code>",<duration>{,<flag>{,<type>{,<char_id>}}};
+ * @param "script code"
+ * @param duration
+ * @param flag
+ * @param char_id
+ **/
+BUILDIN_FUNC(bonus_script) {
+	uint8 i, flag = 0;
+	uint32 dur;
+	bool isBuff = true;
+	TBL_PC* sd;
+	const char *script_str = NULL;
+	struct script_code *script = NULL;
+
+	if (script_hasdata(st,6))
+		sd = map_charid2sd(script_getnum(st,6));
+	else
+		sd = script_rid2sd(st);
+
+	if (sd == NULL)
+		return 0;
+	
+	script_str = script_getstr(st,2);
+	dur = 1000 * abs(script_getnum(st,3));
+	FETCH(4,flag);
+	if (script_getnum(st,5) == 1)
+		isBuff = false;
+
+	if (!strlen(script_str) || !dur) {
+		//ShowWarning("buildin_bonus_script: Invalid value(s). Skipping...\n");
+		return 0;
+	}
+
+	//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 0;
+	}
+
+	if (!(script = parse_script(script_str,"bonus_script",0,1))) {
+		//ShowWarning("buildin_bonus_script: Failed to parse script '%s'. Skipping...\n",script_str);
+		return 0;
+	}
+
+	//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 (max: %d). Skipping...\n",MAX_PC_BONUS_SCRIPT);
+		return 0;
+	}
+
+	//Add the script data
+	sd->bonus_script[i].script_str = script_str;
+	sd->bonus_script[i].script = script;
+	sd->bonus_script[i].tick = gettick() + dur;
+	sd->bonus_script[i].flag = flag;
+	sd->bonus_script[i].isBuff = isBuff;
+
+	status_calc_pc(sd,false);
+	return 0;
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.c
@@ -18543,9 +18607,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(breakequip,"i"),
 	BUILDIN_DEF(sit,"?"),
 	BUILDIN_DEF(stand,"?"),
-	/**
-	 * @commands (script based)
-	 **/
+	//@commands (script based)
 	BUILDIN_DEF(bindatcmd, "ss??"),
 	BUILDIN_DEF(unbindatcmd, "s"),
 	BUILDIN_DEF(useatcmd, "s"),
@@ -18573,8 +18635,8 @@ struct script_function buildin_func[] = {
 
 	BUILDIN_DEF(is_clientver,"ii?"),
 	BUILDIN_DEF(getserverdef,"i"),
-
 	BUILDIN_DEF2(montransform, "transform", "vii????"), // Monster Transform [malufett/Hercules]
+	BUILDIN_DEF(bonus_script,"si???"),
 
 #include "../custom/script_def.inc"
 

+ 20 - 4
src/map/skill.c

@@ -6822,7 +6822,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 				break;
 			}
-			if(status_isimmune(bl) || !tsc || !tsc->count)
+			if(status_isimmune(bl))
+				break;
+
+			//Remove bonus_script when dispelled
+			if (dstsd)
+				pc_bonus_script_check(dstsd,BSF_REM_ON_DISPELL);
+
+			if(!tsc || !tsc->count)
 				break;
 
 			if( sd && dstsd && !map_flag_vs(sd->bl.m) && sd->status.guild_id == dstsd->status.guild_id ) {
@@ -6830,8 +6837,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				break;
 			}
 
-			for(i=0;i<SC_MAX;i++)
-			{
+			for(i=0;i<SC_MAX;i++) {
 				if (!tsc->data[i])
 					continue;
 				switch (i) {
@@ -6912,6 +6918,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			}
 			break;
 		}
+
 		//Affect all targets on splash area.
 		map_foreachinrange(skill_area_sub, bl, i, BL_CHAR,
 			src, skill_id, skill_lv, tick, flag|1,
@@ -8316,7 +8323,15 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 				break;
 			}
-			if(status_isimmune(bl) || !tsc || !tsc->count)
+			
+			if(status_isimmune(bl))
+				break;
+
+			//Remove bonus_script when cleared
+			if (dstsd)
+				pc_bonus_script_check(dstsd,BSF_REM_ON_CLEARANCE);
+
+			if(!tsc || !tsc->count)
 				break;
 			for( i = 0; i < SC_MAX; i++ ) {
 				if (!tsc->data[i])
@@ -8383,6 +8398,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			}
 			break;
 		}
+
 		map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, tick, flag|1, skill_castend_damage_id);
 		break;
 

+ 8 - 0
src/map/status.c

@@ -2787,6 +2787,14 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
 			run_script(data->script,0,sd->bl.id,0);
 	}
 
+	for (i = 0; i < MAX_PC_BONUS_SCRIPT; i++) { // Script Bonus
+		if (!(&sd->bonus_script[i]) || !sd->bonus_script[i].script)
+			continue;
+		if (!sd->bonus_script[i].tid) //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);
+	}
+
 	if( sd->pd ) { // Pet Bonus
 		struct pet_data *pd = sd->pd;
 		if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly )

+ 14 - 0
src/map/status.h

@@ -1670,6 +1670,20 @@ enum scb_flag
 	SCB_ALL		= 0x3FFFFFFF
 };
 
+///Enum for bonus_script's flag
+enum e_bonus_script_flag {
+	BSF_REM_ON_DEAD			= 0x1,
+	BSF_REM_ON_DISPELL		= 0x2,
+	BSF_REM_ON_CLEARANCE	= 0x4,
+	//BSF_DB_SAVE				= 0x8, //TODO!
+};
+
+///Enum for bonus_script's type
+enum e_bonus_script_type {
+	BST_BUFF	= 0,
+	BST_DEBUFF	= 1,
+};
+
 //Define to determine who gets HP/SP consumed on doing skills/etc. [Skotlex]
 #define BL_CONSUME (BL_PC|BL_HOM|BL_MER|BL_ELEM)
 //Define to determine who has regen