reactOnKillSteal: реакция на киллстил

Дополнения к OpenKore, расширяющиее её функциональность, называются плагинами.

Модератор: 4epT

Аватара пользователя
Kissa2k
Профессионал
Сообщения: 1304
Зарегистрирован: Пн дек 04, 2006 8:33 pm

reactOnKillSteal: реакция на киллстил

Сообщение Kissa2k »

Попробуем возродить старый плагин из пыли забвения :)
Версия 2.0.1, похоже, утеряна навсегда, поэтому этот плагин будет базироваться на 1.5.0.
Разумеется с улучшениями для последних на этот момент версий Openkore (svn-ревизии 8445 и выше).
Скачать:
reactOnKillSteal.zip
(3.38 КБ) 76 скачиваний
Настройки плагина в config.txt:
  • reactOnKillSteal (boolean flag)
    - enables/disables the plugin.
  • reactOnKillSteal_timeout (seconds)
    - specifies the minimum time to wait before reacting again.
  • reactOnKillSteal_timeout (seconds)
    - specifies the minimum time to wait before reacting again.
  • reactOnKillSteal_timeoutSeed (seconds)
    - if this is set, the timeout will be increased by a random value less than or equal to the specified time (in seconds). Use this to prevent Kore from reacting in fixed time intervals, which makes it more suspicious.
  • reactOnKillSteal_forgetReactions (seconds)
    - if this option is set, the number of reactions you executed for a certain player will be forgotten if the specified number of seconds has elapsed since the last reaction to that player was executed.
  • reactOnKillSteal (commands)
    commands: a double semi-colon ;; separated list of console commands.
    A random command out of this list will be executed if the conditions are met for this block. This command will be executed the first time you detect a kill steal hit to a monster.

    reactOnKillSteal Attributes:
    • reactions (range)
      - if this option is set, only react if the total number of your reactions to the player due to kill stealing is within the specified range. This is useful for choosing the right flavor of reactions based on how many times you have already reacted. Use this to switch between reactOnKillSteal blocks.
    • attackTargetOnly (boolean flag)
      - by default, Kore will react when all monsters who are currently attacking Kore (aggressives) is kill stealed. Set this option to 1 to ignore kill stealing of monsters that you are not attacking.
    • damage (range)
      - if this option is set, only react when the damage done to monster due to the kill steal attack is within the specified range. If this is not set Kore will react on kill steals regardless of the damage. If you want to react only on misses, set this to "0".
    • isSkill (boolean flag)
      - if this option is not specified, Kore will react regardless of the type of action done to kill steal your monster. Set this to 1 if you want to react only if a skill is used. Otherwise set this to 0 if you don't want to react on skills.
    • skills (list of skills)
      skills: a comma-separated list of skill names.
      - if this option is set, only react when the skill used againts our monster is in this list.
    • notSkills (list of skills)
      skills: a comma-separated list of skill names.
      - if this option is set, never react when the skill used againts our monster is in this list.
    • notTankModeTarget (name)
      - if this option is set, Kore will not react on beeing kill stealed by player that specified as tank.
    • isCasting (boolean flag)
      - if this option is not specified, Kore will react whether a skill is being cast or not. Set this to 1 if you want to react only if a skill is being cast. Otherwise set this to 0 if you don't want to react on skill casting.
    • monster_name (list of names)
      - if this option is set, Kore will react only if the name of the monster being kill stealed is in this list.
    • monster_notName (list of names)
      - if this option is set, Kore will never react if the name of the monster being kill stealed is in this list.
    • monster_whenStatusActive (list of statuses)
      - if this option is set, Kore will react only if one of the statuses in this list is currently active on the monster being kill stealed.
    • monster_whenStatusInactive (list of statuses)
      - if this option is set, Kore will react only if all statuses in this list is not active on the monster being kill stealed.
    • monster_whenGround (list of ground spells)
      - if this option is set, Kore will react only if one of the ground spells in this list is currently active on the ground where the monster is standing.
    • monster_whenNotGround (list of ground spells)
      - if this option is set, Kore will react only if all ground spells in this list is not active on the ground where the monster is standing.
    • monster_dist (range)
      - if this option is set, Kore will react only if the distance between you and the monster being kill stealed is within the specified range.
    • player_id (list of IDs)
      - if this option is set, Kore will react only if the ID of the kill stealer is in this list.
    • player_notId (list of IDs)
      - if this option is set, Kore will never react if the ID of the kill stealer is in this list.
    • player_name (list of names)
      - if this option is set, Kore will react only if the name of the kill stealer is in this list.
    • player_notName (list of names)
      - if this option is set, Kore will never react if the name of the kill stealer is in this list.
    • player_whenStatusActive (list of statuses)
      - if this option is set, Kore will react only if one of the statuses in this list is currently active on the kill stealer.
    • player_whenStatusInactive (list of statuses)
      - if this option is set, Kore will react only if all statuses in this list is not active on the kill stealer.
    • player_whenGround (list of ground spells)
      - if this option is set, Kore will react only if one of the ground spells in this list is currently active on the ground where the kill stealer is standing.
    • player_whenNotGround (list of ground spells)
      - if this option is set, Kore will react only if all ground spells in this list is not active on the ground where the kill stealer is standing.
    • player_lvl (range)
      - if this option is set, Kore will react only if the level of the kill stealer is within the specified range.
    • player_dist (range)
      - if this option is set, Kore will react only if the distance between you and the kill stealer is within the specified range.
    • player_isJob (list of job classes)
      - if this option is set, Kore will react only if the job of the kill stealer is in this list.
    • player_isNotJob (list of job classes)
      - if this option is set, Kore will never react if the job of the kill stealer is in this list.
    • player_inGuild (list of guild names)
      - if this option is set, Kore will react only if the name of the guild of the kill stealer is in this list.
    • player_notInGuild (list of guild names)
      - if this option is set, Kore will never react if the name of the guild of the kill stealer is in this list.
    • player_sex (flag)
      flag: 1 = Boy, 0 = Girl
      - if this option is set, Kore will react only if the kill stealer is of the specified sex.
    • player_damage (range)
      - if this option is set, only react when the total damage done by the kill stealer to the monster is within the specified range.
    • player_misses (range)
      - if this option is set, only react when the total number of misses by the kill stealer to the monster is within the specified range.
    • player_ksCount (range)
      - only react if the number of unique monsters kill stealed by this player from you is within the given range. This is useful to determine how you will react on players who keep on kill stealing your monster no matter how you react. For example, if this count reaches 5 or more, you may conclude that the kill stealer is a ks-bot who just ignores your warnings. You may also configure the intensity of your reactions on such events.
