Jelajahi Sumber

setpcblock script command (#4052)

* Added setpcblock and getpcblock script commands.
'setpcblock' command prevents/allows the player from doing the given type of action.
'getpcblock' command return the bit-mask value of the currently enabled block flags.

The available type are:
	PCBLOCK_MOVE
	PCBLOCK_ATTACK
	PCBLOCK_SKILL
	PCBLOCK_USEITEM
	PCBLOCK_CHAT
	PCBLOCK_IMMUNE
	PCBLOCK_SITSTAND
	PCBLOCK_COMMANDS
	PCBLOCK_NPCCLICK
	PCBLOCK_EMOTION
	PCBLOCK_ALL

Thanks to @sigtus, @secretdataz, @Lemongrass3110 and @aleos89 for the help and reviews !
Credit to https://github.com/HerculesWS/Hercules/pull/842 for the idea.
Atemo 6 tahun lalu
induk
melakukan
7dde174c83

+ 4 - 1
conf/msg_conf/map_msg.conf

@@ -856,7 +856,10 @@
 
 793: Usage @camerainfo range rotation latitude
 
-//794-899 free
+// pcblock command
+794: This action is currently blocked.
+
+//795-899 free
 
 //------------------------------------
 // More atcommands message

+ 51 - 0
doc/script_commands.txt

@@ -6133,6 +6133,57 @@ Examples:
 
 ---------------------------------------
 
+*setpcblock <type>,<state>{,<account ID>};
+*getpcblock {<account ID>};
+
+'setpcblock' command prevents/allows the player from doing the given <type> of action according
+to the <state> during the player session (note: @reloadscript also removes all <type>).
+The <type> values are bit-masks, multiples of <type> can be added to change the player action.
+
+The action is blocked when the <state> is true, while false allows the action again.
+
+'getpcblock' command return the bit-mask value of the currently
+enabled block flags.
+
+The available <type> are:
+	PCBLOCK_MOVE
+	PCBLOCK_ATTACK
+	PCBLOCK_SKILL
+	PCBLOCK_USEITEM
+	PCBLOCK_CHAT
+	PCBLOCK_IMMUNE
+	PCBLOCK_SITSTAND
+	PCBLOCK_COMMANDS
+	PCBLOCK_NPCCLICK
+	PCBLOCK_EMOTION
+	PCBLOCK_ALL
+
+Examples:
+
+// Make the attached player invulnerable to monster (same as @monsterignore)
+	setpcblock PCBLOCK_IMMUNE, true;
+
+// Prevents the attached player from attacking and using skills
+	setpcblock PCBLOCK_ATTACK|PCBLOCK_SKILL, true;
+
+// Re-enables attack, skills and item use
+	setpcblock PCBLOCK_ATTACK|PCBLOCK_SKILL|PCBLOCK_USEITEM, false;
+
+// getpcblock related checks
+	if (getpcblock() & PCBLOCK_IMMUNE)
+		mes "You are invulnerable!";
+
+	if (getpcblock() & (PCBLOCK_MOVE|PCBLOCK_SITSTAND))
+		mes "You can't walk or sit.";
+
+	if ((getpcblock() & (PCBLOCK_ATTACK|PCBLOCK_SKILL)) == 0)
+		mes "You can attack and use skills.";
+
+	if (getpcblock() & PCBLOCK_CHAT)
+		mes "You can't chat.";
+
+---------------------------------------
+
 ==================================
 |5.- Mob / NPC -related commands.|
 ==================================

+ 17 - 10
src/map/atcommand.cpp

@@ -3922,6 +3922,7 @@ ACMD_FUNC(reload) {
 		for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) ){
 			pc_close_npc(pl_sd,1);
 			clif_cutin(pl_sd, "", 255);
+			pl_sd->state.block_action = 0;
 		}
 		mapit_free(iter);
 
