Support for henna duration and skills.

This commit is contained in:
MobiusDev 2017-10-17 12:59:00 +00:00
parent c777a4609a
commit 9894a46ce7
20 changed files with 712 additions and 128 deletions

View File

@ -26,20 +26,19 @@
</xs:element> </xs:element>
<xs:element name="cancel" minOccurs="1" maxOccurs="1"> <xs:element name="cancel" minOccurs="1" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:attribute name="count" type="xs:positiveInteger" use="required" /> <xs:attribute name="count" type="xs:integer" use="required" />
<xs:attribute name="fee" type="xs:integer" use="required" /> <xs:attribute name="fee" type="xs:integer" use="required" />
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="skills" minOccurs="0"> <xs:element name="duration" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:attribute name="time" type="xs:positiveInteger" use="required" />
<xs:element name="skill"> </xs:complexType>
<xs:complexType> </xs:element>
<xs:attribute type="xs:positiveInteger" name="id" use="required"/> <xs:element name="skill" minOccurs="0" maxOccurs="unbounded">
<xs:attribute type="xs:positiveInteger" name="level" use="required"/> <xs:complexType>
</xs:complexType> <xs:attribute type="xs:positiveInteger" name="id" use="required"/>
</xs:element> <xs:attribute type="xs:positiveInteger" name="level" use="required"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="classId" minOccurs="0" maxOccurs="unbounded"> <xs:element name="classId" minOccurs="0" maxOccurs="unbounded">

View File

@ -31,13 +31,14 @@ import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.L2Henna; import com.l2jmobius.gameserver.model.items.L2Henna;
import com.l2jmobius.gameserver.model.skills.Skill;
/** /**
* This class holds the henna related information.<br> * This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br> * Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br> * Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna. * Allowed classes to wear each henna.
* @author Zoey76 * @author Zoey76, Mobius
*/ */
public final class HennaData implements IGameXmlReader public final class HennaData implements IGameXmlReader
{ {
@ -87,6 +88,7 @@ public final class HennaData implements IGameXmlReader
{ {
final StatsSet set = new StatsSet(); final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>(); final List<ClassId> wearClassIds = new ArrayList<>();
final List<Skill> skills = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes(); NamedNodeMap attrs = d.getAttributes();
Node attr; Node attr;
for (int i = 0; i < attrs.getLength(); i++) for (int i = 0; i < attrs.getLength(); i++)
@ -126,6 +128,17 @@ public final class HennaData implements IGameXmlReader
set.set("cancel_fee", attr.getNodeValue()); set.set("cancel_fee", attr.getNodeValue());
break; break;
} }
case "duration":
{
attr = attrs.getNamedItem("time"); // in minutes
set.set("duration", attr.getNodeValue());
break;
}
case "skill":
{
skills.add(SkillData.getInstance().getSkill(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "classId": case "classId":
{ {
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent()))); wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
@ -134,6 +147,7 @@ public final class HennaData implements IGameXmlReader
} }
} }
final L2Henna henna = new L2Henna(set); final L2Henna henna = new L2Henna(set);
henna.setSkills(skills);
henna.setWearClassIds(wearClassIds); henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna); _hennaList.put(henna.getDyeId(), henna);
} }

View File

