Skill.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:12k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - Character skill
  3. #
  4. #  This software is open source, licensed under the GNU General Public
  5. #  License, version 2.
  6. #  Basically, this means that you're allowed to modify and distribute
  7. #  this software. However, if you distribute modified versions, you MUST
  8. #  also distribute the source code.
  9. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  10. #
  11. #  $Revision: 5532 $
  12. #  $Id: Skills.pm 5532 2007-03-18 16:57:38Z vcl_kore $
  13. #
  14. #########################################################################
  15. ##
  16. # MODULE DESCRIPTION: Character skill model.
  17. #
  18. # This class models a character skill (for example, Heal, Bash, Flee, Twohand Quicken, etc.).
  19. # In our model, a skill always has the following properties:
  20. # <ul>
  21. # <li>
  22. #     A skill identifier. This identifier has multiple forms:
  23. #     <ul>
  24. #     <li>A full name, e.g. "Increase AGI".</li>
  25. #     <li>A handle, or internal name, e.g. "AL_INCAGI".</li>
  26. #     <li>A skill Identifier Number (IDN). In case case of Increase AGI, this is 29.</li>
  27. #     </ul>
  28. # </li>
  29. # <li>
  30. #    A skill level. This may not always be necessary, depending on the situation.
  31. #    For example, when you want to use a skill, then it's necessary to know the associated level.
  32. #    But when the programmer just wants to query the maximum level of a skill, only
  33. #    a skill identifier is required.
  34. # </li>
  35. # </ul>
  36. #
  37. # A skill may also have the following properties, which is updated on-the-fly as the RO
  38. # server sends us the information:
  39. # `l
  40. # - The SP usage.
  41. # - The maximum available skill level at the moment.
  42. # - The skill range.
  43. # - The skill's target type (whether it's used on yourself, on a location, on a monster, etc.)
  44. # - The skill's owner type: whether the skill is a character skill or homunculus skill.
  45. # `l`
  46. package Skill;
  47. use strict;
  48. use Modules 'register';
  49. use Carp::Assert;
  50. use Utils::TextReader;
  51. use Utils::Exceptions;
  52. # Target type constants. See $Skill->getTargetType() for description.
  53. use constant {
  54. # Passive skill; cannot be used.
  55. TARGET_PASSIVE => 0,
  56. # Used on enemies (i.e. monsters, and also people when in WoE/PVP).
  57. TARGET_ENEMY => 1,
  58. # Used on locations.
  59. TARGET_LOCATION => 2,
  60. # Always used on yourself, there's no targeting involved. Though some
  61. # of these skills (like Gloria) have effect on the entire party.
  62. TARGET_SELF => 4,
  63. # Can be used on all actors.
  64. TARGET_ACTORS => 16
  65. };
  66. # Owner type constants. See $Skill->getOwnerType() for description.
  67. use enum qw(OWNER_CHAR OWNER_HOMUN OWNER_MERC);
  68. ##
  69. # Skill->new(options...)
  70. #
  71. # Creates a new Skills object. In the options, you must specify an identifier
  72. # for a skill.
  73. #
  74. # To specificy a skill identifier, use one of the following keys:
  75. # `l
  76. # - idn - A skill Identifier Number (IDN).
  77. # - name - A skill name. This is case-<b>in</b>sensitive.
  78. # - handle - A skill handle. This is case-sensitive.
  79. # - auto - Attempt to autodetect the value.
  80. # `l`
  81. #
  82. # For example, all of the following constructors create identical Skill objects:
  83. # <pre class="example">
  84. # $heal = new Skill(idn => 28);
  85. # $heal = new Skill(handle => 'AL_HEAL');
  86. # $heal = new Skill(name => 'heal');
  87. # $heal = new Skill(auto => 'Heal');
  88. # $heal = new Skill(auto => 'AL_HEAL');
  89. # $heal = new Skill(auto => 28);
  90. #
  91. # $heal->getIDN();        # returns 28
  92. # $heal->getHandle();     # returns 'AL_HEAL'
  93. # $heal->getName();       # returns 'Heal'
  94. # </pre>
  95. #
  96. # You may also specify a 'level' option to set the skill level.
  97. #
  98. # Example:
  99. # $heal = new Skill(name => 'Heal');
  100. # $heal->level();  # returns undef
  101. #
  102. # $heal = new Skill(name => 'Heal', level => 5);
  103. # $heal->level();  # returns 5
  104. sub new {
  105. my $class = shift;
  106. my %args = @_;
  107. my %self;
  108. if (defined $args{auto}) {
  109. if ($args{auto} =~ /^d+$/) {
  110. $args{idn} = $args{auto};
  111. } elsif (uc($args{auto}) eq $args{auto}) {
  112. $args{handle} = $args{auto};
  113. } else {
  114. $args{name} = $args{auto};
  115. }
  116. }
  117. if (defined $args{idn}) {
  118. $self{idn} = $args{idn};
  119. } elsif (defined $args{handle}) {
  120. $self{idn} = lookupIDNByHandle($args{handle});
  121. } elsif (defined $args{name}) {
  122. $self{idn} = lookupIDNByName($args{name});
  123. } else {
  124. ArgumentException->throw("No valid skill identifier specified.");
  125. }
  126. $self{level} = $args{level};
  127. return bless %self, $class;
  128. }
  129. ##
  130. # int $Skill->getIDN()
  131. #
  132. # Returns this skill's Identifier Number (IDN).
  133. sub getIDN {
  134. return $_[0]->{idn};
  135. }
  136. ##
  137. # String $Skill->getHandle()
  138. #
  139. # Returns the skill's handle.
  140. sub getHandle {
  141. my ($self) = @_;
  142. my $idn = $self->{idn};
  143. if (defined $idn) {
  144. my $entry = $Skill::DynamicInfo::skills{$idn} || $Skill::StaticInfo::ids{$idn};
  145. return $entry->{handle} if ($entry);
  146. }
  147. return undef;
  148. }
  149. ##
  150. # String $Skill->getName()
  151. # Ensures: defined(result)
  152. #
  153. # Returns the skill's name.
  154. sub getName {
  155. my ($self) = @_;
  156. my $idn = $self->{idn};
  157. if (defined $idn) {
  158. my $entry = $Skill::StaticInfo::ids{$idn};
  159. if ($entry) {
  160. return $entry->{name};
  161. } else {
  162. my $handle = $self->getHandle();
  163. if ($handle) {
  164. return handleToName($handle);
  165. } else {
  166. return "Unknown $idn";
  167. }
  168. }
  169. } else {
  170. return "Unknown";
  171. }
  172. }
  173. ##
  174. # int $Skill->getLevel()
  175. #
  176. # Returns the skill level. The value may be undef, if no skill level was set.
  177. sub getLevel {
  178. return $_[0]->{level};
  179. }
  180. ##
  181. # int $Skill->getSP(int level)
  182. # Requires: $level > 0
  183. #
  184. # Returns the SP required for level $level of this skill, or undef if that's unknown.
  185. sub getSP {
  186. my ($self, $level) = @_;
  187. assert($level > 0) if DEBUG;
  188. my $targetType = $self->getTargetType();
  189. if (defined $targetType && $targetType == TARGET_PASSIVE) {
  190. print "PASSIVE! $targetTypen";
  191. return 0;
  192. } else {
  193. my $idn = $self->{idn};
  194. # First check dynamic database.
  195. my $entry = $Skill::DynamicInfo::skills{$idn};
  196. if ($entry && $entry->{level} == $level) {
  197. return $entry->{sp};
  198. }
  199. # No luck, check static database.
  200. my $handle = $self->getHandle();
  201. $entry = $Skill::StaticInfo::sps{$handle};
  202. if ($entry) {
  203. return $entry->[$level - 1];
  204. } else {
  205. return undef;
  206. }
  207. }
  208. }
  209. ##
  210. # int $Skill->getRange()
  211. #
  212. # Returns the skill's range.
  213. sub getRange {
  214. my ($self) = @_;
  215. my $entry = $Skill::DynamicInfo::skills{$self->{idn}};
  216. if ($entry) {
  217. return $entry->{range};
  218. } else {
  219. return 1.42; # sqrt(2)
  220. }
  221. }
  222. ##
  223. # int $Skill->getTargetType()
  224. #
  225. # Returns the skill's target type, which specifies on what kind of target
  226. # this skill can be used. Returns one of:
  227. # `l
  228. # - Skill::TARGET_PASSIVE  - Passive skill; cannot be used.
  229. # - Skill::TARGET_ENEMY    - Used on enemies (i.e. monsters, and also people when in WoE/PVP).
  230. # - Skill::TARGET_LOCATION - Used on locations.
  231. # - Skill::TARGET_SELF     - Always used on yourself, there's no targeting involved. Though
  232. #                            some of these skills (like Gloria) have effect on the entire party.
  233. # - Skill::TARGET_ACTORS   - Can be used on all actors.
  234. # `l`
  235. sub getTargetType {
  236. my ($self) = @_;
  237. if (!defined $self->{idn}) {
  238. return undef;
  239. } else {
  240. my $entry = $Skill::DynamicInfo::skills{$self->{idn}};
  241. return $entry->{targetType} if ($entry);
  242. # TODO: use skillsarea.txt
  243. # Do we even need this file? All the info is already given by the server.
  244. # We don't know the target type so we just assume that it's used on
  245. # an enemy (which is usually correct).
  246. return TARGET_ENEMY;
  247. }
  248. }
  249. ##
  250. # int $Skill->getOwnerType()
  251. #
  252. # Returns the skill's owner type, which specifies whether this skill belongs to the character
  253. # or to the homunculus. Returns one of:
  254. # `l
  255. # - Skill::OWNER_CHAR  - This skill belongs to the character.
  256. # - Skill::OWNER_HOMUN - This skill belongs to the character's homunculus.
  257. # - Skill::OWNER_MERC - This skill belongs to the character's mercenary.
  258. # `l`
  259. sub getOwnerType {
  260. my ($self) = @_;
  261. if (defined $self->{idn}) {
  262. my $entry = $Skill::DynamicInfo::skills{$self->{idn}};
  263. return $entry->{ownerType} if ($entry);
  264. }
  265. # Just assume that this is a character skill. This is usually the case.
  266. return OWNER_CHAR;
  267. }
  268. sub getOwner {
  269. my ($self) = @_;
  270. my $type = $self->getOwnerType ();
  271. if ($Globals::char) {
  272. return $Globals::char->{homunculus} if $type == OWNER_HOMUN && $Globals::char->{homunculus};
  273. return $Globals::char->{mercenary} if $type == OWNER_MERC && $Globals::char->{mercenary};
  274. }
  275. return $Globals::char;
  276. }
  277. # Lookup an IDN by skill handle.
  278. sub lookupIDNByHandle {
  279. my ($handle) = @_;
  280. my $idn = $Skill::DynamicInfo::handles{$handle};
  281. if (defined $idn) {
  282. return $idn;
  283. } else {
  284. return $Skill::StaticInfo::handles{$handle};
  285. }
  286. }
  287. # Lookup an IDN by full skill name.
  288. sub lookupIDNByName {
  289. my ($name) = @_;
  290. my $idn = $Skill::StaticInfo::names{lc($name)};
  291. if (defined $idn) {
  292. return $idn;
  293. } else {
  294. # This case is probably rare so the lookup is not indexed,
  295. # in order to save memory. We use a 'slow' linear search instead.
  296. foreach my $handle (keys %Skill::DynamicInfo::handles) {
  297. my $convertedName = handleToName($handle);
  298. if ($name eq $convertedName) {
  299. return $Skill::DynamicInfo::handles{$handle};
  300. }
  301. }
  302. return undef;
  303. }
  304. }
  305. # Attempt to convert handle name to human readable name.
  306. sub handleToName {
  307. my ($handle) = @_;
  308. $handle =~ s/^[A-Z]+_//; # Remove prefix (WIZ_)
  309. $handle =~ s/_+/ /g; # Replace underscores with spaces.
  310. $handle = lc($handle); # Convert to lowercase.
  311. # Change first character and every character
  312. # that follows a space, to uppercase.
  313. $handle =~ s/(^| )([a-z])/$1 . uc($2)/ge;
  314. return $handle;
  315. }
  316. #############################################################
  317. # Static information database. Information in this database
  318. # is loaded from a file on disk.
  319. #############################################################
  320. package Skill::StaticInfo;
  321. # The following variables contain information about skills, but indexed differently.
  322. # This allow you to quickly lookup skill information with any skill identifier form.
  323. #
  324. # %ids = (
  325. #     1 => {
  326. #         handle => 'NV_BASIC',
  327. #         name   => 'Basic Skill'
  328. #     },
  329. #     2 => {
  330. #         handle => 'SM_SWORD',
  331. #         name   => 'Sword Mastery'
  332. #     },
  333. #     3 => {
  334. #         handle => 'SM_TWOHAND',
  335. #         name   => 'Two-Handed Sword Mastery'
  336. #     },
  337. #     ...
  338. # );
  339. #
  340. # %handles = (
  341. #     NV_BASIC   => 1,
  342. #     SM_SWORD   => 2,
  343. #     SM_TWOHAND => 3,
  344. #     ...
  345. # );
  346. #
  347. # %names = (
  348. #     'basic skill' => 1,
  349. #     'sword mastery' => 2,
  350. #     'two-handed sword mastery' => 3,
  351. #     ...
  352. # );
  353. our %ids;
  354. our %handles;
  355. our %names;
  356. # Contains SP usage information.
  357. # %sps = (
  358. #     SM_BASH => [
  359. #         8,     # SP usage for level 1
  360. #         8,     # SP usage for level 2
  361. #         ...
  362. #         15,    # SP usage for level 6
  363. #         ...
  364. #     ],
  365. #     SM_PROVOKE => [
  366. #         4,     # SP usage for level 1
  367. #         5,     # SP usage for level 2
  368. #         ...
  369. #     ],
  370. #     ...
  371. # );
  372. our %sps;
  373. sub parseSkillsDatabase {
  374. my ($file) = @_;
  375. my $reader = new Utils::TextReader($file);
  376. %ids = ();
  377. %handles = ();
  378. %names = ();
  379. while (!$reader->eof()) {
  380. my $line = $reader->readLine();
  381. next if ($line =~ /^///);
  382. $line =~ s/[rn]//g;
  383. $line =~ s/s+$//g;
  384. my ($id, $handle, $name) = split(' ', $line, 3);
  385. if ($id && $handle ne "" && $name ne "") {
  386. $ids{$id}{handle} = $handle;
  387. $ids{$id}{name} = $name;
  388. $handles{$handle} = $id;
  389. $names{lc($name)} = $id;
  390. }
  391. }
  392. return 1;
  393. }
  394. sub parseSPDatabase {
  395. my ($file) = @_;
  396. my $reader = new Utils::TextReader($file);
  397. my $ID;
  398. while (!$reader->eof()) {
  399. my $line = $reader->readLine();
  400. if ($line =~ /^@/) {
  401. undef $ID;
  402. } elsif (!$ID) {
  403. ($ID) = $line =~ /(.*)#/;
  404. $sps{$ID} = [];
  405. } else {
  406. my ($sp) = $line =~ /(d+)#/;
  407. push @{$sps{$ID}}, $sp;
  408. }
  409. }
  410. return 1;
  411. }
  412. #############################################################
  413. # Dynamic information database. Information in this database
  414. # is sent by the RO server.
  415. #############################################################
  416. package Skill::DynamicInfo;
  417. # The skills information as sent by the RO server. This variable maps a skill IDN
  418. # to another hash, with the following members:
  419. # - handle     - Handle name.
  420. # - level      - Character's current maximum skill level.
  421. # - sp         - The SP usage for the current maximum level.
  422. # - range      - Skill range.
  423. # - targetType - The skill's target type.
  424. our %skills;
  425. # Maps handle names to skill IDNs.
  426. our %handles;
  427. sub add {
  428. my ($idn, $handle, $level, $sp, $range, $targetType, $ownerType) = @_;
  429. $skills{$idn} = {
  430. handle     => $handle,
  431. level      => $level,
  432. sp         => $sp,
  433. range      => $range,
  434. targetType => $targetType,
  435. ownerType  => $ownerType
  436. };
  437. $handles{$handle} = $idn;
  438. }
  439. sub clear {
  440. %skills = ();
  441. %handles = ();
  442. }
  443. 1;