1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License as published by 4 * the Free Software Foundation; either version 2, or (at your option) 5 * any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 15 * 02111-1307, USA. 16 * 17 * http://www.gnu.org/copyleft/gpl.html 18 */ 19 package net.sf.l2j.gameserver.model.actor.instance; 20 21 import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; 22 23 import java.text.DateFormat; 24 import java.util.List; 25 26 import javolution.text.TextBuilder; 27 import javolution.util.FastList; 28 import net.sf.l2j.Config; 29 import net.sf.l2j.gameserver.Olympiad; 30 import net.sf.l2j.gameserver.SevenSigns; 31 import net.sf.l2j.gameserver.SevenSignsFestival; 32 import net.sf.l2j.gameserver.ThreadPoolManager; 33 import net.sf.l2j.gameserver.ai.CtrlIntention; 34 import net.sf.l2j.gameserver.cache.HtmCache; 35 import net.sf.l2j.gameserver.datatables.BuffTemplateTable; 36 import net.sf.l2j.gameserver.datatables.ClanTable; 37 import net.sf.l2j.gameserver.datatables.HelperBuffTable; 38 import net.sf.l2j.gameserver.datatables.ItemTable; 39 import net.sf.l2j.gameserver.datatables.SkillTable; 40 import net.sf.l2j.gameserver.datatables.SpawnTable; 41 import net.sf.l2j.gameserver.idfactory.IdFactory; 42 import net.sf.l2j.gameserver.instancemanager.CastleManager; 43 import net.sf.l2j.gameserver.instancemanager.DimensionalRiftManager; 44 import net.sf.l2j.gameserver.instancemanager.QuestManager; 45 import net.sf.l2j.gameserver.instancemanager.TownManager; 46 import net.sf.l2j.gameserver.instancemanager.games.Lottery; 47 import net.sf.l2j.gameserver.model.L2Attackable; 48 import net.sf.l2j.gameserver.model.L2Character; 49 import net.sf.l2j.gameserver.model.L2Clan; 50 import net.sf.l2j.gameserver.model.L2DropCategory; 51 import net.sf.l2j.gameserver.model.L2DropData; 52 import net.sf.l2j.gameserver.model.L2ItemInstance; 53 import net.sf.l2j.gameserver.model.L2Multisell; 54 import net.sf.l2j.gameserver.model.L2Object; 55 import net.sf.l2j.gameserver.model.L2Skill; 56 import net.sf.l2j.gameserver.model.L2Spawn; 57 import net.sf.l2j.gameserver.model.L2Summon; 58 import net.sf.l2j.gameserver.model.L2World; 59 import net.sf.l2j.gameserver.model.MobGroupTable; 60 import net.sf.l2j.gameserver.model.L2Skill.SkillType; 61 import net.sf.l2j.gameserver.model.actor.knownlist.NpcKnownList; 62 import net.sf.l2j.gameserver.model.actor.stat.NpcStat; 63 import net.sf.l2j.gameserver.model.actor.status.NpcStatus; 64 import net.sf.l2j.gameserver.model.entity.Castle; 65 import net.sf.l2j.gameserver.model.entity.L2Event; 66 import net.sf.l2j.gameserver.model.entity.L2JOneoEvents.CTF; 67 import net.sf.l2j.gameserver.model.entity.L2JOneoEvents.VIP; 68 import net.sf.l2j.gameserver.model.quest.Quest; 69 import net.sf.l2j.gameserver.model.quest.QuestState; 70 import net.sf.l2j.gameserver.model.zone.type.L2TownZone; 71 import net.sf.l2j.gameserver.network.L2GameClient; 72 import net.sf.l2j.gameserver.network.SystemMessageId; 73 import net.sf.l2j.gameserver.serverpackets.ActionFailed; 74 import net.sf.l2j.gameserver.serverpackets.ExShowVariationCancelWindow; 75 import net.sf.l2j.gameserver.serverpackets.ExShowVariationMakeWindow; 76 import net.sf.l2j.gameserver.serverpackets.InventoryUpdate; 77 import net.sf.l2j.gameserver.serverpackets.MyTargetSelected; 78 import net.sf.l2j.gameserver.serverpackets.NpcHtmlMessage; 79 import net.sf.l2j.gameserver.serverpackets.NpcInfo; 80 import net.sf.l2j.gameserver.serverpackets.RadarControl; 81 import net.sf.l2j.gameserver.serverpackets.SocialAction; 82 import net.sf.l2j.gameserver.serverpackets.StatusUpdate; 83 import net.sf.l2j.gameserver.serverpackets.SystemMessage; 84 import net.sf.l2j.gameserver.serverpackets.ValidateLocation; 85 import net.sf.l2j.gameserver.skills.Stats; 86 import net.sf.l2j.gameserver.taskmanager.DecayTaskManager; 87 import net.sf.l2j.gameserver.templates.L2HelperBuff; 88 import net.sf.l2j.gameserver.templates.L2Item; 89 import net.sf.l2j.gameserver.templates.L2NpcTemplate; 90 import net.sf.l2j.gameserver.templates.L2Weapon; 91 import net.sf.l2j.util.Rnd; 92 93 /** 94 * This class represents a Non-Player-Character in the world. It can be a 95 * monster or a friendly character. It also uses a template to fetch some static 96 * values. The templates are hardcoded in the client, so we can rely on them.
97 *
98 * 99 * L2Character :
100 *
101 *
  • L2Attackable
  • 102 *
  • L2BoxInstance
  • 103 *
  • L2FolkInstance
  • 104 * 105 * @version $Revision: 1.32.2.7.2.24 $ $Date: 2005/04/11 10:06:09 $ 106 */ 107 public class L2NpcInstance extends L2Character 108 { 109 // private static Logger _log = 110 // Logger.getLogger(L2NpcInstance.class.getName()); 111 112 /** 113 * The interaction distance of the L2NpcInstance(is used as offset in 114 * MovetoLocation method) 115 */ 116 public static final int INTERACTION_DISTANCE = 150; 117 118 /** The L2Spawn object that manage this L2NpcInstance */ 119 private L2Spawn _spawn; 120 121 /** The flag to specify if this L2NpcInstance is busy */ 122 private boolean _isBusy = false; 123 124 /** The busy message for this L2NpcInstance */ 125 private String _busyMessage = ""; 126 127 /** true if endDecayTask has already been called */ 128 volatile boolean _isDecayed = false; 129 130 /** true if a Dwarf has used Spoil on this L2NpcInstance */ 131 private boolean _isSpoil = false; 132 133 /** 134 * The castle index in the array of L2Castle this L2NpcInstance belongs 135 * to 136 */ 137 private int _castleIndex = -2; 138 139 public boolean isEventMob = false, _isEventVIPNPC = false, 140 _isEventVIPNPCEnd = false; 141 142 public boolean _isEventMobCTF = false; 143 144 public boolean isPrivateEventMob = false; 145 146 private boolean _isInTown = false; 147 148 private int _isSpoiledBy = 0; 149 150 protected RandomAnimationTask _rAniTask = null; 151 152 /** Task launching the function onRandomAnimation() */ 153 protected class RandomAnimationTask implements Runnable 154 { 155 public void run() 156 { 157 try 158 { 159 if (this != _rAniTask) 160 { 161 return; // Shouldn't happen, but who knows... just to 162 // make 163 // sure every active npc has only one timer. 164 } 165 if (isMob()) 166 { 167 // Cancel further animation timers until intention is 168 // changed to ACTIVE again. 169 if (getAI().getIntention() != AI_INTENTION_ACTIVE) 170 { 171 return; 172 } 173 } else 174 { 175 if (!isInActiveRegion()) 176 { 177 return; 178 } 179 // update knownlist to remove playable which aren't in 180 // range 181 // any more 182 getKnownList().updateKnownObjects(); 183 } 184 185 if (!(isDead() || isStunned() || isSleeping() || isParalyzed())) 186 { 187 onRandomAnimation(); 188 } 189 190 startRandomAnimationTimer(); 191 } catch (Throwable t) 192 { 193 } 194 } 195 } 196 197 /** 198 * Send a packet SocialAction to all L2PcInstance in the _KnownPlayers 199 * of the L2NpcInstance and create a new RandomAnimation Task.
    200 *
    201 */ 202 public void onRandomAnimation() 203 { 204 // Send a packet SocialAction to all L2PcInstance in the _KnownPlayers 205 // of the L2NpcInstance 206 SocialAction sa = new SocialAction(getObjectId(), Rnd.get(2, 3)); 207 broadcastPacket(sa); 208 } 209 210 /** 211 * Create a RandomAnimation Task that will be launched after the 212 * calculated delay.
    213 *
    214 */ 215 public void startRandomAnimationTimer() 216 { 217 if (!hasRandomAnimation()) 218 { 219 return; 220 } 221 222 int minWait = isMob() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; 223 224 int maxWait = isMob() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; 225 226 // Calculate the delay before the next animation 227 int interval = Rnd.get(minWait, maxWait) * 1000; 228 229 // Create a RandomAnimation Task that will be launched after the 230 // calculated delay 231 _rAniTask = new RandomAnimationTask(); 232 ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval); 233 } 234 235 /** 236 * Check if the server allows Random Animation.
    237 *
    238 */ 239 public boolean hasRandomAnimation() 240 { 241 return Config.MAX_NPC_ANIMATION > 0; 242 } 243 244 public class destroyTemporalNPC implements Runnable 245 { 246 private L2Spawn _oldSpawn; 247 248 public destroyTemporalNPC(L2Spawn spawn) 249 { 250 _oldSpawn = spawn; 251 } 252 253 public void run() 254 { 255 try 256 { 257 _oldSpawn.getLastSpawn().deleteMe(); 258 _oldSpawn.stopRespawn(); 259 SpawnTable.getInstance().deleteSpawn(_oldSpawn, false); 260 } catch (Throwable t) 261 { 262 t.printStackTrace(); 263 } 264 } 265 } 266 267 public class destroyTemporalSummon implements Runnable 268 { 269 L2Summon _summon; 270 271 L2PcInstance _player; 272 273 public destroyTemporalSummon(L2Summon summon, L2PcInstance player) 274 { 275 _summon = summon; 276 _player = player; 277 } 278 279 public void run() 280 { 281 _summon.unSummon(_player); 282 } 283 } 284 285 /** 286 * Constructor of L2NpcInstance (use L2Character constructor).
    287 *
    288 * 289 * Actions :
    290 *
    291 *
  • Call the L2Character constructor to set the _template of the 292 * L2Character (copy skills from template to object and link 293 * _calculators to NPC_STD_CALCULATOR)
  • 294 *
  • Set the name of the L2Character
  • 295 *
  • Create a RandomAnimation Task that will be launched after the 296 * calculated delay if the server allow it
  • 297 *
    298 *
    299 * 300 * @param objectId 301 * Identifier of the object to initialized 302 * @param template 303 * The L2NpcTemplate to apply to the NPC 304 * 305 */ 306 public L2NpcInstance(int objectId, L2NpcTemplate template) 307 { 308 // Call the L2Character constructor to set the _template of the 309 // L2Character, copy skills from template to object 310 // and link _calculators to NPC_STD_CALCULATOR 311 super(objectId, template); 312 getKnownList(); // init knownlist 313 getStat(); // init stats 314 getStatus(); // init status 315 initCharStatusUpdateValues(); 316 317 // initialize the "current" equipment 318 _currentLHandId = getTemplate().lhand; 319 _currentRHandId = getTemplate().rhand; 320 321 if (template == null) 322 { 323 _log.severe("No template for Npc. Please check your datapack is setup correctly."); 324 return; 325 } 326 327 // Set the name of the L2Character 328 setName(template.name); 329 } 330 331 @Override 332 public NpcKnownList getKnownList() 333 { 334 if (super.getKnownList() == null || !(super.getKnownList() instanceof NpcKnownList)) 335 { 336 setKnownList(new NpcKnownList(this)); 337 } 338 return (NpcKnownList) super.getKnownList(); 339 } 340 341 @Override 342 public NpcStat getStat() 343 { 344 if (super.getStat() == null || !(super.getStat() instanceof NpcStat)) 345 { 346 setStat(new NpcStat(this)); 347 } 348 return (NpcStat) super.getStat(); 349 } 350 351 @Override 352 public NpcStatus getStatus() 353 { 354 if (super.getStatus() == null || !(super.getStatus() instanceof NpcStatus)) 355 { 356 setStatus(new NpcStatus(this)); 357 } 358 return (NpcStatus) super.getStatus(); 359 } 360 361 /** Return the L2NpcTemplate of the L2NpcInstance. */ 362 @Override 363 public final L2NpcTemplate getTemplate() 364 { 365 return (L2NpcTemplate) super.getTemplate(); 366 } 367 368 /** 369 * Return the generic Identifier of this L2NpcInstance contained in the 370 * L2NpcTemplate.
    371 *
    372 */ 373 public int getNpcId() 374 { 375 return getTemplate().npcId; 376 } 377 378 @Override 379 public boolean isAttackable() 380 { 381 return false; 382 } 383 384 /** 385 * Return the faction Identifier of this L2NpcInstance contained in the 386 * L2NpcTemplate.
    387 *
    388 * 389 * Concept :
    390 *
    391 * If a NPC belows to a Faction, other NPC of the faction inside the 392 * Faction range will help it if it's attacked
    393 *
    394 * 395 */ 396 public final String getFactionId() 397 { 398 return getTemplate().factionId; 399 } 400 401 /** 402 * Return the Level of this L2NpcInstance contained in the 403 * L2NpcTemplate.
    404 *
    405 */ 406 @Override 407 public final int getLevel() 408 { 409 return getTemplate().level; 410 } 411 412 /** 413 * Return true if the L2NpcInstance is agressive (ex : L2MonsterInstance 414 * in function of aggroRange).
    415 *
    416 */ 417 public boolean isAggressive() 418 { 419 return false; 420 } 421 422 /** 423 * Return the Aggro Range of this L2NpcInstance contained in the 424 * L2NpcTemplate.
    425 *
    426 */ 427 public int getAggroRange() 428 { 429 return getTemplate().aggroRange; 430 } 431 432 /** 433 * Return the Faction Range of this L2NpcInstance contained in the 434 * L2NpcTemplate.
    435 *
    436 */ 437 public int getFactionRange() 438 { 439 return getTemplate().factionRange; 440 } 441 442 /** 443 * Return true if this L2NpcInstance is undead in function of the 444 * L2NpcTemplate.
    445 *
    446 */ 447 @Override 448 public boolean isUndead() 449 { 450 return getTemplate().isUndead; 451 } 452 453 /** 454 * Send a packet NpcInfo with state of abnormal effect to all 455 * L2PcInstance in the _KnownPlayers of the L2NpcInstance.
    456 *
    457 */ 458 @Override 459 public void updateAbnormalEffect() 460 { 461 // NpcInfo info = new NpcInfo(this); 462 // broadcastPacket(info); 463 464 // Send a Server->Client packet NpcInfo with state of abnormal effect to 465 // all L2PcInstance in the _KnownPlayers of the L2NpcInstance 466 for (L2PcInstance player : getKnownList().getKnownPlayers().values()) 467 { 468 if (player != null) 469 { 470 player.sendPacket(new NpcInfo(this, player)); 471 } 472 } 473 } 474 475 /** 476 * Return the distance under which the object must be add to 477 * _knownObject in function of the object type.
    478 *
    479 * 480 * Values :
    481 *
    482 *
  • object is a L2FolkInstance : 0 (don't remember it)
  • 483 *
  • object is a L2Character : 0 (don't remember it)
  • 484 *
  • object is a L2PlayableInstance : 1500
  • 485 *
  • others : 500
  • 486 *
    487 *
    488 * 489 * Overriden in :
    490 *
    491 *
  • L2Attackable
  • 492 *
    493 *
    494 * 495 * @param object 496 * The Object to add to _knownObject 497 * 498 */ 499 public int getDistanceToWatchObject(L2Object object) 500 { 501 if (object instanceof L2FestivalGuideInstance) 502 { 503 return 10000; 504 } 505 506 if (object instanceof L2FolkInstance || !(object instanceof L2Character)) 507 { 508 return 0; 509 } 510 511 if (object instanceof L2PlayableInstance) 512 { 513 return 1500; 514 } 515 516 return 500; 517 } 518 519 /** 520 * Return the distance after which the object must be remove from 521 * _knownObject in function of the object type.
    522 *
    523 * 524 * Values :
    525 *
    526 *
  • object is not a L2Character : 0 (don't remember it)
  • 527 *
  • object is a L2FolkInstance : 0 (don't remember it)
  • 528 *
  • object is a L2PlayableInstance : 3000
  • 529 *
  • others : 1000
  • 530 *
    531 *
    532 * 533 * Overriden in :
    534 *
    535 *
  • L2Attackable
  • 536 *
    537 *
    538 * 539 * @param object 540 * The Object to remove from _knownObject 541 * 542 */ 543 public int getDistanceToForgetObject(L2Object object) 544 { 545 return 2 * getDistanceToWatchObject(object); 546 } 547 548 /** 549 * Return false.
    550 *
    551 * 552 * Overriden in :
    553 *
    554 *
  • L2MonsterInstance : Check if the attacker is not another 555 * L2MonsterInstance
  • 556 *
  • L2PcInstance
  • 557 *
    558 *
    559 */ 560 @Override 561 public boolean isAutoAttackable(@SuppressWarnings("unused") 562 L2Character attacker) 563 { 564 return false; 565 } 566 567 /** 568 * Return the Identifier of the item in the left hand of this 569 * L2NpcInstance contained in the L2NpcTemplate.
    570 *
    571 */ 572 public int getLeftHandItem() 573 { 574 return _currentLHandId; 575 } 576 577 /** 578 * Return the Identifier of the item in the right hand of this 579 * L2NpcInstance contained in the L2NpcTemplate.
    580 *
    581 */ 582 public int getRightHandItem() 583 { 584 return _currentRHandId; 585 } 586 587 /** 588 * Return true if this L2NpcInstance has drops that can be sweeped.
    589 *
    590 */ 591 public boolean isSpoil() 592 { 593 return _isSpoil; 594 } 595 596 /** 597 * Set the spoil state of this L2NpcInstance.
    598 *
    599 */ 600 public void setSpoil(boolean isSpoil) 601 { 602 _isSpoil = isSpoil; 603 } 604 605 public final int getIsSpoiledBy() 606 { 607 return _isSpoiledBy; 608 } 609 610 public final void setIsSpoiledBy(int value) 611 { 612 _isSpoiledBy = value; 613 } 614 615 /** 616 * Return the busy status of this L2NpcInstance.
    617 *
    618 */ 619 public final boolean isBusy() 620 { 621 return _isBusy; 622 } 623 624 /** 625 * Set the busy status of this L2NpcInstance.
    626 *
    627 */ 628 public void setBusy(boolean isBusy) 629 { 630 _isBusy = isBusy; 631 } 632 633 /** 634 * Return the busy message of this L2NpcInstance.
    635 *
    636 */ 637 public final String getBusyMessage() 638 { 639 return _busyMessage; 640 } 641 642 /** 643 * Set the busy message of this L2NpcInstance.
    644 *
    645 */ 646 public void setBusyMessage(String message) 647 { 648 _busyMessage = message; 649 } 650 651 /** 652 * Manage actions when a player click on the L2NpcInstance.
    653 *
    654 * 655 * Actions on first click on the L2NpcInstance (Select it) :
    656 *
    657 *
  • Set the L2NpcInstance as target of the L2PcInstance player (if 658 * necessary)
  • 659 *
  • Send a Server->Client packet MyTargetSelected to the 660 * L2PcInstance player (display the select window)
  • 661 *
  • If L2NpcInstance is autoAttackable, send a Server->Client packet 662 * StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP 663 * bar
  • 664 *
  • Send a Server->Client packet ValidateLocation to correct the 665 * L2NpcInstance position and heading on the client
  • 666 *
    667 *
    668 * 669 * Actions on second click on the L2NpcInstance (Attack 670 * it/Intercat with it) :
    671 *
    672 *
  • Send a Server->Client packet MyTargetSelected to the 673 * L2PcInstance player (display the select window)
  • 674 *
  • If L2NpcInstance is autoAttackable, notify the L2PcInstance AI 675 * with AI_INTENTION_ATTACK (after a height verification)
  • 676 *
  • If L2NpcInstance is NOT autoAttackable, notify the L2PcInstance 677 * AI with AI_INTENTION_INTERACT (after a distance verification) and 678 * show message
  • 679 *
    680 *
    681 * 682 * Caution : Each group of 683 * Server->Client packet must be terminated by a ActionFailed packet in 684 * order to avoid that client wait an other packet
    685 *
    686 * 687 * Example of use :
    688 *
    689 *
  • Client packet : Action, AttackRequest
  • 690 *
    691 *
    692 * 693 * Overriden in :
    694 *
    695 *
  • L2ArtefactInstance : Manage only fisrt click to select Artefact
  • 696 *
    697 *
    698 *
  • L2GuardInstance :
  • 699 *
    700 *
    701 * 702 * @param player 703 * The L2PcInstance that start an action on the 704 * L2NpcInstance 705 * 706 */ 707 @Override 708 public void onAction(L2PcInstance player) 709 { 710 if (player.isOutOfControl()) 711 { 712 player.sendPacket(new ActionFailed()); 713 return; 714 } 715 // Check if the L2PcInstance already target the L2NpcInstance 716 if (this != player.getTarget()) 717 { 718 if (Config.DEBUG) 719 { 720 _log.fine("new target selected:" + getObjectId()); 721 } 722 723 // Set the target of the L2PcInstance player 724 player.setTarget(this); 725 726 // Send a Server->Client packet MyTargetSelected to the 727 // L2PcInstance 728 // player 729 // The player.getLevel() - getLevel() permit to display the 730 // correct 731 // color in the select window 732 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); 733 player.sendPacket(my); 734 735 // Check if the player is attackable (without a forced attack) 736 if (isAutoAttackable(player)) 737 { 738 // Send a Server->Client packet StatusUpdate of the 739 // L2NpcInstance to the L2PcInstance to update its HP bar 740 StatusUpdate su = new StatusUpdate(getObjectId()); 741 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp()); 742 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); 743 player.sendPacket(su); 744 } 745 746 // Send a Server->Client packet ValidateLocation to correct the 747 // L2NpcInstance position and heading on the client 748 player.sendPacket(new ValidateLocation(this)); 749 } else 750 { 751 player.sendPacket(new ValidateLocation(this)); 752 753 // Check if the player is attackable (without a forced attack) 754 // and 755 // isn't dead 756 if (isAutoAttackable(player) && !isAlikeDead()) 757 { 758 // Check the height difference 759 if (Math.abs(player.getZ() - getZ()) < 400) // this max 760 // heigth 761 // difference might 762 // need some 763 // tweaking 764 { 765 // Set the L2PcInstance Intention to AI_INTENTION_ATTACK 766 player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); 767 // player.startAttack(this); 768 } else 769 { 770 // Send a Server->Client packet ActionFailed (target is 771 // out 772 // of attack range) to the L2PcInstance player 773 player.sendPacket(new ActionFailed()); 774 } 775 } else if (!isAutoAttackable(player)) 776 { 777 // Calculate the distance between the L2PcInstance and the 778 // L2NpcInstance 779 if (!isInsideRadius(player, INTERACTION_DISTANCE, false, false)) 780 { 781 // player.setCurrentState(L2Character.STATE_INTERACT); 782 // player.setInteractTarget(this); 783 // player.moveTo(this.getX(), this.getY(), this.getZ(), 784 // INTERACTION_DISTANCE); 785 786 // Notify the L2PcInstance AI with AI_INTENTION_INTERACT 787 player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); 788 789 } else 790 { 791 // Send a Server->Client packet SocialAction to the all 792 // L2PcInstance on the _knownPlayer of the L2NpcInstance 793 // to display a social action of the L2NpcInstance on 794 // their 795 // client 796 SocialAction sa = new SocialAction(getObjectId(), Rnd.get(8)); 797 broadcastPacket(sa); 798 799 // Open a chat window on client with the text of the 800 // L2NpcInstance 801 if (isEventMob) 802 { 803 L2Event.showEventHtml(player, String.valueOf(getObjectId())); 804 } else if (_isEventVIPNPCEnd) 805 { 806 VIP.showEndHTML(player, String.valueOf(getObjectId())); 807 } else if (_isEventMobCTF) 808 { 809 CTF.showEventHtml(player, String.valueOf(getObjectId())); 810 } else 811 { 812 Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.NPC_FIRST_TALK); 813 if (qlst != null && qlst.length == 1) 814 { 815 qlst[0].notifyFirstTalk(this, player); 816 } else 817 { 818 showChatWindow(player, 0); 819 } 820 } 821 } 822 } else 823 player.sendPacket(new ActionFailed()); 824 } 825 } 826 827 /** 828 * Manage and Display the GM console to modify the L2NpcInstance (GM 829 * only).
    830 *
    831 * 832 * Actions (If the L2PcInstance is a GM only) :
    833 *
    834 *
  • Set the L2NpcInstance as target of the L2PcInstance player (if 835 * necessary)
  • 836 *
  • Send a Server->Client packet MyTargetSelected to the 837 * L2PcInstance player (display the select window)
  • 838 *
  • If L2NpcInstance is autoAttackable, send a Server->Client packet 839 * StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP 840 * bar
  • 841 *
  • Send a Server->Client NpcHtmlMessage() containing the GM console 842 * about this L2NpcInstance
  • 843 *
    844 *
    845 * 846 * Caution : Each group of 847 * Server->Client packet must be terminated by a ActionFailed packet in 848 * order to avoid that client wait an other packet
    849 *
    850 * 851 * Example of use :
    852 *
    853 *
  • Client packet : Action
  • 854 *
    855 *
    856 * 857 * @param client 858 * The thread that manage the player that pessed Shift 859 * and click on the L2NpcInstance 860 * 861 */ 862 @Override 863 public void onActionShift(L2GameClient client) 864 { 865 // Get the L2PcInstance corresponding to the thread 866 L2PcInstance player = client.getActiveChar(); 867 if (player == null) 868 { 869 return; 870 } 871 872 // Check if the L2PcInstance is a GM 873 if (player.getAccessLevel() >= Config.GM_ACCESSLEVEL) 874 { 875 // Set the target of the L2PcInstance player 876 player.setTarget(this); 877 878 // Send a Server->Client packet MyTargetSelected to the 879 // L2PcInstance 880 // player 881 // The player.getLevel() - getLevel() permit to display the 882 // correct 883 // color in the select window 884 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); 885 player.sendPacket(my); 886 887 // Check if the player is attackable (without a forced attack) 888 if (isAutoAttackable(player)) 889 { 890 // Send a Server->Client packet StatusUpdate of the 891 // L2NpcInstance to the L2PcInstance to update its HP bar 892 StatusUpdate su = new StatusUpdate(getObjectId()); 893 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp()); 894 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); 895 player.sendPacket(su); 896 } 897 898 // Send a Server->Client NpcHtmlMessage() containing the GM 899 // console 900 // about this L2NpcInstance 901 NpcHtmlMessage html = new NpcHtmlMessage(0); 902 TextBuilder html1 = new TextBuilder("
    NPC Information
    "); 903 String className = getClass().getName().substring(43); 904 html1.append("
    "); 905 906 html1.append("Instance Type: " + className + "Faction: " + getFactionId() + "Location ID: " + (getSpawn() != null ? getSpawn().getLocation() : 0) + ""); 907 908 if (this instanceof L2ControllableMobInstance) 909 { 910 html1.append("Mob Group: " + MobGroupTable.getInstance().getGroupForMob((L2ControllableMobInstance) this).getGroupId() + "
    "); 911 } else 912 { 913 html1.append("Respawn Time: " + (getSpawn() != null ? getSpawn().getRespawnDelay() / 1000 + " Seconds
    " : "? Seconds
    ")); 914 } 915 916 html1.append(""); 917 html1.append(""); 918 html1.append(""); 919 html1.append(""); 920 html1.append("
    Object ID" + getObjectId() + "NPC ID" + getTemplate().npcId + "
    Castle" + getCastle().getCastleId() + "Coords" + getX() + "," + getY() + "," + getZ() + "
    Level" + getLevel() + "Aggro" + (this instanceof L2Attackable ? ((L2Attackable) this).getAggroRange() : 0) + "

    "); 921 922 html1.append("Combat"); 923 html1.append(""); 924 html1.append(""); 925 html1.append(""); 926 html1.append(""); 927 html1.append(""); 928 html1.append(""); 929 html1.append(""); 930 html1.append(""); 931 html1.append("
    Current HP" + getCurrentHp() + "Current MP" + getCurrentMp() + "
    Max.HP" + (int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null)) + "*" + getStat().calcStat(Stats.MAX_HP, 1, this, null) + "Max.MP" + getMaxMp() + "
    P.Atk." + getPAtk(null) + "M.Atk." + getMAtk(null, null) + "
    P.Def." + getPDef(null) + "M.Def." + getMDef(null, null) + "
    Accuracy" + getAccuracy() + "Evasion" + getEvasionRate(null) + "
    Critical" + getCriticalHit(null, null) + "Speed" + getRunSpeed() + "
    Atk.Speed" + getPAtkSpd() + "Cast.Speed" + getMAtkSpd() + "

    "); 932 933 html1.append("Basic Stats"); 934 html1.append(""); 935 html1.append(""); 936 html1.append(""); 937 html1.append("
    STR" + getSTR() + "DEX" + getDEX() + "CON" + getCON() + "
    INT" + getINT() + "WIT" + getWIT() + "MEN" + getMEN() + "
    "); 938 939 html1.append("
    "); 940 html1.append(""); 941 html1.append(""); 942 html1.append(""); 943 html1.append("

    "); 944 html1.append(""); 945 946 html.setHtml(html1.toString()); 947 player.sendPacket(html); 948 } else if (Config.GAME_VIEWNPC || Config.GAME_VIEWNPC_COMBAT || Config.GAME_VIEWNPC_BASIC || Config.GAME_VIEWNPC_DROP) 949 { 950 // Set the target of the L2PcInstance player 951 player.setTarget(this); 952 953 // Send a Server->Client packet MyTargetSelected to the 954 // L2PcInstance player 955 // The player.getLevel() - getLevel() permit to display the 956 // correct color in the select window 957 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); 958 player.sendPacket(my); 959 960 // Check if the player is attackable (without a forced attack) 961 if (isAutoAttackable(player)) 962 { 963 // Send a Server->Client packet StatusUpdate of the 964 // L2NpcInstance to the L2PcInstance to update its HP bar 965 StatusUpdate su = new StatusUpdate(getObjectId()); 966 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp()); 967 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); 968 player.sendPacket(su); 969 } 970 971 NpcHtmlMessage html = new NpcHtmlMessage(0); 972 TextBuilder html1 = new TextBuilder(""); 973 974 // npc name 975 html1.append("
    [Npc]

    "); 976 html1.append(getName()); 977 978 // shows npc combat stats 979 if (Config.GAME_VIEWNPC_COMBAT) 980 { 981 html1.append("
    [Combat Stats]
    "); 982 html1.append(""); 983 html1.append(""); 984 html1.append(""); 985 html1.append(""); 986 html1.append(""); 987 html1.append(""); 988 html1.append(""); 989 html1.append(""); 990 html1.append("
    Max.HP" + (int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null)) + "*" + (int) getStat().calcStat(Stats.MAX_HP, 1, this, null) + "Max.MP" + getMaxMp() + "
    P.Atk." + getPAtk(null) + "M.Atk." + getMAtk(null, null) + "
    P.Def." + getPDef(null) + "M.Def." + getMDef(null, null) + "
    Accuracy" + getAccuracy() + "Evasion" + getEvasionRate(null) + "
    Critical" + getCriticalHit(null, null) + "Speed" + getRunSpeed() + "
    Atk.Speed" + getPAtkSpd() + "Cast.Speed" + getMAtkSpd() + "
    Race" + getTemplate().race + "
    "); 991 } 992 993 // shows basic stats 994 if (Config.GAME_VIEWNPC_BASIC) 995 { 996 html1.append("
    [Basic Stats]
    "); 997 html1.append(""); 998 html1.append(""); 999 html1.append(""); 1000 html1.append("
    STR" + getSTR() + "DEX" + getDEX() + "CON" + getCON() + "
    INT" + getINT() + "WIT" + getWIT() + "MEN" + getMEN() + "
    "); 1001 } 1002 1003 // shows drop stats 1004 if (Config.GAME_VIEWNPC_DROP) 1005 { 1006 html1.append("
    [Drop Info]
    "); 1007 html1.append("
    "); 1008 1009 for (L2DropCategory cat : getTemplate().getDropData()) 1010 { 1011 for (L2DropData drop : cat.getAllDrops()) 1012 { 1013 if (!Config.GAME_VIEWNPC_QUESTDROP) 1014 { 1015 // default info about drop item 1016 String name = ItemTable.getInstance().getTemplate(drop.getItemId()).getName(); 1017 int chance = drop.getChance(); // drop chance 1018 int min_qty = drop.getMinDrop(); // min. q-ty 1019 // of items 1020 int max_qty = drop.getMaxDrop(); // max. q-ty 1021 // of items 1022 1023 // calculating stats using drop-config 1024 if (drop.getItemId() == 57) 1025 { 1026 chance *= Config.RATE_DROP_ADENA; 1027 min_qty *= Config.RATE_DROP_ADENA; // temporary 1028 // calculation 1029 max_qty *= Config.RATE_DROP_ADENA; // temporary 1030 // calculation 1031 } else if (cat.isSweep()) 1032 { 1033 chance *= Config.RATE_DROP_SPOIL; 1034 } else 1035 { 1036 chance *= Config.RATE_DROP_ITEMS; 1037 } 1038 1039 // chance in percents 1040 if (chance > drop.MAX_CHANCE) 1041 { 1042 chance = drop.getChance(); 1043 } 1044 double percentChance = chance * 0.0001; 1045 1046 // output line (if min. q-ty = max. q-ty shows 1047 // only min.) 1048 html1.append((drop.isQuestDrop() ? "Quest: " : cat.isSweep() ? "Spoil: " : "Drop: ") + name + " " + percentChance + "%" + " [" + min_qty + (min_qty == max_qty ? "" : " - " + max_qty) + "]"); 1049 } 1050 } 1051 } 1052 1053 html1.append(""); 1054 } 1055 1056 html.setHtml(html1.toString()); 1057 player.sendPacket(html); 1058 } 1059 1060 // Send a Server->Client ActionFailed to the L2PcInstance in order to 1061 // avoid that the client wait another packet 1062 player.sendPacket(new ActionFailed()); 1063 } 1064 1065 /** Return the L2Castle this L2NpcInstance belongs to. */ 1066 public final Castle getCastle() 1067 { 1068 // Get castle this NPC belongs to (excluding L2Attackable) 1069 if (_castleIndex < 0) 1070 { 1071 L2TownZone town = TownManager.getInstance().getTown(getX(), getY(), getZ()); 1072 1073 if (town != null) 1074 { 1075 _castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById()); 1076 } 1077 1078 if (_castleIndex < 0) 1079 { 1080 _castleIndex = CastleManager.getInstance().findNearestCastleIndex(this); 1081 } else 1082 { 1083 _isInTown = true; // Npc was spawned in town 1084 } 1085 } 1086 1087 if (_castleIndex < 0) 1088 { 1089 return null; 1090 } 1091 1092 return CastleManager.getInstance().getCastles().get(_castleIndex); 1093 } 1094 1095 public final boolean getIsInTown() 1096 { 1097 if (_castleIndex < 0) 1098 { 1099 getCastle(); 1100 } 1101 return _isInTown; 1102 } 1103 1104 /** 1105 * Open a quest or chat window on client with the text of the 1106 * L2NpcInstance in function of the command.
    1107 *
    1108 * 1109 * Example of use :
    1110 *
    1111 *
  • Client packet : RequestBypassToServer
  • 1112 *
    1113 *
    1114 * 1115 * @param command 1116 * The command string received from client 1117 * 1118 */ 1119 public void onBypassFeedback(L2PcInstance player, String command) 1120 { 1121 // Get the distance between the L2PcInstance and the L2NpcInstance 1122 if (!isInsideRadius(player, INTERACTION_DISTANCE, false, false)) 1123 { 1124 player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); 1125 // player.moveTo(this.getX(), this.getY(), this.getZ(), 1126 // INTERACTION_DISTANCE); 1127 } else 1128 { 1129 if (isBusy() && getBusyMessage().length() > 0) 1130 { 1131 player.sendPacket(new ActionFailed()); 1132 1133 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1134 html.setFile("data/html/npcbusy.htm"); 1135 html.replace("%busymessage%", getBusyMessage()); 1136 html.replace("%npcname%", getName()); 1137 html.replace("%playername%", player.getName()); 1138 player.sendPacket(html); 1139 } else if (command.equalsIgnoreCase("TerritoryStatus")) 1140 { 1141 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1142 { 1143 if (getCastle().getOwnerId() > 0) 1144 { 1145 html.setFile("data/html/territorystatus.htm"); 1146 L2Clan clan = ClanTable.getInstance().getClan(getCastle().getOwnerId()); 1147 html.replace("%clanname%", clan.getName()); 1148 html.replace("%clanleadername%", clan.getLeaderName()); 1149 } else 1150 { 1151 html.setFile("data/html/territorynoclan.htm"); 1152 } 1153 } 1154 html.replace("%castlename%", getCastle().getName()); 1155 html.replace("%taxpercent%", "" + getCastle().getTaxPercent()); 1156 html.replace("%objectId%", String.valueOf(getObjectId())); 1157 { 1158 if (getCastle().getCastleId() > 6) 1159 { 1160 html.replace("%territory%", "The Kingdom of Elmore"); 1161 } else 1162 { 1163 html.replace("%territory%", "The Kingdom of Aden"); 1164 } 1165 } 1166 player.sendPacket(html); 1167 } else if (command.startsWith("Quest")) 1168 { 1169 String quest = ""; 1170 try 1171 { 1172 quest = command.substring(5).trim(); 1173 } catch (IndexOutOfBoundsException ioobe) 1174 { 1175 } 1176 if (quest.length() == 0) 1177 { 1178 showQuestWindow(player); 1179 } else 1180 { 1181 showQuestWindow(player, quest); 1182 } 1183 } else if (command.startsWith("Chat")) 1184 { 1185 int val = 0; 1186 try 1187 { 1188 val = Integer.parseInt(command.substring(5)); 1189 } catch (IndexOutOfBoundsException ioobe) 1190 { 1191 } catch (NumberFormatException nfe) 1192 { 1193 } 1194 showChatWindow(player, val); 1195 } else if (command.startsWith("Link")) 1196 { 1197 String path = command.substring(5).trim(); 1198 if (path.indexOf("..") != -1) 1199 { 1200 return; 1201 } 1202 String filename = "data/html/" + path; 1203 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1204 html.setFile(filename); 1205 html.replace("%objectId%", String.valueOf(getObjectId())); 1206 player.sendPacket(html); 1207 } else if (command.startsWith("NobleTeleport")) 1208 { 1209 if (!player.isNoble()) 1210 { 1211 String filename = "data/html/teleporter/nobleteleporter-no.htm"; 1212 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1213 html.setFile(filename); 1214 html.replace("%objectId%", String.valueOf(getObjectId())); 1215 html.replace("%npcname%", getName()); 1216 player.sendPacket(html); 1217 return; 1218 } 1219 int val = 0; 1220 try 1221 { 1222 val = Integer.parseInt(command.substring(5)); 1223 } catch (IndexOutOfBoundsException ioobe) 1224 { 1225 } catch (NumberFormatException nfe) 1226 { 1227 } 1228 showChatWindow(player, val); 1229 } else if (command.startsWith("Loto")) 1230 { 1231 int val = 0; 1232 try 1233 { 1234 val = Integer.parseInt(command.substring(5)); 1235 } catch (IndexOutOfBoundsException ioobe) 1236 { 1237 } catch (NumberFormatException nfe) 1238 { 1239 } 1240 if (val == 0) 1241 { 1242 // new loto ticket 1243 for (int i = 0; i < 5; i++) 1244 { 1245 player.setLoto(i, 0); 1246 } 1247 } 1248 showLotoWindow(player, val); 1249 } else if (command.startsWith("CPRecovery")) 1250 { 1251 makeCPRecovery(player); 1252 } else if (command.startsWith("SupportMagic")) 1253 { 1254 makeSupportMagic(player); 1255 } else if (command.startsWith("multisell")) 1256 { 1257 L2Multisell.getInstance().SeparateAndSend(Integer.parseInt(command.substring(9).trim()), player, false, getCastle().getTaxRate()); 1258 } else if (command.startsWith("exc_multisell")) 1259 { 1260 L2Multisell.getInstance().SeparateAndSend(Integer.parseInt(command.substring(13).trim()), player, true, getCastle().getTaxRate()); 1261 } else if (command.startsWith("Augment")) 1262 { 1263 int cmdChoice = Integer.parseInt(command.substring(8, 9).trim()); 1264 switch (cmdChoice) 1265 { 1266 case 1: 1267 player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_TO_BE_AUGMENTED)); 1268 player.sendPacket(new ExShowVariationMakeWindow()); 1269 break; 1270 case 2: 1271 player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_FROM_WHICH_YOU_WISH_TO_REMOVE_AUGMENTATION)); 1272 player.sendPacket(new ExShowVariationCancelWindow()); 1273 break; 1274 } 1275 } else if (command.startsWith("MakeBuffs")) 1276 { 1277 makeBuffs(player, command.substring(9).trim()); 1278 } else if (command.startsWith("npcfind_byid")) 1279 { 1280 try 1281 { 1282 L2Spawn spawn = SpawnTable.getInstance().getTemplate(Integer.parseInt(command.substring(12).trim())); 1283 1284 if (spawn != null) 1285 { 1286 player.sendPacket(new RadarControl(0, 1, spawn.getLocx(), spawn.getLocy(), spawn.getLocz())); 1287 } 1288 } catch (NumberFormatException nfe) 1289 { 1290 player.sendMessage("Wrong command parameters"); 1291 } 1292 } else if (command.startsWith("newbie_give_coupon")) 1293 { 1294 try 1295 { 1296 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1297 if (player.getLevel() > 25 || player.getLevel() < 6 || !player.isNewbie()) 1298 { 1299 html.setFile("data/html/adventurers_guide/31760-3.htm"); 1300 player.sendPacket(html); 1301 } else if (player.getCoupon(0)) 1302 { 1303 html.setFile("data/html/adventurers_guide/31760-1.htm"); 1304 player.sendPacket(html); 1305 } else 1306 { 1307 player.getInventory().addItem("Weapon Coupon", 7832, 1, player, this); 1308 player.addCoupon(1); 1309 html.setFile("data/html/adventurers_guide/31760-2.htm"); 1310 player.sendPacket(html); 1311 } 1312 } catch (NumberFormatException nfe) 1313 { 1314 player.sendMessage("Wrong command parameters"); 1315 } 1316 } else if (command.startsWith("newbie_give_weapon")) 1317 { 1318 try 1319 { 1320 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1321 if (player.getLevel() > 25 || player.getLevel() < 6 || !player.isNewbie()) 1322 { 1323 html.setFile("data/html/adventurers_guide/31760-3.htm"); 1324 player.sendPacket(html); 1325 } else 1326 /* 1327 * if (player.getCoupon(0)) { 1328 * html.setFile("data/html/adventurers_guide/31760-1.htm"); 1329 * player.sendPacket(html); } else 1330 */{ 1331 L2Multisell.getInstance().SeparateAndSend(10010, player, false, getCastle().getTaxRate()); 1332 } 1333 } catch (NumberFormatException nfe) 1334 { 1335 player.sendMessage("Wrong command parameters"); 1336 } 1337 } else if (command.startsWith("newbie_return_weapon")) 1338 { 1339 try 1340 { 1341 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1342 if (player.getLevel() > 25 || player.getLevel() < 6 || !player.isNewbie()) 1343 { 1344 html.setFile("data/html/adventurers_guide/31760-3.htm"); 1345 player.sendPacket(html); 1346 } else 1347 /* 1348 * if (player.getCoupon(0) || !player.isNewbie()) { 1349 * html.setFile("data/html/adventurers_guide/31760-1.htm"); 1350 * player.sendPacket(html); } else 1351 */{ 1352 L2Multisell.getInstance().SeparateAndSend(10011, player, false, getCastle().getTaxRate()); 1353 } 1354 } catch (NumberFormatException nfe) 1355 { 1356 player.sendMessage("Wrong command parameters"); 1357 } 1358 } else if (command.startsWith("traveller_give_coupon")) 1359 { 1360 try 1361 { 1362 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1363 if (player.getLevel() > 25 || player.getClassId().level() != 1 || !player.isNewbie()) 1364 { 1365 html.setFile("data/html/adventurers_guide/31760-6.htm"); 1366 player.sendPacket(html); 1367 } else if (player.getCoupon(1)) 1368 { 1369 html.setFile("data/html/adventurers_guide/31760-4.htm"); 1370 player.sendPacket(html); 1371 } else 1372 { 1373 player.getInventory().addItem("Weapon Coupon", 7833, 1, player, this); 1374 player.addCoupon(2); 1375 html.setFile("data/html/adventurers_guide/31760-5.htm"); 1376 player.sendPacket(html); 1377 } 1378 } catch (NumberFormatException nfe) 1379 { 1380 player.sendMessage("Wrong command parameters"); 1381 } 1382 } else if (command.startsWith("traveller_give_weapon")) 1383 { 1384 try 1385 { 1386 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1387 if (player.getLevel() > 25 || player.getClassId().level() != 1 || !player.isNewbie()) 1388 { 1389 html.setFile("data/html/adventurers_guide/31760-6.htm"); 1390 player.sendPacket(html); 1391 } else 1392 /* 1393 * if (player.getCoupon(1)) { 1394 * html.setFile("data/html/adventurers_guide/31760-4.htm"); 1395 * player.sendPacket(html); } else 1396 */{ 1397 L2Multisell.getInstance().SeparateAndSend(10012, player, false, getCastle().getTaxRate()); 1398 } 1399 } catch (NumberFormatException nfe) 1400 { 1401 player.sendMessage("Wrong command parameters"); 1402 } 1403 } else if (command.startsWith("traveller_return_weapon")) 1404 { 1405 try 1406 { 1407 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1408 if (player.getLevel() > 25 || player.getClassId().level() != 1 || !player.isNewbie()) 1409 { 1410 html.setFile("data/html/adventurers_guide/31760-6.htm"); 1411 player.sendPacket(html); 1412 } else 1413 /* 1414 * if (player.getCoupon(1)) { 1415 * html.setFile("data/html/adventurers_guide/31760-4.htm"); 1416 * player.sendPacket(html); } else 1417 */{ 1418 L2Multisell.getInstance().SeparateAndSend(10013, player, false, getCastle().getTaxRate()); 1419 } 1420 } catch (NumberFormatException nfe) 1421 { 1422 player.sendMessage("Wrong command parameters"); 1423 } 1424 } else if (command.startsWith("EnterRift")) 1425 { 1426 try 1427 { 1428 Byte b1 = Byte.parseByte(command.substring(10)); // Selected 1429 // Area: 1430 // Recruit, 1431 // Soldier 1432 // etc 1433 DimensionalRiftManager.getInstance().start(player, b1, this); 1434 } catch (Exception e) 1435 { 1436 } 1437 } else if (command.startsWith("ChangeRiftRoom")) 1438 { 1439 if (player.isInParty() && player.getParty().isInDimensionalRift()) 1440 { 1441 player.getParty().getDimensionalRift().manualTeleport(player, this); 1442 } else 1443 { 1444 DimensionalRiftManager.getInstance().handleCheat(player, this); 1445 } 1446 } else if (command.startsWith("ExitRift")) 1447 { 1448 if (player.isInParty() && player.getParty().isInDimensionalRift()) 1449 { 1450 player.getParty().getDimensionalRift().manualExitRift(player, this); 1451 } else 1452 { 1453 DimensionalRiftManager.getInstance().handleCheat(player, this); 1454 } 1455 } 1456 } 1457 } 1458 1459 /** 1460 * Throws an action command to L2BufferInstance.
    1461 * 1462 * @param player 1463 * --> Target player 1464 * @param buffTemplate 1465 * --> Name of the Buff Template to Add 1466 */ 1467 public void makeBuffs(L2PcInstance player, String buffTemplate) 1468 { 1469 int _templateId = 0; 1470 1471 try 1472 { 1473 _templateId = Integer.parseInt(buffTemplate); 1474 } catch (NumberFormatException e) 1475 { 1476 _templateId = BuffTemplateTable.getInstance().getTemplateIdByName(buffTemplate); 1477 } 1478 if (_templateId > 0) 1479 { 1480 L2BufferInstance.makeBuffs(player, _templateId, this, true); 1481 } 1482 } 1483 1484 /** 1485 * Return null (regular NPCs don't have weapons instancies).
    1486 *
    1487 */ 1488 @Override 1489 public L2ItemInstance getActiveWeaponInstance() 1490 { 1491 // regular NPCs dont have weapons instancies 1492 return null; 1493 } 1494 1495 /** 1496 * Return the weapon item equiped in the right hand of the L2NpcInstance 1497 * or null.
    1498 *
    1499 */ 1500 @Override 1501 public L2Weapon getActiveWeaponItem() 1502 { 1503 // Get the weapon identifier equiped in the right hand of the 1504 // L2NpcInstance 1505 int weaponId = getTemplate().rhand; 1506 1507 if (weaponId < 1) 1508 { 1509 return null; 1510 } 1511 1512 // Get the weapon item equiped in the right hand of the L2NpcInstance 1513 L2Item item = ItemTable.getInstance().getTemplate(getTemplate().rhand); 1514 1515 if (!(item instanceof L2Weapon)) 1516 { 1517 return null; 1518 } 1519 1520 return (L2Weapon) item; 1521 } 1522 1523 /** 1524 * Return null (regular NPCs don't have weapons instancies).
    1525 *
    1526 */ 1527 @Override 1528 public L2ItemInstance getSecondaryWeaponInstance() 1529 { 1530 // regular NPCs dont have weapons instancies 1531 return null; 1532 } 1533 1534 /** 1535 * Return the weapon item equiped in the left hand of the L2NpcInstance 1536 * or null.
    1537 *
    1538 */ 1539 @Override 1540 public L2Weapon getSecondaryWeaponItem() 1541 { 1542 // Get the weapon identifier equiped in the right hand of the 1543 // L2NpcInstance 1544 int weaponId = getTemplate().lhand; 1545 1546 if (weaponId < 1) 1547 { 1548 return null; 1549 } 1550 1551 // Get the weapon item equiped in the right hand of the L2NpcInstance 1552 L2Item item = ItemTable.getInstance().getTemplate(getTemplate().lhand); 1553 1554 if (!(item instanceof L2Weapon)) 1555 { 1556 return null; 1557 } 1558 1559 return (L2Weapon) item; 1560 } 1561 1562 /** 1563 * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in 1564 * order to display the message of the L2NpcInstance.
    1565 *
    1566 * 1567 * @param player 1568 * The L2PcInstance who talks with the L2NpcInstance 1569 * @param content 1570 * The text of the L2NpcMessage 1571 * 1572 */ 1573 public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content) 1574 { 1575 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in 1576 // order to display the message of the L2NpcInstance 1577 content = content.replaceAll("%objectId%", String.valueOf(getObjectId())); 1578 NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId()); 1579 npcReply.setHtml(content); 1580 player.sendPacket(npcReply); 1581 } 1582 1583 /** 1584 * Return the pathfile of the selected HTML file in function of the 1585 * npcId and of the page number.
    1586 *
    1587 * 1588 * Format of the pathfile :
    1589 *
    1590 *
  • if the file exists on the server (page number = 0) : 1591 * data/html/default/12006.htm (npcId-page number)
  • 1592 *
  • if the file exists on the server (page number > 0) : 1593 * data/html/default/12006-1.htm (npcId-page number)
  • 1594 *
  • if the file doesn't exist on the server : 1595 * data/html/npcdefault.htm (message : "I have nothing to say to 1596 * you")
  • 1597 *
    1598 *
    1599 * 1600 * Overriden in :
    1601 *
    1602 *
  • L2GuardInstance : Set the pathfile to 1603 * data/html/guard/12006-1.htm (npcId-page number)
  • 1604 *
    1605 *
    1606 * 1607 * @param npcId 1608 * The Identifier of the L2NpcInstance whose text must be 1609 * display 1610 * @param val 1611 * The number of the page to display 1612 * 1613 */ 1614 public String getHtmlPath(int npcId, int val) 1615 { 1616 String pom = ""; 1617 1618 if (val == 0) 1619 { 1620 pom = "" + npcId; 1621 } else 1622 { 1623 pom = npcId + "-" + val; 1624 } 1625 1626 String temp = "data/html/default/" + pom + ".htm"; 1627 1628 if (!Config.LAZY_CACHE) 1629 { 1630 // If not running lazy cache the file must be in the cache or it 1631 // doesnt exist 1632 if (HtmCache.getInstance().contains(temp)) 1633 { 1634 return temp; 1635 } 1636 } else 1637 { 1638 if (HtmCache.getInstance().isLoadable(temp)) 1639 { 1640 return temp; 1641 } 1642 } 1643 1644 // If the file is not found, the standard message "I have nothing to say 1645 // to you" is returned 1646 return "data/html/npcdefault.htm"; 1647 } 1648 1649 /** 1650 * Open a choose quest window on client with all quests available of the 1651 * L2NpcInstance.
    1652 *
    1653 * 1654 * Actions :
    1655 *
    1656 *
  • Send a Server->Client NpcHtmlMessage containing the text of the 1657 * L2NpcInstance to the L2PcInstance
  • 1658 *
    1659 *
    1660 * 1661 * @param player 1662 * The L2PcInstance that talk with the L2NpcInstance 1663 * @param quests 1664 * The table containing quests of the L2NpcInstance 1665 * 1666 */ 1667 public void showQuestChooseWindow(L2PcInstance player, Quest[] quests) 1668 { 1669 TextBuilder sb = new TextBuilder(); 1670 1671 sb.append("Talk about:
    "); 1672 1673 for (Quest q : quests) 1674 { 1675 sb.append("").append(q.getDescr()).append("
    "); 1676 } 1677 1678 sb.append(""); 1679 1680 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in 1681 // order to display the message of the L2NpcInstance 1682 insertObjectIdAndShowChatWindow(player, sb.toString()); 1683 } 1684 1685 /** 1686 * Open a quest window on client with the text of the L2NpcInstance.
    1687 *
    1688 * 1689 * Actions :
    1690 *
    1691 *
  • Get the text of the quest state in the folder 1692 * data/jscript/quests/questId/stateId.htm
  • 1693 *
  • Send a Server->Client NpcHtmlMessage containing the text of the 1694 * L2NpcInstance to the L2PcInstance
  • 1695 *
  • Send a Server->Client ActionFailed to the L2PcInstance in order 1696 * to avoid that the client wait another packet
  • 1697 *
    1698 *
    1699 * 1700 * @param player 1701 * The L2PcInstance that talk with the L2NpcInstance 1702 * @param questId 1703 * The Identifier of the quest to display the message 1704 * 1705 */ 1706 public void showQuestWindow(L2PcInstance player, String questId) 1707 { 1708 String content; 1709 1710 if (player.getWeightPenalty() >= 3) 1711 { 1712 player.sendPacket(new SystemMessage(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT)); 1713 return; 1714 } 1715 1716 // FileInputStream fis = null; 1717 1718 // Get the state of the selected quest 1719 QuestState qs = player.getQuestState(questId); 1720 1721 if (qs != null) 1722 { 1723 // If the quest is alreday started, no need to show a window 1724 if (!qs.getQuest().notifyTalk(this, qs)) 1725 { 1726 return; 1727 } 1728 } else 1729 { 1730 Quest q = QuestManager.getInstance().getQuest(questId); 1731 if (q != null) 1732 { 1733 // check for start point 1734 Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START); 1735 1736 if (qlst != null && qlst.length > 0) 1737 { 1738 for (int i = 0; i < qlst.length; i++) 1739 { 1740 if (qlst[i] == q) 1741 { 1742 qs = q.newQuestState(player); 1743 // disabled by mr. becouse quest dialog only 1744 // show on 1745 // second click. 1746 // if(qs.getState().getName().equalsIgnoreCase("completed")) 1747 // { 1748 if (!qs.getQuest().notifyTalk(this, qs)) 1749 { 1750 return; // no need to show a window 1751 } 1752 // } 1753 break; 1754 } 1755 } 1756 } 1757 } 1758 } 1759 1760 if (qs == null) 1761 { 1762 // no quests found 1763 content = "You are either not carrying out your quest or don't meet the criteria."; 1764 } else 1765 { 1766 questId = qs.getQuest().getName(); 1767 String stateId = qs.getStateId(); 1768 String path = "data/jscript/quests/" + questId + "/" + stateId + ".htm"; 1769 content = HtmCache.getInstance().getHtm(path); // TODO path for 1770 // quests html 1771 1772 if (Config.DEBUG) 1773 { 1774 if (content != null) 1775 { 1776 _log.fine("Showing quest window for quest " + questId + " html path: " + path); 1777 } else 1778 { 1779 _log.fine("File not exists for quest " + questId + " html path: " + path); 1780 } 1781 } 1782 } 1783 1784 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in 1785 // order to display the message of the L2NpcInstance 1786 if (content != null) 1787 { 1788 insertObjectIdAndShowChatWindow(player, content); 1789 } 1790 1791 // Send a Server->Client ActionFailed to the L2PcInstance in order to 1792 // avoid that the client wait another packet 1793 player.sendPacket(new ActionFailed()); 1794 } 1795 1796 /** 1797 * Collect awaiting quests/start points and display a QuestChooseWindow 1798 * (if several available) or QuestWindow.
    1799 *
    1800 * 1801 * @param player 1802 * The L2PcInstance that talk with the L2NpcInstance 1803 * 1804 */ 1805 public void showQuestWindow(L2PcInstance player) 1806 { 1807 // collect awaiting quests and start points 1808 List options = new FastList(); 1809 1810 QuestState[] awaits = player.getQuestsForTalk(getTemplate().npcId); 1811 Quest[] starts = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START); 1812 1813 // Quests are limited between 1 and 999 because those are the quests 1814 // that are supported by the client. 1815 // By limitting them there, we are allowed to create custom quests at 1816 // higher IDs without interfering 1817 if (awaits != null) 1818 { 1819 for (QuestState x : awaits) 1820 { 1821 if (!options.contains(x)) 1822 { 1823 if (x.getQuest().getQuestIntId() > 0 && x.getQuest().getQuestIntId() < 1000) 1824 { 1825 options.add(x.getQuest()); 1826 } 1827 } 1828 } 1829 } 1830 1831 if (starts != null) 1832 { 1833 for (Quest x : starts) 1834 { 1835 if (!options.contains(x)) 1836 { 1837 if (x.getQuestIntId() > 0 && x.getQuestIntId() < 1000) 1838 { 1839 options.add(x); 1840 } 1841 } 1842 } 1843 } 1844 1845 // Display a QuestChooseWindow (if several quests are available) or 1846 // QuestWindow 1847 if (options.size() > 1) 1848 { 1849 showQuestChooseWindow(player, options.toArray(new Quest[options.size()])); 1850 } else if (options.size() == 1) 1851 { 1852 showQuestWindow(player, options.get(0).getName()); 1853 } else 1854 { 1855 showQuestWindow(player, ""); 1856 } 1857 } 1858 1859 /** 1860 * Open a Loto window on client with the text of the L2NpcInstance.
    1861 *
    1862 * 1863 * Actions :
    1864 *
    1865 *
  • Get the text of the selected HTML file in function of the npcId 1866 * and of the page number
  • 1867 *
  • Send a Server->Client NpcHtmlMessage containing the text of the 1868 * L2NpcInstance to the L2PcInstance
  • 1869 *
  • Send a Server->Client ActionFailed to the L2PcInstance in order 1870 * to avoid that the client wait another packet
  • 1871 *
    1872 * 1873 * @param player 1874 * The L2PcInstance that talk with the L2NpcInstance 1875 * @param val 1876 * The number of the page of the L2NpcInstance to display 1877 * 1878 */ 1879 // 0 - first buy lottery ticket window 1880 // 1-20 - buttons 1881 // 21 - second buy lottery ticket window 1882 // 22 - selected ticket with 5 numbers 1883 // 23 - current lottery jackpot 1884 // 24 - Previous winning numbers/Prize claim 1885 // >24 - check lottery ticket by item object id 1886 public void showLotoWindow(L2PcInstance player, int val) 1887 { 1888 int npcId = getTemplate().npcId; 1889 String filename; 1890 SystemMessage sm; 1891 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 1892 1893 if (val == 0) // 0 - first buy lottery ticket window 1894 { 1895 filename = getHtmlPath(npcId, 1); 1896 html.setFile(filename); 1897 } else if (val >= 1 && val <= 21) // 1-20 - buttons, 21 - second 1898 // buy 1899 // lottery ticket window 1900 { 1901 if (!Lottery.getInstance().isStarted()) 1902 { 1903 // tickets can't be sold 1904 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD)); 1905 return; 1906 } 1907 if (!Lottery.getInstance().isSellableTickets()) 1908 { 1909 // tickets can't be sold 1910 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE)); 1911 return; 1912 } 1913 1914 filename = getHtmlPath(npcId, 5); 1915 html.setFile(filename); 1916 1917 int count = 0; 1918 int found = 0; 1919 // counting buttons and unsetting button if found 1920 for (int i = 0; i < 5; i++) 1921 { 1922 if (player.getLoto(i) == val) 1923 { 1924 // unsetting button 1925 player.setLoto(i, 0); 1926 found = 1; 1927 } else if (player.getLoto(i) > 0) 1928 { 1929 count++; 1930 } 1931 } 1932 1933 // if not rearched limit 5 and not unseted value 1934 if (count < 5 && found == 0 && val <= 20) 1935 { 1936 for (int i = 0; i < 5; i++) 1937 { 1938 if (player.getLoto(i) == 0) 1939 { 1940 player.setLoto(i, val); 1941 break; 1942 } 1943 } 1944 } 1945 1946 // setting pusshed buttons 1947 count = 0; 1948 for (int i = 0; i < 5; i++) 1949 { 1950 if (player.getLoto(i) > 0) 1951 { 1952 count++; 1953 String button = String.valueOf(player.getLoto(i)); 1954 if (player.getLoto(i) < 10) 1955 { 1956 button = "0" + button; 1957 } 1958 String search = "fore=\"L2UI.lottoNum" + button + "\" back=\"L2UI.lottoNum" + button + "a_check\""; 1959 String replace = "fore=\"L2UI.lottoNum" + button + "a_check\" back=\"L2UI.lottoNum" + button + "\""; 1960 html.replace(search, replace); 1961 } 1962 } 1963 1964 if (count == 5) 1965 { 1966 String search = "0\">Return"; 1967 String replace = "22\">The winner selected the numbers above."; 1968 html.replace(search, replace); 1969 } 1970 } else if (val == 22) // 22 - selected ticket with 5 numbers 1971 { 1972 if (!Lottery.getInstance().isStarted()) 1973 { 1974 // tickets can't be sold 1975 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD)); 1976 return; 1977 } 1978 if (!Lottery.getInstance().isSellableTickets()) 1979 { 1980 // tickets can't be sold 1981 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE)); 1982 return; 1983 } 1984 1985 int price = Config.ALT_LOTTERY_TICKET_PRICE; 1986 int lotonumber = Lottery.getInstance().getId(); 1987 int enchant = 0; 1988 int type2 = 0; 1989 1990 for (int i = 0; i < 5; i++) 1991 { 1992 if (player.getLoto(i) == 0) 1993 { 1994 return; 1995 } 1996 1997 if (player.getLoto(i) < 17) 1998 { 1999 enchant += Math.pow(2, player.getLoto(i) - 1); 2000 } else 2001 { 2002 type2 += Math.pow(2, player.getLoto(i) - 17); 2003 } 2004 } 2005 if (player.getAdena() < price) 2006 { 2007 sm = new SystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA); 2008 player.sendPacket(sm); 2009 return; 2010 } 2011 if (!player.reduceAdena("Loto", price, this, true)) 2012 { 2013 return; 2014 } 2015 Lottery.getInstance().increasePrize(price); 2016 2017 sm = new SystemMessage(SystemMessageId.ACQUIRED); 2018 sm.addNumber(lotonumber); 2019 sm.addItemName(4442); 2020 player.sendPacket(sm); 2021 2022 L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4442); 2023 item.setCount(1); 2024 item.setCustomType1(lotonumber); 2025 item.setEnchantLevel(enchant); 2026 item.setCustomType2(type2); 2027 player.getInventory().addItem("Loto", item, player, this); 2028 2029 InventoryUpdate iu = new InventoryUpdate(); 2030 iu.addItem(item); 2031 L2ItemInstance adenaupdate = player.getInventory().getItemByItemId(57); 2032 iu.addModifiedItem(adenaupdate); 2033 player.sendPacket(iu); 2034 2035 filename = getHtmlPath(npcId, 3); 2036 html.setFile(filename); 2037 } else if (val == 23) // 23 - current lottery jackpot 2038 { 2039 filename = getHtmlPath(npcId, 3); 2040 html.setFile(filename); 2041 } else if (val == 24) // 24 - Previous winning numbers/Prize 2042 // claim 2043 { 2044 filename = getHtmlPath(npcId, 4); 2045 html.setFile(filename); 2046 2047 int lotonumber = Lottery.getInstance().getId(); 2048 String message = ""; 2049 for (L2ItemInstance item : player.getInventory().getItems()) 2050 { 2051 if (item == null) 2052 { 2053 continue; 2054 } 2055 if (item.getItemId() == 4442 && item.getCustomType1() < lotonumber) 2056 { 2057 message = message + "" + item.getCustomType1() + " Event Number "; 2058 int[] numbers = Lottery.getInstance().decodeNumbers(item.getEnchantLevel(), item.getCustomType2()); 2059 for (int i = 0; i < 5; i++) 2060 { 2061 message += numbers[i] + " "; 2062 } 2063 int[] check = Lottery.getInstance().checkTicket(item); 2064 if (check[0] > 0) 2065 { 2066 switch (check[0]) 2067 { 2068 case 1: 2069 message += "- 1st Prize"; 2070 break; 2071 case 2: 2072 message += "- 2nd Prize"; 2073 break; 2074 case 3: 2075 message += "- 3th Prize"; 2076 break; 2077 case 4: 2078 message += "- 4th Prize"; 2079 break; 2080 } 2081 message += " " + check[1] + "a."; 2082 } 2083 message += "
    "; 2084 } 2085 } 2086 if (message == "") 2087 { 2088 message += "There is no winning lottery ticket...
    "; 2089 } 2090 html.replace("%result%", message); 2091 } else if (val > 24) // >24 - check lottery ticket by 2092 // item object id 2093 { 2094 int lotonumber = Lottery.getInstance().getId(); 2095 L2ItemInstance item = player.getInventory().getItemByObjectId(val); 2096 if (item == null || item.getItemId() != 4442 || item.getCustomType1() >= lotonumber) 2097 { 2098 return; 2099 } 2100 int[] check = Lottery.getInstance().checkTicket(item); 2101 2102 sm = new SystemMessage(SystemMessageId.DISSAPEARED_ITEM); 2103 sm.addItemName(4442); 2104 player.sendPacket(sm); 2105 2106 int adena = check[1]; 2107 if (adena > 0) 2108 { 2109 player.addAdena("Loto", adena, this, true); 2110 } 2111 player.destroyItem("Loto", item, this, false); 2112 return; 2113 } 2114 html.replace("%objectId%", String.valueOf(getObjectId())); 2115 html.replace("%race%", "" + Lottery.getInstance().getId()); 2116 html.replace("%adena%", "" + Lottery.getInstance().getPrize()); 2117 html.replace("%ticket_price%", "" + Config.ALT_LOTTERY_TICKET_PRICE); 2118 html.replace("%prize5%", "" + Config.ALT_LOTTERY_5_NUMBER_RATE * 100); 2119 html.replace("%prize4%", "" + Config.ALT_LOTTERY_4_NUMBER_RATE * 100); 2120 html.replace("%prize3%", "" + Config.ALT_LOTTERY_3_NUMBER_RATE * 100); 2121 html.replace("%prize2%", "" + Config.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE); 2122 html.replace("%enddate%", "" + DateFormat.getDateInstance().format(Lottery.getInstance().getEndDate())); 2123 player.sendPacket(html); 2124 2125 // Send a Server->Client ActionFailed to the L2PcInstance in order to 2126 // avoid that the client wait another packet 2127 player.sendPacket(new ActionFailed()); 2128 } 2129 2130 public void makeCPRecovery(L2PcInstance player) 2131 { 2132 if (getNpcId() != 31225 && getNpcId() != 31226) 2133 { 2134 return; 2135 } 2136 if (player.isCursedWeaponEquiped()) 2137 { 2138 player.sendMessage("Go away, you're not welcome here."); 2139 return; 2140 } 2141 2142 int neededmoney = 100; 2143 SystemMessage sm; 2144 if (!player.reduceAdena("RestoreCP", neededmoney, player.getLastFolkNPC(), true)) 2145 { 2146 return; 2147 } 2148 player.setCurrentCp(getCurrentCp() + 5000); 2149 // cp restored 2150 sm = new SystemMessage(SystemMessageId.S1_CP_WILL_BE_RESTORED); 2151 sm.addString(player.getName()); 2152 player.sendPacket(sm); 2153 } 2154 2155 /** 2156 * Add Newbie helper buffs to L2Player according to its level.
    2157 *
    2158 * 2159 * Actions :
    2160 *
    2161 *
  • Get the range level in wich player must be to obtain buff
  • 2162 *
  • If player level is out of range, display a message and return 2163 *
  • 2164 *
  • According to player level cast buff
  • 2165 *
    2166 *
    2167 * 2168 * Newbie Helper Buff list is define in sql 2169 * table helper_buff_list
    2170 *
    2171 * 2172 * @param player 2173 * The L2PcInstance that talk with the L2NpcInstance 2174 * 2175 */ 2176 public void makeSupportMagic(L2PcInstance player) 2177 { 2178 if (player == null) 2179 { 2180 return; 2181 } 2182 2183 // Prevent a cursed weapon weilder of being buffed 2184 if (player.isCursedWeaponEquiped()) 2185 { 2186 return; 2187 } 2188 2189 int player_level = player.getLevel(); 2190 int lowestLevel = 0; 2191 int higestLevel = 0; 2192 2193 // Select the player 2194 setTarget(player); 2195 2196 // Calculate the min and max level between wich the player must be to 2197 // obtain buff 2198 if (player.isMageClass()) 2199 { 2200 lowestLevel = HelperBuffTable.getInstance().getMagicClassLowestLevel(); 2201 higestLevel = HelperBuffTable.getInstance().getMagicClassHighestLevel(); 2202 } else 2203 { 2204 lowestLevel = HelperBuffTable.getInstance().getPhysicClassLowestLevel(); 2205 higestLevel = HelperBuffTable.getInstance().getPhysicClassHighestLevel(); 2206 } 2207 2208 // If the player is too high level, display a message and return 2209 if (player_level > higestLevel || !player.isNewbie()) 2210 { 2211 String content = "Newbie Guide:
    Only a novice character of level " + higestLevel + " or less can receive my support magic.
    Your novice character is the first one that you created and raised in this world."; 2212 insertObjectIdAndShowChatWindow(player, content); 2213 return; 2214 } 2215 2216 // If the player is too low level, display a message and return 2217 if (player_level < lowestLevel) 2218 { 2219 String content = "Come back here when you have reached level " + lowestLevel + ". I will give you support magic then."; 2220 insertObjectIdAndShowChatWindow(player, content); 2221 return; 2222 } 2223 2224 L2Skill skill = null; 2225 // Go through the Helper Buff list define in sql table helper_buff_list 2226 // and cast skill 2227 for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable()) 2228 { 2229 if (helperBuffItem.isMagicClassBuff() == player.isMageClass()) 2230 { 2231 if (player_level >= helperBuffItem.getLowerLevel() && player_level <= helperBuffItem.getUpperLevel()) 2232 { 2233 skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel()); 2234 if (skill.getSkillType() == SkillType.SUMMON) 2235 { 2236 player.doCast(skill); 2237 } else 2238 { 2239 doCast(skill); 2240 } 2241 } 2242 } 2243 } 2244 2245 } 2246 2247 public void showChatWindow(L2PcInstance player) 2248 { 2249 showChatWindow(player, 0); 2250 } 2251 2252 /** 2253 * Returns true if html exists 2254 * 2255 * @param player 2256 * @param type 2257 * @return boolean 2258 */ 2259 private boolean showPkDenyChatWindow(L2PcInstance player, String type) 2260 { 2261 String html = HtmCache.getInstance().getHtm("data/html/" + type + "/" + getNpcId() + "-pk.htm"); 2262 2263 if (html != null) 2264 { 2265 NpcHtmlMessage pkDenyMsg = new NpcHtmlMessage(getObjectId()); 2266 pkDenyMsg.setHtml(html); 2267 player.sendPacket(pkDenyMsg); 2268 player.sendPacket(new ActionFailed()); 2269 return true; 2270 } 2271 2272 return false; 2273 } 2274 2275 /** 2276 * Open a chat window on client with the text of the L2NpcInstance.
    2277 *
    2278 * 2279 * Actions :
    2280 *
    2281 *
  • Get the text of the selected HTML file in function of the npcId 2282 * and of the page number
  • 2283 *
  • Send a Server->Client NpcHtmlMessage containing the text of the 2284 * L2NpcInstance to the L2PcInstance
  • 2285 *
  • Send a Server->Client ActionFailed to the L2PcInstance in order 2286 * to avoid that the client wait another packet
  • 2287 *
    2288 * 2289 * @param player 2290 * The L2PcInstance that talk with the L2NpcInstance 2291 * @param val 2292 * The number of the page of the L2NpcInstance to display 2293 * 2294 */ 2295 public void showChatWindow(L2PcInstance player, int val) 2296 { 2297 if (player.getKarma() > 0) 2298 { 2299 if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && this instanceof L2MerchantInstance) 2300 { 2301 if (showPkDenyChatWindow(player, "merchant")) 2302 { 2303 return; 2304 } 2305 } else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_GK && this instanceof L2TeleporterInstance) 2306 { 2307 if (showPkDenyChatWindow(player, "teleporter")) 2308 { 2309 return; 2310 } 2311 } else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE && this instanceof L2WarehouseInstance) 2312 { 2313 if (showPkDenyChatWindow(player, "warehouse")) 2314 { 2315 return; 2316 } 2317 } else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && this instanceof L2FishermanInstance) 2318 { 2319 if (showPkDenyChatWindow(player, "fisherman")) 2320 { 2321 return; 2322 } 2323 } 2324 } 2325 2326 if (getTemplate().type == "L2Auctioneer" && val == 0) 2327 { 2328 return; 2329 } 2330 2331 int npcId = getTemplate().npcId; 2332 2333 /* For use with Seven Signs implementation */ 2334 String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH; 2335 int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE); 2336 int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS); 2337 int playerCabal = SevenSigns.getInstance().getPlayerCabal(player); 2338 boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod(); 2339 int compWinner = SevenSigns.getInstance().getCabalHighestScore(); 2340 2341 switch (npcId) 2342 { 2343 case 31078: 2344 case 31079: 2345 case 31080: 2346 case 31081: 2347 case 31082: // Dawn Priests 2348 case 31083: 2349 case 31084: 2350 case 31168: 2351 case 31692: 2352 case 31694: 2353 case 31997: 2354 switch (playerCabal) 2355 { 2356 case SevenSigns.CABAL_DAWN: 2357 if (isSealValidationPeriod) 2358 { 2359 if (compWinner == SevenSigns.CABAL_DAWN) 2360 { 2361 if (compWinner != sealGnosisOwner) 2362 { 2363 filename += "dawn_priest_2c.htm"; 2364 } else 2365 { 2366 filename += "dawn_priest_2a.htm"; 2367 } 2368 } else 2369 { 2370 filename += "dawn_priest_2b.htm"; 2371 } 2372 } else 2373 { 2374 filename += "dawn_priest_1b.htm"; 2375 } 2376 break; 2377 case SevenSigns.CABAL_DUSK: 2378 if (isSealValidationPeriod) 2379 { 2380 filename += "dawn_priest_3b.htm"; 2381 } else 2382 { 2383 filename += "dawn_priest_3a.htm"; 2384 } 2385 break; 2386 default: 2387 if (isSealValidationPeriod) 2388 { 2389 if (compWinner == SevenSigns.CABAL_DAWN) 2390 { 2391 filename += "dawn_priest_4.htm"; 2392 } else 2393 { 2394 filename += "dawn_priest_2b.htm"; 2395 } 2396 } else 2397 { 2398 filename += "dawn_priest_1a.htm"; 2399 } 2400 break; 2401 } 2402 break; 2403 case 31085: 2404 case 31086: 2405 case 31087: 2406 case 31088: // Dusk Priest 2407 case 31089: 2408 case 31090: 2409 case 31091: 2410 case 31169: 2411 case 31693: 2412 case 31695: 2413 case 31998: 2414 switch (playerCabal) 2415 { 2416 case SevenSigns.CABAL_DUSK: 2417 if (isSealValidationPeriod) 2418 { 2419 if (compWinner == SevenSigns.CABAL_DUSK) 2420 { 2421 if (compWinner != sealGnosisOwner) 2422 { 2423 filename += "dusk_priest_2c.htm"; 2424 } else 2425 { 2426 filename += "dusk_priest_2a.htm"; 2427 } 2428 } else 2429 { 2430 filename += "dusk_priest_2b.htm"; 2431 } 2432 } else 2433 { 2434 filename += "dusk_priest_1b.htm"; 2435 } 2436 break; 2437 case SevenSigns.CABAL_DAWN: 2438 if (isSealValidationPeriod) 2439 { 2440 filename += "dusk_priest_3b.htm"; 2441 } else 2442 { 2443 filename += "dusk_priest_3a.htm"; 2444 } 2445 break; 2446 default: 2447 if (isSealValidationPeriod) 2448 { 2449 if (compWinner == SevenSigns.CABAL_DUSK) 2450 { 2451 filename += "dusk_priest_4.htm"; 2452 } else 2453 { 2454 filename += "dusk_priest_2b.htm"; 2455 } 2456 } else 2457 { 2458 filename += "dusk_priest_1a.htm"; 2459 } 2460 break; 2461 } 2462 break; 2463 case 31095: // 2464 case 31096: // 2465 case 31097: // 2466 case 31098: // Enter Necropolises 2467 case 31099: // 2468 case 31100: // 2469 case 31101: // 2470 case 31102: // 2471 if (isSealValidationPeriod) 2472 { 2473 if (playerCabal != compWinner || sealAvariceOwner != compWinner) 2474 { 2475 switch (compWinner) 2476 { 2477 case SevenSigns.CABAL_DAWN: 2478 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); 2479 filename += "necro_no.htm"; 2480 break; 2481 case SevenSigns.CABAL_DUSK: 2482 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); 2483 filename += "necro_no.htm"; 2484 break; 2485 case SevenSigns.CABAL_NULL: 2486 filename = getHtmlPath(npcId, val); // do 2487 // the 2488 // default! 2489 break; 2490 } 2491 } else 2492 { 2493 filename = getHtmlPath(npcId, val); // do the 2494 // default! 2495 } 2496 } else 2497 { 2498 if (playerCabal == SevenSigns.CABAL_NULL) 2499 { 2500 filename += "necro_no.htm"; 2501 } else 2502 { 2503 filename = getHtmlPath(npcId, val); // do the 2504 // default! 2505 } 2506 } 2507 break; 2508 case 31114: // 2509 case 31115: // 2510 case 31116: // Enter Catacombs 2511 case 31117: // 2512 case 31118: // 2513 case 31119: // 2514 if (isSealValidationPeriod) 2515 { 2516 if (playerCabal != compWinner || sealGnosisOwner != compWinner) 2517 { 2518 switch (compWinner) 2519 { 2520 case SevenSigns.CABAL_DAWN: 2521 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); 2522 filename += "cata_no.htm"; 2523 break; 2524 case SevenSigns.CABAL_DUSK: 2525 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); 2526 filename += "cata_no.htm"; 2527 break; 2528 case SevenSigns.CABAL_NULL: 2529 filename = getHtmlPath(npcId, val); // do 2530 // the 2531 // default! 2532 break; 2533 } 2534 } else 2535 { 2536 filename = getHtmlPath(npcId, val); // do the 2537 // default! 2538 } 2539 } else 2540 { 2541 if (playerCabal == SevenSigns.CABAL_NULL) 2542 { 2543 filename += "cata_no.htm"; 2544 } else 2545 { 2546 filename = getHtmlPath(npcId, val); // do the 2547 // default! 2548 } 2549 } 2550 break; 2551 case 31111: // Gatekeeper Spirit (Disciples) 2552 if (playerCabal == sealAvariceOwner && playerCabal == compWinner) 2553 { 2554 switch (sealAvariceOwner) 2555 { 2556 case SevenSigns.CABAL_DAWN: 2557 filename += "spirit_dawn.htm"; 2558 break; 2559 case SevenSigns.CABAL_DUSK: 2560 filename += "spirit_dusk.htm"; 2561 break; 2562 case SevenSigns.CABAL_NULL: 2563 filename += "spirit_null.htm"; 2564 break; 2565 } 2566 } else 2567 { 2568 filename += "spirit_null.htm"; 2569 } 2570 break; 2571 case 31112: // Gatekeeper Spirit (Disciples) 2572 filename += "spirit_exit.htm"; 2573 break; 2574 case 31127: // 2575 case 31128: // 2576 case 31129: // Dawn Festival Guides 2577 case 31130: // 2578 case 31131: // 2579 filename += "festival/dawn_guide.htm"; 2580 break; 2581 case 31137: // 2582 case 31138: // 2583 case 31139: // Dusk Festival Guides 2584 case 31140: // 2585 case 31141: // 2586 filename += "festival/dusk_guide.htm"; 2587 break; 2588 case 31092: // Black Marketeer of Mammon 2589 filename += "blkmrkt_1.htm"; 2590 break; 2591 case 31113: // Merchant of Mammon 2592 switch (compWinner) 2593 { 2594 case SevenSigns.CABAL_DAWN: 2595 if (playerCabal != compWinner || playerCabal != sealAvariceOwner) 2596 { 2597 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); 2598 return; 2599 } 2600 break; 2601 case SevenSigns.CABAL_DUSK: 2602 if (playerCabal != compWinner || playerCabal != sealAvariceOwner) 2603 { 2604 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); 2605 return; 2606 } 2607 break; 2608 } 2609 filename += "mammmerch_1.htm"; 2610 break; 2611 case 31126: // Blacksmith of Mammon 2612 switch (compWinner) 2613 { 2614 case SevenSigns.CABAL_DAWN: 2615 if (playerCabal != compWinner || playerCabal != sealGnosisOwner) 2616 { 2617 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); 2618 return; 2619 } 2620 break; 2621 case SevenSigns.CABAL_DUSK: 2622 if (playerCabal != compWinner || playerCabal != sealGnosisOwner) 2623 { 2624 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); 2625 return; 2626 } 2627 break; 2628 } 2629 filename += "mammblack_1.htm"; 2630 break; 2631 case 31132: 2632 case 31133: 2633 case 31134: 2634 case 31135: 2635 case 31136: // Festival Witches 2636 case 31142: 2637 case 31143: 2638 case 31144: 2639 case 31145: 2640 case 31146: 2641 filename += "festival/festival_witch.htm"; 2642 break; 2643 case 31688: 2644 if (player.isNoble()) 2645 { 2646 filename = Olympiad.OLYMPIAD_HTML_FILE + "noble_main.htm"; 2647 } else 2648 { 2649 filename = getHtmlPath(npcId, val); 2650 } 2651 break; 2652 case 31690: 2653 case 31769: 2654 case 31770: 2655 case 31771: 2656 case 31772: 2657 if (player.isHero()) 2658 { 2659 filename = Olympiad.OLYMPIAD_HTML_FILE + "hero_main.htm"; 2660 } else 2661 { 2662 filename = getHtmlPath(npcId, val); 2663 } 2664 break; 2665 default: 2666 if (npcId >= 31865 && npcId <= 31918) 2667 { 2668 filename += "rift/GuardianOfBorder.htm"; 2669 break; 2670 } 2671 if (npcId >= 31093 && npcId <= 31094 || npcId >= 31172 && npcId <= 31201 || npcId >= 31239 && npcId <= 31254) 2672 { 2673 return; 2674 } 2675 // Get the text of the selected HTML file in function of the 2676 // npcId and of the page number 2677 filename = getHtmlPath(npcId, val); 2678 break; 2679 } 2680 2681 // Send a Server->Client NpcHtmlMessage containing the text of the 2682 // L2NpcInstance to the L2PcInstance 2683 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 2684 html.setFile(filename); 2685 2686 // String word = "npc-"+npcId+(val>0 ? "-"+val : "" )+"-dialog-append"; 2687 2688 if (this instanceof L2MerchantInstance) 2689 { 2690 if (Config.LIST_PET_RENT_NPC.contains(npcId)) 2691 { 2692 html.replace("_Quest", "_RentPet\">Rent Pet
    Client ActionFailed to the L2PcInstance in order to 2701 // avoid that the client wait another packet 2702 player.sendPacket(new ActionFailed()); 2703 } 2704 2705 /** 2706 * Open a chat window on client with the text specified by the given 2707 * file name and path,
    2708 * relative to the datapack root.
    2709 *
    2710 * Added by Tempy 2711 * 2712 * @param player 2713 * The L2PcInstance that talk with the L2NpcInstance 2714 * @param filename 2715 * The filename that contains the text to send 2716 * 2717 */ 2718 public void showChatWindow(L2PcInstance player, String filename) 2719 { 2720 // Send a Server->Client NpcHtmlMessage containing the text of the 2721 // L2NpcInstance to the L2PcInstance 2722 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); 2723 html.setFile(filename); 2724 html.replace("%objectId%", String.valueOf(getObjectId())); 2725 player.sendPacket(html); 2726 2727 // Send a Server->Client ActionFailed to the L2PcInstance in order to 2728 // avoid that the client wait another packet 2729 player.sendPacket(new ActionFailed()); 2730 } 2731 2732 /** 2733 * Return the Exp Reward of this L2NpcInstance contained in the 2734 * L2NpcTemplate (modified by RATE_XP).
    2735 *
    2736 */ 2737 public int getExpReward() 2738 { 2739 double rateXp = getStat().calcStat(Stats.MAX_HP, 1, this, null); 2740 return (int) (getTemplate().rewardExp * rateXp * Config.RATE_XP); 2741 } 2742 2743 /** 2744 * Return the SP Reward of this L2NpcInstance contained in the 2745 * L2NpcTemplate (modified by RATE_SP).
    2746 *
    2747 */ 2748 public int getSpReward() 2749 { 2750 double rateSp = getStat().calcStat(Stats.MAX_HP, 1, this, null); 2751 return (int) (getTemplate().rewardSp * rateSp * Config.RATE_SP); 2752 } 2753 2754 /** 2755 * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).
    2756 *
    2757 * 2758 * Actions :
    2759 *
    2760 *
  • Create a DecayTask to remove the corpse of the L2NpcInstance 2761 * after 7 seconds
  • 2762 *
  • Set target to null and cancel Attack or Cast
  • 2763 *
  • Stop movement
  • 2764 *
  • Stop HP/MP/CP Regeneration task
  • 2765 *
  • Stop all active skills effects in progress on the L2Character 2766 *
  • 2767 *
  • Send the Server->Client packet StatusUpdate with current HP and 2768 * MP to all other L2PcInstance to inform
  • 2769 *
  • Notify L2Character AI
  • 2770 *
    2771 *
    2772 * 2773 * Overriden in :
    2774 *
    2775 *
  • L2Attackable
  • 2776 *
    2777 *
    2778 * 2779 * @param killer 2780 * The L2Character who killed it 2781 * 2782 */ 2783 @Override 2784 public boolean doDie(L2Character killer) 2785 { 2786 if (!super.doDie(killer)) 2787 { 2788 return false; 2789 } 2790 2791 // normally this wouldn't really be needed, but for those few exceptions, 2792 // we do need to reset the weapons back to the initial templated weapon. 2793 _currentLHandId = getTemplate().lhand; 2794 _currentRHandId = getTemplate().rhand; 2795 2796 2797 DecayTaskManager.getInstance().addDecayTask(this); 2798 return true; 2799 } 2800 2801 /** 2802 * Set the spawn of the L2NpcInstance.
    2803 *
    2804 * 2805 * @param spawn 2806 * The L2Spawn that manage the L2NpcInstance 2807 * 2808 */ 2809 public void setSpawn(L2Spawn spawn) 2810 { 2811 _spawn = spawn; 2812 } 2813 2814 @Override 2815 public void onSpawn() 2816 { 2817 super.onSpawn(); 2818 } 2819 2820 /** 2821 * Remove the L2NpcInstance from the world and update its spawn object 2822 * (for a complete removal use the deleteMe method).
    2823 *
    2824 * 2825 * Actions :
    2826 *
    2827 *
  • Remove the L2NpcInstance from the world when the decay task is 2828 * launched
  • 2829 *
  • Decrease its spawn counter
  • 2830 *
  • Manage Siege task (killFlag, killCT)
  • 2831 *
    2832 *
    2833 * 2834 * Caution : This method DOESN'T REMOVE 2835 * the object from _allObjects of L2World
    2836 * Caution : This method DOESN'T SEND 2837 * Server->Client packets to players
    2838 *
    2839 * 2840 */ 2841 @Override 2842 public void onDecay() 2843 { 2844 if (isDecayed()) 2845 { 2846 return; 2847 } 2848 setDecayed(true); 2849 2850 // Manage Life Control Tower 2851 if (this instanceof L2ControlTowerInstance) 2852 { 2853 ((L2ControlTowerInstance) this).onDeath(); 2854 } 2855 2856 // Remove the L2NpcInstance from the world when the decay task is 2857 // launched 2858 super.onDecay(); 2859 2860 // Decrease its spawn counter 2861 if (_spawn != null) 2862 { 2863 _spawn.decreaseCount(this); 2864 } 2865 } 2866 2867 /** 2868 * Remove PROPERLY the L2NpcInstance from the world.
    2869 *
    2870 * 2871 * Actions :
    2872 *
    2873 *
  • Remove the L2NpcInstance from the world and update its spawn 2874 * object
  • 2875 *
  • Remove all L2Object from _knownObjects and _knownPlayer of the 2876 * L2NpcInstance then cancel Attak or Cast and notify AI
  • 2877 *
  • Remove L2Object object from _allObjects of L2World
  • 2878 *
    2879 *
    2880 * 2881 * Caution : This method DOESN'T SEND 2882 * Server->Client packets to players
    2883 *
    2884 * 2885 */ 2886 public void deleteMe() 2887 { 2888 if (getWorldRegion() != null) 2889 { 2890 getWorldRegion().removeFromZones(this); 2891 // FIXME this is just a temp hack, we should find a better 2892 // solution 2893 } 2894 2895 try 2896 { 2897 decayMe(); 2898 } catch (Throwable t) 2899 { 2900 _log.severe("deletedMe(): " + t); 2901 } 2902 2903 // Remove all L2Object from _knownObjects and _knownPlayer of the 2904 // L2Character then cancel Attak or Cast and notify AI 2905 try 2906 { 2907 getKnownList().removeAllKnownObjects(); 2908 } catch (Throwable t) 2909 { 2910 _log.severe("deletedMe(): " + t); 2911 } 2912 2913 // Remove L2Object object from _allObjects of L2World 2914 L2World.getInstance().removeObject(this); 2915 } 2916 2917 /** 2918 * Return the L2Spawn object that manage this L2NpcInstance.
    2919 *
    2920 */ 2921 public L2Spawn getSpawn() 2922 { 2923 return _spawn; 2924 } 2925 2926 @Override 2927 public String toString() 2928 { 2929 return getTemplate().name; 2930 } 2931 2932 public boolean isDecayed() 2933 { 2934 return _isDecayed; 2935 } 2936 2937 public void setDecayed(boolean decayed) 2938 { 2939 _isDecayed = decayed; 2940 } 2941 2942 public void endDecayTask() 2943 { 2944 if (!isDecayed()) 2945 { 2946 DecayTaskManager.getInstance().cancelDecayTask(this); 2947 onDecay(); 2948 } 2949 } 2950 2951 public boolean isMob() // rather delete this check 2952 { 2953 return false; // This means we use MAX_NPC_ANIMATION instead of 2954 // MAX_MONSTER_ANIMATION 2955 } 2956 2957 // Two functions to change the appearance of the equipped weapons on the NPC 2958 // This is only useful for a few NPCs and is most likely going to be called from AI 2959 public void setLHandId(int newWeaponId) 2960 { 2961 _currentLHandId = newWeaponId; 2962 } 2963 public void setRHandId(int newWeaponId) 2964 { 2965 _currentRHandId = newWeaponId; 2966 } 2967 }