Special keywords for commands
To be able to maximize the power offered by console commands used in this plugin, some keywords can be used with commands. These are useful when using the "pm" command, as well as skill use commands.
  • @monsterNum - this keyword will be replaced by the number of monster being kill stealed.
  • @monsterName - this keyword will be replaced by the name of monster being kill stealed.
  • @playerNum - this keyword will be replaced by the player number of kill stealer.
  • @playerName - this keyword will be replaced by the player name of kill stealer.
Примеры использования:

Код: Выделить всё

reactOnKillSteal e omg;;c это мой монстр {
	reactions 0
	player_ksCount < 3
	attackTargetOnly 1
}

reactOnKillSteal c монстр мой!;;pm @playerName киллстилер! {
	reactions 1
	player_ksCount < 3
	attackTargetOnly 1
	notSkills Lex Aeterna, Decrease AGI
}
Плагин:

Код: Выделить всё

# reactOnKillSteal
# This plugin is licensed under the GNU GPL.
# original code by hakore (hakore@users.sourceforge.net)
# ported to 2.1 by Kissa2k
# (c) 2013 GPL

package reactOnKillSteal;

use strict;
use Plugins;
use Globals;
use Utils;
use Log qw(message error warning debug);
use Commands;
use Misc;

my %ksCounts;
my %ksReactions;
my $forget_time;


