Servitor Share effect.

Contributed by Patrioch.
This commit is contained in:
MobiusDev
2017-09-10 01:57:38 +00:00
parent af0d56b56a
commit 9c145fb785
51 changed files with 349 additions and 720 deletions

View File

@@ -16,58 +16,53 @@
*/
package handlers.effecthandlers;
import com.l2jmobius.gameserver.ThreadPoolManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.effects.AbstractEffect;
import com.l2jmobius.gameserver.model.effects.EffectFlag;
import com.l2jmobius.gameserver.model.skills.BuffInfo;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.Stats;
/**
* Servitor Share effect implementation.<br>
* Synchronizing effects on player and servitor if one of them gets removed for some reason the same will happen to another. Partner's effect exit is executed in own thread, since there is no more queue to schedule the effects,<br>
* partner's effect is called while this effect is still exiting issuing an exit call for the effect, causing a stack over flow.
* @author UnAfraid, Zoey76
* Servitor Share effect implementation.
*/
public final class ServitorShare extends AbstractEffect
{
private static final class ScheduledEffectExitTask implements Runnable
{
private final L2Character _effected;
private final int _skillId;
public ScheduledEffectExitTask(L2Character effected, int skillId)
{
_effected = effected;
_skillId = skillId;
}
@Override
public void run()
{
_effected.stopSkillEffects(false, _skillId);
}
}
private final Map<Stats, Float> _sharedStats = new HashMap<>();
public ServitorShare(StatsSet params)
{
}
@Override
public long getEffectFlags()
{
return EffectFlag.SERVITOR_SHARE.getMask();
}
@Override
public void onExit(BuffInfo info)
{
final L2Character effected = info.getEffected().isSummon() ? ((L2Summon) info.getEffected()).getOwner() : info.getEffected();
if (effected != null)
if (params.isEmpty())
{
ThreadPoolManager.schedule(new ScheduledEffectExitTask(effected, info.getSkill().getId()), 100);
return;
}
for (Entry<String, Object> param : params.getSet().entrySet())
{
_sharedStats.put(Stats.valueOf(param.getKey()), (Float.parseFloat((String) param.getValue())) / 100);
}
}
}
@Override
public boolean canPump(L2Character effector, L2Character effected, Skill skill)
{
return effected.isSummon();
}
@Override
public void pump(L2Character effected, Skill skill)
{
final L2PcInstance owner = effected.getActingPlayer();
if (owner != null)
{
for (Entry<Stats, Float> stats : _sharedStats.entrySet())
{
effected.getStat().mergeAdd(stats.getKey(), owner.getStat().getValue(stats.getKey()) * stats.getValue());
}
}
}
}

View File

@@ -3427,14 +3427,10 @@
</effects>
</skill>
<skill id="1557" toLevel="1" name="Servitor Share">
<!-- High Five Skill -->
<!-- Totally Unconfirmed -->
<abnormalLvl>1</abnormalLvl>
<abnormalTime>1200</abnormalTime>
<abnormalType>ABILITY_CHANGE</abnormalType>
<castRange>400</castRange> <!-- No cast range -->
<effectPoint>379</effectPoint>
<effectRange>900</effectRange>
<hitTime>1000</hitTime>
<icon>icon.skill1557</icon>
<isMagic>1</isMagic> <!-- Magic Skill -->
@@ -3448,30 +3444,25 @@
<magicCriticalRate>5</magicCriticalRate>
<specialLevel>-1</specialLevel>
<irreplacableBuff>true</irreplacableBuff>
<!-- Should be self -->
<targetType>SELF</targetType>
<affectScope>SINGLE</affectScope>
<isSharedWithSummon>false</isSharedWithSummon>
<conditions>
<condition name="OpHaveSummon" />
</conditions>
<effects>
<!-- Note: 0.5 means 50% of owner's patk! -->
<effect name="ServitorShare">
<share stat="pAtk" val="0.5" />
<share stat="pDef" val="0.5" />
<share stat="mAtk" val="0.25" />
<share stat="mDef" val="0.25" />
<share stat="maxHp" val="0.1" />
<share stat="maxMp" val="0.1" />
<share stat="rCrit" val="0.2" />
<share stat="pAtkSpd" val="0.1" />
<share stat="mAtkSpd" val="0.03" />
</effect>
<PHYSICAL_ATTACK>50</PHYSICAL_ATTACK>
<PHYSICAL_DEFENCE>50</PHYSICAL_DEFENCE>
<MAGIC_ATTACK>25</MAGIC_ATTACK>
<MAGICAL_DEFENCE>25</MAGICAL_DEFENCE>
<MAX_HP>10</MAX_HP>
<MAX_MP>10</MAX_MP>
<CRITICAL_RATE>20</CRITICAL_RATE>
<PHYSICAL_ATTACK_SPEED>10</PHYSICAL_ATTACK_SPEED>
<MAGIC_ATTACK_SPEED>3</MAGIC_ATTACK_SPEED>
</effect>
</effects>
<!-- Shouldn't have any self effects -->
<selfEffects>
<effect name="ServitorShare" /> <!-- This effect is used to cancel pet's effect when player's have been canceled. -->
</selfEffects>
</skill>
<skill id="1558" toLevel="24" name="Dimension Spiral">
<!-- Glory Days confirmed -->