@ -164,6 +164,7 @@ import com.l2jmobius.gameserver.model.actor.status.PcStatus;
import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask; import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask; import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask; import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.HennaDurationTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask; import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask;
@ -595,9 +596,10 @@ public final class L2PcInstance extends L2Playable
private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet();
private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet();
// hennas /** Hennas */
private final L2Henna[] _henna = new L2Henna[3]; private final L2Henna[] _henna = new L2Henna[3];
private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>(); private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>();
private final Map<Integer, ScheduledFuture<?>> _hennaRemoveSchedules = new ConcurrentHashMap<>(3);
/** The Pet of the L2PcInstance */ /** The Pet of the L2PcInstance */
private L2PetInstance _pet = null; private L2PetInstance _pet = null;
@ -7752,9 +7754,9 @@ public final class L2PcInstance extends L2Playable
*/ */
private void restoreHenna() private void restoreHenna()
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
_henna[i] = null; _henna[i - 1] = null;
} }
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
@ -7779,7 +7781,28 @@ public final class L2PcInstance extends L2Playable
{ {
continue; continue;
} }
_henna[slot - 1] = HennaData.getInstance().getHenna(symbolId);
final L2Henna henna = HennaData.getInstance().getHenna(symbolId);
// Task for henna duration
if (henna.getDuration() > 0)
{
final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
if (remainingTime < 0)
{
removeHenna(slot);
continue;
}
_hennaRemoveSchedules.put(slot, ThreadPoolManager.schedule(new HennaDurationTask(this, slot), System.currentTimeMillis() + remainingTime));
}
_henna[slot - 1] = henna;
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
} }
} }
} }
@ -7835,27 +7858,25 @@ public final class L2PcInstance extends L2Playable
return false; return false;
} }
slot--; final L2Henna henna = _henna[slot - 1];
final L2Henna henna = _henna[slot];
if (henna == null) if (henna == null)
{ {
return false; return false;
} }
_henna[slot] = null; _henna[slot - 1] = null;
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA)) PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA))
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, slot + 1); statement.setInt(2, slot);
statement.setInt(3, getClassIndex()); statement.setInt(3, getClassIndex());
statement.execute(); statement.execute();
} }
catch (Exception e) catch (Exception e)
{ {
_log.log(Level.SEVERE, "Failed remocing character henna.", e); _log.log(Level.SEVERE, "Failed removing character henna.", e);
} }
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
@ -7868,16 +7889,43 @@ public final class L2PcInstance extends L2Playable
final UserInfo ui = new UserInfo(this, false); final UserInfo ui = new UserInfo(this, false);
ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
sendPacket(ui); sendPacket(ui);
// Add the recovered dyes to the player's inventory and notify them.
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
reduceAdena("Henna", henna.getCancelFee(), this, false);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
sm.addItemName(henna.getDyeItemId()); if ((henna.getDuration() < 0) || (remainingTime > 0))
sm.addLong(henna.getCancelCount()); {
sendPacket(sm); // Add the recovered dyes to the player's inventory and notify them.
if (henna.getCancelFee() > 0)
{
reduceAdena("Henna", henna.getCancelFee(), this, false);
}
if (henna.getCancelCount() > 0)
{
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
sm.addItemName(henna.getDyeItemId());
sm.addLong(henna.getCancelCount());
sendPacket(sm);
}
}
sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED); sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED);
// Remove henna duration task
if (henna.getDuration() > 0)
{
getVariables().remove("HennaDuration" + slot);
if (_hennaRemoveSchedules.get(slot) != null)
{
_hennaRemoveSchedules.get(slot).cancel(false);
_hennaRemoveSchedules.remove(slot);
}
}
// Remove henna skills
for (Skill skill : henna.getSkills())
{
removeSkill(skill, false);
}
// Notify to scripts // Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this);
return true; return true;
@ -7890,11 +7938,11 @@ public final class L2PcInstance extends L2Playable
*/ */
public boolean addHenna(L2Henna henna) public boolean addHenna(L2Henna henna)
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
if (_henna[i] == null) if (_henna[i - 1] == null)
{ {
_henna[i] = henna; _henna[i - 1] = henna;
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
recalcHennaStats(); recalcHennaStats();
@ -7904,7 +7952,7 @@ public final class L2PcInstance extends L2Playable
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, henna.getDyeId()); statement.setInt(2, henna.getDyeId());
statement.setInt(3, i + 1); statement.setInt(3, i);
statement.setInt(4, getClassIndex()); statement.setInt(4, getClassIndex());
statement.execute(); statement.execute();
} }
@ -7913,6 +7961,19 @@ public final class L2PcInstance extends L2Playable
_log.log(Level.SEVERE, "Failed saving character henna.", e); _log.log(Level.SEVERE, "Failed saving character henna.", e);
} }
// Task for henna duration
if (henna.getDuration() > 0)
{
getVariables().set("HennaDuration" + i, System.currentTimeMillis() + (henna.getDuration() * 60000));
_hennaRemoveSchedules.put(i, ThreadPoolManager.schedule(new HennaDurationTask(this, i), System.currentTimeMillis() + (henna.getDuration() * 60000)));
}
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
// Send Server->Client HennaInfo packet to this L2PcInstance // Send Server->Client HennaInfo packet to this L2PcInstance
sendPacket(new HennaInfo(this)); sendPacket(new HennaInfo(this));

View File

@ -0,0 +1,43 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.actor.tasks.player;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author Mobius
*/
public class HennaDurationTask implements Runnable
{
private final L2PcInstance _player;
private final int _slot;
public HennaDurationTask(L2PcInstance player, int slot)
{
_player = player;
_slot = slot;
}
@Override
public void run()
{
if (_player != null)
{
_player.removeHenna(_slot);
}
}
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.BaseStats; import com.l2jmobius.gameserver.model.stats.BaseStats;
import com.l2jmobius.gameserver.model.stats.Stats; import com.l2jmobius.gameserver.model.stats.Stats;
@ -40,6 +41,8 @@ public class L2Henna
private final int _wear_count; private final int _wear_count;
private final int _cancel_fee; private final int _cancel_fee;
private final int _cancel_count; private final int _cancel_count;
private final int _duration;
private final List<Skill> _skills;
private final List<ClassId> _wear_class; private final List<ClassId> _wear_class;
public L2Henna(StatsSet set) public L2Henna(StatsSet set)
@ -59,6 +62,8 @@ public class L2Henna
_wear_count = set.getInt("wear_count"); _wear_count = set.getInt("wear_count");
_cancel_fee = set.getInt("cancel_fee"); _cancel_fee = set.getInt("cancel_fee");
_cancel_count = set.getInt("cancel_count"); _cancel_count = set.getInt("cancel_count");
_duration = set.getInt("duration", -1);
_skills = new ArrayList<>();
_wear_class = new ArrayList<>(); _wear_class = new ArrayList<>();
} }
@ -128,6 +133,30 @@ public class L2Henna
return _cancel_count; return _cancel_count;
} }
/**
* @return the duration of this dye.
*/
public int getDuration()
{
return _duration;
}
/**
* @param skillList the list of skills related to this dye.
*/
public void setSkills(List<Skill> skillList)
{
_skills.addAll(skillList);
}
/**
* @return the skills related to this dye.
*/
public List<Skill> getSkills()
{
return _skills;
}
/** /**
* @return the list with the allowed classes to wear this dye. * @return the list with the allowed classes to wear this dye.
*/ */

View File