Plugins::register('reactOnKillSteal', 'react when kill stealed', \&Unload);
my $hooks = Plugins::addHooks(
            ['is_casting', \&onParseMsg, undef],
            ['packet_skilluse', \&onParseMsg, undef],
            ['packet_attack', \&onParseMsg, undef],
            ['AI_pre', \&onAIpre, undef]
);

sub Unload {
	Plugins::delHooks($hooks);
	message "reactOnKillSteal plugin unloaded.\n";
};

sub onAIpre {
	return unless ($main::conState == 5);
	return if (!$config{'reactOnKillSteal'});
	my $command = $ai_v{temp}{reactOnKillSteal}{command};

	if ($command && timeOut($ai_v{temp}{reactOnKillSteal}{time}, $ai_v{temp}{reactOnKillSteal}{timeOut_delay})) {
		if (!Commands::run($command)) {
			main::parseCommand($command) if (defined &main::parseCommand);
		}
		$ai_v{temp}{reactOnKillSteal}{isEmoticon} = 1 if ($command =~ /^e\s/);
		undef $ai_v{temp}{reactOnKillSteal}{command};
		$ai_v{temp}{reactOnKillSteal}{time} = time;
	}

	if ($config{'reactOnKillSteal_forgetReactions'} && timeOut($forget_time, 30)) {
		foreach (keys %ksReactions) {
			if (timeOut($ksReactions{$_}{'time'}, $config{'reactOnKillSteal_forgetReactions'})) {
				delete $ksReactions{$_};
			}
		}
		$forget_time = time;
	}
}

