· 8 years ago · Jan 05, 2018, 11:24 PM
1/*
2 * This program is free software: you can redistribute it and/or modify it under
3 * the terms of the GNU General Public License as published by the Free Software
4 * Foundation, either version 3 of the License, or (at your option) any later
5 * version.
6 *
7 * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10 * details.
11 *
12 * You should have received a copy of the GNU General Public License along with
13 * this program. If not, see <http://www.gnu.org/licenses/>.
14 */
15package com.l2jserver.gameserver.model.actor;
16
17import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
18
19import java.text.DateFormat;
20import java.util.Collection;
21import java.util.List;
22import java.util.logging.Level;
23
24import javolution.util.FastList;
25
26import com.l2jserver.Config;
27import com.l2jserver.gameserver.SevenSigns;
28import com.l2jserver.gameserver.SevenSignsFestival;
29import com.l2jserver.gameserver.ThreadPoolManager;
30import com.l2jserver.gameserver.ai.CtrlIntention;
31import com.l2jserver.gameserver.cache.HtmCache;
32import com.l2jserver.gameserver.datatables.ClanTable;
33import com.l2jserver.gameserver.datatables.HelperBuffTable;
34import com.l2jserver.gameserver.datatables.ItemTable;
35import com.l2jserver.gameserver.datatables.NpcTable;
36import com.l2jserver.gameserver.datatables.SkillTable;
37import com.l2jserver.gameserver.datatables.SpawnTable;
38import com.l2jserver.gameserver.idfactory.IdFactory;
39import com.l2jserver.gameserver.instancemanager.CastleManager;
40import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager;
41import com.l2jserver.gameserver.instancemanager.FortManager;
42import com.l2jserver.gameserver.instancemanager.QuestManager;
43import com.l2jserver.gameserver.instancemanager.TownManager;
44import com.l2jserver.gameserver.instancemanager.games.Lottery;
45import com.l2jserver.gameserver.model.L2Clan;
46import com.l2jserver.gameserver.model.L2DropCategory;
47import com.l2jserver.gameserver.model.L2DropData;
48import com.l2jserver.gameserver.model.L2ItemInstance;
49import com.l2jserver.gameserver.model.L2Multisell;
50import com.l2jserver.gameserver.model.L2NpcAIData;
51import com.l2jserver.gameserver.model.L2Object;
52import com.l2jserver.gameserver.model.L2Skill;
53import com.l2jserver.gameserver.model.L2Spawn;
54import com.l2jserver.gameserver.model.L2World;
55import com.l2jserver.gameserver.model.L2WorldRegion;
56import com.l2jserver.gameserver.model.MobGroupTable;
57import com.l2jserver.gameserver.model.actor.instance.L2ClanHallManagerInstance;
58import com.l2jserver.gameserver.model.actor.instance.L2ControllableMobInstance;
59import com.l2jserver.gameserver.model.actor.instance.L2DoormenInstance;
60import com.l2jserver.gameserver.model.actor.instance.L2FestivalGuideInstance;
61import com.l2jserver.gameserver.model.actor.instance.L2FishermanInstance;
62import com.l2jserver.gameserver.model.actor.instance.L2MerchantInstance;
63import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
64import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
65import com.l2jserver.gameserver.model.actor.instance.L2SummonInstance;
66import com.l2jserver.gameserver.model.actor.instance.L2TeleporterInstance;
67import com.l2jserver.gameserver.model.actor.instance.L2TrainerInstance;
68import com.l2jserver.gameserver.model.actor.instance.L2WarehouseInstance;
69import com.l2jserver.gameserver.model.actor.knownlist.NpcKnownList;
70import com.l2jserver.gameserver.model.actor.stat.NpcStat;
71import com.l2jserver.gameserver.model.actor.status.NpcStatus;
72import com.l2jserver.gameserver.model.entity.Castle;
73import com.l2jserver.gameserver.model.entity.Fort;
74import com.l2jserver.gameserver.model.entity.L2Event;
75import com.l2jserver.gameserver.model.olympiad.Olympiad;
76import com.l2jserver.gameserver.model.quest.Quest;
77import com.l2jserver.gameserver.model.quest.QuestState;
78import com.l2jserver.gameserver.model.quest.State;
79import com.l2jserver.gameserver.model.zone.type.L2TownZone;
80import com.l2jserver.gameserver.network.SystemMessageId;
81import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo;
82import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
83import com.l2jserver.gameserver.network.serverpackets.EtcStatusUpdate;
84import com.l2jserver.gameserver.network.serverpackets.ExShowBaseAttributeCancelWindow;
85import com.l2jserver.gameserver.network.serverpackets.ExShowVariationCancelWindow;
86import com.l2jserver.gameserver.network.serverpackets.ExShowVariationMakeWindow;
87import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
88import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
89import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected;
90import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
91import com.l2jserver.gameserver.network.serverpackets.RadarControl;
92import com.l2jserver.gameserver.network.serverpackets.ServerObjectInfo;
93import com.l2jserver.gameserver.network.serverpackets.SocialAction;
94import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
95import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
96import com.l2jserver.gameserver.network.serverpackets.ValidateLocation;
97import com.l2jserver.gameserver.skills.Stats;
98import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
99import com.l2jserver.gameserver.templates.L2HelperBuff;
100import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
101import com.l2jserver.gameserver.templates.chars.L2NpcTemplate.AIType;
102import com.l2jserver.gameserver.templates.item.L2Item;
103import com.l2jserver.gameserver.templates.item.L2Weapon;
104import com.l2jserver.gameserver.templates.skills.L2SkillType;
105import com.l2jserver.gameserver.util.Broadcast;
106import com.l2jserver.gameserver.util.StringUtil;
107import com.l2jserver.util.Rnd;
108
109/**
110 * This class represents a Non-Player-Character in the world. It can be a monster or a friendly character.
111 * It also uses a template to fetch some static values. The templates are hardcoded in the client, so we can rely on them.<BR><BR>
112 *
113 * L2Character :<BR><BR>
114 * <li>L2Attackable</li>
115 * <li>L2BoxInstance</li>
116 * <li>L2FolkInstance</li>
117 *
118 * @version $Revision: 1.32.2.7.2.24 $ $Date: 2005/04/11 10:06:09 $
119 */
120public class L2Npc extends L2Character
121{
122 //private static Logger _log = Logger.getLogger(L2NpcInstance.class.getName());
123
124 /** The interaction distance of the L2NpcInstance(is used as offset in MovetoLocation method) */
125 public static final int INTERACTION_DISTANCE = 150;
126
127 /** The L2Spawn object that manage this L2NpcInstance */
128 private L2Spawn _spawn;
129
130 /** The flag to specify if this L2NpcInstance is busy */
131 private boolean _isBusy = false;
132
133 /** The busy message for this L2NpcInstance */
134 private String _busyMessage = "";
135
136 /** True if endDecayTask has already been called */
137 volatile boolean _isDecayed = false;
138
139 /** True if a Dwarf has used Spoil on this L2NpcInstance */
140 private boolean _isSpoil = false;
141
142 /** The castle index in the array of L2Castle this L2NpcInstance belongs to */
143 private int _castleIndex = -2;
144
145 /** The fortress index in the array of L2Fort this L2NpcInstance belongs to */
146 private int _fortIndex = -2;
147
148 public boolean isEventMob = false;
149 private boolean _isInTown = false;
150
151 private int _isSpoiledBy = 0;
152
153 /** Time of last social packet broadcast*/
154 private long _lastSocialBroadcast = 0;
155
156 /** Minimum interval between social packets*/
157 private int _minimalSocialInterval = 6000;
158
159 protected RandomAnimationTask _rAniTask = null;
160 private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions
161 private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions
162 private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions
163 private int _currentCollisionHeight; // used for npc grow effect skills
164 private int _currentCollisionRadius; // used for npc grow effect skills
165
166 public boolean _soulshotcharged = false;
167 public boolean _spiritshotcharged = false;
168 private int _soulshotamount = 0;
169 private int _spiritshotamount = 0;
170 public boolean _ssrecharged = true;
171 public boolean _spsrecharged = true;
172
173 //AI Recall
174 public int getSoulShot()
175 {
176 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
177 L2NpcAIData AI = npcData.getAIDataStatic();
178
179 if (AI == null)
180 return 0;
181 else
182 return AI.getSoulShot();
183
184 }
185 public int getSpiritShot()
186 {
187 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
188 L2NpcAIData AI = npcData.getAIDataStatic();
189
190 if (AI == null)
191 return 0;
192 else
193 return AI.getSpiritShot();
194
195 }
196 public int getSoulShotChance()
197 {
198 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
199 L2NpcAIData AI = npcData.getAIDataStatic();
200
201 if (AI == null)
202 return 0;
203 else
204 return AI.getSoulShotChance();
205
206 }
207
208 public int getSpiritShotChance()
209 {
210 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
211 L2NpcAIData AI = npcData.getAIDataStatic();
212
213 if (AI == null)
214 return 0;
215 else
216 return AI.getSpiritShotChance();
217
218 }
219
220 public boolean useSoulShot()
221 {
222 if(_soulshotcharged)
223 return true;
224 if(_ssrecharged)
225 {
226 _soulshotamount = getSoulShot();
227 _ssrecharged = false;
228 }
229 else if (_soulshotamount>0)
230 {
231 if (Rnd.get(100) <= getSoulShotChance())
232 {
233 _soulshotamount = _soulshotamount - 1;
234 Broadcast.toSelfAndKnownPlayersInRadius(this, new MagicSkillUse(this, this, 2154, 1, 0, 0), 360000);
235 _soulshotcharged = true;
236 }
237 }
238 else return false;
239
240 return _soulshotcharged;
241 }
242 public boolean useSpiritShot()
243 {
244
245 if(_spiritshotcharged)
246 return true;
247 else
248 {
249 //_spiritshotcharged = false;
250 if(_spsrecharged)
251 {
252 _spiritshotamount = getSpiritShot();
253 _spsrecharged = false;
254 }
255 else if (_spiritshotamount>0)
256 {
257 if (Rnd.get(100) <= getSpiritShotChance())
258 {
259 _spiritshotamount = _spiritshotamount - 1;
260 Broadcast.toSelfAndKnownPlayersInRadius(this, new MagicSkillUse(this, this, 2061, 1, 0, 0), 360000);
261 _spiritshotcharged = true;
262 }
263 }
264 else return false;
265 }
266
267 return _spiritshotcharged;
268 }
269 public int getEnemyRange()
270 {
271 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
272 L2NpcAIData AI = npcData.getAIDataStatic();
273
274 if (AI == null)
275 return 0;
276 else
277 return AI.getEnemyRange();
278
279 }
280
281
282
283 public String getEnemyClan()
284 {
285 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
286 L2NpcAIData AI = npcData.getAIDataStatic();
287
288 if (AI == null || AI.getEnemyClan() == null || "".equals(AI.getEnemyClan()))
289 return "none";
290 else
291 return AI.getEnemyClan();
292
293 }
294
295 public String getClan()
296 {
297 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
298 L2NpcAIData AI = npcData.getAIDataStatic();
299
300 if (AI == null || AI.getClan() == null || "".equals(AI.getClan()))
301 return "none";
302 else
303 return AI.getClan();
304
305 }
306
307 // GET THE PRIMARY ATTACK
308 public int getPrimaryAttack()
309 {
310 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
311 L2NpcAIData AI = npcData.getAIDataStatic();
312
313 if (AI == null)
314 return 0;
315 else
316 return AI.getPrimaryAttack();
317
318 }
319
320 public int getSkillChance()
321 {
322 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
323 L2NpcAIData AI = npcData.getAIDataStatic();
324
325 if (AI == null)
326 return 20;
327 else
328 return AI.getSkillChance();
329
330 }
331
332 public int getCanMove()
333 {
334 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
335 L2NpcAIData AI = npcData.getAIDataStatic();
336
337 if (AI == null)
338 return 0;
339 else
340 return AI.getCanMove();
341
342 }
343 public int getIsChaos()
344 {
345
346 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
347 L2NpcAIData AI = npcData.getAIDataStatic();
348
349 if (AI == null)
350 return 0;
351 else
352 return AI.getIsChaos();
353
354 }
355
356 public int getCanDodge()
357 {
358
359 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
360 L2NpcAIData AI = npcData.getAIDataStatic();
361
362 if (AI == null)
363 return 0;
364 else
365 return AI.getDodge();
366
367 }
368 public int getSSkillChance()
369 {
370
371 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
372 L2NpcAIData AI = npcData.getAIDataStatic();
373
374 if (AI == null)
375 return 0;
376 else
377 return AI.getShortRangeChance();
378
379 }
380 public int getLSkillChance()
381 {
382
383 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
384 L2NpcAIData AI = npcData.getAIDataStatic();
385
386 if (AI == null)
387 return 0;
388 else
389 return AI.getLongRangeChance();
390
391 }
392 public int getSwitchRangeChance()
393 {
394
395 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
396 L2NpcAIData AI = npcData.getAIDataStatic();
397
398 if (AI == null)
399 return 0;
400 else
401 return AI.getSwitchRangeChance();
402
403 }
404
405 public boolean hasLSkill()
406 {
407
408 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
409 L2NpcAIData AI = npcData.getAIDataStatic();
410
411 if (AI == null || AI.getLongRangeSkill() == 0)
412 return false;
413 else
414 return true;
415
416 }
417
418 public boolean hasSSkill()
419 {
420
421 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(this.getTemplate().npcId);
422 L2NpcAIData AI = npcData.getAIDataStatic();
423
424 if (AI == null || AI.getShortRangeSkill() == 0)
425 return false;
426 else
427 return true;
428
429 }
430
431 public FastList<L2Skill> getLrangeSkill()
432 {
433 FastList<L2Skill> skilldata = new FastList <L2Skill>();
434 boolean hasLrange = false;
435 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(getTemplate().npcId);
436 L2NpcAIData AI = npcData.getAIDataStatic();
437
438 if(AI == null || AI.getLongRangeSkill() == 0)
439 return null;
440
441 switch(AI.getLongRangeSkill())
442 {
443 case -1:
444 {
445 L2Skill[] skills = null;
446 skills = getAllSkills();
447 if(skills != null)
448 for (L2Skill sk: skills)
449 {
450 if(sk == null || sk.getSkillType() == L2SkillType.PASSIVE
451 || sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF)
452 {
453 continue;
454 }
455 if(sk.getCastRange()>=200)
456 {
457 skilldata.add(sk);
458 hasLrange = true;
459 }
460 }
461 break;
462 }
463 case 1:
464 {
465 if(npcData._universalskills != null)
466 for (L2Skill sk: npcData._universalskills)
467 {
468 if(sk.getCastRange()>=200)
469 {
470 skilldata.add(sk);
471 hasLrange = true;
472 }
473 }
474 break;
475 }
476 default:
477 {
478 for (L2Skill sk: this.getAllSkills())
479 {
480 if(sk.getId() == AI.getLongRangeSkill())
481 {
482 skilldata.add(sk);
483 hasLrange = true;
484 }
485 }
486 }
487 }
488
489 if(hasLrange)
490 return skilldata;
491 else return null;
492 }
493
494 public FastList<L2Skill> getSrangeSkill()
495 {
496 FastList<L2Skill> skilldata = new FastList <L2Skill>();
497 boolean hasSrange = false;
498 L2NpcTemplate npcData = NpcTable.getInstance().getTemplate(getTemplate().npcId);
499 L2NpcAIData AI = npcData.getAIDataStatic();
500
501 if(AI == null || AI.getShortRangeSkill() == 0)
502 return null;
503
504 switch(AI.getShortRangeSkill())
505 {
506 case -1:
507 {
508 L2Skill[] skills = null;
509 skills = getAllSkills();
510 if(skills != null)
511 for (L2Skill sk: skills)
512 {
513 if(sk == null || sk.getSkillType() == L2SkillType.PASSIVE
514 || sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF)
515 {
516 continue;
517 }
518 if(sk.getCastRange()<=200)
519 {
520 skilldata.add(sk);
521 hasSrange = true;
522 }
523 }
524 break;
525 }
526 case 1:
527 {
528 if(npcData._universalskills != null)
529 for (L2Skill sk: npcData._universalskills)
530 {
531 if(sk.getCastRange()<=200)
532 {
533 skilldata.add(sk);
534 hasSrange = true;
535 }
536 }
537 break;
538 }
539 default:
540 {
541 for (L2Skill sk: this.getAllSkills())
542 {
543 if(sk.getId() == AI.getShortRangeSkill())
544 {
545 skilldata.add(sk);
546 hasSrange = true;
547 }
548 }
549 }
550 }
551
552
553 if(hasSrange)
554 return skilldata;
555 else return null;
556 }
557
558 /** Task launching the function onRandomAnimation() */
559 protected class RandomAnimationTask implements Runnable
560 {
561 public void run()
562 {
563 try
564 {
565 if (this != _rAniTask)
566 return; // Shouldn't happen, but who knows... just to make sure every active npc has only one timer.
567 if (isMob())
568 {
569 // Cancel further animation timers until intention is changed to ACTIVE again.
570 if (getAI().getIntention() != AI_INTENTION_ACTIVE)
571 return;
572 }
573 else
574 {
575 if (!isInActiveRegion()) // NPCs in inactive region don't run this task
576 return;
577 }
578
579 if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
580 onRandomAnimation();
581
582 startRandomAnimationTimer();
583 }
584 catch (Exception e)
585 {
586 _log.log(Level.SEVERE, "", e);
587 }
588 }
589 }
590
591 /**
592 * Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance and create a new RandomAnimation Task.<BR><BR>
593 */
594 public void onRandomAnimation()
595 {
596 // Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
597 long now = System.currentTimeMillis();
598 if (now - _lastSocialBroadcast > _minimalSocialInterval)
599 {
600 _lastSocialBroadcast = now;
601 broadcastPacket(new SocialAction(getObjectId(), Rnd.get(2, 3)));
602 }
603 }
604
605 /**
606 * Create a RandomAnimation Task that will be launched after the calculated delay.<BR><BR>
607 */
608 public void startRandomAnimationTimer()
609 {
610 if (!hasRandomAnimation())
611 return;
612
613 int minWait = isMob() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION;
614 int maxWait = isMob() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION;
615
616 // Calculate the delay before the next animation
617 int interval = Rnd.get(minWait, maxWait) * 1000;
618
619 // Create a RandomAnimation Task that will be launched after the calculated delay
620 _rAniTask = new RandomAnimationTask();
621 ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
622 }
623
624 /**
625 * Check if the server allows Random Animation.<BR><BR>
626 */
627 public boolean hasRandomAnimation()
628 {
629 return (Config.MAX_NPC_ANIMATION > 0 && !getTemplate().AI.equals(AIType.CORPSE));
630 }
631
632 /**
633 * Constructor of L2NpcInstance (use L2Character constructor).<BR><BR>
634 *
635 * <B><U> Actions</U> :</B><BR><BR>
636 * <li>Call the L2Character constructor to set the _template of the L2Character (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR) </li>
637 * <li>Set the name of the L2Character</li>
638 * <li>Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it </li><BR><BR>
639 *
640 * @param objectId Identifier of the object to initialized
641 * @param template The L2NpcTemplate to apply to the NPC
642 *
643 */
644 public L2Npc(int objectId, L2NpcTemplate template)
645 {
646 // Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object
647 // and link _calculators to NPC_STD_CALCULATOR
648 super(objectId, template);
649 initCharStatusUpdateValues();
650
651 // initialize the "current" equipment
652 _currentLHandId = getTemplate().lhand;
653 _currentRHandId = getTemplate().rhand;
654 _currentEnchant = Config.ENABLE_RANDOM_ENCHANT_EFFECT ? Rnd.get(4,21) : getTemplate().enchantEffect;
655 // initialize the "current" collisions
656 _currentCollisionHeight = getTemplate().collisionHeight;
657 _currentCollisionRadius = getTemplate().collisionRadius;
658
659 if (template == null)
660 {
661 _log.severe("No template for Npc. Please check your datapack is setup correctly.");
662 return;
663 }
664
665 // Set the name of the L2Character
666 setName(template.name);
667 }
668
669 @Override
670 public NpcKnownList getKnownList()
671 {
672 return (NpcKnownList) super.getKnownList();
673 }
674
675 @Override
676 public void initKnownList()
677 {
678 setKnownList(new NpcKnownList(this));
679 }
680
681 @Override
682 public NpcStat getStat()
683 {
684 return (NpcStat) super.getStat();
685 }
686
687 @Override
688 public void initCharStat()
689 {
690 setStat(new NpcStat(this));
691 }
692
693 @Override
694 public NpcStatus getStatus()
695 {
696 return (NpcStatus) super.getStatus();
697 }
698
699 @Override
700 public void initCharStatus()
701 {
702 setStatus(new NpcStatus(this));
703 }
704
705 /** Return the L2NpcTemplate of the L2NpcInstance. */
706 @Override
707 public final L2NpcTemplate getTemplate()
708 {
709 return (L2NpcTemplate) super.getTemplate();
710 }
711
712 /**
713 * Return the generic Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
714 */
715 public int getNpcId()
716 {
717 return getTemplate().npcId;
718 }
719
720 @Override
721 public boolean isAttackable()
722 {
723 return Config.ALT_ATTACKABLE_NPCS;
724 }
725
726 /**
727 * Return the faction Identifier of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
728 *
729 * <B><U> Concept</U> :</B><BR><BR>
730 * If a NPC belows to a Faction, other NPC of the faction inside the Faction range will help it if it's attacked<BR><BR>
731 *
732 */
733 public final String getFactionId()
734 {
735 return getTemplate().factionId;
736 }
737
738 /**
739 * Return the Level of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
740 */
741 @Override
742 public final int getLevel()
743 {
744 return getTemplate().level;
745 }
746
747 /**
748 * Return True if the L2NpcInstance is agressive (ex : L2MonsterInstance in function of aggroRange).<BR><BR>
749 */
750 public boolean isAggressive()
751 {
752 return false;
753 }
754
755 /**
756 * Return the Aggro Range of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
757 */
758 public int getAggroRange()
759 {
760 return getTemplate().aggroRange;
761 }
762
763 /**
764 * Return the Faction Range of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
765 */
766 public int getFactionRange()
767 {
768 return getTemplate().factionRange;
769 }
770
771 /**
772 * Return True if this L2NpcInstance is undead in function of the L2NpcTemplate.<BR><BR>
773 */
774 @Override
775 public boolean isUndead()
776 {
777 return getTemplate().isUndead;
778 }
779
780 /**
781 * Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance.<BR><BR>
782 */
783 @Override
784 public void updateAbnormalEffect()
785 {
786 // Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
787 Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values();
788 //synchronized (getKnownList().getKnownPlayers())
789 {
790 for (L2PcInstance player : plrs)
791 {
792 if (getRunSpeed() == 0)
793 player.sendPacket(new ServerObjectInfo(this, player));
794 else
795 player.sendPacket(new AbstractNpcInfo.NpcInfo(this, player));
796 }
797 }
798 }
799
800 /**
801 * Return the distance under which the object must be add to _knownObject in
802 * function of the object type.<BR>
803 * <BR>
804 *
805 * <B><U> Values </U> :</B><BR>
806 * <BR>
807 * <li> object is a L2FolkInstance : 0 (don't remember it) </li>
808 * <li> object is a L2Character : 0 (don't remember it) </li>
809 * <li> object is a L2PlayableInstance : 1500 </li>
810 * <li> others : 500 </li>
811 * <BR>
812 * <BR>
813 *
814 * <B><U> Override in </U> :</B><BR>
815 * <BR>
816 * <li> L2Attackable</li>
817 * <BR>
818 * <BR>
819 *
820 * @param object
821 * The Object to add to _knownObject
822 *
823 */
824 public int getDistanceToWatchObject(L2Object object)
825 {
826 if (object instanceof L2FestivalGuideInstance)
827 return 10000;
828
829 if (object instanceof L2NpcInstance || !(object instanceof L2Character))
830 return 0;
831
832 if (object instanceof L2Playable)
833 return 1500;
834
835 return 500;
836 }
837
838 /**
839 * Return the distance after which the object must be remove from _knownObject in function of the object type.<BR><BR>
840 *
841 * <B><U> Values </U> :</B><BR><BR>
842 * <li> object is not a L2Character : 0 (don't remember it) </li>
843 * <li> object is a L2FolkInstance : 0 (don't remember it)</li>
844 * <li> object is a L2PlayableInstance : 3000 </li>
845 * <li> others : 1000 </li><BR><BR>
846 *
847 * <B><U> Overridden in </U> :</B><BR><BR>
848 * <li> L2Attackable</li><BR><BR>
849 *
850 * @param object The Object to remove from _knownObject
851 *
852 */
853 public int getDistanceToForgetObject(L2Object object)
854 {
855 return 2 * getDistanceToWatchObject(object);
856 }
857
858 /**
859 * Return False.<BR><BR>
860 *
861 * <B><U> Overridden in </U> :</B><BR><BR>
862 * <li> L2MonsterInstance : Check if the attacker is not another L2MonsterInstance</li>
863 * <li> L2PcInstance</li><BR><BR>
864 */
865 @Override
866 public boolean isAutoAttackable(L2Character attacker)
867 {
868 return false;
869 }
870
871 /**
872 * Return the Identifier of the item in the left hand of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
873 */
874 public int getLeftHandItem()
875 {
876 return _currentLHandId;
877 }
878
879 /**
880 * Return the Identifier of the item in the right hand of this L2NpcInstance contained in the L2NpcTemplate.<BR><BR>
881 */
882 public int getRightHandItem()
883 {
884 return _currentRHandId;
885 }
886
887 public int getEnchantEffect()
888 {
889 return _currentEnchant;
890 }
891
892 /**
893 * Return True if this L2NpcInstance has drops that can be sweeped.<BR><BR>
894 */
895 public boolean isSpoil()
896 {
897 return _isSpoil;
898 }
899
900 /**
901 * Set the spoil state of this L2NpcInstance.<BR><BR>
902 */
903 public void setSpoil(boolean isSpoil)
904 {
905 _isSpoil = isSpoil;
906 }
907
908 public final int getIsSpoiledBy()
909 {
910 return _isSpoiledBy;
911 }
912
913 public final void setIsSpoiledBy(int value)
914 {
915 _isSpoiledBy = value;
916 }
917
918 /**
919 * Return the busy status of this L2NpcInstance.<BR><BR>
920 */
921 public final boolean isBusy()
922 {
923 return _isBusy;
924 }
925
926 /**
927 * Set the busy status of this L2NpcInstance.<BR><BR>
928 */
929 public void setBusy(boolean isBusy)
930 {
931 _isBusy = isBusy;
932 }
933
934 /**
935 * Return the busy message of this L2NpcInstance.<BR><BR>
936 */
937 public final String getBusyMessage()
938 {
939 return _busyMessage;
940 }
941
942 /**
943 * Set the busy message of this L2NpcInstance.<BR><BR>
944 */
945 public void setBusyMessage(String message)
946 {
947 _busyMessage = message;
948 }
949
950 /**
951 * Return true if this L2Npc instance can be warehouse manager.<BR><BR>
952 */
953 public boolean isWarehouse()
954 {
955 return false;
956 }
957
958 protected boolean canTarget(L2PcInstance player)
959 {
960 if (player.isOutOfControl())
961 {
962 player.sendPacket(ActionFailed.STATIC_PACKET);
963 return false;
964 }
965 if (player.isLockedTarget() && player.getLockedTarget() != this)
966 {
967 player.sendPacket(new SystemMessage(SystemMessageId.FAILED_CHANGE_TARGET));
968 player.sendPacket(ActionFailed.STATIC_PACKET);
969 return false;
970 }
971 // TODO: More checks...
972
973 return true;
974 }
975
976 public boolean canInteract(L2PcInstance player)
977 {
978 // TODO: NPC busy check etc...
979
980 if (player.isCastingNow() || player.isCastingSimultaneouslyNow())
981 return false;
982 if (player.isDead() || player.isFakeDeath())
983 return false;
984 if (player.isSitting())
985 return false;
986 if (player.getPrivateStoreType() != 0)
987 return false;
988 if (!isInsideRadius(player, INTERACTION_DISTANCE, true, false))
989 return false;
990 if (player.getInstanceId() != getInstanceId()
991 && player.getInstanceId() != -1)
992 return false;
993
994 return true;
995 }
996
997 /**
998 * Manage actions when a player click on the L2NpcInstance.<BR><BR>
999 *
1000 * <B><U> Actions on first click on the L2NpcInstance (Select it)</U> :</B><BR><BR>
1001 * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li>
1002 * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
1003 * <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar </li>
1004 * <li>Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client </li><BR><BR>
1005 *
1006 * <B><U> Actions on second click on the L2NpcInstance (Attack it/Intercat with it)</U> :</B><BR><BR>
1007 * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
1008 * <li>If L2NpcInstance is autoAttackable, notify the L2PcInstance AI with AI_INTENTION_ATTACK (after a height verification)</li>
1009 * <li>If L2NpcInstance is NOT autoAttackable, notify the L2PcInstance AI with AI_INTENTION_INTERACT (after a distance verification) and show message</li><BR><BR>
1010 *
1011 * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
1012 * that client wait an other packet</B></FONT><BR><BR>
1013 *
1014 * <B><U> Example of use </U> :</B><BR><BR>
1015 * <li> Client packet : Action, AttackRequest</li><BR><BR>
1016 *
1017 * <B><U> Overridden in </U> :</B><BR><BR>
1018 * <li> L2ArtefactInstance : Manage only fisrt click to select Artefact</li><BR><BR>
1019 * <li> L2GuardInstance : </li><BR><BR>
1020 *
1021 * @param player The L2PcInstance that start an action on the L2NpcInstance
1022 *
1023 */
1024 @Override
1025 public void onAction(L2PcInstance player, boolean interact)
1026 {
1027 if (!canTarget(player))
1028 return;
1029
1030 player.setLastFolkNPC(this);
1031
1032 // Check if the L2PcInstance already target the L2NpcInstance
1033 if (this != player.getTarget())
1034 {
1035 if (Config.DEBUG)
1036 _log.fine("new target selected:" + getObjectId());
1037
1038 // Set the target of the L2PcInstance player
1039 player.setTarget(this);
1040
1041 // Check if the player is attackable (without a forced attack)
1042 if (isAutoAttackable(player))
1043 {
1044 // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
1045 // The player.getLevel() - getLevel() permit to display the correct color in the select window
1046 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
1047 player.sendPacket(my);
1048
1049 // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
1050 StatusUpdate su = new StatusUpdate(getObjectId());
1051 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
1052 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
1053 player.sendPacket(su);
1054 }
1055 else
1056 {
1057 // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
1058 MyTargetSelected my = new MyTargetSelected(getObjectId(), 0);
1059 player.sendPacket(my);
1060 }
1061
1062 // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client
1063 player.sendPacket(new ValidateLocation(this));
1064 }
1065 else if (interact)
1066 {
1067 player.sendPacket(new ValidateLocation(this));
1068 // Check if the player is attackable (without a forced attack) and isn't dead
1069 if (isAutoAttackable(player) && !isAlikeDead())
1070 {
1071 // Check the height difference
1072 if (Math.abs(player.getZ() - getZ()) < 400) // this max heigth difference might need some tweaking
1073 {
1074 // Set the L2PcInstance Intention to AI_INTENTION_ATTACK
1075 player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
1076 // player.startAttack(this);
1077 }
1078 else
1079 {
1080 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
1081 player.sendPacket(ActionFailed.STATIC_PACKET);
1082 }
1083 }
1084 else if (!isAutoAttackable(player))
1085 {
1086 // Calculate the distance between the L2PcInstance and the L2NpcInstance
1087 if (!canInteract(player))
1088 {
1089 // Notify the L2PcInstance AI with AI_INTENTION_INTERACT
1090 player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
1091 }
1092 else
1093 {
1094 // Send a Server->Client packet SocialAction to the all L2PcInstance on the _knownPlayer of the L2NpcInstance
1095 // to display a social action of the L2NpcInstance on their client
1096 long now = System.currentTimeMillis();
1097 if (now - _lastSocialBroadcast > _minimalSocialInterval && !getTemplate().AI.equals(AIType.CORPSE))
1098 {
1099 _lastSocialBroadcast = now;
1100 broadcastPacket(new SocialAction(getObjectId(), Rnd.get(8)));
1101 }
1102
1103 // Open a chat window on client with the text of the L2NpcInstance
1104 if (isEventMob)
1105 {
1106 L2Event.showEventHtml(player, String.valueOf(getObjectId()));
1107 }
1108 else
1109 {
1110 Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
1111 if ((qlsa != null) && qlsa.length > 0)
1112 player.setLastQuestNpcObject(getObjectId());
1113 Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.ON_FIRST_TALK);
1114 if ((qlst != null) && qlst.length == 1)
1115 qlst[0].notifyFirstTalk(this, player);
1116 else
1117 showChatWindow(player);
1118 }
1119 }
1120 }
1121 }
1122 player.sendPacket(ActionFailed.STATIC_PACKET);
1123 }
1124
1125 /**
1126 * Manage and Display the GM console to modify the L2NpcInstance (GM only).<BR><BR>
1127 *
1128 * <B><U> Actions (If the L2PcInstance is a GM only)</U> :</B><BR><BR>
1129 * <li>Set the L2NpcInstance as target of the L2PcInstance player (if necessary)</li>
1130 * <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li>
1131 * <li>If L2NpcInstance is autoAttackable, send a Server->Client packet StatusUpdate to the L2PcInstance in order to update L2NpcInstance HP bar </li>
1132 * <li>Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance </li><BR><BR>
1133 *
1134 * <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
1135 * that client wait an other packet</B></FONT><BR><BR>
1136 *
1137 * <B><U> Example of use </U> :</B><BR><BR>
1138 * <li> Client packet : Action</li><BR><BR>
1139 *
1140 * @param client The thread that manage the player that pessed Shift and click on the L2NpcInstance
1141 *
1142 */
1143 @Override
1144 public void onActionShift(L2PcInstance player)
1145 {
1146 if (player == null)
1147 return;
1148
1149 // Check if the L2PcInstance is a GM
1150 if (player.getAccessLevel().isGm())
1151 {
1152 // Set the target of the L2PcInstance player
1153 player.setTarget(this);
1154
1155 // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
1156 // The player.getLevel() - getLevel() permit to display the correct color in the select window
1157 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
1158 player.sendPacket(my);
1159
1160 // Check if the player is attackable (without a forced attack)
1161 if (isAutoAttackable(player))
1162 {
1163 // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
1164 StatusUpdate su = new StatusUpdate(getObjectId());
1165 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
1166 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
1167 player.sendPacket(su);
1168 }
1169
1170 // Send a Server->Client NpcHtmlMessage() containing the GM console about this L2NpcInstance
1171 NpcHtmlMessage html = new NpcHtmlMessage(0);
1172 final StringBuilder html1 = StringUtil.startAppend(500,
1173 "<html><body><center><font color=\"LEVEL\">NPC Info</font></center><br>" +
1174 "Instance Type: ",
1175 getClass().getSimpleName(),
1176 "<br1>Faction: ",
1177 getFactionId() != null ? getFactionId() : "null"
1178 );
1179 StringUtil.append(html1,
1180 "<br1>Coords: ",
1181 String.valueOf(getX()),
1182 ", ",
1183 String.valueOf(getY()),
1184 ", ",
1185 String.valueOf(getZ())
1186 );
1187 if (getSpawn() != null)
1188 StringUtil.append(html1,
1189 "<br1>Spawn: ",
1190 String.valueOf(getSpawn().getLocx()),
1191 ", ",
1192 String.valueOf(getSpawn().getLocy()),
1193 ", ",
1194 String.valueOf(getSpawn().getLocz()),
1195 " ; Loc ID: ",
1196 String.valueOf(getSpawn().getLocation()),
1197 "<br1>Distance from spawn 2D: ",
1198 String.valueOf((int)Math.sqrt(getPlanDistanceSq(getSpawn().getLocx(), getSpawn().getLocy()))),
1199 " ; 3D: ",
1200 String.valueOf((int)Math.sqrt(getDistanceSq(getSpawn().getLocx(), getSpawn().getLocy(), getSpawn().getLocz())))
1201 );
1202
1203 if (this instanceof L2ControllableMobInstance)
1204 {
1205 StringUtil.append(html1,
1206 "<br1>Mob Group: ",
1207 String.valueOf(MobGroupTable.getInstance().getGroupForMob((L2ControllableMobInstance) this).getGroupId()),
1208 "<br>"
1209 );
1210 }
1211 else
1212 {
1213 StringUtil.append(html1,
1214 "<br1>Respawn Time: ",
1215 (getSpawn() != null ? String.valueOf(getSpawn().getRespawnDelay() / 1000) : "?"),
1216 " Seconds<br>"
1217 );
1218 }
1219
1220 StringUtil.append(html1,
1221 "<table border=\"0\" width=\"100%\">" +
1222 "<tr><td>Level</td><td>",
1223 String.valueOf(getLevel()),
1224 "</td><td> </td><td>NPC ID</td><td>",
1225 String.valueOf(getTemplate().npcId),
1226 "</td></tr>" +
1227 "<tr><td>Aggro</td><td>" +
1228 String.valueOf((this instanceof L2Attackable) ? ((L2Attackable) this).getAggroRange() : 0),
1229 "</td><td> </td><td>Object ID</td><td>",
1230 String.valueOf(getObjectId()),
1231 "</td></tr>" +
1232 "<tr><td>Castle</td><td>",
1233 String.valueOf(getCastle().getCastleId()),
1234 "</td><td> </td><td>AI </td><td>",
1235 (hasAI() ? String.valueOf(getAI().getIntention().name()) : "NULL"),
1236 "</td></tr>" +
1237 "</table><br>" +
1238 "<font color=\"LEVEL\">Combat</font>" +
1239 "<table border=\"0\" width=\"100%\">" +
1240 "<tr><td>Current HP</td><td>",
1241 String.valueOf(getCurrentHp()),
1242 "</td><td>Current MP</td><td>",
1243 String.valueOf(getCurrentMp()),
1244 "</td></tr>" +
1245 "<tr><td>Max.HP</td><td>",
1246 String.valueOf((int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null))),
1247 "*",
1248 String.valueOf((int) (getStat().calcStat(Stats.MAX_HP, 1, this, null))),
1249 "</td><td>Max.MP</td><td>",
1250 String.valueOf(getMaxMp()),
1251 "</td></tr>" +
1252 "<tr><td>P.Atk.</td><td>",
1253 String.valueOf(getPAtk(null)),
1254 "</td><td>M.Atk.</td><td>",
1255 String.valueOf(getMAtk(null, null)),
1256 "</td></tr>" +
1257 "<tr><td>P.Def.</td><td>",
1258 String.valueOf(getPDef(null)),
1259 "</td><td>M.Def.</td><td>",
1260 String.valueOf(getMDef(null, null)),
1261 "</td></tr>" +
1262 "<tr><td>Accuracy</td><td>" +
1263 String.valueOf(getAccuracy()),
1264 "</td><td>Evasion</td><td>",
1265 String.valueOf(getEvasionRate(null)),
1266 "</td></tr>" +
1267 "<tr><td>Critical</td><td>",
1268 String.valueOf(getCriticalHit(null, null)),
1269 "</td><td>Speed</td><td>",
1270 String.valueOf(getRunSpeed()),
1271 "</td></tr>" +
1272 "<tr><td>Atk.Speed</td><td>",
1273 String.valueOf(getPAtkSpd()),
1274 "</td><td>Cast.Speed</td><td>",
1275 String.valueOf(getMAtkSpd()),
1276 "</td></tr>" +
1277 "</table><br>" +
1278 "<font color=\"LEVEL\">Basic Stats</font>" +
1279 "<table border=\"0\" width=\"100%\">" +
1280 "<tr><td>STR</td><td>",
1281 String.valueOf(getSTR()),
1282 "</td><td>DEX</td><td>",
1283 String.valueOf(getDEX()),
1284 "</td><td>CON</td><td>",
1285 String.valueOf(getCON()),
1286 "</td></tr>" +
1287 "<tr><td>INT</td><td>",
1288 String.valueOf(getINT()),
1289 "</td><td>WIT</td><td>",
1290 String.valueOf(getWIT()),
1291 "</td><td>MEN</td><td>",
1292 String.valueOf(getMEN()),
1293 "</td></tr>" +
1294 "</table>" +
1295 "<br><center><table><tr><td><button value=\"Edit NPC\" action=\"bypass -h admin_edit_npc ",
1296 String.valueOf(getTemplate().npcId),
1297 "\" width=100 height=20 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"><br1></td>" +
1298 "<td><button value=\"Kill\" action=\"bypass -h admin_kill\" width=40 height=20 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td><br1></tr>" +
1299 "<tr><td><button value=\"Show DropList\" action=\"bypass -h admin_show_droplist ",
1300 String.valueOf(getTemplate().npcId),
1301 "\" width=100 height=20 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td></tr>" +
1302 "<td><button value=\"Delete\" action=\"bypass -h admin_delete\" width=40 height=20 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td></tr>" +
1303 "<tr><td><button value=\"Show SkillList\" action=\"bypass -h admin_show_skilllist_npc ",
1304 String.valueOf(getTemplate().npcId),
1305 "\" width=100 height=20 back=\"L2UI_ct1.button_df\" fore=\"L2UI_ct1.button_df\"></td><td></td></tr></table></center><br></body></html>"
1306 );
1307
1308 html.setHtml(html1.toString());
1309 player.sendPacket(html);
1310 }
1311 else if (Config.ALT_GAME_VIEWNPC)
1312 {
1313 // Set the target of the L2PcInstance player
1314 player.setTarget(this);
1315
1316 // Send a Server->Client packet MyTargetSelected to the L2PcInstance player
1317 // The player.getLevel() - getLevel() permit to display the correct color in the select window
1318 MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel());
1319 player.sendPacket(my);
1320
1321 // Check if the player is attackable (without a forced attack)
1322 if (isAutoAttackable(player))
1323 {
1324 // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar
1325 StatusUpdate su = new StatusUpdate(getObjectId());
1326 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
1327 su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
1328 player.sendPacket(su);
1329 }
1330
1331 NpcHtmlMessage html = new NpcHtmlMessage(0);
1332 final StringBuilder html1 = StringUtil.startAppend(
1333 1000,
1334 "<html><body>" +
1335 "<br><center><font color=\"LEVEL\">[Combat Stats]</font></center>" +
1336 "<table border=0 width=\"100%\">" +
1337 "<tr><td>Max.HP</td><td>",
1338 String.valueOf((int) (getMaxHp() / getStat().calcStat(Stats.MAX_HP, 1, this, null))),
1339 "*",
1340 String.valueOf((int) getStat().calcStat(Stats.MAX_HP, 1, this, null)),
1341 "</td><td>Max.MP</td><td>",
1342 String.valueOf(getMaxMp()),
1343 "</td></tr>" +
1344 "<tr><td>P.Atk.</td><td>",
1345 String.valueOf(getPAtk(null)),
1346 "</td><td>M.Atk.</td><td>",
1347 String.valueOf(getMAtk(null, null)),
1348 "</td></tr>" +
1349 "<tr><td>P.Def.</td><td>",
1350 String.valueOf(getPDef(null)),
1351 "</td><td>M.Def.</td><td>",
1352 String.valueOf(getMDef(null, null)),
1353 "</td></tr>" +
1354 "<tr><td>Accuracy</td><td>",
1355 String.valueOf(getAccuracy()),
1356 "</td><td>Evasion</td><td>",
1357 String.valueOf(getEvasionRate(null)),
1358 "</td></tr>" +
1359 "<tr><td>Critical</td><td>",
1360 String.valueOf(getCriticalHit(null, null)),
1361 "</td><td>Speed</td><td>",
1362 String.valueOf(getRunSpeed()),
1363 "</td></tr>" +
1364 "<tr><td>Atk.Speed</td><td>",
1365 String.valueOf(getPAtkSpd()),
1366 "</td><td>Cast.Speed</td><td>",
1367 String.valueOf(getMAtkSpd()),
1368 "</td></tr>" +
1369 "<tr><td>Race</td><td>",
1370 getTemplate().getRace().toString(),
1371 "</td><td></td><td></td></tr>" +
1372 "</table>" +
1373 "<br><center><font color=\"LEVEL\">[Basic Stats]</font></center>" +
1374 "<table border=0 width=\"100%\">" +
1375 "<tr><td>STR</td><td>",
1376 String.valueOf(getSTR()),
1377 "</td><td>DEX</td><td>",
1378 String.valueOf(getDEX()),
1379 "</td><td>CON</td><td>",
1380 String.valueOf(getCON()),
1381 "</td></tr>" +
1382 "<tr><td>INT</td><td>",
1383 String.valueOf(getINT()),
1384 "</td><td>WIT</td><td>",
1385 String.valueOf(getWIT()),
1386 "</td><td>MEN</td><td>",
1387 String.valueOf(getMEN()),
1388 "</td></tr>" +
1389 "</table>"
1390 );
1391
1392 if (getTemplate().getDropData() != null)
1393 {
1394 StringUtil.append(html1,
1395 "<br><center><font color=\"LEVEL\">[Drop Info]</font></center>" +
1396 "<br>Rates legend: <font color=\"ff0000\">50%+</font> <font color=\"00ff00\">30%+</font> <font color=\"0000ff\">less than 30%</font>" +
1397 "<table border=0 width=\"100%\">"
1398 );
1399 for (L2DropCategory cat : getTemplate().getDropData())
1400 {
1401 for (L2DropData drop : cat.getAllDrops())
1402 {
1403 final L2Item item = ItemTable.getInstance().getTemplate(drop.getItemId());
1404 if (item == null)
1405 continue;
1406
1407 final String color;
1408
1409 if (drop.getChance() >= 500000)
1410 color = "ff0000";
1411 else if (drop.getChance() >= 300000)
1412 color = "00ff00";
1413 else
1414 color = "0000ff";
1415
1416 StringUtil.append(html1,
1417 "<tr><td><font color=\"",
1418 color,
1419 "\">",
1420 item.getName(),
1421 "</font></td><td>",
1422 (drop.isQuestDrop() ? "Quest" : (cat.isSweep() ? "Sweep" : "Drop")),
1423 "</td></tr>"
1424 );
1425 }
1426 }
1427 html1.append("</table>");
1428 }
1429 html1.append("</body></html>");
1430
1431 html.setHtml(html1.toString());
1432 player.sendPacket(html);
1433 }
1434
1435 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
1436 player.sendPacket(ActionFailed.STATIC_PACKET);
1437 }
1438
1439 /** Return the L2Castle this L2NpcInstance belongs to. */
1440 public final Castle getCastle()
1441 {
1442 // Get castle this NPC belongs to (excluding L2Attackable)
1443 if (_castleIndex < 0)
1444 {
1445 L2TownZone town = TownManager.getTown(getX(), getY(), getZ());
1446
1447 if (town != null)
1448 _castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById());
1449
1450 if (_castleIndex < 0)
1451 {
1452 _castleIndex = CastleManager.getInstance().findNearestCastleIndex(this);
1453 }
1454 else
1455 _isInTown = true; // Npc was spawned in town
1456 }
1457
1458 if (_castleIndex < 0)
1459 return null;
1460
1461 return CastleManager.getInstance().getCastles().get(_castleIndex);
1462 }
1463
1464 /** Return the L2Fort this L2NpcInstance belongs to. */
1465 public final Fort getFort()
1466 {
1467 // Get Fort this NPC belongs to (excluding L2Attackable)
1468 if (_fortIndex < 0)
1469 {
1470 Fort fort = FortManager.getInstance().getFort(getX(), getY(), getZ());
1471 if (fort != null)
1472 _fortIndex = FortManager.getInstance().getFortIndex(fort.getFortId());
1473
1474 if (_fortIndex < 0)
1475 _fortIndex = FortManager.getInstance().findNearestFortIndex(this);
1476 }
1477
1478 if (_fortIndex < 0)
1479 return null;
1480
1481 return FortManager.getInstance().getForts().get(_fortIndex);
1482 }
1483
1484 public final boolean getIsInTown()
1485 {
1486 if (_castleIndex < 0)
1487 getCastle();
1488
1489 return _isInTown;
1490 }
1491
1492 /**
1493 * Open a quest or chat window on client with the text of the L2NpcInstance in function of the command.<BR><BR>
1494 *
1495 * <B><U> Example of use </U> :</B><BR><BR>
1496 * <li> Client packet : RequestBypassToServer</li><BR><BR>
1497 *
1498 * @param command The command string received from client
1499 *
1500 */
1501 public void onBypassFeedback(L2PcInstance player, String command)
1502 {
1503 //if (canInteract(player))
1504 {
1505 if (isBusy() && getBusyMessage().length() > 0)
1506 {
1507 player.sendPacket(ActionFailed.STATIC_PACKET);
1508
1509 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
1510 html.setFile("data/html/npcbusy.htm");
1511 html.replace("%busymessage%", getBusyMessage());
1512 html.replace("%npcname%", getName());
1513 html.replace("%playername%", player.getName());
1514 player.sendPacket(html);
1515 }
1516 else if (command.equalsIgnoreCase("TerritoryStatus"))
1517 {
1518 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
1519 {
1520 if (getCastle().getOwnerId() > 0)
1521 {
1522 html.setFile("data/html/territorystatus.htm");
1523 L2Clan clan = ClanTable.getInstance().getClan(getCastle().getOwnerId());
1524 html.replace("%clanname%", clan.getName());
1525 html.replace("%clanleadername%", clan.getLeaderName());
1526 }
1527 else
1528 {
1529 html.setFile("data/html/territorynoclan.htm");
1530 }
1531 }
1532 html.replace("%castlename%", getCastle().getName());
1533 html.replace("%taxpercent%", "" + getCastle().getTaxPercent());
1534 html.replace("%objectId%", String.valueOf(getObjectId()));
1535 {
1536 if (getCastle().getCastleId() > 6)
1537 {
1538 html.replace("%territory%", "The Kingdom of Elmore");
1539 }
1540 else
1541 {
1542 html.replace("%territory%", "The Kingdom of Aden");
1543 }
1544 }
1545 player.sendPacket(html);
1546 }
1547 else if (command.startsWith("Quest"))
1548 {
1549 String quest = "";
1550 try
1551 {
1552 quest = command.substring(5).trim();
1553 }
1554 catch (IndexOutOfBoundsException ioobe)
1555 {
1556 }
1557 if (quest.length() == 0)
1558 showQuestWindow(player);
1559 else
1560 showQuestWindow(player, quest);
1561 }
1562 else if (command.startsWith("Chat"))
1563 {
1564 int val = 0;
1565 try
1566 {
1567 val = Integer.parseInt(command.substring(5));
1568 }
1569 catch (IndexOutOfBoundsException ioobe)
1570 {
1571 }
1572 catch (NumberFormatException nfe)
1573 {
1574 }
1575 showChatWindow(player, val);
1576 }
1577 else if (command.startsWith("Link"))
1578 {
1579 String path = command.substring(5).trim();
1580 if (path.indexOf("..") != -1)
1581 return;
1582 String filename = "data/html/" + path;
1583 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
1584 html.setFile(filename);
1585 html.replace("%objectId%", String.valueOf(getObjectId()));
1586 player.sendPacket(html);
1587 }
1588 else if (command.startsWith("NobleTeleport"))
1589 {
1590 if (!player.isNoble())
1591 {
1592 String filename = "data/html/teleporter/nobleteleporter-no.htm";
1593 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
1594 html.setFile(filename);
1595 html.replace("%objectId%", String.valueOf(getObjectId()));
1596 html.replace("%npcname%", getName());
1597 player.sendPacket(html);
1598 return;
1599 }
1600 int val = 0;
1601 try
1602 {
1603 val = Integer.parseInt(command.substring(5));
1604 }
1605 catch (IndexOutOfBoundsException ioobe)
1606 {
1607 }
1608 catch (NumberFormatException nfe)
1609 {
1610 }
1611 showChatWindow(player, val);
1612 }
1613 else if (command.startsWith("Loto"))
1614 {
1615 int val = 0;
1616 try
1617 {
1618 val = Integer.parseInt(command.substring(5));
1619 }
1620 catch (IndexOutOfBoundsException ioobe)
1621 {
1622 }
1623 catch (NumberFormatException nfe)
1624 {
1625 }
1626 if (val == 0)
1627 {
1628 // new loto ticket
1629 for (int i = 0; i < 5; i++)
1630 player.setLoto(i, 0);
1631 }
1632 showLotoWindow(player, val);
1633 }
1634 else if (command.startsWith("CPRecovery"))
1635 {
1636 makeCPRecovery(player);
1637 }
1638 else if (command.startsWith("SupportMagicServitor"))
1639 {
1640 makeSupportMagic(player,true);
1641 }
1642 else if (command.startsWith("SupportMagic"))
1643 {
1644 makeSupportMagic(player,false);
1645 }
1646 else if (command.startsWith("GiveBlessing"))
1647 {
1648 giveBlessingSupport(player);
1649 }
1650 else if (command.startsWith("multisell"))
1651 {
1652 int listId = Integer.parseInt(command.substring(9).trim());
1653 L2Multisell.getInstance().separateAndSend(listId, player, getNpcId(), false, getCastle().getTaxRate());
1654 }
1655 else if (command.startsWith("exc_multisell"))
1656 {
1657 int listId = Integer.parseInt(command.substring(13).trim());
1658 L2Multisell.getInstance().separateAndSend(listId, player, getNpcId(), true, getCastle().getTaxRate());
1659 }
1660 else if (command.startsWith("Augment"))
1661 {
1662 int cmdChoice = Integer.parseInt(command.substring(8, 9).trim());
1663 switch (cmdChoice)
1664 {
1665 case 1:
1666 player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_TO_BE_AUGMENTED));
1667 player.sendPacket(new ExShowVariationMakeWindow());
1668 break;
1669 case 2:
1670 player.sendPacket(new SystemMessage(SystemMessageId.SELECT_THE_ITEM_FROM_WHICH_YOU_WISH_TO_REMOVE_AUGMENTATION));
1671 player.sendPacket(new ExShowVariationCancelWindow());
1672 break;
1673 }
1674 }
1675 else if (command.startsWith("npcfind_byid"))
1676 {
1677 try
1678 {
1679 L2Spawn spawn = SpawnTable.getInstance().getTemplate(Integer.parseInt(command.substring(12).trim()));
1680
1681 if (spawn != null)
1682 {
1683 player.sendPacket(new RadarControl(2, 2, spawn.getLocx(), spawn.getLocy(), spawn.getLocz()));
1684 player.sendPacket(new RadarControl(0, 1, spawn.getLocx(), spawn.getLocy(), spawn.getLocz()));
1685 }
1686 }
1687 catch (NumberFormatException nfe)
1688 {
1689 player.sendMessage("Wrong command parameters");
1690 }
1691 }
1692 else if (command.startsWith("EnterRift"))
1693 {
1694 try
1695 {
1696 Byte b1 = Byte.parseByte(command.substring(10)); // Selected Area: Recruit, Soldier etc
1697 DimensionalRiftManager.getInstance().start(player, b1, this);
1698 }
1699 catch (Exception e)
1700 {
1701 }
1702 }
1703 else if (command.startsWith("ChangeRiftRoom"))
1704 {
1705 if (player.isInParty() && player.getParty().isInDimensionalRift())
1706 {
1707 player.getParty().getDimensionalRift().manualTeleport(player, this);
1708 }
1709 else
1710 {
1711 DimensionalRiftManager.getInstance().handleCheat(player, this);
1712 }
1713 }
1714 else if (command.startsWith("remove_dp"))
1715 {
1716 int cmdChoice = Integer.parseInt(command.substring(10, 11).trim());
1717 int[] pen_clear_price =
1718 {
1719 3600, 8640, 25200, 50400, 86400, 144000, 144000, 144000
1720 };
1721 switch (cmdChoice)
1722 {
1723 case 1:
1724 String filename = "data/html/default/30981-1.htm";
1725 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
1726 html.setFile(filename);
1727 html.replace("%objectId%", String.valueOf(getObjectId()));
1728 html.replace("%dp_price%", String.valueOf(pen_clear_price[player.getExpertiseIndex()]));
1729 player.sendPacket(html);
1730 break;
1731 case 2:
1732 NpcHtmlMessage Reply = new NpcHtmlMessage(getObjectId());
1733 final StringBuilder replyMSG = StringUtil.startAppend(400,
1734 "<html><body>Black Judge:<br>"
1735 );
1736
1737 if (player.getDeathPenaltyBuffLevel() > 0)
1738 {
1739 if (player.getAdena() >= pen_clear_price[player.getExpertiseIndex()])
1740 {
1741 if (!player.reduceAdena("DeathPenality", pen_clear_price[player.getExpertiseIndex()], this, true))
1742 return;
1743 player.setDeathPenaltyBuffLevel(player.getDeathPenaltyBuffLevel() - 1);
1744 player.sendPacket(new SystemMessage(SystemMessageId.DEATH_PENALTY_LIFTED));
1745 player.sendPacket(new EtcStatusUpdate(player));
1746 return;
1747 }
1748 else
1749 {
1750 replyMSG.append("The wound you have received from death's touch is too deep to be healed for the money you have to give me. Find more money if you wish death's mark to be fully removed from you.");
1751 }
1752 }
1753 else
1754 {
1755 replyMSG.append("You have no more death wounds that require healing.<br>" +
1756 "Go forth and fight, both for this world and your own glory.");
1757 }
1758
1759 replyMSG.append("</body></html>");
1760 Reply.setHtml(replyMSG.toString());
1761 player.sendPacket(Reply);
1762 break;
1763 }
1764 }
1765 else if (command.startsWith("ExitRift"))
1766 {
1767 if (player.isInParty() && player.getParty().isInDimensionalRift())
1768 {
1769 player.getParty().getDimensionalRift().manualExitRift(player, this);
1770 }
1771 else
1772 {
1773 DimensionalRiftManager.getInstance().handleCheat(player, this);
1774 }
1775 }
1776 else if (command.startsWith("ReleaseAttribute"))
1777 {
1778 player.sendPacket(new ExShowBaseAttributeCancelWindow(player));
1779 }
1780 else
1781 {
1782 _log.info(getClass().getSimpleName()+": Unknown NPC bypass: \""+command+"\" NpcId: "+getNpcId());
1783 }
1784 }
1785 }
1786
1787 /**
1788 * Return null (regular NPCs don't have weapons instancies).<BR><BR>
1789 */
1790 @Override
1791 public L2ItemInstance getActiveWeaponInstance()
1792 {
1793 // regular NPCs dont have weapons instancies
1794 return null;
1795 }
1796
1797 /**
1798 * Return the weapon item equiped in the right hand of the L2NpcInstance or null.<BR><BR>
1799 */
1800 @Override
1801 public L2Weapon getActiveWeaponItem()
1802 {
1803 // Get the weapon identifier equiped in the right hand of the L2NpcInstance
1804 int weaponId = getTemplate().rhand;
1805
1806 if (weaponId < 1)
1807 return null;
1808
1809 // Get the weapon item equiped in the right hand of the L2NpcInstance
1810 L2Item item = ItemTable.getInstance().getTemplate(getTemplate().rhand);
1811
1812 if (!(item instanceof L2Weapon))
1813 return null;
1814
1815 return (L2Weapon) item;
1816 }
1817
1818 public void giveBlessingSupport(L2PcInstance player)
1819 {
1820 if (player == null)
1821 return;
1822
1823 // Blessing of protection - author kerberos_20. Used codes from Rayan - L2Emu project.
1824 // Prevent a cursed weapon weilder of being buffed - I think no need of that becouse karma check > 0
1825 // if (player.isCursedWeaponEquiped())
1826 // return;
1827
1828 int player_level = player.getLevel();
1829 // Select the player
1830 setTarget(player);
1831 // If the player is too high level, display a message and return
1832 if (player_level > 39 || player.getClassId().level() >= 2)
1833 {
1834 String content = "<html><body>Newbie Guide:<br>I'm sorry, but you are not eligible to receive the protection blessing.<br1>It can only be bestowed on <font color=\"LEVEL\">characters below level 39 who have not made a seccond transfer.</font></body></html>";
1835 insertObjectIdAndShowChatWindow(player, content);
1836 return;
1837 }
1838 L2Skill skill = SkillTable.getInstance().getInfo(5182, 1);
1839 doCast(skill);
1840 }
1841
1842 /**
1843 * Return null (regular NPCs don't have weapons instancies).<BR><BR>
1844 */
1845 @Override
1846 public L2ItemInstance getSecondaryWeaponInstance()
1847 {
1848 // regular NPCs dont have weapons instancies
1849 return null;
1850 }
1851
1852 /**
1853 * Return the weapon item equiped in the left hand of the L2NpcInstance or null.<BR><BR>
1854 */
1855 @Override
1856 public L2Weapon getSecondaryWeaponItem()
1857 {
1858 // Get the weapon identifier equiped in the right hand of the L2NpcInstance
1859 int weaponId = getTemplate().lhand;
1860
1861 if (weaponId < 1)
1862 return null;
1863
1864 // Get the weapon item equiped in the right hand of the L2NpcInstance
1865 L2Item item = ItemTable.getInstance().getTemplate(getTemplate().lhand);
1866
1867 if (!(item instanceof L2Weapon))
1868 return null;
1869
1870 return (L2Weapon) item;
1871 }
1872
1873 /**
1874 * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance.<BR><BR>
1875 *
1876 * @param player The L2PcInstance who talks with the L2NpcInstance
1877 * @param content The text of the L2NpcMessage
1878 *
1879 */
1880 public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content)
1881 {
1882 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
1883 content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));
1884 NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId());
1885 npcReply.setHtml(content);
1886 player.sendPacket(npcReply);
1887 }
1888
1889 /**
1890 * Return the pathfile of the selected HTML file in function of the npcId and of the page number.<BR><BR>
1891 *
1892 * <B><U> Format of the pathfile </U> :</B><BR><BR>
1893 * <li> if the file exists on the server (page number = 0) : <B>data/html/default/12006.htm</B> (npcId-page number)</li>
1894 * <li> if the file exists on the server (page number > 0) : <B>data/html/default/12006-1.htm</B> (npcId-page number)</li>
1895 * <li> if the file doesn't exist on the server : <B>data/html/npcdefault.htm</B> (message : "I have nothing to say to you")</li><BR><BR>
1896 *
1897 * <B><U> Overridden in </U> :</B><BR><BR>
1898 * <li> L2GuardInstance : Set the pathfile to data/html/guard/12006-1.htm (npcId-page number)</li><BR><BR>
1899 *
1900 * @param npcId The Identifier of the L2NpcInstance whose text must be display
1901 * @param val The number of the page to display
1902 *
1903 */
1904 public String getHtmlPath(int npcId, int val)
1905 {
1906 String pom = "";
1907
1908 if (val == 0)
1909 pom = "" + npcId;
1910 else
1911 pom = npcId + "-" + val;
1912
1913 String temp = "data/html/default/" + pom + ".htm";
1914
1915 if (!Config.LAZY_CACHE)
1916 {
1917 // If not running lazy cache the file must be in the cache or it doesnt exist
1918 if (HtmCache.getInstance().contains(temp))
1919 return temp;
1920 }
1921 else
1922 {
1923 if (HtmCache.getInstance().isLoadable(temp))
1924 return temp;
1925 }
1926
1927 // If the file is not found, the standard message "I have nothing to say to you" is returned
1928 return "data/html/npcdefault.htm";
1929 }
1930
1931 /**
1932 * Open a choose quest window on client with all quests available of the L2NpcInstance.<BR><BR>
1933 *
1934 * <B><U> Actions</U> :</B><BR><BR>
1935 * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li><BR><BR>
1936 *
1937 * @param player The L2PcInstance that talk with the L2NpcInstance
1938 * @param quests The table containing quests of the L2NpcInstance
1939 *
1940 */
1941 public void showQuestChooseWindow(L2PcInstance player, Quest[] quests)
1942 {
1943 final StringBuilder sb = StringUtil.startAppend(150,
1944 "<html><body>"
1945 );
1946 for (Quest q : quests)
1947 {
1948 StringUtil.append(sb,
1949 "<a action=\"bypass -h npc_",
1950 String.valueOf(getObjectId()),
1951 "_Quest ",
1952 q.getName(),
1953 "\">[",
1954 q.getDescr()
1955 );
1956
1957 QuestState qs = player.getQuestState(q.getScriptName());
1958 if (qs != null)
1959 {
1960 if (qs.getState() == State.STARTED && qs.getInt("cond") > 0)
1961 sb.append(" (In Progress)");
1962 else if (qs.getState() == State.COMPLETED)
1963 sb.append(" (Done)");
1964 }
1965 sb.append("]</a><br>");
1966 }
1967
1968 sb.append("</body></html>");
1969
1970 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
1971 insertObjectIdAndShowChatWindow(player, sb.toString());
1972 }
1973
1974 /**
1975 * Open a quest window on client with the text of the L2NpcInstance.<BR><BR>
1976 *
1977 * <B><U> Actions</U> :</B><BR><BR>
1978 * <li>Get the text of the quest state in the folder data/scripts/quests/questId/stateId.htm </li>
1979 * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
1980 * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR><BR>
1981 *
1982 * @param player The L2PcInstance that talk with the L2NpcInstance
1983 * @param questId The Identifier of the quest to display the message
1984 *
1985 */
1986 public void showQuestWindow(L2PcInstance player, String questId)
1987 {
1988 String content = null;
1989
1990 Quest q = QuestManager.getInstance().getQuest(questId);
1991
1992 // Get the state of the selected quest
1993 QuestState qs = player.getQuestState(questId);
1994
1995 if (q == null)
1996 {
1997 // no quests found
1998 content = "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>";
1999 }
2000 else
2001 {
2002 if ((q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000) && (player.getWeightPenalty() >= 3 || player.getInventoryLimit() * 0.8 <= player.getInventory().getSize()))
2003 {
2004 player.sendPacket(new SystemMessage(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT));
2005 return;
2006 }
2007
2008 if (qs == null)
2009 {
2010 if (q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000)
2011 {
2012 Quest[] questList = player.getAllActiveQuests();
2013 if (questList.length >= 25) // if too many ongoing quests, don't show window and send message
2014 {
2015 player.sendPacket(new SystemMessage(SystemMessageId.TOO_MANY_QUESTS));
2016 return;
2017 }
2018 }
2019 // check for start point
2020 Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
2021
2022 if (qlst != null && qlst.length > 0)
2023 {
2024 for (Quest temp : qlst)
2025 {
2026 if (temp == q)
2027 {
2028 qs = q.newQuestState(player);
2029 break;
2030 }
2031 }
2032 }
2033 }
2034 }
2035
2036 if (qs != null)
2037 {
2038 // If the quest is alreday started, no need to show a window
2039 if (!qs.getQuest().notifyTalk(this, qs))
2040 return;
2041
2042 questId = qs.getQuest().getName();
2043 String stateId = State.getStateName(qs.getState());
2044 String path = "data/scripts/quests/" + questId + "/" + stateId + ".htm";
2045 content = HtmCache.getInstance().getHtm(path); //TODO path for quests html
2046
2047 if (Config.DEBUG)
2048 {
2049 if (content != null)
2050 {
2051 _log.fine("Showing quest window for quest " + questId + " html path: " + path);
2052 }
2053 else
2054 {
2055 _log.fine("File not exists for quest " + questId + " html path: " + path);
2056 }
2057 }
2058 }
2059
2060 // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
2061 if (content != null)
2062 insertObjectIdAndShowChatWindow(player, content);
2063
2064 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
2065 player.sendPacket(ActionFailed.STATIC_PACKET);
2066 }
2067
2068 /**
2069 * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.<BR><BR>
2070 *
2071 * @param player The L2PcInstance that talk with the L2NpcInstance
2072 *
2073 */
2074 public void showQuestWindow(L2PcInstance player)
2075 {
2076 // collect awaiting quests and start points
2077 List<Quest> options = new FastList<Quest>();
2078
2079 QuestState[] awaits = player.getQuestsForTalk(getTemplate().npcId);
2080 Quest[] starts = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
2081
2082 // Quests are limited between 1 and 999 because those are the quests that are supported by the client.
2083 // By limiting them there, we are allowed to create custom quests at higher IDs without interfering
2084 if (awaits != null)
2085 {
2086 for (QuestState x : awaits)
2087 {
2088 if (!options.contains(x.getQuest()))
2089 if ((x.getQuest().getQuestIntId() > 0) && (x.getQuest().getQuestIntId() < 20000))
2090 options.add(x.getQuest());
2091 }
2092 }
2093
2094 if (starts != null)
2095 {
2096 for (Quest x : starts)
2097 {
2098 if (!options.contains(x))
2099 if ((x.getQuestIntId() > 0) && (x.getQuestIntId() < 20000))
2100 options.add(x);
2101 }
2102 }
2103
2104 // Display a QuestChooseWindow (if several quests are available) or QuestWindow
2105 if (options.size() > 1)
2106 {
2107 showQuestChooseWindow(player, options.toArray(new Quest[options.size()]));
2108 }
2109 else if (options.size() == 1)
2110 {
2111 showQuestWindow(player, options.get(0).getName());
2112 }
2113 else
2114 {
2115 showQuestWindow(player, "");
2116 }
2117 }
2118
2119 /**
2120 * Open a Loto window on client with the text of the L2NpcInstance.<BR><BR>
2121 *
2122 * <B><U> Actions</U> :</B><BR><BR>
2123 * <li>Get the text of the selected HTML file in function of the npcId and of the page number </li>
2124 * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
2125 * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR>
2126 *
2127 * @param player The L2PcInstance that talk with the L2NpcInstance
2128 * @param val The number of the page of the L2NpcInstance to display
2129 *
2130 */
2131 // 0 - first buy lottery ticket window
2132 // 1-20 - buttons
2133 // 21 - second buy lottery ticket window
2134 // 22 - selected ticket with 5 numbers
2135 // 23 - current lottery jackpot
2136 // 24 - Previous winning numbers/Prize claim
2137 // >24 - check lottery ticket by item object id
2138 public void showLotoWindow(L2PcInstance player, int val)
2139 {
2140 int npcId = getTemplate().npcId;
2141 String filename;
2142 SystemMessage sm;
2143 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
2144
2145 if (val == 0) // 0 - first buy lottery ticket window
2146 {
2147 filename = (getHtmlPath(npcId, 1));
2148 html.setFile(filename);
2149 }
2150 else if (val >= 1 && val <= 21) // 1-20 - buttons, 21 - second buy lottery ticket window
2151 {
2152 if (!Lottery.getInstance().isStarted())
2153 {
2154 //tickets can't be sold
2155 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD));
2156 return;
2157 }
2158 if (!Lottery.getInstance().isSellableTickets())
2159 {
2160 //tickets can't be sold
2161 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE));
2162 return;
2163 }
2164
2165 filename = (getHtmlPath(npcId, 5));
2166 html.setFile(filename);
2167
2168 int count = 0;
2169 int found = 0;
2170 // counting buttons and unsetting button if found
2171 for (int i = 0; i < 5; i++)
2172 {
2173 if (player.getLoto(i) == val)
2174 {
2175 //unsetting button
2176 player.setLoto(i, 0);
2177 found = 1;
2178 }
2179 else if (player.getLoto(i) > 0)
2180 {
2181 count++;
2182 }
2183 }
2184
2185 //if not rearched limit 5 and not unseted value
2186 if (count < 5 && found == 0 && val <= 20)
2187 for (int i = 0; i < 5; i++)
2188 if (player.getLoto(i) == 0)
2189 {
2190 player.setLoto(i, val);
2191 break;
2192 }
2193
2194 //setting pusshed buttons
2195 count = 0;
2196 for (int i = 0; i < 5; i++)
2197 if (player.getLoto(i) > 0)
2198 {
2199 count++;
2200 String button = String.valueOf(player.getLoto(i));
2201 if (player.getLoto(i) < 10)
2202 button = "0" + button;
2203 String search = "fore=\"L2UI.lottoNum" + button + "\" back=\"L2UI.lottoNum" + button + "a_check\"";
2204 String replace = "fore=\"L2UI.lottoNum" + button + "a_check\" back=\"L2UI.lottoNum" + button + "\"";
2205 html.replace(search, replace);
2206 }
2207
2208 if (count == 5)
2209 {
2210 String search = "0\">Return";
2211 String replace = "22\">The winner selected the numbers above.";
2212 html.replace(search, replace);
2213 }
2214 }
2215 else if (val == 22) //22 - selected ticket with 5 numbers
2216 {
2217 if (!Lottery.getInstance().isStarted())
2218 {
2219 //tickets can't be sold
2220 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD));
2221 return;
2222 }
2223 if (!Lottery.getInstance().isSellableTickets())
2224 {
2225 //tickets can't be sold
2226 player.sendPacket(new SystemMessage(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE));
2227 return;
2228 }
2229
2230 long price = Config.ALT_LOTTERY_TICKET_PRICE;
2231 int lotonumber = Lottery.getInstance().getId();
2232 int enchant = 0;
2233 int type2 = 0;
2234
2235 for (int i = 0; i < 5; i++)
2236 {
2237 if (player.getLoto(i) == 0)
2238 return;
2239
2240 if (player.getLoto(i) < 17)
2241 enchant += Math.pow(2, player.getLoto(i) - 1);
2242 else
2243 type2 += Math.pow(2, player.getLoto(i) - 17);
2244 }
2245 if (player.getAdena() < price)
2246 {
2247 sm = new SystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
2248 player.sendPacket(sm);
2249 return;
2250 }
2251 if (!player.reduceAdena("Loto", price, this, true))
2252 return;
2253 Lottery.getInstance().increasePrize(price);
2254
2255 sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_S2);
2256 sm.addNumber(lotonumber);
2257 sm.addItemName(4442);
2258 player.sendPacket(sm);
2259
2260 L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4442);
2261 item.setCount(1);
2262 item.setCustomType1(lotonumber);
2263 item.setEnchantLevel(enchant);
2264 item.setCustomType2(type2);
2265 player.getInventory().addItem("Loto", item, player, this);
2266
2267 InventoryUpdate iu = new InventoryUpdate();
2268 iu.addItem(item);
2269 L2ItemInstance adenaupdate = player.getInventory().getItemByItemId(57);
2270 iu.addModifiedItem(adenaupdate);
2271 player.sendPacket(iu);
2272
2273 filename = (getHtmlPath(npcId, 3));
2274 html.setFile(filename);
2275 }
2276 else if (val == 23) //23 - current lottery jackpot
2277 {
2278 filename = (getHtmlPath(npcId, 3));
2279 html.setFile(filename);
2280 }
2281 else if (val == 24) // 24 - Previous winning numbers/Prize claim
2282 {
2283 filename = (getHtmlPath(npcId, 4));
2284 html.setFile(filename);
2285
2286 int lotonumber = Lottery.getInstance().getId();
2287 String message = "";
2288 for (L2ItemInstance item : player.getInventory().getItems())
2289 {
2290 if (item == null)
2291 continue;
2292 if (item.getItemId() == 4442 && item.getCustomType1() < lotonumber)
2293 {
2294 message = message + "<a action=\"bypass -h npc_%objectId%_Loto " + item.getObjectId() + "\">" + item.getCustomType1() + " Event Number ";
2295 int[] numbers = Lottery.getInstance().decodeNumbers(item.getEnchantLevel(), item.getCustomType2());
2296 for (int i = 0; i < 5; i++)
2297 {
2298 message += numbers[i] + " ";
2299 }
2300 long[] check = Lottery.getInstance().checkTicket(item);
2301 if (check[0] > 0)
2302 {
2303 switch ((int)check[0])
2304 {
2305 case 1:
2306 message += "- 1st Prize";
2307 break;
2308 case 2:
2309 message += "- 2nd Prize";
2310 break;
2311 case 3:
2312 message += "- 3th Prize";
2313 break;
2314 case 4:
2315 message += "- 4th Prize";
2316 break;
2317 }
2318 message += " " + check[1] + "a.";
2319 }
2320 message += "</a><br>";
2321 }
2322 }
2323 if (message.isEmpty())
2324 {
2325 message += "There is no winning lottery ticket...<br>";
2326 }
2327 html.replace("%result%", message);
2328 }
2329 else if (val > 24) // >24 - check lottery ticket by item object id
2330 {
2331 int lotonumber = Lottery.getInstance().getId();
2332 L2ItemInstance item = player.getInventory().getItemByObjectId(val);
2333 if (item == null || item.getItemId() != 4442 || item.getCustomType1() >= lotonumber)
2334 return;
2335 long[] check = Lottery.getInstance().checkTicket(item);
2336
2337 sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
2338 sm.addItemName(4442);
2339 sm.addItemNumber(1);
2340 player.sendPacket(sm);
2341
2342 long adena = check[1];
2343 if (adena > 0)
2344 player.addAdena("Loto", adena, this, true);
2345 player.destroyItem("Loto", item, this, false);
2346 return;
2347 }
2348 html.replace("%objectId%", String.valueOf(getObjectId()));
2349 html.replace("%race%", "" + Lottery.getInstance().getId());
2350 html.replace("%adena%", "" + Lottery.getInstance().getPrize());
2351 html.replace("%ticket_price%", "" + Config.ALT_LOTTERY_TICKET_PRICE);
2352 html.replace("%prize5%", "" + (Config.ALT_LOTTERY_5_NUMBER_RATE * 100));
2353 html.replace("%prize4%", "" + (Config.ALT_LOTTERY_4_NUMBER_RATE * 100));
2354 html.replace("%prize3%", "" + (Config.ALT_LOTTERY_3_NUMBER_RATE * 100));
2355 html.replace("%prize2%", "" + Config.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE);
2356 html.replace("%enddate%", "" + DateFormat.getDateInstance().format(Lottery.getInstance().getEndDate()));
2357 player.sendPacket(html);
2358
2359 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
2360 player.sendPacket(ActionFailed.STATIC_PACKET);
2361 }
2362
2363 public void makeCPRecovery(L2PcInstance player)
2364 {
2365 if (getNpcId() != 31225 && getNpcId() != 31226)
2366 return;
2367 if (player.isCursedWeaponEquipped())
2368 {
2369 player.sendMessage("Go away, you're not welcome here.");
2370 player.sendPacket(ActionFailed.STATIC_PACKET);
2371 return;
2372 }
2373
2374 int neededmoney = 100;
2375 if (!player.reduceAdena("RestoreCP", neededmoney, player.getLastFolkNPC(), true))
2376 return;
2377 L2Skill skill = SkillTable.getInstance().getInfo(4380, 1);
2378 if (skill != null)
2379 {
2380 setTarget(player);
2381 doCast(skill);
2382 }
2383 player.sendPacket(ActionFailed.STATIC_PACKET);
2384 }
2385
2386 /**
2387 * Add Newbie helper buffs to L2Player according to its level.<BR><BR>
2388 *
2389 * <B><U> Actions</U> :</B><BR><BR>
2390 * <li>Get the range level in wich player must be to obtain buff </li>
2391 * <li>If player level is out of range, display a message and return </li>
2392 * <li>According to player level cast buff </li><BR><BR>
2393 *
2394 * <FONT COLOR=#FF0000><B> Newbie Helper Buff list is define in sql table helper_buff_list</B></FONT><BR><BR>
2395 *
2396 * @param player The L2PcInstance that talk with the L2NpcInstance
2397 *
2398 */
2399 public void makeSupportMagic(L2PcInstance player, boolean isSummon)
2400 {
2401 if (player == null)
2402 return;
2403
2404 // Prevent a cursed weapon weilder of being buffed
2405 if (player.isCursedWeaponEquipped())
2406 return;
2407
2408 int player_level = player.getLevel();
2409 int lowestLevel = 0;
2410 int highestLevel = 0;
2411
2412 if (isSummon)
2413 {
2414 if (player.getPet() == null || !(player.getPet() instanceof L2SummonInstance))
2415 {
2416 String content = "<html><body>Only servitors can receive this Support Magic. If you do not have a servitor, you cannot access these spells.</body></html>";
2417 insertObjectIdAndShowChatWindow(player, content);
2418 return;
2419 }
2420 setTarget(player.getPet());
2421 }
2422 else
2423 // Select the player
2424 setTarget(player);
2425
2426 if (isSummon)
2427 {
2428 lowestLevel = HelperBuffTable.getInstance().getServitorLowestLevel();
2429 highestLevel = HelperBuffTable.getInstance().getServitorHighestLevel();
2430 }
2431 else
2432 {
2433 // Calculate the min and max level between which the player must be to obtain buff
2434 if (player.isMageClass())
2435 {
2436 lowestLevel = HelperBuffTable.getInstance().getMagicClassLowestLevel();
2437 highestLevel = HelperBuffTable.getInstance().getMagicClassHighestLevel();
2438 }
2439 else
2440 {
2441 lowestLevel = HelperBuffTable.getInstance().getPhysicClassLowestLevel();
2442 highestLevel = HelperBuffTable.getInstance().getPhysicClassHighestLevel();
2443 }
2444 }
2445 // If the player is too high level, display a message and return
2446 if (player_level > highestLevel)
2447 {
2448 String content = "<html><body>Newbie Guide:<br>Only a <font color=\"LEVEL\">novice character of level " + highestLevel
2449 + " or less</font> can receive my support magic.<br>Your novice character is the first one that you created and raised in this world.</body></html>";
2450 insertObjectIdAndShowChatWindow(player, content);
2451 return;
2452 }
2453
2454 // If the player is too low level, display a message and return
2455 if (player_level < lowestLevel)
2456 {
2457 String content = "<html><body>Come back here when you have reached level " + lowestLevel + ". I will give you support magic then.</body></html>";
2458 insertObjectIdAndShowChatWindow(player, content);
2459 return;
2460 }
2461
2462 L2Skill skill = null;
2463 if (isSummon)
2464 {
2465 for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable())
2466 {
2467 if (helperBuffItem.isForSummon())
2468 {
2469 skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
2470 if (skill != null)
2471 doCast(skill);
2472 }
2473 }
2474 }
2475 else
2476 {
2477 // Go through the Helper Buff list define in sql table helper_buff_list and cast skill
2478 for (L2HelperBuff helperBuffItem : HelperBuffTable.getInstance().getHelperBuffTable())
2479 {
2480 if (helperBuffItem.isMagicClassBuff() == player.isMageClass())
2481 {
2482 if (player_level >= helperBuffItem.getLowerLevel() && player_level <= helperBuffItem.getUpperLevel())
2483 {
2484 skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
2485 if (skill.getSkillType() == L2SkillType.SUMMON)
2486 player.doSimultaneousCast(skill);
2487 else
2488 doCast(skill);
2489 }
2490 }
2491 }
2492 }
2493 }
2494
2495 public void showChatWindow(L2PcInstance player)
2496 {
2497 showChatWindow(player, 0);
2498 }
2499
2500 /**
2501 * Returns true if html exists
2502 * @param player
2503 * @param type
2504 * @return boolean
2505 */
2506 private boolean showPkDenyChatWindow(L2PcInstance player, String type)
2507 {
2508 String html = HtmCache.getInstance().getHtm("data/html/" + type + "/" + getNpcId() + "-pk.htm");
2509
2510 if (html != null)
2511 {
2512 NpcHtmlMessage pkDenyMsg = new NpcHtmlMessage(getObjectId());
2513 pkDenyMsg.setHtml(html);
2514 player.sendPacket(pkDenyMsg);
2515 player.sendPacket(ActionFailed.STATIC_PACKET);
2516 return true;
2517 }
2518
2519 return false;
2520 }
2521
2522 /**
2523 * Open a chat window on client with the text of the L2NpcInstance.<BR><BR>
2524 *
2525 * <B><U> Actions</U> :</B><BR><BR>
2526 * <li>Get the text of the selected HTML file in function of the npcId and of the page number </li>
2527 * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance </li>
2528 * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet </li><BR>
2529 *
2530 * @param player The L2PcInstance that talk with the L2NpcInstance
2531 * @param val The number of the page of the L2NpcInstance to display
2532 *
2533 */
2534 public void showChatWindow(L2PcInstance player, int val)
2535 {
2536 if (player.isCursedWeaponEquipped() && (!(player.getTarget() instanceof L2ClanHallManagerInstance) || !(player.getTarget() instanceof L2DoormenInstance)))
2537 {
2538 player.setTarget(player);
2539 return;
2540 }
2541 if (player.getKarma() > 0)
2542 {
2543 if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && this instanceof L2MerchantInstance)
2544 {
2545 if (showPkDenyChatWindow(player, "merchant"))
2546 return;
2547 }
2548 else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_GK && this instanceof L2TeleporterInstance)
2549 {
2550 if (showPkDenyChatWindow(player, "teleporter"))
2551 return;
2552 }
2553 else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE && this instanceof L2WarehouseInstance)
2554 {
2555 if (showPkDenyChatWindow(player, "warehouse"))
2556 return;
2557 }
2558 else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && this instanceof L2FishermanInstance)
2559 {
2560 if (showPkDenyChatWindow(player, "fisherman"))
2561 return;
2562 }
2563 }
2564
2565 if ("L2Auctioneer".equals(getTemplate().type) && val == 0)
2566 return;
2567
2568 int npcId = getTemplate().npcId;
2569
2570 /* For use with Seven Signs implementation */
2571 String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH;
2572 int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE);
2573 int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS);
2574 int playerCabal = SevenSigns.getInstance().getPlayerCabal(player);
2575 int compWinner = SevenSigns.getInstance().getCabalHighestScore();
2576
2577 switch (npcId)
2578 {
2579 case 31127: //
2580 case 31128: //
2581 case 31129: // Dawn Festival Guides
2582 case 31130: //
2583 case 31131: //
2584 filename += "festival/dawn_guide.htm";
2585 break;
2586 case 31137: //
2587 case 31138: //
2588 case 31139: // Dusk Festival Guides
2589 case 31140: //
2590 case 31141: //
2591 filename += "festival/dusk_guide.htm";
2592 break;
2593 case 31092: // Black Marketeer of Mammon
2594 filename += "blkmrkt_1.htm";
2595 break;
2596 case 31113: // Merchant of Mammon
2597 if (Config.ALT_STRICT_SEVENSIGNS)
2598 {
2599 switch (compWinner)
2600 {
2601 case SevenSigns.CABAL_DAWN:
2602 if (playerCabal != compWinner || playerCabal != sealAvariceOwner)
2603 {
2604 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN));
2605 player.sendPacket(ActionFailed.STATIC_PACKET);
2606 return;
2607 }
2608 break;
2609 case SevenSigns.CABAL_DUSK:
2610 if (playerCabal != compWinner || playerCabal != sealAvariceOwner)
2611 {
2612 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK));
2613 player.sendPacket(ActionFailed.STATIC_PACKET);
2614 return;
2615 }
2616 break;
2617 default:
2618 player.sendPacket(new SystemMessage(SystemMessageId.QUEST_EVENT_PERIOD));
2619 return;
2620 }
2621 }
2622 filename += "mammmerch_1.htm";
2623 break;
2624 case 31126: // Blacksmith of Mammon
2625 if (Config.ALT_STRICT_SEVENSIGNS)
2626 {
2627 switch (compWinner)
2628 {
2629 case SevenSigns.CABAL_DAWN:
2630 if (playerCabal != compWinner || playerCabal != sealGnosisOwner)
2631 {
2632 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN));
2633 player.sendPacket(ActionFailed.STATIC_PACKET);
2634 return;
2635 }
2636 break;
2637 case SevenSigns.CABAL_DUSK:
2638 if (playerCabal != compWinner || playerCabal != sealGnosisOwner)
2639 {
2640 player.sendPacket(new SystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK));
2641 player.sendPacket(ActionFailed.STATIC_PACKET);
2642 return;
2643 }
2644 break;
2645 default:
2646 player.sendPacket(new SystemMessage(SystemMessageId.QUEST_EVENT_PERIOD));
2647 return;
2648 }
2649 }
2650 filename += "mammblack_1.htm";
2651 break;
2652 case 31132:
2653 case 31133:
2654 case 31134:
2655 case 31135:
2656 case 31136: // Festival Witches
2657 case 31142:
2658 case 31143:
2659 case 31144:
2660 case 31145:
2661 case 31146:
2662 filename += "festival/festival_witch.htm";
2663 break;
2664 case 31688:
2665 if (player.isNoble())
2666 filename = Olympiad.OLYMPIAD_HTML_PATH + "noble_main.htm";
2667 else
2668 filename = (getHtmlPath(npcId, val));
2669 break;
2670 case 31690:
2671 case 31769:
2672 case 31770:
2673 case 31771:
2674 case 31772:
2675 if (player.isHero() || player.isNoble())
2676 filename = Olympiad.OLYMPIAD_HTML_PATH + "hero_main.htm";
2677 else
2678 filename = (getHtmlPath(npcId, val));
2679 break;
2680 case 36402:
2681 if (player.olyBuff > 0)
2682 filename = (player.olyBuff == 5 ? Olympiad.OLYMPIAD_HTML_PATH + "olympiad_buffs.htm" : Olympiad.OLYMPIAD_HTML_PATH + "olympiad_5buffs.htm");
2683 else
2684 filename = Olympiad.OLYMPIAD_HTML_PATH + "olympiad_nobuffs.htm";
2685 break;
2686 default:
2687 if (npcId >= 31865 && npcId <= 31918)
2688 {
2689 if (val == 0 )
2690 filename += "rift/GuardianOfBorder.htm";
2691 else
2692 filename += "rift/GuardianOfBorder-" + val + ".htm";
2693 break;
2694 }
2695 if ((npcId >= 31093 && npcId <= 31094) || (npcId >= 31172 && npcId <= 31201) || (npcId >= 31239 && npcId <= 31254))
2696 return;
2697 // Get the text of the selected HTML file in function of the npcId and of the page number
2698 filename = (getHtmlPath(npcId, val));
2699 break;
2700 }
2701
2702 // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
2703 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
2704 html.setFile(filename);
2705
2706 if (this instanceof L2MerchantInstance)
2707 {
2708 if (Config.LIST_PET_RENT_NPC.contains(npcId))
2709 html.replace("_Quest", "_RentPet\">Rent Pet</a><br><a action=\"bypass -h npc_%objectId%_Quest");
2710 }
2711
2712 html.replace("%objectId%", String.valueOf(getObjectId()));
2713 html.replace("%festivalMins%", SevenSignsFestival.getInstance().getTimeToNextFestivalStr());
2714 player.sendPacket(html);
2715
2716 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
2717 player.sendPacket(ActionFailed.STATIC_PACKET);
2718 }
2719
2720 /**
2721 * Open a chat window on client with the text specified by the given file name and path,<BR>
2722 * relative to the datapack root.
2723 * <BR><BR>
2724 * Added by Tempy
2725 * @param player The L2PcInstance that talk with the L2NpcInstance
2726 * @param filename The filename that contains the text to send
2727 *
2728 */
2729 public void showChatWindow(L2PcInstance player, String filename)
2730 {
2731 // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
2732 NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
2733 html.setFile(filename);
2734 html.replace("%objectId%", String.valueOf(getObjectId()));
2735 player.sendPacket(html);
2736
2737 // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
2738 player.sendPacket(ActionFailed.STATIC_PACKET);
2739 }
2740
2741 /**
2742 * Return the Exp Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_XP).<BR><BR>
2743 */
2744 public int getExpReward()
2745 {
2746 double rateXp = getStat().calcStat(Stats.MAX_HP, 1, this, null);
2747 return (int) (getTemplate().rewardExp * rateXp * Config.RATE_XP);
2748 }
2749
2750 /**
2751 * Return the SP Reward of this L2NpcInstance contained in the L2NpcTemplate (modified by RATE_SP).<BR><BR>
2752 */
2753 public int getSpReward()
2754 {
2755 double rateSp = getStat().calcStat(Stats.MAX_HP, 1, this, null);
2756 return (int) (getTemplate().rewardSp * rateSp * Config.RATE_SP);
2757 }
2758
2759 /**
2760 * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).<BR><BR>
2761 *
2762 * <B><U> Actions</U> :</B><BR><BR>
2763 * <li>Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds </li>
2764 * <li>Set target to null and cancel Attack or Cast </li>
2765 * <li>Stop movement </li>
2766 * <li>Stop HP/MP/CP Regeneration task </li>
2767 * <li>Stop all active skills effects in progress on the L2Character </li>
2768 * <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform </li>
2769 * <li>Notify L2Character AI </li><BR><BR>
2770 *
2771 * <B><U> Overridden in </U> :</B><BR><BR>
2772 * <li> L2Attackable </li><BR><BR>
2773 *
2774 * @param killer The L2Character who killed it
2775 *
2776 */
2777 @Override
2778 public boolean doDie(L2Character killer)
2779 {
2780 if (!super.doDie(killer))
2781 return false;
2782
2783 // normally this wouldn't really be needed, but for those few exceptions,
2784 // we do need to reset the weapons back to the initial templated weapon.
2785 _currentLHandId = getTemplate().lhand;
2786 _currentRHandId = getTemplate().rhand;
2787 _currentCollisionHeight = getTemplate().collisionHeight;
2788 _currentCollisionRadius = getTemplate().collisionRadius;
2789 DecayTaskManager.getInstance().addDecayTask(this);
2790 return true;
2791 }
2792
2793 /**
2794 * Set the spawn of the L2NpcInstance.<BR><BR>
2795 *
2796 * @param spawn The L2Spawn that manage the L2NpcInstance
2797 *
2798 */
2799 public void setSpawn(L2Spawn spawn)
2800 {
2801 _spawn = spawn;
2802 }
2803
2804 @Override
2805 public void onSpawn()
2806 {
2807 super.onSpawn();
2808
2809 if (getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN) != null)
2810 for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN))
2811 quest.notifySpawn(this);
2812 }
2813
2814 /**
2815 * Remove the L2NpcInstance from the world and update its spawn object (for a complete removal use the deleteMe method).<BR><BR>
2816 *
2817 * <B><U> Actions</U> :</B><BR><BR>
2818 * <li>Remove the L2NpcInstance from the world when the decay task is launched </li>
2819 * <li>Decrease its spawn counter </li>
2820 * <li>Manage Siege task (killFlag, killCT) </li><BR><BR>
2821 *
2822 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
2823 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR><BR>
2824 *
2825 */
2826 @Override
2827 public void onDecay()
2828 {
2829 if (isDecayed())
2830 return;
2831 setDecayed(true);
2832
2833 // Remove the L2NpcInstance from the world when the decay task is launched
2834 super.onDecay();
2835
2836 // Decrease its spawn counter
2837 if (_spawn != null)
2838 _spawn.decreaseCount(this);
2839 }
2840
2841 /**
2842 * Remove PROPERLY the L2NpcInstance from the world.<BR><BR>
2843 *
2844 * <B><U> Actions</U> :</B><BR><BR>
2845 * <li>Remove the L2NpcInstance from the world and update its spawn object </li>
2846 * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2NpcInstance then cancel Attack or Cast and notify AI </li>
2847 * <li>Remove L2Object object from _allObjects of L2World </li><BR><BR>
2848 *
2849 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR><BR>
2850 *
2851 */
2852 public void deleteMe()
2853 {
2854 L2WorldRegion oldRegion = getWorldRegion();
2855
2856 try
2857 {
2858 decayMe();
2859 }
2860 catch (Exception e)
2861 {
2862 _log.log(Level.SEVERE, "Failed decayMe().", e);
2863 }
2864 try
2865 {
2866 if (_fusionSkill != null)
2867 abortCast();
2868
2869 for (L2Character character : getKnownList().getKnownCharacters())
2870 if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
2871 character.abortCast();
2872 }
2873 catch (Exception e)
2874 {
2875 _log.log(Level.SEVERE, "deleteMe()", e);
2876 }
2877 if (oldRegion != null)
2878 oldRegion.removeFromZones(this);
2879
2880 // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI
2881 try
2882 {
2883 getKnownList().removeAllKnownObjects();
2884 }
2885 catch (Exception e)
2886 {
2887 _log.log(Level.SEVERE, "Failed removing cleaning knownlist.", e);
2888 }
2889
2890 // Remove L2Object object from _allObjects of L2World
2891 L2World.getInstance().removeObject(this);
2892 }
2893
2894 /**
2895 * Return the L2Spawn object that manage this L2NpcInstance.<BR><BR>
2896 */
2897 public L2Spawn getSpawn()
2898 {
2899 return _spawn;
2900 }
2901
2902 @Override
2903 public String toString()
2904 {
2905 return getTemplate().name;
2906 }
2907
2908 public boolean isDecayed()
2909 {
2910 return _isDecayed;
2911 }
2912
2913 public void setDecayed(boolean decayed)
2914 {
2915 _isDecayed = decayed;
2916 }
2917
2918 public void endDecayTask()
2919 {
2920 if (!isDecayed())
2921 {
2922 DecayTaskManager.getInstance().cancelDecayTask(this);
2923 onDecay();
2924 }
2925 }
2926
2927 public boolean isMob() // rather delete this check
2928 {
2929 return false; // This means we use MAX_NPC_ANIMATION instead of MAX_MONSTER_ANIMATION
2930 }
2931
2932 // Two functions to change the appearance of the equipped weapons on the NPC
2933 // This is only useful for a few NPCs and is most likely going to be called from AI
2934 public void setLHandId(int newWeaponId)
2935 {
2936 _currentLHandId = newWeaponId;
2937 updateAbnormalEffect();
2938 }
2939
2940 public void setRHandId(int newWeaponId)
2941 {
2942 _currentRHandId = newWeaponId;
2943 updateAbnormalEffect();
2944 }
2945
2946 public void setLRHandId(int newLWeaponId, int newRWeaponId)
2947 {
2948 _currentRHandId = newRWeaponId;
2949 _currentLHandId = newLWeaponId;
2950 updateAbnormalEffect();
2951 }
2952
2953 public void setEnchant(int newEnchantValue)
2954 {
2955 _currentEnchant = newEnchantValue;
2956 updateAbnormalEffect();
2957 }
2958
2959 public void setCollisionHeight(int height)
2960 {
2961 _currentCollisionHeight = height;
2962 }
2963
2964 public void setCollisionRadius(int radius)
2965 {
2966 _currentCollisionRadius = radius;
2967 }
2968
2969 public int getCollisionHeight()
2970 {
2971 return _currentCollisionHeight;
2972 }
2973
2974 public int getCollisionRadius()
2975 {
2976 return _currentCollisionRadius;
2977 }
2978
2979 @Override
2980 public void sendInfo(L2PcInstance activeChar)
2981 {
2982 if (Config.CHECK_KNOWN)
2983 activeChar.sendMessage("Added NPC: "+getName());
2984
2985 if (getRunSpeed() == 0)
2986 activeChar.sendPacket(new ServerObjectInfo(this, activeChar));
2987 else
2988 activeChar.sendPacket(new AbstractNpcInfo.NpcInfo(this, activeChar));
2989 }
2990
2991 public void showNoTeachHtml(L2PcInstance player)
2992 {
2993 int npcId = getNpcId();
2994 String html = "";
2995
2996 if (this instanceof L2WarehouseInstance)
2997 html = HtmCache.getInstance().getHtm("data/html/warehouse/" + npcId + "-noteach.htm");
2998 else if (this instanceof L2TrainerInstance)
2999 html = HtmCache.getInstance().getHtm("data/html/trainer/" + npcId + "-noteach.htm");
3000
3001 if (html == null)
3002 {
3003 _log.warning("Npc " + npcId + " missing noTeach html!");
3004 NpcHtmlMessage msg = new NpcHtmlMessage(getObjectId());
3005 final String sb = StringUtil.concat(
3006 "<html><body>" +
3007 "I cannot teach you any skills.<br>You must find your current class teachers.",
3008 "</body></html>"
3009 );
3010 msg.setHtml(sb);
3011 player.sendPacket(msg);
3012 return;
3013 }
3014 else
3015 {
3016 NpcHtmlMessage noTeachMsg = new NpcHtmlMessage(getObjectId());
3017 noTeachMsg.setHtml(html);
3018 noTeachMsg.replace("%objectId%", String.valueOf(getObjectId()));
3019 player.sendPacket(noTeachMsg);
3020 }
3021 }
3022
3023 public void scheduleDespawn(long delay)
3024 {
3025 ThreadPoolManager.getInstance().scheduleGeneral(this.new DespawnTask(this), delay);
3026 }
3027
3028 public class DespawnTask implements Runnable
3029 {
3030 L2Npc _npc;
3031
3032 public DespawnTask(L2Npc npc)
3033 {
3034 _npc = npc;
3035 }
3036
3037 @Override
3038 public void run()
3039 {
3040 if (_npc != null)
3041 _npc.deleteMe();
3042 }
3043 }
3044}