@ -26,20 +26,19 @@
</xs:element> </xs:element>
<xs:element name="cancel" minOccurs="1" maxOccurs="1"> <xs:element name="cancel" minOccurs="1" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:attribute name="count" type="xs:positiveInteger" use="required" /> <xs:attribute name="count" type="xs:integer" use="required" />
<xs:attribute name="fee" type="xs:integer" use="required" /> <xs:attribute name="fee" type="xs:integer" use="required" />
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="skills" minOccurs="0"> <xs:element name="duration" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:attribute name="time" type="xs:positiveInteger" use="required" />
<xs:element name="skill"> </xs:complexType>
<xs:complexType> </xs:element>
<xs:attribute type="xs:positiveInteger" name="id" use="required"/> <xs:element name="skill" minOccurs="0" maxOccurs="unbounded">
<xs:attribute type="xs:positiveInteger" name="level" use="required"/> <xs:complexType>
</xs:complexType> <xs:attribute type="xs:positiveInteger" name="id" use="required"/>
</xs:element> <xs:attribute type="xs:positiveInteger" name="level" use="required"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="classId" minOccurs="0" maxOccurs="unbounded"> <xs:element name="classId" minOccurs="0" maxOccurs="unbounded">

View File

@ -31,13 +31,14 @@ import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.L2Henna; import com.l2jmobius.gameserver.model.items.L2Henna;
import com.l2jmobius.gameserver.model.skills.Skill;
/** /**
* This class holds the henna related information.<br> * This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br> * Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br> * Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna. * Allowed classes to wear each henna.
* @author Zoey76 * @author Zoey76, Mobius
*/ */
public final class HennaData implements IGameXmlReader public final class HennaData implements IGameXmlReader
{ {
@ -87,6 +88,7 @@ public final class HennaData implements IGameXmlReader
{ {
final StatsSet set = new StatsSet(); final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>(); final List<ClassId> wearClassIds = new ArrayList<>();
final List<Skill> skills = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes(); NamedNodeMap attrs = d.getAttributes();
Node attr; Node attr;
for (int i = 0; i < attrs.getLength(); i++) for (int i = 0; i < attrs.getLength(); i++)
@ -126,6 +128,17 @@ public final class HennaData implements IGameXmlReader
set.set("cancel_fee", attr.getNodeValue()); set.set("cancel_fee", attr.getNodeValue());
break; break;
} }
case "duration":
{
attr = attrs.getNamedItem("time"); // in minutes
set.set("duration", attr.getNodeValue());
break;
}
case "skill":
{
skills.add(SkillData.getInstance().getSkill(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "classId": case "classId":
{ {
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent()))); wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
@ -134,6 +147,7 @@ public final class HennaData implements IGameXmlReader
} }
} }
final L2Henna henna = new L2Henna(set); final L2Henna henna = new L2Henna(set);
henna.setSkills(skills);
henna.setWearClassIds(wearClassIds); henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna); _hennaList.put(henna.getDyeId(), henna);
} }

View File

@ -163,6 +163,7 @@ import com.l2jmobius.gameserver.model.actor.status.PcStatus;
import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask; import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask; import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask; import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.HennaDurationTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask; import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask;
@ -594,9 +595,10 @@ public final class L2PcInstance extends L2Playable
private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet();
private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet();
// hennas /** Hennas */
private final L2Henna[] _henna = new L2Henna[3]; private final L2Henna[] _henna = new L2Henna[3];
private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>(); private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>();
private final Map<Integer, ScheduledFuture<?>> _hennaRemoveSchedules = new ConcurrentHashMap<>(3);
/** The Pet of the L2PcInstance */ /** The Pet of the L2PcInstance */
private L2PetInstance _pet = null; private L2PetInstance _pet = null;
@ -7752,9 +7754,9 @@ public final class L2PcInstance extends L2Playable
*/ */
private void restoreHenna() private void restoreHenna()
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
_henna[i] = null; _henna[i - 1] = null;
} }
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
@ -7779,7 +7781,28 @@ public final class L2PcInstance extends L2Playable
{ {
continue; continue;
} }
_henna[slot - 1] = HennaData.getInstance().getHenna(symbolId);
final L2Henna henna = HennaData.getInstance().getHenna(symbolId);
// Task for henna duration
if (henna.getDuration() > 0)
{
final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
if (remainingTime < 0)
{
removeHenna(slot);
continue;
}
_hennaRemoveSchedules.put(slot, ThreadPoolManager.schedule(new HennaDurationTask(this, slot), System.currentTimeMillis() + remainingTime));
}
_henna[slot - 1] = henna;
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
} }
} }
} }
@ -7835,27 +7858,25 @@ public final class L2PcInstance extends L2Playable
return false; return false;
} }
slot--; final L2Henna henna = _henna[slot - 1];
final L2Henna henna = _henna[slot];
if (henna == null) if (henna == null)
{ {
return false; return false;
} }
_henna[slot] = null; _henna[slot - 1] = null;
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA)) PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA))
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, slot + 1); statement.setInt(2, slot);
statement.setInt(3, getClassIndex()); statement.setInt(3, getClassIndex());
statement.execute(); statement.execute();
} }
catch (Exception e) catch (Exception e)
{ {
_log.log(Level.SEVERE, "Failed remocing character henna.", e); _log.log(Level.SEVERE, "Failed removing character henna.", e);
} }
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
@ -7868,16 +7889,43 @@ public final class L2PcInstance extends L2Playable
final UserInfo ui = new UserInfo(this, false); final UserInfo ui = new UserInfo(this, false);
ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
sendPacket(ui); sendPacket(ui);
// Add the recovered dyes to the player's inventory and notify them.
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
reduceAdena("Henna", henna.getCancelFee(), this, false);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
sm.addItemName(henna.getDyeItemId()); if ((henna.getDuration() < 0) || (remainingTime > 0))
sm.addLong(henna.getCancelCount()); {
sendPacket(sm); // Add the recovered dyes to the player's inventory and notify them.
if (henna.getCancelFee() > 0)
{
reduceAdena("Henna", henna.getCancelFee(), this, false);
}
if (henna.getCancelCount() > 0)
{
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
sm.addItemName(henna.getDyeItemId());
sm.addLong(henna.getCancelCount());
sendPacket(sm);
}
}
sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED); sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED);
// Remove henna duration task
if (henna.getDuration() > 0)
{
getVariables().remove("HennaDuration" + slot);
if (_hennaRemoveSchedules.get(slot) != null)
{
_hennaRemoveSchedules.get(slot).cancel(false);
_hennaRemoveSchedules.remove(slot);
}
}
// Remove henna skills
for (Skill skill : henna.getSkills())
{
removeSkill(skill, false);
}
// Notify to scripts // Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this);
return true; return true;
@ -7890,11 +7938,11 @@ public final class L2PcInstance extends L2Playable
*/ */
public boolean addHenna(L2Henna henna) public boolean addHenna(L2Henna henna)
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
if (_henna[i] == null) if (_henna[i - 1] == null)
{ {
_henna[i] = henna; _henna[i - 1] = henna;
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
recalcHennaStats(); recalcHennaStats();
@ -7904,7 +7952,7 @@ public final class L2PcInstance extends L2Playable
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, henna.getDyeId()); statement.setInt(2, henna.getDyeId());
statement.setInt(3, i + 1); statement.setInt(3, i);
statement.setInt(4, getClassIndex()); statement.setInt(4, getClassIndex());
statement.execute(); statement.execute();
} }
@ -7913,6 +7961,19 @@ public final class L2PcInstance extends L2Playable
_log.log(Level.SEVERE, "Failed saving character henna.", e); _log.log(Level.SEVERE, "Failed saving character henna.", e);
} }
// Task for henna duration
if (henna.getDuration() > 0)
{
getVariables().set("HennaDuration" + i, System.currentTimeMillis() + (henna.getDuration() * 60000));
_hennaRemoveSchedules.put(i, ThreadPoolManager.schedule(new HennaDurationTask(this, i), System.currentTimeMillis() + (henna.getDuration() * 60000)));
}
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
// Send Server->Client HennaInfo packet to this L2PcInstance // Send Server->Client HennaInfo packet to this L2PcInstance
sendPacket(new HennaInfo(this)); sendPacket(new HennaInfo(this));