sub onParseMsg {
	return unless ($main::conState == 5);
	return if (!$config{'reactOnKillSteal'});
	my ($trigger, $args) = @_;

	my $ID1 = $args->{sourceID};
	my $ID2 = $args->{targetID};

	if ($ID1 eq $accountID && $monsters{$ID2}) {
		# You attack monster
		# If monster is untouched mark it so we know we need to react if this is kill stealed
		$monsters{$ID2}{reactOnKillSteal} = 1 if !$monsters{$ID2}{reactOnKillSteal} && checkMonsterUnengaged($ID2);

	} elsif ($ID2 eq $accountID && $monsters{$ID1}) {
		# Monster attacks you
		# If monster is untouched mark it so we know we need to react if this is kill stealed
		$monsters{$ID1}{reactOnKillSteal} = 1 if !$monsters{$ID1}{reactOnKillSteal} && checkMonsterUnengaged($ID1);

	} elsif (
	  # Player attacks monster
	  !$monsters{$ID1} && $monsters{$ID2}

	  # Ignore Player Unknown #0
	  && (unpack("V1", $ID1) ne "0")

	  # We only trigger reaction on KS if we are currently attacking a monster
	  && AI::action eq "attack"
	  && $monsters{$ai_seq_args[0]{ID}} 
	  && (!timeOut(AI::args->{ai_attack_giveup}) || $config{attackNoGiveup})

	  # If the monster is your target or you are the target of the monster
	  && ($ID2 eq AI::args->{ID} || $monsters{$ID2}{target} eq $accountID)

	  # And if the monster is previouly marked 'reactOnKillSteal'
	  && $monsters{$ID2}{reactOnKillSteal}
	) {
		# This is a new monster, increment kill steal count
		if (!$monsters{$ID2}{ksedByPlayer}{$ID1}) {
			$ksCounts{$ID1}++;
			$monsters{$ID2}{ksedByPlayer}{$ID1} = 1;
		}
		
		# Stop here if the previous reaction is not yet executed
		return if $ai_v{temp}{reactOnKillSteal}{command};
		
		# Stop here if the timeout has not yet elapsed
		return unless timeOut($ai_v{temp}{reactOnKillSteal}{time}, $ai_v{temp}{reactOnKillSteal}{timeOut});
		undef $ai_v{temp}{reactOnKillSteal}{timeOut};
		
		my $damage;
		my $skillID;
		
		# resolve $damage and $skillID values from the the 3 different hooks
		if ($trigger eq 'packet_attack') {
			$damage = $args->{dmg};
			$skillID = 0;
		} elsif ($trigger eq 'packet_skilluse') {
			$damage = $args->{damage};
			$skillID = $args->{skillID};
		} else {
			$damage = 0;
			$skillID = $args->{skillID};
		}
		
		# Determine the reactOnKillSteal block to use
		for (my $i = 0; exists $config{"reactOnKillSteal_$i"}; $i++) {
			my $prefix = "reactOnKillSteal_$i";
			next if (!$config{$prefix});
			
			if (
				checkKillStealCondition($prefix, $ID1, $ID2, $damage, $skillID, ($trigger eq 'is_casting') ? 1 : 0)
				&& checkPlayerCondition($prefix . "_player", $ID1)
				&& checkMonsterCondition($prefix . "_monster", $ID2)
			) {
				# determine the set of commands to use
				my $commandList;
				
				my $j = $ksReactions{$ID1}{$i};
				
				if (!$j) {
					$commandList = $config{$prefix};
					$ai_v{temp}{reactOnKillSteal}{lastCommand} = $prefix;
				
				} elsif (exists $config{$prefix . "_altCommand_" . ($j - 1)}) {
					if ($config{$prefix . "_altCommand_" . ($j - 1)}) {
						$commandList = $config{$prefix . "_altCommand_" . ($j - 1)};
						$ai_v{temp}{reactOnKillSteal}{lastCommand} = $prefix . "_altCommand_" . ($j - 1);
					} else {
						$commandList = $config{$ai_v{temp}{reactOnKillSteal}{lastCommand}};
					}
				} elsif (exists $config{$prefix . "_altCommand_persist"}) {
					if ($config{$prefix . "_altCommand_persist"}) {
						$commandList = $config{$prefix . "_altCommand_persist"};
						$ai_v{temp}{reactOnKillSteal}{lastCommand} = $prefix . "_altCommand_persist";
					} else {
						$commandList = $config{$ai_v{temp}{reactOnKillSteal}{lastCommand}};
					}	
				}
				
				if ($commandList) {
					# Choose a random command from the list
					my $command;
					my @commands;
					
					@commands = split(/\s*;+\s*/, $commandList);
					$command = $commands[rand(@commands)];
					
					if ($command) {
						# remove leading whitespaces
						$command =~ s/^\s*//g;
						
						# resolve keywords
						$command =~ s/\@monsterNum/$monsters{$ID2}{binID}/gi;
						$command =~ s/\@monsterName/$monsters{$ID2}{name}/gi;
						$command =~ s/\@playerNum/$players{$ID1}{binID}/gi;
						$command =~ s/\@playerName/$players{$ID1}{name}/gi;
						
						my $source = Actor::get($ID1);
						my $target = Actor::get($ID2);
						message("$source kill steals my $target! [" . int($ksCounts{$ID1}) . "x]\n", "reactOnKillSteal");
						
						my $delay = getCommandChatDelay($command);
						
						# ensure that you will not spam emoticons that overwrite each other
						$delay = 3.9 if ($command =~ /^e\s/ && $ai_v{temp}{reactOnKillSteal}{isEmoticon} == 1);
						undef $ai_v{temp}{reactOnKillSteal}{isEmoticon};
						
						message("Reacting to kill steal - execute command \"$command\" (delay " . int($delay * 1000) . "ms)\n", "reactOnKillSteal");
						
						$ai_v{temp}{reactOnKillSteal}{command} = $command;
						$ai_v{temp}{reactOnKillSteal}{timeOut_delay} = $delay;
					}
					
					$ksReactions{$ID1}{$i}++;
					$ksReactions{$ID1}{total}++;
					$ksReactions{$ID1}{time} = time;
					
					my $timeOut = $config{"reactOnKillSteal_timeout"};
					$timeOut += rand($config{"reactOnKillSteal_timeoutSeed"}) if $config{"reactOnKillSteal_timeoutSeed"};
					
					$ai_v{temp}{reactOnKillSteal}{timeOut} = $timeOut;
					$ai_v{temp}{reactOnKillSteal}{time} = time;
				}
				last;
			
			}
		
		}
	
	}

};