@@ -5977,7 +5978,7 @@ ACMD_FUNC(autotrade) {
 
 	sd->state.autotrade = 1;
 	if (battle_config.autotrade_monsterignore)
-		sd->state.monster_ignore = 1;
+		sd->state.block_action |= PCBLOCK_IMMUNE;
 
 	if( sd->state.vending ){
 		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_table, sd->vender_id ) != SQL_SUCCESS ){
@@ -6634,7 +6635,7 @@ ACMD_FUNC(npctalk)
 	bool ifcolor=(*(command + 8) != 'c' && *(command + 8) != 'C')?0:1;
 	unsigned long color=0;
 
-	if (sd->sc.cant.chat)
+	if (sd->sc.cant.chat || (sd->state.block_action & PCBLOCK_CHAT))
 		return -1; //no "chatting" while muted.
 
 	if(!ifcolor) {
@@ -6683,7 +6684,7 @@ ACMD_FUNC(pettalk)
 		return -1;
 	}
 
-	if (sd->sc.cant.chat)
+	if (sd->sc.cant.chat || (sd->state.block_action & PCBLOCK_CHAT))
 		return -1; //no "chatting" while muted.
 
 	if (!message || !*message || sscanf(message, "%99[^\n]", mes) < 1) {
@@ -7567,7 +7568,7 @@ ACMD_FUNC(homtalk)
 		sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
 	}
 
-	if (sd->sc.cant.chat)
+	if (sd->sc.cant.chat || (sd->state.block_action & PCBLOCK_CHAT))
 		return -1; //no "chatting" while muted.
 
 	if ( !hom_is_active(sd->hd) ) {
@@ -7975,7 +7976,7 @@ ACMD_FUNC(me)
 	memset(tempmes, '\0', sizeof(tempmes));
 	memset(atcmd_output, '\0', sizeof(atcmd_output));
 
-	if (sd->sc.cant.chat)
+	if (sd->sc.cant.chat || (sd->state.block_action & PCBLOCK_CHAT))
 		return -1; //no "chatting" while muted.
 
 	if (!message || !*message || sscanf(message, "%255[^\n]", tempmes) < 0) {
@@ -8095,12 +8096,12 @@ ACMD_FUNC(monsterignore)
 {
 	nullpo_retr(-1, sd);
 
-	if (!sd->state.monster_ignore) {
-		sd->state.monster_ignore = 1;
-		clif_displaymessage(sd->fd, msg_txt(sd,1305)); // You are now immune to attacks.
-	} else {
-		sd->state.monster_ignore = 0;
+	if (sd->state.block_action & PCBLOCK_IMMUNE) {
+		sd->state.block_action &= ~PCBLOCK_IMMUNE;
 		clif_displaymessage(sd->fd, msg_txt(sd,1306)); // Returned to normal state.
+	} else {
+		sd->state.block_action |= PCBLOCK_IMMUNE;
+		clif_displaymessage(sd->fd, msg_txt(sd,1305)); // You are now immune to attacks.
 	}
 
 	return 0;
@@ -10564,6 +10565,12 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
 	if( sscanf(atcmd_msg, "%255s %255[^\n]", command, params) < 2 )
 		params[0] = '\0';
 
+	if (type == 1 && (sd->state.block_action & PCBLOCK_COMMANDS)) {
+		sprintf(output,msg_txt(sd,154), command); // %s failed.
+		clif_displaymessage(fd, output);
+		return true;
+	}
+
 	// @commands (script based)
 	if((type == 1 || type == 3) && atcmd_binding_count > 0) {
 		struct atcmd_binding_data *binding = get_atcommandbind_byname(command);

+ 1 - 1
src/map/battle.cpp

@@ -7769,7 +7769,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 			sd = BL_CAST(BL_PC, t_bl);
 			sc = status_get_sc(t_bl);
 
-			if( (sd->state.monster_ignore || (sc->data[SC_KINGS_GRACE] && s_bl->type != BL_PC)) && flag&BCT_ENEMY )
+			if( ((sd->state.block_action & PCBLOCK_IMMUNE) || (sc->data[SC_KINGS_GRACE] && s_bl->type != BL_PC)) && flag&BCT_ENEMY )
 				return 0; // Global immunity only to Attacks
 			if( sd->status.karma && s_bl->type == BL_PC && ((TBL_PC*)s_bl)->status.karma )
 				state |= BCT_ENEMY; // Characters with bad karma may fight amongst them

+ 4 - 1
src/map/buyingstore.cpp

@@ -700,7 +700,10 @@ void do_init_buyingstore_autotrade( void ) {
 				CREATE(at->sd, struct map_session_data, 1);
 				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
 				at->sd->state.autotrade = 1|4;
-				at->sd->state.monster_ignore = (battle_config.autotrade_monsterignore);
+				if (battle_config.autotrade_monsterignore)
+					at->sd->state.block_action |= PCBLOCK_IMMUNE;
+				else
+					at->sd->state.block_action &= ~PCBLOCK_IMMUNE;
 				chrif_authreq(at->sd, true);
 				uidb_put(buyingstore_autotrader_db, at->char_id, at);
 			}

+ 23 - 2
src/map/clif.cpp

@@ -10100,7 +10100,7 @@ static bool clif_process_message(struct map_session_data* sd, bool whisperFormat
 	if( is_atcommand( fd, sd, out_message, 1 )  )
 		return false;
 
-	if (sd->sc.cant.chat)
+	if (sd->sc.cant.chat || (sd->state.block_action & PCBLOCK_CHAT))
 		return false; //no "chatting" while muted.
 
 	if( battle_config.min_chat_delay ) { //[Skotlex]
@@ -11102,6 +11102,11 @@ void clif_parse_Emotion(int fd, struct map_session_data *sd)
 		if (battle_config.idletime_option&IDLE_EMOTION)
 			sd->idletime = last_tick;
 
+		if (sd->state.block_action & PCBLOCK_EMOTION) {
+			clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+			return;
+		}
+
 		if(battle_config.client_reshuffle_dice && emoticon>=ET_DICE1 && emoticon<=ET_DICE6) {// re-roll dice
 			emoticon = rnd()%6+ET_DICE1;
 		}
@@ -11202,6 +11207,11 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		)) //No sitting during these states either.
 			break;
 
+		if (sd->state.block_action & PCBLOCK_SITSTAND) {
+			clif_displaymessage(sd->fd, msg_txt(sd,794)); // This action is currently blocked.
+			break;
+		}
+
 		if (battle_config.idletime_option&IDLE_SIT)
 			sd->idletime = last_tick;
 
@@ -11219,6 +11229,11 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
 		if (sd->sc.opt1 && sd->sc.opt1 != OPT1_STONEWAIT && sd->sc.opt1 != OPT1_BURNING)
 			break;
 
+		if (sd->state.block_action & PCBLOCK_SITSTAND) {
+			clif_displaymessage(sd->fd, msg_txt(sd,794)); // This action is currently blocked.
+			break;
+		}
+
 		if (pc_setstand(sd, false)) {
 			if (battle_config.idletime_option&IDLE_SIT)
 				sd->idletime = last_tick;
@@ -11501,7 +11516,7 @@ void clif_parse_UseItem(int fd, struct map_session_data *sd)
 		return;
 	}
 
-	if ( (!sd->npc_id && pc_istrading(sd)) || sd->chatID )
+	if ( (!sd->npc_id && pc_istrading(sd)) || sd->chatID || (sd->state.block_action & PCBLOCK_USEITEM) )
 		return;
 
 	//Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
@@ -12215,6 +12230,9 @@ void clif_parse_skill_toid( struct map_session_data* sd, uint16 skill_id, uint16
 	if (inf&INF_GROUND_SKILL || !inf)
 		return; //Using a ground/passive skill on a target? WRONG.
 
+	if (sd->state.block_action & PCBLOCK_SKILL)
+		return;
+
 	if( SKILL_CHK_HOMUN(skill_id) ) {
 		clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id);
 		return;
@@ -12328,6 +12346,9 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin
 	if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) )
 		return; //Using a target skill on the ground? WRONG.
 
+	if (sd->state.block_action & PCBLOCK_SKILL)
+		return;
+
 	if( SKILL_CHK_HOMUN(skill_id) ) {
 		clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
 		return;

+ 9 - 1
src/map/npc.cpp

@@ -984,6 +984,9 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
 	//if(sd->npc_id)
 	//	return 1;
 
+	if (sd->state.block_action & PCBLOCK_NPCCLICK)
+		return 0;
+
 	struct map_data *mapdata = map_getmapdata(m);
 
 	for(i=0;i<mapdata->npc_num;i++)
@@ -1284,7 +1287,12 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd)
 	//Hidden/Disabled npc.
 	if (nd->class_ < 0 || nd->sc.option&(OPTION_INVISIBLE|OPTION_HIDE))
 		return 1;
-	
+
+	if (sd->state.block_action & PCBLOCK_NPCCLICK) {
+		clif_msg(sd, WORK_IN_PROGRESS);
+		return 1;
+	}
+
 	switch(nd->subtype) {
 		case NPCTYPE_SHOP:
 			clif_npcbuysell(sd,nd->bl.id);

+ 3 - 0
src/map/pc.cpp

@@ -9346,6 +9346,9 @@ bool pc_can_attack( struct map_session_data *sd, int target_id ) {
 	if( pc_is90overweight(sd) || pc_isridingwug(sd) )
 		return false;
 
+	if (sd->state.block_action & PCBLOCK_ATTACK)
+		return false;
+
 	if( sd->sc.data[SC_BASILICA] ||
 		sd->sc.data[SC__SHADOWFORM] ||
 		sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||

+ 1 - 1
src/map/pc.hpp

@@ -257,7 +257,6 @@ struct map_session_data {
 		unsigned int noask :1; // [LuzZza]
 		unsigned int trading :1; //[Skotlex] is 1 only after a trade has started.
 		unsigned int deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE
-		unsigned int monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
 		unsigned int size :2; // for tiny/large types
 		unsigned int night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
 		unsigned int using_fake_npc :1;
@@ -295,6 +294,7 @@ struct map_session_data {
 		bool mail_writing; // Whether the player is currently writing a mail in RODEX or not
 		bool cashshop_open;
 		bool sale_open;
+		unsigned int block_action : 10;
 	} state;
 	struct {
 		unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;

+ 28 - 0
src/map/script.cpp

@@ -17245,6 +17245,32 @@ BUILDIN_FUNC(pcblockskill)
 	return SCRIPT_CMD_SUCCESS;
 }
 
+BUILDIN_FUNC(setpcblock)
+{
+	TBL_PC *sd;
+
+	if (script_mapid2sd(4, sd)) {
+		enum e_pcblock_action_flag type = (e_pcblock_action_flag)script_getnum(st, 2);
+
+		if (script_getnum(st, 3) > 0)
+			sd->state.block_action |= type;
+		else
+			sd->state.block_action &= ~type;
+	}
+	return SCRIPT_CMD_SUCCESS;
+}
+
+BUILDIN_FUNC(getpcblock)
+{
+	TBL_PC *sd;
+
+	if (script_mapid2sd(2, sd))
+		script_pushint(st, sd->state.block_action);
+	else
+		script_pushint(st, 0);
+	return SCRIPT_CMD_SUCCESS;
+}
+
 BUILDIN_FUNC(pcfollow)
 {
 	TBL_PC *sd;
@@ -24505,6 +24531,8 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF2(pcblockmove,"unitblockmove","ii"),
 	BUILDIN_DEF(pcblockskill,"ii"),
 	BUILDIN_DEF2(pcblockskill,"unitblockskill","ii"),
+	BUILDIN_DEF(setpcblock, "ii?"),
+	BUILDIN_DEF(getpcblock, "?"),
 	// <--- [zBuffer] List of player cont commands
 	// [zBuffer] List of unit control commands --->
 	BUILDIN_DEF(unitexists,"i"),

+ 17 - 0
src/map/script.hpp

@@ -1898,6 +1898,23 @@ enum e_hat_effects {
 	HAT_EF_MAX
 };
 
+/**
+ * Player blocking actions related flags.
+ */
+enum e_pcblock_action_flag : uint16 {
+	PCBLOCK_MOVE     = 0x001,
+	PCBLOCK_ATTACK   = 0x002,
+	PCBLOCK_SKILL    = 0x004,
+	PCBLOCK_USEITEM  = 0x008,
+	PCBLOCK_CHAT     = 0x010,
+	PCBLOCK_IMMUNE   = 0x020,
+	PCBLOCK_SITSTAND = 0x040,
+	PCBLOCK_COMMANDS = 0x080,
+	PCBLOCK_NPCCLICK = 0x100,
+	PCBLOCK_EMOTION  = 0x200,
+	PCBLOCK_ALL      = 0x3FF,
+};
+
 /**
  * used to generate quick script_array entries
  **/

+ 13 - 0
src/map/script_constants.hpp

@@ -7210,6 +7210,19 @@
 	/* timer related */
 	export_constant(INFINITE_TICK);
 
+	/* block action */
+	export_constant(PCBLOCK_MOVE);
+	export_constant(PCBLOCK_ATTACK);
+	export_constant(PCBLOCK_SKILL);
+	export_constant(PCBLOCK_USEITEM);
+	export_constant(PCBLOCK_CHAT);
+	export_constant(PCBLOCK_IMMUNE);
+	export_constant(PCBLOCK_SITSTAND);
+	export_constant(PCBLOCK_COMMANDS);
+	export_constant(PCBLOCK_NPCCLICK);
+	export_constant(PCBLOCK_EMOTION);
+	export_constant(PCBLOCK_ALL);
+
 	#undef export_constant
 	#undef export_constant2
 	#undef export_parameter

+ 1 - 1
src/map/unit.cpp

@@ -1376,7 +1376,7 @@ int unit_can_move(struct block_list *bl) {
 	if (DIFF_TICK(ud->canmove_tick, gettick()) > 0)
 		return 0;
 
-	if ((sd && (pc_issit(sd) || sd->state.vending || sd->state.buyingstore)) || ud->state.blockedmove)
+	if ((sd && (pc_issit(sd) || sd->state.vending || sd->state.buyingstore || (sd->state.block_action & PCBLOCK_MOVE))) || ud->state.blockedmove)
 		return 0; // Can't move
 
 	// Status changes that block movement

+ 4 - 1
src/map/vending.cpp

@@ -599,7 +599,10 @@ void do_init_vending_autotrade(void)
 				CREATE(at->sd, struct map_session_data, 1);
 				pc_setnewpc(at->sd, at->account_id, at->char_id, 0, gettick(), at->sex, 0);
 				at->sd->state.autotrade = 1|2;
-				at->sd->state.monster_ignore = (battle_config.autotrade_monsterignore);
+				if (battle_config.autotrade_monsterignore)
+					at->sd->state.block_action |= PCBLOCK_IMMUNE;
+				else
+					at->sd->state.block_action &= ~PCBLOCK_IMMUNE;
 				chrif_authreq(at->sd, true);
 				uidb_put(vending_autotrader_db, at->char_id, at);
 			}