View File

@ -0,0 +1,43 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.actor.tasks.player;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author Mobius
*/
public class HennaDurationTask implements Runnable
{
private final L2PcInstance _player;
private final int _slot;
public HennaDurationTask(L2PcInstance player, int slot)
{
_player = player;
_slot = slot;
}
@Override
public void run()
{
if (_player != null)
{
_player.removeHenna(_slot);
}
}
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.BaseStats; import com.l2jmobius.gameserver.model.stats.BaseStats;
import com.l2jmobius.gameserver.model.stats.Stats; import com.l2jmobius.gameserver.model.stats.Stats;
@ -40,6 +41,8 @@ public class L2Henna
private final int _wear_count; private final int _wear_count;
private final int _cancel_fee; private final int _cancel_fee;
private final int _cancel_count; private final int _cancel_count;
private final int _duration;
private final List<Skill> _skills;
private final List<ClassId> _wear_class; private final List<ClassId> _wear_class;
public L2Henna(StatsSet set) public L2Henna(StatsSet set)
@ -59,6 +62,8 @@ public class L2Henna
_wear_count = set.getInt("wear_count"); _wear_count = set.getInt("wear_count");
_cancel_fee = set.getInt("cancel_fee"); _cancel_fee = set.getInt("cancel_fee");
_cancel_count = set.getInt("cancel_count"); _cancel_count = set.getInt("cancel_count");
_duration = set.getInt("duration", -1);
_skills = new ArrayList<>();
_wear_class = new ArrayList<>(); _wear_class = new ArrayList<>();
} }
@ -128,6 +133,30 @@ public class L2Henna
return _cancel_count; return _cancel_count;
} }
/**
* @return the duration of this dye.
*/
public int getDuration()
{
return _duration;
}
/**
* @param skillList the list of skills related to this dye.
*/
public void setSkills(List<Skill> skillList)
{
_skills.addAll(skillList);
}
/**
* @return the skills related to this dye.
*/
public List<Skill> getSkills()
{
return _skills;
}
/** /**
* @return the list with the allowed classes to wear this dye. * @return the list with the allowed classes to wear this dye.
*/ */

View File

@ -26,20 +26,19 @@
</xs:element> </xs:element>
<xs:element name="cancel" minOccurs="1" maxOccurs="1"> <xs:element name="cancel" minOccurs="1" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:attribute name="count" type="xs:positiveInteger" use="required" /> <xs:attribute name="count" type="xs:integer" use="required" />
<xs:attribute name="fee" type="xs:integer" use="required" /> <xs:attribute name="fee" type="xs:integer" use="required" />
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="skills" minOccurs="0"> <xs:element name="duration" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:attribute name="time" type="xs:positiveInteger" use="required" />
<xs:element name="skill"> </xs:complexType>
<xs:complexType> </xs:element>
<xs:attribute type="xs:positiveInteger" name="id" use="required"/> <xs:element name="skill" minOccurs="0" maxOccurs="unbounded">
<xs:attribute type="xs:positiveInteger" name="level" use="required"/> <xs:complexType>
</xs:complexType> <xs:attribute type="xs:positiveInteger" name="id" use="required"/>
</xs:element> <xs:attribute type="xs:positiveInteger" name="level" use="required"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="classId" minOccurs="0" maxOccurs="unbounded"> <xs:element name="classId" minOccurs="0" maxOccurs="unbounded">