##
# checkMonsterUnengaged(ID)
# ID: ID of the monster
#
# determines if the monster hasn't attacked a player
# and players haven't attacked the monster.
sub checkMonsterUnengaged {
	my $ID = shift;
	my $monster = $monsters{$ID};

	# If monster hasn't been attacked by other players
	if (!binSize([keys %{$monster->{'missedFromPlayer'}}])
	 && !binSize([keys %{$monster->{'dmgFromPlayer'}}])
	 && !binSize([keys %{$monster->{'castOnByPlayer'}}])

	 # and it hasn't attacked any other player
	 && !binSize([keys %{$monster->{'missedToPlayer'}}])
	 && !binSize([keys %{$monster->{'dmgToPlayer'}}])
	 && !binSize([keys %{$monster->{'castOnToPlayer'}}])
	) {
		return 1;
	} else {
		return 0;
	}
};



##
# getCommandChatDelay(command)
# command: console command
#
# determines if the console command is a chat
# and returns a corresponding delay (to simulate typing).
sub getCommandChatDelay {
	my $command = shift;
	my $isChat = 0;
	my $message;
	if ($command =~ /^c\s/ || $command =~ /^g\s/ || $command =~ /^p\s/) {
		(undef, $message) = split(/ +/, $command, 2);
		$isChat = 1;
	} elsif ($command =~ /^pm\s/) {
		(undef, undef, $message) = split(/ /, $command, 3);
		$isChat = 1;
	}
		
	## COPIED FROM processChatResponse, ChatQueue.pm
	# Calculate a small delay (to simulate typing)
	# The average typing speed is 65 words per minute.
	# The average length of a word used by RO players is 4.25 characters (yes I measured it).
	# So the average user types 65 * 4.25 = 276.25 charcters per minute, or
	# 276.25 / 60 = 4.6042 characters per second
	# We also add a random delay of 0.5-1.5 seconds. <-- changed this to 0-0.75 seconds.
	return rand(0.75) + length($message) / 4.6042 if $isChat;
};



