/** * License * THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS * CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). * THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. * ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR * COPYRIGHT LAW IS PROHIBITED. * * BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND * AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE * MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED * HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. * */ package l1j.server.server.model; import static l1j.server.server.model.skill.L1SkillId.ABSOLUTE_BARRIER; import static l1j.server.server.model.skill.L1SkillId.BERSERKERS; import static l1j.server.server.model.skill.L1SkillId.COUNTER_MAGIC; import static l1j.server.server.model.skill.L1SkillId.EARTH_BIND; import static l1j.server.server.model.skill.L1SkillId.FREEZING_BLIZZARD; import static l1j.server.server.model.skill.L1SkillId.FREEZING_BREATH; import static l1j.server.server.model.skill.L1SkillId.ICE_LANCE; import static l1j.server.server.model.skill.L1SkillId.ILLUSION_AVATAR; import static l1j.server.server.model.skill.L1SkillId.STATUS_FREEZE; import l1j.server.server.ActionCodes; import l1j.server.server.WarTimeController; import l1j.server.server.datatables.SkillsTable; import l1j.server.server.datatables.WeaponSkillTable; import l1j.server.server.model.Instance.L1ItemInstance; import l1j.server.server.model.Instance.L1MonsterInstance; import l1j.server.server.model.Instance.L1NpcInstance; import l1j.server.server.model.Instance.L1PcInstance; import l1j.server.server.model.Instance.L1PetInstance; import l1j.server.server.model.Instance.L1SummonInstance; import l1j.server.server.model.skill.L1SkillUse; import l1j.server.server.serverpackets.S_DoActionGFX; import l1j.server.server.serverpackets.S_EffectLocation; import l1j.server.server.serverpackets.S_Paralysis; import l1j.server.server.serverpackets.S_ServerMessage; import l1j.server.server.serverpackets.S_SkillSound; import l1j.server.server.serverpackets.S_UseAttackSkill; import l1j.server.server.templates.L1Skills; import l1j.server.server.utils.Random; // Referenced classes of package l1j.server.server.model: // L1PcInstance public class L1WeaponSkill { private int _weaponId; private int _probability; private int _fixDamage; private int _randomDamage; private int _area; private int _skillId; private int _skillTime; private int _effectId; private int _effectTarget; // エフェクトの対象 0:相手 1:自分 private boolean _isArrowType; private int _attr; public L1WeaponSkill(int weaponId, int probability, int fixDamage, int randomDamage, int area, int skillId, int skillTime, int effectId, int effectTarget, boolean isArrowType, int attr) { _weaponId = weaponId; _probability = probability; _fixDamage = fixDamage; _randomDamage = randomDamage; _area = area; _skillId = skillId; _skillTime = skillTime; _effectId = effectId; _effectTarget = effectTarget; _isArrowType = isArrowType; _attr = attr; } public int getWeaponId() { return _weaponId; } public int getProbability() { return _probability; } public int getFixDamage() { return _fixDamage; } public int getRandomDamage() { return _randomDamage; } public int getArea() { return _area; } public int getSkillId() { return _skillId; } public int getSkillTime() { return _skillTime; } public int getEffectId() { return _effectId; } public int getEffectTarget() { return _effectTarget; } public boolean isArrowType() { return _isArrowType; } public int getAttr() { return _attr; } public static double getWeaponSkillDamage(L1PcInstance pc, L1Character cha, int weaponId) { L1WeaponSkill weaponSkill = WeaponSkillTable.getInstance().getTemplate( weaponId); if ((pc == null) || (cha == null) || (weaponSkill == null)) { return 0; } int chance = Random.nextInt(100) + 1; if (weaponSkill.getProbability() < chance) { return 0; } int skillId = weaponSkill.getSkillId(); if (skillId != 0) { L1Skills skill = SkillsTable.getInstance().getTemplate(skillId); if ((skill != null) && skill.getTarget().equals("buff")) { if (!isFreeze(cha)) { // 凍結状態orカウンターマジック中 cha.setSkillEffect(skillId, weaponSkill.getSkillTime() * 1000); } } } int effectId = weaponSkill.getEffectId(); if (effectId != 0) { int chaId = 0; if (weaponSkill.getEffectTarget() == 0) { chaId = cha.getId(); } else { chaId = pc.getId(); } boolean isArrowType = weaponSkill.isArrowType(); if (!isArrowType) { pc.sendPackets(new S_SkillSound(chaId, effectId)); pc.broadcastPacket(new S_SkillSound(chaId, effectId)); } else { int[] data = {ActionCodes.ACTION_Attack, 0, effectId, 6}; S_UseAttackSkill packet = new S_UseAttackSkill(pc, cha.getId(), cha.getX(), cha.getY(), data, false); pc.sendPackets(packet); pc.broadcastPacket(packet); } } double damage = 0; int randomDamage = weaponSkill.getRandomDamage(); if (randomDamage != 0) { damage = Random.nextInt(randomDamage); } damage += weaponSkill.getFixDamage(); int area = weaponSkill.getArea(); if ((area > 0) || (area == -1)) { // 範囲の場合 for (L1Object object : L1World.getInstance().getVisibleObjects(cha, area)) { if (object == null) { continue; } if (!(object instanceof L1Character)) { continue; } if (object.getId() == pc.getId()) { continue; } if (object.getId() == cha.getId()) { // 攻撃対象はL1Attackで処理するため除外 continue; } // 攻撃対象がMOBの場合は、範囲内のMOBにのみ当たる // 攻撃対象がPC,Summon,Petの場合は、範囲内のPC,Summon,Pet,MOBに当たる if (cha instanceof L1MonsterInstance) { if (!(object instanceof L1MonsterInstance)) { continue; } } if ((cha instanceof L1PcInstance) || (cha instanceof L1SummonInstance) || (cha instanceof L1PetInstance)) { if (!((object instanceof L1PcInstance) || (object instanceof L1SummonInstance) || (object instanceof L1PetInstance) || (object instanceof L1MonsterInstance))) { continue; } } // 判斷是否在攻城戰中 boolean isNowWar = false; int castleId = L1CastleLocation.getCastleIdByArea((L1Character)object); if (castleId > 0) { isNowWar = WarTimeController.getInstance().isNowWar(castleId); } if (!isNowWar) { // 非攻城戰區域 // 對象不是怪物 且在安全區 不會打到 if ( !(object instanceof L1MonsterInstance) && ((L1Character)object).getZoneType()== 1 ) continue; // 寵物減傷 if (object instanceof L1PetInstance) damage /= 8; else if (object instanceof L1SummonInstance) { L1SummonInstance summon = (L1SummonInstance) object; if (summon.isExsistMaster()) damage /= 8; } } damage = calcDamageReduction(pc, (L1Character) object, damage, weaponSkill.getAttr()); if (damage <= 0) { continue; } if (object instanceof L1PcInstance) { L1PcInstance targetPc = (L1PcInstance) object; targetPc.sendPackets(new S_DoActionGFX(targetPc.getId(), ActionCodes.ACTION_Damage)); targetPc.broadcastPacket(new S_DoActionGFX( targetPc.getId(), ActionCodes.ACTION_Damage)); targetPc.receiveDamage(pc, (int) damage, false); } else if ((object instanceof L1SummonInstance) || (object instanceof L1PetInstance) || (object instanceof L1MonsterInstance)) { L1NpcInstance targetNpc = (L1NpcInstance) object; targetNpc.broadcastPacket(new S_DoActionGFX(targetNpc .getId(), ActionCodes.ACTION_Damage)); targetNpc.receiveDamage(pc, (int) damage); } } } return calcDamageReduction(pc, cha, damage, weaponSkill.getAttr()); } public static double getBaphometStaffDamage(L1PcInstance pc, L1Character cha) { double dmg = 0; int chance = Random.nextInt(100) + 1; if (14 >= chance) { int locx = cha.getX(); int locy = cha.getY(); int sp = pc.getSp(); int intel = pc.getInt(); double bsk = 0; if (pc.hasSkillEffect(BERSERKERS)) { bsk = 0.2; } dmg = (intel + sp) * (1.8 + bsk) + Random.nextInt(intel + sp) * 1.8; S_EffectLocation packet = new S_EffectLocation(locx, locy, 129); pc.sendPackets(packet); pc.broadcastPacket(packet); } return calcDamageReduction(pc, cha, dmg, L1Skills.ATTR_EARTH); } /** 骰子匕首 */ public static double getDiceDaggerDamage(L1PcInstance pc, L1Character cha, L1ItemInstance weapon) { double dmg = 0; int chance = Random.nextInt(100) + 1; if (2 >= chance) { dmg = cha.getCurrentHp() * 2 / 3; if (cha.getCurrentHp() - dmg < 0) { dmg = 0; } String msg = weapon.getLogName(); pc.sendPackets(new S_ServerMessage(158, msg)); // \f1%0%s 消失。 pc.getInventory().removeItem(weapon, 1); } return dmg; } public static double getKiringkuDamage(L1PcInstance pc, L1Character cha) { int dmg = 0; int dice = 5; int diceCount = 2; int value = 0; int kiringkuDamage = 0; int charaIntelligence = 0; if (pc.getWeapon().getItem().getItemId() == 270) { value = 16; } else { value = 14; } for (int i = 0; i < diceCount; i++) { kiringkuDamage += (Random.nextInt(dice) + 1); } kiringkuDamage += value; int spByItem = pc.getSp() - pc.getTrueSp(); // アイテムによるSP変動 charaIntelligence = pc.getInt() + spByItem - 12; if (charaIntelligence < 1) { charaIntelligence = 1; } double kiringkuCoefficientA = (1.0 + charaIntelligence * 3.0 / 32.0); kiringkuDamage *= kiringkuCoefficientA; double kiringkuFloor = Math.floor(kiringkuDamage); dmg += kiringkuFloor + pc.getWeapon().getEnchantLevel() + pc.getOriginalMagicDamage(); if (pc.hasSkillEffect(ILLUSION_AVATAR)) { dmg += 10; } if (pc.getWeapon().getItem().getItemId() == 270) { pc.sendPackets(new S_SkillSound(pc.getId(), 6983)); pc.broadcastPacket(new S_SkillSound(pc.getId(), 6983)); } else { pc.sendPackets(new S_SkillSound(pc.getId(), 7049)); pc.broadcastPacket(new S_SkillSound(pc.getId(), 7049)); } return calcDamageReduction(pc, cha, dmg, 0); } public static double getAreaSkillWeaponDamage(L1PcInstance pc, L1Character cha, int weaponId) { double dmg = 0; int probability = 0; int attr = 0; int chance = Random.nextInt(100) + 1; if (weaponId == 263 || weaponId == 287) { // フリージングランサー probability = 5; attr = L1Skills.ATTR_WATER; } else if (weaponId == 260) { // レイジングウィンド probability = 4; attr = L1Skills.ATTR_WIND; } if (probability >= chance) { int sp = pc.getSp(); int intel = pc.getInt(); int area = 0; int effectTargetId = 0; int effectId = 0; L1Character areaBase = cha; double damageRate = 0; if (weaponId == 263 || weaponId == 290) { // フリージングランサー area = 3; damageRate = 1.4D; effectTargetId = cha.getId(); effectId = 1804; areaBase = cha; } else if (weaponId == 260) { // レイジングウィンド area = 4; damageRate = 1.5D; effectTargetId = pc.getId(); effectId = 758; areaBase = pc; } double bsk = 0; if (pc.hasSkillEffect(BERSERKERS)) { bsk = 0.2; } dmg = (intel + sp) * (damageRate + bsk) + Random.nextInt(intel + sp) * damageRate; pc.sendPackets(new S_SkillSound(effectTargetId, effectId)); pc.broadcastPacket(new S_SkillSound(effectTargetId, effectId)); for (L1Object object : L1World.getInstance().getVisibleObjects( areaBase, area)) { if (object == null) { continue; } if (!(object instanceof L1Character)) { continue; } if (object.getId() == pc.getId()) { continue; } if (object.getId() == cha.getId()) { // 攻撃対象は除外 continue; } // 攻撃対象がMOBの場合は、範囲内のMOBにのみ当たる // 攻撃対象がPC,Summon,Petの場合は、範囲内のPC,Summon,Pet,MOBに当たる if (cha instanceof L1MonsterInstance) { if (!(object instanceof L1MonsterInstance)) { continue; } } if ((cha instanceof L1PcInstance) || (cha instanceof L1SummonInstance) || (cha instanceof L1PetInstance)) { if (!((object instanceof L1PcInstance) || (object instanceof L1SummonInstance) || (object instanceof L1PetInstance) || (object instanceof L1MonsterInstance))) { continue; } } dmg = calcDamageReduction(pc, (L1Character) object, dmg, attr); if (dmg <= 0) { continue; } if (object instanceof L1PcInstance) { L1PcInstance targetPc = (L1PcInstance) object; targetPc.sendPackets(new S_DoActionGFX(targetPc.getId(), ActionCodes.ACTION_Damage)); targetPc.broadcastPacket(new S_DoActionGFX( targetPc.getId(), ActionCodes.ACTION_Damage)); targetPc.receiveDamage(pc, (int) dmg, false); } else if ((object instanceof L1SummonInstance) || (object instanceof L1PetInstance) || (object instanceof L1MonsterInstance)) { L1NpcInstance targetNpc = (L1NpcInstance) object; targetNpc.broadcastPacket(new S_DoActionGFX(targetNpc .getId(), ActionCodes.ACTION_Damage)); targetNpc.receiveDamage(pc, (int) dmg); } } } return calcDamageReduction(pc, cha, dmg, attr); } public static double getLightningEdgeDamage(L1PcInstance pc, L1Character cha) { double dmg = 0; int chance = Random.nextInt(100) + 1; if (4 >= chance) { int sp = pc.getSp(); int intel = pc.getInt(); double bsk = 0; if (pc.hasSkillEffect(BERSERKERS)) { bsk = 0.2; } dmg = (intel + sp) * (2 + bsk) + Random.nextInt(intel + sp) * 2; pc.sendPackets(new S_SkillSound(cha.getId(), 10)); pc.broadcastPacket(new S_SkillSound(cha.getId(), 10)); } return calcDamageReduction(pc, cha, dmg, L1Skills.ATTR_WIND); } public static void giveArkMageDiseaseEffect(L1PcInstance pc, L1Character cha) { int chance = Random.nextInt(1000) + 1; int probability = (5 - ((cha.getMr() / 10) * 5)) * 10; if (probability == 0) { probability = 10; } if (probability >= chance) { L1SkillUse l1skilluse = new L1SkillUse(); l1skilluse.handleCommands(pc, 56, cha.getId(), cha.getX(), cha.getY(), null, 0, L1SkillUse.TYPE_GMBUFF); } } public static void giveFettersEffect(L1PcInstance pc, L1Character cha) { int fettersTime = 8000; if (isFreeze(cha)) { // 凍結状態orカウンターマジック中 return; } if ((Random.nextInt(100) + 1) <= 2) { L1EffectSpawn.getInstance().spawnEffect(81182, fettersTime, cha.getX(), cha.getY(), cha.getMapId()); if (cha instanceof L1PcInstance) { L1PcInstance targetPc = (L1PcInstance) cha; targetPc.setSkillEffect(STATUS_FREEZE, fettersTime); targetPc.sendPackets(new S_SkillSound(targetPc.getId(), 4184)); targetPc.broadcastPacket(new S_SkillSound(targetPc.getId(), 4184)); targetPc.sendPackets(new S_Paralysis(S_Paralysis.TYPE_BIND, true)); } else if ((cha instanceof L1MonsterInstance) || (cha instanceof L1SummonInstance) || (cha instanceof L1PetInstance)) { L1NpcInstance npc = (L1NpcInstance) cha; npc.setSkillEffect(STATUS_FREEZE, fettersTime); npc.broadcastPacket(new S_SkillSound(npc.getId(), 4184)); npc.setParalyzed(true); } } } public static double calcDamageReduction(L1PcInstance pc, L1Character cha, double dmg, int attr) { // 凍結状態orカウンターマジック中 if (isFreeze(cha)) { return 0; } // MRによるダメージ軽減 int mr = cha.getMr(); double mrFloor = 0; if (mr <= 100) { mrFloor = Math.floor((mr - pc.getOriginalMagicHit()) / 2); } else if (mr >= 100) { mrFloor = Math.floor((mr - pc.getOriginalMagicHit()) / 10); } double mrCoefficient = 0; if (mr <= 100) { mrCoefficient = 1 - 0.01 * mrFloor; } else if (mr >= 100) { mrCoefficient = 0.6 - 0.01 * mrFloor; } dmg *= mrCoefficient; // 属性によるダメージ軽減 int resist = 0; if (attr == L1Skills.ATTR_EARTH) { resist = cha.getEarth(); } else if (attr == L1Skills.ATTR_FIRE) { resist = cha.getFire(); } else if (attr == L1Skills.ATTR_WATER) { resist = cha.getWater(); } else if (attr == L1Skills.ATTR_WIND) { resist = cha.getWind(); } int resistFloor = (int) (0.32 * Math.abs(resist)); if (resist >= 0) { resistFloor *= 1; } else { resistFloor *= -1; } double attrDeffence = resistFloor / 32.0; dmg = (1.0 - attrDeffence) * dmg; return dmg; } private static boolean isFreeze(L1Character cha) { if (cha.hasSkillEffect(STATUS_FREEZE)) { return true; } if (cha.hasSkillEffect(ABSOLUTE_BARRIER)) { return true; } if (cha.hasSkillEffect(ICE_LANCE)) { return true; } if (cha.hasSkillEffect(FREEZING_BLIZZARD)) { return true; } if (cha.hasSkillEffect(FREEZING_BREATH)) { return true; } if (cha.hasSkillEffect(EARTH_BIND)) { return true; } // カウンターマジック判定 if (cha.hasSkillEffect(COUNTER_MAGIC)) { cha.removeSkillEffect(COUNTER_MAGIC); int castgfx = SkillsTable.getInstance().getTemplate(COUNTER_MAGIC) .getCastGfx(); cha.broadcastPacket(new S_SkillSound(cha.getId(), castgfx)); if (cha instanceof L1PcInstance) { L1PcInstance pc = (L1PcInstance) cha; pc.sendPackets(new S_SkillSound(pc.getId(), castgfx)); } return true; } return false; } }