View File

@ -31,13 +31,14 @@ import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.L2Henna; import com.l2jmobius.gameserver.model.items.L2Henna;
import com.l2jmobius.gameserver.model.skills.Skill;
/** /**
* This class holds the henna related information.<br> * This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br> * Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br> * Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna. * Allowed classes to wear each henna.
* @author Zoey76 * @author Zoey76, Mobius
*/ */
public final class HennaData implements IGameXmlReader public final class HennaData implements IGameXmlReader
{ {
@ -87,6 +88,7 @@ public final class HennaData implements IGameXmlReader
{ {
final StatsSet set = new StatsSet(); final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>(); final List<ClassId> wearClassIds = new ArrayList<>();
final List<Skill> skills = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes(); NamedNodeMap attrs = d.getAttributes();
Node attr; Node attr;
for (int i = 0; i < attrs.getLength(); i++) for (int i = 0; i < attrs.getLength(); i++)
@ -126,6 +128,17 @@ public final class HennaData implements IGameXmlReader
set.set("cancel_fee", attr.getNodeValue()); set.set("cancel_fee", attr.getNodeValue());
break; break;
} }
case "duration":
{
attr = attrs.getNamedItem("time"); // in minutes
set.set("duration", attr.getNodeValue());
break;
}
case "skill":
{
skills.add(SkillData.getInstance().getSkill(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "classId": case "classId":
{ {
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent()))); wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
@ -134,6 +147,7 @@ public final class HennaData implements IGameXmlReader
} }
} }
final L2Henna henna = new L2Henna(set); final L2Henna henna = new L2Henna(set);
henna.setSkills(skills);
henna.setWearClassIds(wearClassIds); henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna); _hennaList.put(henna.getDyeId(), henna);
} }

View File

@ -164,6 +164,7 @@ import com.l2jmobius.gameserver.model.actor.status.PcStatus;
import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask; import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask; import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask; import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.HennaDurationTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask; import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask;
@ -596,9 +597,10 @@ public final class L2PcInstance extends L2Playable
private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet();
private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet();
// hennas /** Hennas */
private final L2Henna[] _henna = new L2Henna[3]; private final L2Henna[] _henna = new L2Henna[3];
private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>(); private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>();
private final Map<Integer, ScheduledFuture<?>> _hennaRemoveSchedules = new ConcurrentHashMap<>(3);
/** The Pet of the L2PcInstance */ /** The Pet of the L2PcInstance */
private L2PetInstance _pet = null; private L2PetInstance _pet = null;
@ -7754,9 +7756,9 @@ public final class L2PcInstance extends L2Playable
*/ */
private void restoreHenna() private void restoreHenna()
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
_henna[i] = null; _henna[i - 1] = null;
} }
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
@ -7781,7 +7783,28 @@ public final class L2PcInstance extends L2Playable
{ {
continue; continue;
} }
_henna[slot - 1] = HennaData.getInstance().getHenna(symbolId);
final L2Henna henna = HennaData.getInstance().getHenna(symbolId);
// Task for henna duration
if (henna.getDuration() > 0)
{
final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
if (remainingTime < 0)
{
removeHenna(slot);
continue;
}
_hennaRemoveSchedules.put(slot, ThreadPoolManager.schedule(new HennaDurationTask(this, slot), System.currentTimeMillis() + remainingTime));
}
_henna[slot - 1] = henna;
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
} }
} }
} }
@ -7837,27 +7860,25 @@ public final class L2PcInstance extends L2Playable
return false; return false;
} }
slot--; final L2Henna henna = _henna[slot - 1];
final L2Henna henna = _henna[slot];
if (henna == null) if (henna == null)
{ {
return false; return false;
} }
_henna[slot] = null; _henna[slot - 1] = null;
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA)) PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA))
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, slot + 1); statement.setInt(2, slot);
statement.setInt(3, getClassIndex()); statement.setInt(3, getClassIndex());
statement.execute(); statement.execute();
} }
catch (Exception e) catch (Exception e)
{ {
_log.log(Level.SEVERE, "Failed remocing character henna.", e); _log.log(Level.SEVERE, "Failed removing character henna.", e);
} }
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
@ -7870,16 +7891,43 @@ public final class L2PcInstance extends L2Playable
final UserInfo ui = new UserInfo(this, false); final UserInfo ui = new UserInfo(this, false);
ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
sendPacket(ui); sendPacket(ui);
// Add the recovered dyes to the player's inventory and notify them.
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
reduceAdena("Henna", henna.getCancelFee(), this, false);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
sm.addItemName(henna.getDyeItemId()); if ((henna.getDuration() < 0) || (remainingTime > 0))
sm.addLong(henna.getCancelCount()); {
sendPacket(sm); // Add the recovered dyes to the player's inventory and notify them.
if (henna.getCancelFee() > 0)
{
reduceAdena("Henna", henna.getCancelFee(), this, false);
}
if (henna.getCancelCount() > 0)
{
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
sm.addItemName(henna.getDyeItemId());
sm.addLong(henna.getCancelCount());
sendPacket(sm);
}
}
sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED); sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED);
// Remove henna duration task
if (henna.getDuration() > 0)
{
getVariables().remove("HennaDuration" + slot);
if (_hennaRemoveSchedules.get(slot) != null)
{
_hennaRemoveSchedules.get(slot).cancel(false);
_hennaRemoveSchedules.remove(slot);
}
}
// Remove henna skills
for (Skill skill : henna.getSkills())
{
removeSkill(skill, false);
}
// Notify to scripts // Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this);
return true; return true;
@ -7892,11 +7940,11 @@ public final class L2PcInstance extends L2Playable
*/ */
public boolean addHenna(L2Henna henna) public boolean addHenna(L2Henna henna)
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
if (_henna[i] == null) if (_henna[i - 1] == null)
{ {
_henna[i] = henna; _henna[i - 1] = henna;
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
recalcHennaStats(); recalcHennaStats();
@ -7906,7 +7954,7 @@ public final class L2PcInstance extends L2Playable
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, henna.getDyeId()); statement.setInt(2, henna.getDyeId());
statement.setInt(3, i + 1); statement.setInt(3, i);
statement.setInt(4, getClassIndex()); statement.setInt(4, getClassIndex());
statement.execute(); statement.execute();
} }
@ -7915,6 +7963,19 @@ public final class L2PcInstance extends L2Playable
_log.log(Level.SEVERE, "Failed saving character henna.", e); _log.log(Level.SEVERE, "Failed saving character henna.", e);
} }
// Task for henna duration
if (henna.getDuration() > 0)
{
getVariables().set("HennaDuration" + i, System.currentTimeMillis() + (henna.getDuration() * 60000));
_hennaRemoveSchedules.put(i, ThreadPoolManager.schedule(new HennaDurationTask(this, i), System.currentTimeMillis() + (henna.getDuration() * 60000)));
}
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
// Send Server->Client HennaInfo packet to this L2PcInstance // Send Server->Client HennaInfo packet to this L2PcInstance
sendPacket(new HennaInfo(this)); sendPacket(new HennaInfo(this));