View File

@@ -2950,16 +2950,30 @@
<condition name="OpHaveSummon" />
</conditions>
<effects>
<!-- Note: 0.5 means 50% of owner's patk! -->
<effect name="ServitorShare">
<share stat="pAtk" val="0.60" />
<share stat="pDef" val="0.50" />
<share stat="mDef" val="0.30" />
<share stat="maxHp" val="0.15" />
<share stat="maxMp" val="0.15" />
<share stat="pAtkSpd" val="0.10" />
<share stat="rCrit" val="0.30" />
<share stat="cAtk" val="0.15" />
<PHYSICAL_ATTACK>
<value level="1">60</value>
<value level="2">65</value>
<value level="3">70</value>
<value level="4">70</value>
</PHYSICAL_ATTACK>
<PHYSICAL_DEFENCE>
<value level="1">50</value>
<value level="2">53</value>
<value level="3">55</value>
<value level="4">60</value>
</PHYSICAL_DEFENCE>
<MAGICAL_DEFENCE>
<value level="1">30</value>
<value level="2">35</value>
<value level="3">40</value>
<value level="4">50</value>
</MAGICAL_DEFENCE>
<MAX_HP>15</MAX_HP>
<MAX_MP>15</MAX_MP>
<PHYSICAL_ATTACK_SPEED>10</PHYSICAL_ATTACK_SPEED>
<CRITICAL_RATE>30</CRITICAL_RATE>
<CRITICAL_DAMAGE>15</CRITICAL_DAMAGE>
</effect>
</effects>
</skill>

View File

@@ -239,7 +239,6 @@ public abstract class DocumentBase
case "mul":
case "div":
case "set":
case "share":
case "enchant":
case "enchanthp":
{

View File

@@ -27,7 +27,6 @@ public enum StatFunction
ENCHANTHP("EnchantHp", 40),
MUL("Mul", 20),
SET("Set", 0),
SHARE("Share", 30),
SUB("Sub", 30);
String name;

View File

@@ -3009,7 +3009,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
{
broadcastPacket(su);
}
if (hasServitors() && isAffected(EffectFlag.SERVITOR_SHARE))
if (hasServitors() && hasAbnormalType(AbnormalType.ABILITY_CHANGE))
{
getServitors().values().forEach(L2Summon::broadcastStatusUpdate);
}

View File

@@ -40,6 +40,7 @@ import com.l2jmobius.gameserver.enums.Position;
import com.l2jmobius.gameserver.model.CharEffectList;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.skills.AbnormalType;
import com.l2jmobius.gameserver.model.skills.BuffInfo;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillConditionScope;
@@ -784,6 +785,19 @@ public class CharStat
.forEach(effect -> effect.pump(info.getEffected(), info.getSkill())));
//@formatter:on
if (_activeChar.isSummon() && (_activeChar.getActingPlayer() != null) && _activeChar.getActingPlayer().hasAbnormalType(AbnormalType.ABILITY_CHANGE))
{
//@formatter:off
_activeChar.getActingPlayer().getEffectList().getEffects().stream()
.filter(BuffInfo::isInUse)
.filter(info -> info.isAbnormalType(AbnormalType.ABILITY_CHANGE))
.forEach(info -> info.getEffects().stream()
.filter(effect -> effect.canStart(info))
.filter(effect -> effect.canPump(_activeChar, _activeChar, info.getSkill()))
.forEach(effect -> effect.pump(_activeChar, info.getSkill())));
//@formatter:on
}
// Merge with additional stats
_additionalAdd.stream().filter(holder -> holder.verifyCondition(_activeChar)).forEach(holder -> mergeAdd(holder.getStat(), holder.getValue()));
_additionalMul.stream().filter(holder -> holder.verifyCondition(_activeChar)).forEach(holder -> mergeMul(holder.getStat(), holder.getValue()));