sub checkKillStealCondition {
	my ($prefix, $ID1, $ID2, $damage, $skillID, $isCasting) = @_;

	return 0 if ($config{$prefix . "_disabled"} > 0);
	if ($config{$prefix . "_inLockOnly"} > 0) { return 0 unless $field->baseName() eq $config{lockMap}; }
	
	if ($config{$prefix . "_notParty"} > 0) { 
		return 0 if Utils::existsInList($config{tankersList}, $players{$ID1}{name}) ||
			($chars[$config{'char'}]{'party'} && %{$chars[$config{'char'}]{'party'}}
			&& $chars[$config{'char'}]{'party'}{'users'}{$ID1} && %{$chars[$config{'char'}]{'party'}{'users'}{$ID1}});
	}
	if ($config{$prefix . "_notTankModeTarget"} > 0) { 
		return 0 if $config{'tankMode'} && $config{'tankModeTarget'} eq $players{$ID1}{'name'};
	}
	if ($config{$prefix . "_attackTargetOnly"} > 0) {
		return 0 unless AI::args->{ID} eq $ID2;
	}


	if ($config{$prefix . "_monster_name"}) {
		return 0 unless existsInList($config{$prefix . "_monster_name"}, $monsters{$ID2}{'name'});
	}
	if ($config{$prefix . "_monster_notName"}) {
		return 0 if existsInList($config{$prefix . "_monster_notName"}, $monsters{$ID2}{'name'});
	}

	if ($config{$prefix . "_whenPlayerIsUnknown"} > 0) { 
		return 0 unless !UNIVERSAL::isa($players{$ID1}, 'Actor') && !$config{$prefix . "_whenPlayerIsKnown"};
	}
	if ($config{$prefix . "_whenPlayerIsKnown"} > 0) { 
		return 0 if !UNIVERSAL::isa($players{$ID1}, 'Actor') && !$config{$prefix . "_whenPlayerIsUnknown"};
	}
		
	if ($config{$prefix . "_player_name"}) {
		return 0 unless existsInList($config{$prefix . "_player_name"}, $players{$ID1}{'name'});
	}
	if ($config{$prefix . "_player_notName"}) {
		return 0 if existsInList($config{$prefix . "_player_notName"}, $players{$ID1}{'name'});
	}
	if ($config{$prefix . "_player_id"}) {
		return 0 unless existsInList($config{$prefix . "_player_id"}, unpack("V1", $ID1));
	}
	if ($config{$prefix . "_player_notId"}) {
		return 0 if existsInList($config{$prefix . "_player_notId"}, unpack("V1", $ID1));
	}
		
	if ($config{$prefix . "_player_lvl"}) {
		return 0 unless Utils::inRange($players{$ID1}{lv}, $config{$prefix . "_player_lvl"});
	}
	if ($config{$prefix . "_player_sex"}) {
		return 0 unless $sex_lut{$players{$ID1}{'sex'}} eq $config{$prefix . "_player_sex"};
	}

	if ($config{$prefix . "_player_damage"}) {
		return 0 unless Utils::inRange($monsters{$ID2}{'dmgFromPlayer'}{$ID1}, $config{$prefix . "_player_damage"});
	}
	if ($config{$prefix . "_player_misses"}) {
		return 0 unless Utils::inRange($monsters{$ID2}{'missedFromPlayer'}{$ID1}, $config{$prefix . "_player_misses"});
	}
	if ($config{$prefix . "_player_ksCount"}) {
		return 0 unless Utils::inRange($ksCounts{$ID1}, $config{$prefix . "_player_ksCount"});
	}
	if ($config{$prefix . "_player_reactionCount"}) {
		return 0 unless Utils::inRange($ksReactions{$ID1}{total}, $config{$prefix . "_player_reactionCount"});
	}


	if ($config{$prefix . "_damage"}) { 
		return 0 unless Utils::inRange($damage, $config{$prefix . "_damage"});
	}
	if ($config{$prefix . "_skills"}) {
		message($prefix . "_skills = " . $config{$prefix . "_skills"} . ", \$skillID = $skillID\n", "reactOnKillSteal");
		if (!$skillID) {
			return 0 unless Utils::existsInList($config{$prefix . "_skills"}, "Normal Attack");
		} else {
			return 0 unless Utils::existsInList($config{$prefix . "_skills"}, Skill->new(idn => $skillID)->getName());
		}
	}
	if ($config{$prefix . "_notSkills"}) {
		if (!$skillID) {
			return 0 if Utils::existsInList($config{$prefix . "_notSkills"}, "Normal Attack");
		} else {
			return 0 if Utils::existsInList($config{$prefix . "_notSkills"}, Skill->new(idn => $skillID)->getName());
		}
	}
	if ($config{$prefix . "_isCasting"} > 0) { return 0 unless ($isCasting); }

	return 1;
};

return 1;
Аватара пользователя
4epT
macro-маньячина
Сообщения: 2793
Зарегистрирован: Чт дек 21, 2006 1:23 pm
Сервер RO:: 4game
Discord: ya4ept#8494
Контактная информация:

Re: reactOnKillSteal: реакция на киллстил

Сообщение 4epT »

добавил в SVN8460
Быстро и качественно напишу конфиг (макрос)! Стучи!
¤ Свежий бот ¤ Config checker ¤ Manual ¤
Изображение
Изображение
Ответить