View File

@ -0,0 +1,43 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.actor.tasks.player;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author Mobius
*/
public class HennaDurationTask implements Runnable
{
private final L2PcInstance _player;
private final int _slot;
public HennaDurationTask(L2PcInstance player, int slot)
{
_player = player;
_slot = slot;
}
@Override
public void run()
{
if (_player != null)
{
_player.removeHenna(_slot);
}
}
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.BaseStats; import com.l2jmobius.gameserver.model.stats.BaseStats;
import com.l2jmobius.gameserver.model.stats.Stats; import com.l2jmobius.gameserver.model.stats.Stats;
@ -40,6 +41,8 @@ public class L2Henna
private final int _wear_count; private final int _wear_count;
private final int _cancel_fee; private final int _cancel_fee;
private final int _cancel_count; private final int _cancel_count;
private final int _duration;
private final List<Skill> _skills;
private final List<ClassId> _wear_class; private final List<ClassId> _wear_class;
public L2Henna(StatsSet set) public L2Henna(StatsSet set)
@ -59,6 +62,8 @@ public class L2Henna
_wear_count = set.getInt("wear_count"); _wear_count = set.getInt("wear_count");
_cancel_fee = set.getInt("cancel_fee"); _cancel_fee = set.getInt("cancel_fee");
_cancel_count = set.getInt("cancel_count"); _cancel_count = set.getInt("cancel_count");
_duration = set.getInt("duration", -1);
_skills = new ArrayList<>();
_wear_class = new ArrayList<>(); _wear_class = new ArrayList<>();
} }
@ -128,6 +133,30 @@ public class L2Henna
return _cancel_count; return _cancel_count;
} }
/**
* @return the duration of this dye.
*/
public int getDuration()
{
return _duration;
}
/**
* @param skillList the list of skills related to this dye.
*/
public void setSkills(List<Skill> skillList)
{
_skills.addAll(skillList);
}
/**
* @return the skills related to this dye.
*/
public List<Skill> getSkills()
{
return _skills;
}
/** /**
* @return the list with the allowed classes to wear this dye. * @return the list with the allowed classes to wear this dye.
*/ */

View File

@ -26,20 +26,19 @@
</xs:element> </xs:element>
<xs:element name="cancel" minOccurs="1" maxOccurs="1"> <xs:element name="cancel" minOccurs="1" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:attribute name="count" type="xs:positiveInteger" use="required" /> <xs:attribute name="count" type="xs:integer" use="required" />
<xs:attribute name="fee" type="xs:integer" use="required" /> <xs:attribute name="fee" type="xs:integer" use="required" />
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="skills" minOccurs="0"> <xs:element name="duration" minOccurs="0" maxOccurs="1">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:attribute name="time" type="xs:positiveInteger" use="required" />
<xs:element name="skill"> </xs:complexType>
<xs:complexType> </xs:element>
<xs:attribute type="xs:positiveInteger" name="id" use="required"/> <xs:element name="skill" minOccurs="0" maxOccurs="unbounded">
<xs:attribute type="xs:positiveInteger" name="level" use="required"/> <xs:complexType>
</xs:complexType> <xs:attribute type="xs:positiveInteger" name="id" use="required"/>
</xs:element> <xs:attribute type="xs:positiveInteger" name="level" use="required"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="classId" minOccurs="0" maxOccurs="unbounded"> <xs:element name="classId" minOccurs="0" maxOccurs="unbounded">