View File

@@ -31,6 +31,7 @@ import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLevel
import com.l2jmobius.gameserver.model.holders.ItemSkillHolder;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.items.type.WeaponType;
import com.l2jmobius.gameserver.model.skills.AbnormalType;
import com.l2jmobius.gameserver.model.stats.Formulas;
import com.l2jmobius.gameserver.model.stats.Stats;
import com.l2jmobius.gameserver.model.zone.ZoneId;
@@ -667,13 +668,7 @@ public class PcStat extends PlayableStat
super.onRecalculateStats(broadcast);
final L2PcInstance player = getActiveChar();
// Upon master stats recalculation trigger pet/servitor recalculation too
if (player.hasPet())
{
player.getPet().getStat().recalculateStats(broadcast);
}
if (player.hasServitors())
if (player.hasAbnormalType(AbnormalType.ABILITY_CHANGE) && player.hasServitors())
{
player.getServitors().values().forEach(servitor -> servitor.getStat().recalculateStats(broadcast));
}

View File

@@ -43,7 +43,6 @@ public enum EffectFlag
DEBUFF_BLOCK,
ABNORMAL_SHIELD,
BLOCK_RESURRECTION,
SERVITOR_SHARE,
UNTARGETABLE,
CANNOT_ESCAPE,
DOUBLE_CAST,

View File

@@ -451,4 +451,9 @@ public final class BuffInfo
_scheduledFutureTimeTask = ThreadPoolManager.scheduleAtFixedRate(new BuffTimeTask(this), 0, 1000L);
}
}
public boolean isAbnormalType(AbnormalType type)
{
return _skill.getAbnormalType().equals(type);
}
}

View File

@@ -47,7 +47,6 @@ public enum CommonSkill
ONYX_BEAST_TRANSFORMATION(617, 1),
CREATE_COMMON(1320, 1),
DIVINE_INSPIRATION(1405, 1),
SERVITOR_SHARE(1557, 1),
CARAVANS_SECRET_MEDICINE(2341, 1),
SHILENS_BREATH(14571, 1),
IMPRIT_OF_LIGHT(19034, 1),

View File

@@ -1525,7 +1525,7 @@ public final class Skill implements IIdentifiable
*/
public boolean canBeStolen()
{
return !isPassive() && !isToggle() && !isDebuff() && !isHeroSkill() && !isGMSkill() && !(isStatic() && (getId() != CommonSkill.CARAVANS_SECRET_MEDICINE.getId())) && canBeDispelled() && (getId() != CommonSkill.SERVITOR_SHARE.getId());
return !isPassive() && !isToggle() && !isDebuff() && !isIrreplacableBuff() && !isHeroSkill() && !isGMSkill() && !(isStatic() && (getId() != CommonSkill.CARAVANS_SECRET_MEDICINE.getId())) && canBeDispelled();
}
public boolean isClanSkill()

View File

@@ -1,100 +0,0 @@
/*
* 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.stats.functions;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.conditions.Condition;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.stats.Stats;
/**
* @author UnAfraid
*/
public class FuncShare extends AbstractFunction
{
public FuncShare(Stats stat, int order, Object owner, double value, Condition applayCond)
{
super(stat, order, owner, value, applayCond);
}
@Override
public double calc(L2Character effector, L2Character effected, Skill skill, double initVal)
{
if ((getApplayCond() == null) || getApplayCond().test(effector, effected, skill))
{
if ((effector != null) && effector.isServitor())
{
final L2Summon summon = (L2Summon) effector;
final L2PcInstance player = summon.getOwner();
if (player != null)
{
return initVal + (getBaseValue(getStat(), player) * getValue());
}
}
}
return initVal;
}
public static double getBaseValue(Stats stat, L2PcInstance player)
{
switch (stat)
{
case MAX_HP:
{
return player.getMaxHp();
}
case MAX_MP:
{
return player.getMaxMp();
}
case PHYSICAL_ATTACK:
{
return player.getPAtk();
}
case MAGIC_ATTACK:
{
return player.getMAtk();
}
case PHYSICAL_DEFENCE:
{
return player.getPDef();
}
case MAGICAL_DEFENCE:
{
return player.getMDef();
}
case CRITICAL_RATE:
{
return player.getCriticalHit();
}
case PHYSICAL_ATTACK_SPEED:
{
return player.getPAtkSpd();
}
case MAGIC_ATTACK_SPEED:
{
return player.getMAtkSpd();
}
default:
{
return player.getStat().getValue(stat, 0);
}
}
}
}