View File

@ -31,13 +31,14 @@ import com.l2jmobius.commons.util.IGameXmlReader;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.items.L2Henna; import com.l2jmobius.gameserver.model.items.L2Henna;
import com.l2jmobius.gameserver.model.skills.Skill;
/** /**
* This class holds the henna related information.<br> * This class holds the henna related information.<br>
* Cost and required amount to add the henna to the player.<br> * Cost and required amount to add the henna to the player.<br>
* Cost and retrieved amount for removing the henna from the player.<br> * Cost and retrieved amount for removing the henna from the player.<br>
* Allowed classes to wear each henna. * Allowed classes to wear each henna.
* @author Zoey76 * @author Zoey76, Mobius
*/ */
public final class HennaData implements IGameXmlReader public final class HennaData implements IGameXmlReader
{ {
@ -87,6 +88,7 @@ public final class HennaData implements IGameXmlReader
{ {
final StatsSet set = new StatsSet(); final StatsSet set = new StatsSet();
final List<ClassId> wearClassIds = new ArrayList<>(); final List<ClassId> wearClassIds = new ArrayList<>();
final List<Skill> skills = new ArrayList<>();
NamedNodeMap attrs = d.getAttributes(); NamedNodeMap attrs = d.getAttributes();
Node attr; Node attr;
for (int i = 0; i < attrs.getLength(); i++) for (int i = 0; i < attrs.getLength(); i++)
@ -126,6 +128,17 @@ public final class HennaData implements IGameXmlReader
set.set("cancel_fee", attr.getNodeValue()); set.set("cancel_fee", attr.getNodeValue());
break; break;
} }
case "duration":
{
attr = attrs.getNamedItem("time"); // in minutes
set.set("duration", attr.getNodeValue());
break;
}
case "skill":
{
skills.add(SkillData.getInstance().getSkill(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "classId": case "classId":
{ {
wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent()))); wearClassIds.add(ClassId.getClassId(Integer.parseInt(c.getTextContent())));
@ -134,6 +147,7 @@ public final class HennaData implements IGameXmlReader
} }
} }
final L2Henna henna = new L2Henna(set); final L2Henna henna = new L2Henna(set);
henna.setSkills(skills);
henna.setWearClassIds(wearClassIds); henna.setWearClassIds(wearClassIds);
_hennaList.put(henna.getDyeId(), henna); _hennaList.put(henna.getDyeId(), henna);
} }

View File

@ -163,6 +163,7 @@ import com.l2jmobius.gameserver.model.actor.status.PcStatus;
import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask; import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask; import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask; import com.l2jmobius.gameserver.model.actor.tasks.player.GameGuardCheckTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.HennaDurationTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask; import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask;
import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask; import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask;
@ -593,9 +594,10 @@ public final class L2PcInstance extends L2Playable
private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet();
private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet(); private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet();
// hennas /** Hennas */
private final L2Henna[] _henna = new L2Henna[3]; private final L2Henna[] _henna = new L2Henna[3];
private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>(); private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>();
private final Map<Integer, ScheduledFuture<?>> _hennaRemoveSchedules = new ConcurrentHashMap<>(3);
/** The Pet of the L2PcInstance */ /** The Pet of the L2PcInstance */
private L2PetInstance _pet = null; private L2PetInstance _pet = null;
@ -7724,9 +7726,9 @@ public final class L2PcInstance extends L2Playable
*/ */
private void restoreHenna() private void restoreHenna()
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
_henna[i] = null; _henna[i - 1] = null;
} }
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
@ -7751,7 +7753,28 @@ public final class L2PcInstance extends L2Playable
{ {
continue; continue;
} }
_henna[slot - 1] = HennaData.getInstance().getHenna(symbolId);
final L2Henna henna = HennaData.getInstance().getHenna(symbolId);
// Task for henna duration
if (henna.getDuration() > 0)
{
final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
if (remainingTime < 0)
{
removeHenna(slot);
continue;
}
_hennaRemoveSchedules.put(slot, ThreadPoolManager.schedule(new HennaDurationTask(this, slot), System.currentTimeMillis() + remainingTime));
}
_henna[slot - 1] = henna;
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
} }
} }
} }
@ -7807,27 +7830,25 @@ public final class L2PcInstance extends L2Playable
return false; return false;
} }
slot--; final L2Henna henna = _henna[slot - 1];
final L2Henna henna = _henna[slot];
if (henna == null) if (henna == null)
{ {
return false; return false;
} }
_henna[slot] = null; _henna[slot - 1] = null;
try (Connection con = DatabaseFactory.getInstance().getConnection(); try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA)) PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA))
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, slot + 1); statement.setInt(2, slot);
statement.setInt(3, getClassIndex()); statement.setInt(3, getClassIndex());
statement.execute(); statement.execute();
} }
catch (Exception e) catch (Exception e)
{ {
_log.log(Level.SEVERE, "Failed remocing character henna.", e); _log.log(Level.SEVERE, "Failed removing character henna.", e);
} }
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
@ -7840,16 +7861,43 @@ public final class L2PcInstance extends L2Playable
final UserInfo ui = new UserInfo(this, false); final UserInfo ui = new UserInfo(this, false);
ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
sendPacket(ui); sendPacket(ui);
// Add the recovered dyes to the player's inventory and notify them.
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
reduceAdena("Henna", henna.getCancelFee(), this, false);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); final long remainingTime = getVariables().getLong("HennaDuration" + slot, 0) - System.currentTimeMillis();
sm.addItemName(henna.getDyeItemId()); if ((henna.getDuration() < 0) || (remainingTime > 0))
sm.addLong(henna.getCancelCount()); {
sendPacket(sm); // Add the recovered dyes to the player's inventory and notify them.
if (henna.getCancelFee() > 0)
{
reduceAdena("Henna", henna.getCancelFee(), this, false);
}
if (henna.getCancelCount() > 0)
{
getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
sm.addItemName(henna.getDyeItemId());
sm.addLong(henna.getCancelCount());
sendPacket(sm);
}
}
sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED); sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED);
// Remove henna duration task
if (henna.getDuration() > 0)
{
getVariables().remove("HennaDuration" + slot);
if (_hennaRemoveSchedules.get(slot) != null)
{
_hennaRemoveSchedules.get(slot).cancel(false);
_hennaRemoveSchedules.remove(slot);
}
}
// Remove henna skills
for (Skill skill : henna.getSkills())
{
removeSkill(skill, false);
}
// Notify to scripts // Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this);
return true; return true;
@ -7862,11 +7910,11 @@ public final class L2PcInstance extends L2Playable
*/ */
public boolean addHenna(L2Henna henna) public boolean addHenna(L2Henna henna)
{ {
for (int i = 0; i < 3; i++) for (int i = 1; i < 4; i++)
{ {
if (_henna[i] == null) if (_henna[i - 1] == null)
{ {
_henna[i] = henna; _henna[i - 1] = henna;
// Calculate Henna modifiers of this L2PcInstance // Calculate Henna modifiers of this L2PcInstance
recalcHennaStats(); recalcHennaStats();
@ -7876,7 +7924,7 @@ public final class L2PcInstance extends L2Playable
{ {
statement.setInt(1, getObjectId()); statement.setInt(1, getObjectId());
statement.setInt(2, henna.getDyeId()); statement.setInt(2, henna.getDyeId());
statement.setInt(3, i + 1); statement.setInt(3, i);
statement.setInt(4, getClassIndex()); statement.setInt(4, getClassIndex());
statement.execute(); statement.execute();
} }
@ -7885,6 +7933,19 @@ public final class L2PcInstance extends L2Playable
_log.log(Level.SEVERE, "Failed saving character henna.", e); _log.log(Level.SEVERE, "Failed saving character henna.", e);
} }
// Task for henna duration
if (henna.getDuration() > 0)
{
getVariables().set("HennaDuration" + i, System.currentTimeMillis() + (henna.getDuration() * 60000));
_hennaRemoveSchedules.put(i, ThreadPoolManager.schedule(new HennaDurationTask(this, i), System.currentTimeMillis() + (henna.getDuration() * 60000)));
}
// Reward henna skills
for (Skill skill : henna.getSkills())
{
addSkill(skill, false);
}
// Send Server->Client HennaInfo packet to this L2PcInstance // Send Server->Client HennaInfo packet to this L2PcInstance
sendPacket(new HennaInfo(this)); sendPacket(new HennaInfo(this));

View File

@ -0,0 +1,43 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.model.actor.tasks.player;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author Mobius
*/
public class HennaDurationTask implements Runnable
{
private final L2PcInstance _player;
private final int _slot;
public HennaDurationTask(L2PcInstance player, int slot)
{
_player = player;
_slot = slot;
}
@Override
public void run()
{
if (_player != null)
{
_player.removeHenna(_slot);
}
}
}

View File

@ -23,6 +23,7 @@ import java.util.Map;
import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.base.ClassId; import com.l2jmobius.gameserver.model.base.ClassId;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.BaseStats; import com.l2jmobius.gameserver.model.stats.BaseStats;
import com.l2jmobius.gameserver.model.stats.Stats; import com.l2jmobius.gameserver.model.stats.Stats;
@ -40,6 +41,8 @@ public class L2Henna
private final int _wear_count; private final int _wear_count;
private final int _cancel_fee; private final int _cancel_fee;
private final int _cancel_count; private final int _cancel_count;
private final int _duration;
private final List<Skill> _skills;
private final List<ClassId> _wear_class; private final List<ClassId> _wear_class;
public L2Henna(StatsSet set) public L2Henna(StatsSet set)
@ -59,6 +62,8 @@ public class L2Henna
_wear_count = set.getInt("wear_count"); _wear_count = set.getInt("wear_count");
_cancel_fee = set.getInt("cancel_fee"); _cancel_fee = set.getInt("cancel_fee");
_cancel_count = set.getInt("cancel_count"); _cancel_count = set.getInt("cancel_count");
_duration = set.getInt("duration", -1);
_skills = new ArrayList<>();
_wear_class = new ArrayList<>(); _wear_class = new ArrayList<>();
} }
@ -128,6 +133,30 @@ public class L2Henna
return _cancel_count; return _cancel_count;
} }
/**
* @return the duration of this dye.
*/
public int getDuration()
{
return _duration;
}
/**
* @param skillList the list of skills related to this dye.
*/
public void setSkills(List<Skill> skillList)
{
_skills.addAll(skillList);
}
/**
* @return the skills related to this dye.
*/
public List<Skill> getSkills()
{
return _skills;
}
/** /**
* @return the list with the allowed classes to wear this dye. * @return the list with the allowed classes to wear this dye.
*/ */