private void DecodeAttackInfo(CInPacket p) { for (int i = 0; i < nMobCount; i++) { var info = new AttackEntry(); info.dwMobID = p.Decode4(); info.nHitAction = p.Decode1(); info.nForeAction = p.Decode1(); // COutPacket::Encode1(&v468, v376->nForeAction & 0x7F | (v181 << 7)); info.nFrameIdx = p.Decode1(); // v218 = CMob::GetCurTemplate(v378->pMob) // && (v166 = CMob::GetTemplate(v378->pMob), v166 != CMob::GetCurTemplate(v378->pMob)); // v167 = (_BYTE)v218 << 7; info.CalcDamageStatIndex = p.Decode1(); // COutPacket::Encode1(&v460, v168 & 0x7F | v167); p.Skip(8); // position info info.tDelay = p.Decode2(); for (int j = 0; j < nDamagePerMob; j++) { info.aDamage[j] = Math.Max(0, p.Decode4()); //Log.Debug($"[Damaging Mob] dwMobID: {info.dwMobID} aDamage: {info.aDamage[j]}"); } p.Skip(4); // CMob::GetCrc aAttackInfo[i] = info; } }
/// <summary> /// Not yet working /// </summary> /// <param name="p"></param> /// <returns></returns> public static AttackInfo ParseMelee(CInPacket p) { AttackInfo ret = new AttackInfo(); ret.portals = p.Decode1(); bool unkk = p.Decode1() == 0xFF; p.Skip(unkk ? 7 : 8); ret.tbyte = p.Decode1(); ret.targets = (byte)((ret.tbyte >> 4) & 0xF); ret.hits = (byte)(ret.tbyte & 0xF); p.Skip(8); //-1 ret.skill = p.Decode4(); p.Skip(1); // 0.94 p.Skip(4); // 0.74 p.Skip(4); // 0.74 p.Skip(8); // 0.88 (0) switch (ret.skill) { case 5101004: // Corkscrew case 15101003: // Cygnus corkscrew case 5201002: // Grenade case 14111006: // Poison bomb case 4341002: // Final Cut case 4341003: // Monster Bomb ret.charge = p.Decode4(); break; default: ret.charge = 0; break; } ret.display = p.Decode1(); // Always zero? ret.direction = p.Decode1(); ret.stance = p.Decode1(); p.Skip(4); p.Skip(1); // Weapon class ret.speed = p.Decode1(); // Confirmed ret.lastAttackTickCount = p.Decode4(); // Ticks p.Skip(4); //0 ret.allDamage = new List <AttackPair>(); //if (ret.skill == 4211006) //{ // Meso Explosion // return parseMesoExplosion(lea, ret); //} for (int i = 0; i < ret.targets; i++) { var mobId = p.Decode4(); var atkPair = new AttackPair(mobId); p.Skip(14); for (int j = 0; j < ret.hits; j++) { var damage = p.Decode4(); atkPair.Attack.Add(new Tuple <int, bool>(damage, false)); Logger.Write(LogLevel.Debug, "Attack Mob {0} Dmg {1}", mobId, damage); } p.Skip(4); // CRC of monster [Wz Editing] ret.allDamage.Add(atkPair); } ret.position = p.DecodePos(); return(ret); }
//nType: // public static MapleAttackNew Parse(CInPacket p, int nType) { MapleAttackNew ret = new MapleAttackNew(); p.Skip(8); // -1 ret.tByte1 = p.Decode1(); ret.nDamagePerMob = (byte)(ret.tByte1 & 0xF); ret.nMobCount = (byte)((ret.tByte1 >> 4) & 0xF); p.Skip(8); //-1 var v11 = p.Decode4(); ret.nSkillID = v11; p.Skip(1); // 0.94 p.Skip(4); // 0.74 p.Skip(4); // 0.74 p.Skip(8); // 0.88 (0) //is_keydown_skill if (v11 == 2121001 || v11 == 2221001 || v11 == 2321001 || v11 == 3221001 || v11 == 3121004) { ret.tKeyDown = p.Decode4(); } else { ret.tKeyDown = -1; } /* * switch (ret.skill) * { * case 5101004: // Corkscrew * case 15101003: // Cygnus corkscrew * case 5201002: // Grenade * case 14111006: // Poison bomb * case 4341002: // Final Cut * case 4341003: // Monster Bomb * ret.charge = p.Decode4(); * break; * default: * ret.charge = 0; * break; * } */ p.Decode1(); // bFinalAfterSlashBlast | 8 * bShadowPartner | 16 * v674 | 32 * (nSerialAttackSkillID != 0) | ((_BYTE)v683 << 7)); ret.tByte2 = p.Decode2(); ret.bLeft = (ret.tByte2 >> 15) & 1; ret.nAction = ret.tByte2 & 0x7FFF; p.Skip(4); //CRC i think ret.nAttackActionType = p.Decode1(); ret.nAttackSpeed = p.Decode1(); ret.tAttackTime = p.Decode4(); p.Skip(4); //bmage? //More decode for bullets here //if (ret.skill == 4211006) //{ // Meso Explosion // return parseMesoExplosion(lea, ret); //} for (int i = 0; i < ret.nMobCount; i++) { var info = new AttackInfo(); info.dwMobID = p.Decode4(); //COutPacket::Encode1(&oPacket, v567->nHitAction); //todo fill the reste in p.Skip(14); for (int j = 0; j < ret.nDamagePerMob; j++) { info.aDamage[j] = p.Decode4(); Logger.Write(LogLevel.Debug, "Attack Mob {0} Dmg {1}", info.dwMobID, info.aDamage[j]); } p.Skip(4); // CRC of monster [Wz Editing] ret.aAttackInfo[i] = info; } //ret.position = p.DecodePos(); //if greneade read pos return(ret); }
public static void Handle_SkillUseRequest(WvsGameClient c, CInPacket p) { // Recv [CP_UserSkillUseRequest] [67 00] [86 1E 31 2F] [9B BA 3E 00] [14] 02 12 27 00 00 23 27 00 00 C2 01 var dwTickCount = p.Decode4(); var nSkillID = p.Decode4(); var nSLV = p.Decode1(); var pSkill = c.Character.Skills.Get(nSkillID, true); // do validation here so we dont need to validate in called functions if (pSkill is null || nSLV > pSkill.nSLV) { return; } if (SkillLogic.IsMobCaptureSkill(nSkillID)) { ActiveSkill_MobCapture.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsSummonCapturedMobSkill(nSkillID)) { ActiveSkill_SummonMonster.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsHookAndHitSkill(nSkillID)) { ActiveSkill_HookAndHit.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsSmokeShellSkill(nSkillID)) { ActiveSkill_SmokeShell.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsDamageMeterSkill(nSkillID, c.Character.Stats.nJob)) { ActiveSkill_DamageMeter.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsFlyingSkill(nSkillID, c.Character.Stats.nJob)) { ActiveSkill_Flying.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsClericHealSkill(nSkillID)) { ActiveSkill_Heal.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsOpenGateSkill(nSkillID)) { ActiveSkill_OpenGate.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsRecoveryAura(nSkillID)) { ActiveSkill_RecoveryAura.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsMysticDoorSkill(nSkillID, c.Character.Stats.nJob) || nSkillID == (int)Skills.PRIEST_MYSTIC_DOOR) { ActiveSkill_TownPortal.Handle(nSkillID, nSLV, c.Character, p); } else if (SkillLogic.IsStatChangeAdminSkill(nSkillID, c.Character.Stats.nJob)) { ActiveSkill_StatChangeAdmin.Handle(nSkillID, nSLV, c.Character, p); } else if (pSkill.Template.IsSummonSkill && nSkillID != (int)Skills.BMAGE_REVIVE) { ActiveSkill_Summon.Handle(nSkillID, nSLV, c.Character, p); } else { var bLeft = false; if (pSkill.Template.is_antirepeat_buff_skill) { var x = p.Decode2(); var y = p.Decode2(); bLeft = x < c.Character.Position.X; } // Recv [CP_UserSkillUseRequest] [67 00] [4B 5E 91 0B] [CD 14 F9 01] 14 var nSpiritJavelinItemID = 0; if (nSkillID == (int)Skills.NIGHTLORD_SPIRIT_JAVELIN) //Spirit Claw { nSpiritJavelinItemID = p.Decode4(); //nSpiritJavelinItemID } if (pSkill.Template.is_event_vehicle_skill) { p.Skip(1); // dwAffectedMemberBitmap if (nSkillID == 2311001) { p.Skip(2); // tDelay } } var nRemaining = p.Available - 2; if (nRemaining > 0) { p.Skip(nRemaining); } //if (nRemaining > 0) //{ // var nMobCount = p.Decode1(); // nMobCount // for (int i = 0; i < nMobCount; i++) // { // p.Decode4(); // adwMobID // } //} var tDelay = p.Available >= 2 ? p.Decode2() : (short)0; // tDelay if (nSkillID == 0 || c.Character.Skills.Cast(nSkillID, bLeft, false, nSpiritJavelinItemID)) { new UserEffectPacket(UserEffect.SkillUse) { nSkillID = nSkillID, nSLV = nSLV }.BroadcastEffect(c.Character, false); } } c.Character.Action.Enable(); }
public void CreateMiniRoom(Character c, MR_Type nType, CInPacket p) { switch (nType) { case MR_Type.Omok: // todo break; case MR_Type.MemoryGame: // todo break; case MR_Type.TradingRoom: if (c.CurMiniRoom != null) { c.SendPacket(CPacket.SystemMessage("You are already in a trade.")); } else { Log.Debug($"Adding trade mini room to field minirooms."); Add(new CTradingRoom(c)); } break; // Recv [CP_MiniRoom] 90 00 00 05 07 00 70 65 65 66 61 63 65 00 01 00 76 C0 4C 00 case MR_Type.PersonalShop: c.SendMessage("no"); break; case MR_Type.EntrustedShop: { // TODO check closeness to other shops var sTitle = p.DecodeString(); p.Skip(3); // TODO what is this var nShopTemplateId = p.Decode4(); var(nItemSlot, pItem) = InventoryManipulator.GetAnyItem(c, InventoryType.Cash, nShopTemplateId); Log.Info($"Player [{c.Stats.sCharacterName}] attempting to create a shop with ItemID [{nShopTemplateId}] and Title [{sTitle}]"); if (pItem == null || nItemSlot == 0) { c.SendPacket(CPacket.SystemMessage("Invalid item or item slot.")); // packet editing? } else if (!c.Field.MapId.InRange(910000000, 910000023)) { c.SendPacket(CPacket.SystemMessage("Item does not work in this map.")); } else if (!ItemConstants.is_entrusted_shop_item(pItem.nItemID)) { c.SendPacket(CPacket.SystemMessage("Invalid shop item.")); // packet editing?? } else { var pMiniRoom = new CEntrustedShop(c, nShopTemplateId, sTitle); if (pMiniRoom.HasItems()) { c.SendMessage("Please retrieve items from fredrick before opening a shop."); } else { //Add(pMiniRoom); // packet is sent in here } } break; } case MR_Type.CashTradingRoom: // todo break; default: break; } }
public static void OnPacket(WvsGameClient c, CInPacket p) { var pChar = c.Character; var pMiniRoom = pChar.CurMiniRoom; Log.Debug($"Begin Handle_MiniRoom"); var opcode = p.Decode1(); Log.Debug($"Operation: {opcode}"); switch ((MR_Action)opcode) { case MR_Action.MRP_Create: { var type = p.Decode1(); Log.Debug($"Create Type: {type}"); c.Character.Field.MiniRooms.CreateMiniRoom(c.Character, (MR_Type)type, p); break; } case MR_Action.MRP_Invite: // occurs in some but not all rooms (games, messenger, trade, not shops) { var dwTargetCharId = p.Decode4(); Log.Debug($"Processing trade invite request to char ID {dwTargetCharId}"); if (pMiniRoom is CTradingRoom room) { room.HandleSendInvite(dwTargetCharId); } break; } case MR_Action.MRP_InviteResult: { if (pMiniRoom is CTradingRoom ctr) { ctr.HandleDecline(); } break; } case MR_Action.MRP_Enter: { var targetRoomId = p.Decode4(); // theres two more bytes after this which im curious to know what they do... // the extra bytes might be fm room?? for remote merchants?? Log.Info($"DWID: {targetRoomId}"); var room = pChar.Field.MiniRooms.FirstOrDefault(r => r.dwId == targetRoomId); // if remote merchant operations use this same packet process then it will not work because we're searching by field.. we'd need to search by a range of fields in the channel instead if (room is null) { c.SendPacket(FailedEnterPacket()); } else { room?.HandleVisit(p, c); } break; } case MR_Action.MRP_Chat: { p.Skip(4); // timestamp pChar.CurMiniRoom?.HandleChat(pChar, p.DecodeString()); break; } case MR_Action.MRP_Leave: { pChar.CurMiniRoom?.HandlePlayerExit(pChar, MR_LeaveResult.UserRequest); break; } case MR_Action.ESP_WithdrawAll: // owner close { if (pChar.CurMiniRoom is CEntrustedShop ces && ces.OwnerID == pChar.dwId) { ces.WithdrawAll(); ces.HandlePlayerExit(pChar, MR_LeaveResult.UserRequest); ces.Destroy(); } break; } case MR_Action.MRP_Balloon: // ?? these names lmao { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { // pChar.Field.Broadcast(r.MakeEnterFieldPacket()); // gms spawns the shop after owner fills it with items ces.ShopOpen = true; pChar.CurMiniRoom = null; } break; } case MR_Action.TRP_PutItem: { if (pChar.CurMiniRoom is CTradingRoom ctr) { ctr.HandleAddItem(p, pChar); } break; } case MR_Action.TRP_PutMoney: { if (pChar.CurMiniRoom is CTradingRoom ctr) { ctr.HandleSetMeso(pChar, p.Decode4()); } break; } case MR_Action.TRP_Trade: { if (pChar.CurMiniRoom is CTradingRoom ctr) { ctr.HandleConfirmTrade(pChar); } break; } case MR_Action.PSP_PutItem: case MR_Action.ESP_PutItem: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.AddItem(p); } break; } case MR_Action.ESP_BuyItem: case MR_Action.PSP_BuyItem: { if (pChar.CurMiniRoom is CEntrustedShop ces) { ces.SellItem(c.Character, p); } break; } case MR_Action.ESP_Refresh: case MR_Action.PSP_Refresh: { if (pChar.CurMiniRoom is CEntrustedShop ces) { ces.Refresh(pChar); } break; } case MR_Action.ESP_MoveItemToInventory: case MR_Action.PSP_MoveItemToInventory: { if (pChar.CurMiniRoom is CEntrustedShop ces) { ces.RemoveItem(p); } break; } case MR_Action.PSP_Ban: case MR_Action.MGRP_Ban: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { //ces.BanPlayer(p.DecodeString()); } break; } case MR_Action.ESP_ArrangeItem: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.ArrangeItems(); } break; } case MR_Action.ESP_DeliverVisitList: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.DeliverVisitList(); } break; } case MR_Action.ESP_DeliverBlackList: case MR_Action.PSP_DeliverBlackList: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.DeliverBlackList(); } break; } case MR_Action.ESP_AddBlackList: case MR_Action.PSP_AddBlackList: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.AddBlackList(p); } break; } case MR_Action.PSP_DeleteBlackList: case MR_Action.ESP_DeleteBlackList: { if (pChar.CurMiniRoom is CEntrustedShop ces && pChar.CurMiniRoom.OwnerID == pChar.dwId) { ces.DeleteBlackList(p); } break; } default: Log.Info($"Unhandled MiniRoom packet. OpCode: {opcode}."); Log.Info($"Full packet: {BitConverter.ToString(p.ToArray()).Replace("-", " ")}"); break; } pChar.Action.Enable(); }
public static MapleAttack ParseShoot(CInPacket p, Character c) { MapleAttack ret = new MapleAttack(); p.Skip(8); // -1 var tByte1 = p.Decode1(); ret.nDamagePerMob = (byte)(tByte1 & 0xF); ret.nMobCount = (byte)((tByte1 >> 4) & 0xF); //c.SendMessage("Damage per mob " + ret.nDamagePerMob); //c.SendMessage("Mob count " + ret.nMobCount); p.Skip(8); //-1 ret.nSkillID = p.Decode4(); //2017 ret.ValidateSkill(c); if (!ret.bValidAttack) { return(ret); } var skillTemplate = MasterManager.SkillTemplates[ret.nSkillID]; var nCombatOrders = p.Decode1(); // nCombatOrders p.Skip(4); // get_rand(pDrInfo.dr0, 0); p.Skip(4); // CCrc32::GetCrc32(pData, 4u, n, 0, 0); p.Skip(8); ret.tKeyDown = skillTemplate?.is_keydown_skill ?? false?p.Decode4() : -1; ret.nOption = p.Decode1(); //COutPacket::Encode1(&v498, (bSpark << 7) | (bSpiritJavelin << 6) | 8 * bShadowPartner | 4 * bMortalBlow | 2 * bSoulArrow); var bSpark = (ret.nOption & 0x80) != 0; var bSpiritJavelin = (ret.nOption & 0x40) != 0; var bSerial = (ret.nOption & 0x20) != 0; var bShadowPartner = (ret.nOption & 0x8) != 0; var bMortalBlow = (ret.nOption & 0x4) != 0; var bSoulArrow = (ret.nOption & 0x2) != 0; ret.bNextShootExJablin = p.Decode1() > 0; //v339->m_bNextShootExJablin && CUserLocal::CheckApplyExJablin(v339, pSkill, nAttackAction); ret.nActionAndDir = p.Decode2(); ret.bLeft = (ret.nActionAndDir >> 15) & 1; ret.nAction = ret.nActionAndDir & 0x7FFF; //nAttackAction p.Skip(4); //CRC i think ret.nAttackActionType = p.Decode1(); //nAttackSpeed | 16 * nReduceCount ret.nAttackSpeed = p.Decode1(); ret.tAttackTime = p.Decode4(); p.Skip(4); //nPhase ret.nBulletItemPos = p.Decode2(); ret.nBulletCashItemPos = p.Decode2(); var nShootRange0a = p.Decode1(); // nSLV (BMS) ret.bNoItemConsume = skillTemplate?.is_shoot_skill_not_consuming_bullet ?? false; if (!ret.bNoItemConsume) { if (JobLogic.is_mechanic_job(c.Stats.nJob)) { ret.bNoItemConsume = true; } else if (bSoulArrow) { ret.bNoItemConsume = c.Buffs[(int)Skills.HUNTER_SOUL_ARROW_BOW] != null || c.Buffs[(int)Skills.CROSSBOWMAN_SOUL_ARROW_CROSSBOW] != null || c.Buffs[(int)Skills.WINDBREAKER_SOUL_ARROW_BOW] != null || c.Buffs[(int)Skills.WILDHUNTER_SOUL_ARROW_CROSSBOW] != null; } else if (bSpiritJavelin) { if (ret.bNextShootExJablin) { ret.bNextShootExJablin = false; } else { ret.nSpiritJavelinItemID = p.Decode4(); var nBulletType = (ret.nSpiritJavelinItemID % 10000) + 1; c.Buffs.TryGetValue((int)Skills.NIGHTLORD_SPIRIT_JAVELIN, out AbstractBuff secondaryStat_nSpiritJavelin); if (c.InventoryConsume.Get(ret.nBulletItemPos)?.nItemID != ret.nSpiritJavelinItemID) { c.Buffs.Remove((int)Skills.NIGHTLORD_SPIRIT_JAVELIN); // they have to use the same item as they } else { if (secondaryStat_nSpiritJavelin != null && nBulletType != secondaryStat_nSpiritJavelin.Stat[SecondaryStatFlag.SpiritJavelin].nValue) { ret.bValidAttack = false; // PE return(ret); } ret.bNoItemConsume = true; } } } else if (ret.bNextShootExJablin) { ret.bNoItemConsume = true; } } ret.DecodeAttackInfo(p); p.Skip(4); // char pos if (JobLogic.IsWildhunterJob(c.Stats.nJob)) { var m_ptBodyRelMove = p.Decode2(); // unsure what this is used for } p.Skip(4); // another pos.. unsure for what if (ret.nSkillID == (int)Skills.STRIKER_SPARK) { ret.tReserveSpark = p.Decode4(); } return(ret); }
public static MapleAttack ParseMagic(CInPacket p, Character c) { MapleAttack ret = new MapleAttack(); p.Skip(8); // -1 var tByte1 = p.Decode1(); ret.nDamagePerMob = (byte)(tByte1 & 0xF); ret.nMobCount = (byte)((tByte1 >> 4) & 0xF); p.Skip(8); //-1 var v11 = p.Decode4(); //2017 ret.nSkillID = v11; p.Skip(1); // nCombatOrders p.Skip(4); // get_rand(pDrInfo.dr0, 0); p.Skip(4); // CCrc32::GetCrc32(pData, 4u, n, 0, 0); p.Skip(8); p.Skip(8); p.Skip(4); //dwInit p.Skip(4); //COutPacket::Encode4(&v468, dwInit); p.Skip(4); //SKILLLEVELDATA Crc ? p.Skip(4); //SKILLLEVELDATA Crc ? ret.ValidateSkill(c); if (!ret.bValidAttack) { return(ret); } ret.bNoItemConsume = true; if (v11 == 2121001 || v11 == 2221001 || v11 == 2321001 || v11 == 22121000 || v11 == 22151001) { ret.tKeyDown = p.Decode4(); } else { ret.tKeyDown = -1; } ret.nOption = p.Decode1(); // p.Skip(1); // Zero ! ret.nActionAndDir = p.Decode2(); ret.bLeft = (ret.nActionAndDir >> 15) & 1; ret.nAction = ret.nActionAndDir & 0x7FFF; //nAttackAction p.Skip(4); //CRC i think ret.nAttackActionType = p.Decode1(); //nAttackSpeed | 16 * nReduceCount ret.nAttackSpeed = p.Decode1(); ret.tAttackTime = p.Decode4(); p.Skip(4); //nPhase ret.DecodeAttackInfo(p); //ret.position = p.DecodePos(); //Confirm this return(ret); }
public static MapleAttack ParseMelee(CInPacket p, Character c) { MapleAttack ret = new MapleAttack(); var nFirstByte = p.Decode1(); if (nFirstByte == 1 || nFirstByte == 2) // CUserLocal::TryDoingNormalAttack { p.Skip(8); } else // CUserLocal::TryDoingMeleeAttack { p.Skip(7); } var tByte1 = p.Decode1(); ret.nDamagePerMob = (byte)(tByte1 & 0xF); ret.nMobCount = (byte)((tByte1 >> 4) & 0xF); p.Skip(8); //-1 var v11 = p.Decode4(); ret.nSkillID = v11; ret.ValidateSkill(c); if (!ret.bValidAttack) { return(ret); } ret.bNoItemConsume = true; var template = MasterManager.SkillTemplates[ret.nSkillID]; p.Skip(1); p.Skip(8); // 0.74 p.Skip(8); // 0.88 ret.tKeyDown = template?.is_keydown_skill ?? false?p.Decode4() : -1; var bSomeFlag = p.Decode1(); // bFinalAfterSlashBlast | 8 * bShadowPartner | 16 * v685 | 32 * (nSerialAttackSkillID != 0) | ((_BYTE)v694 << 7)); var bSpark = ((bSomeFlag >> 7) & 1) > 0; var bSerialAttackSkill = ((bSomeFlag >> 5) & 1) > 0; var bShadowPartner = ((bSomeFlag >> 3) & 1) > 0; var bFinalAfterSlashBlast = (bSomeFlag & 1) > 0; ret.nActionAndDir = p.Decode2(); ret.bLeft = (ret.nActionAndDir >> 15) & 1; ret.nAction = ret.nActionAndDir & 0x7FFF; p.Skip(4); //CRC i think ret.nAttackActionType = p.Decode1(); ret.nAttackSpeed = p.Decode1(); ret.tAttackTime = p.Decode4(); p.Skip(4); //bmage? //More decode for bullets here //if (ret.skill == 4211006) //{ // Meso Explosion // return parseMesoExplosion(lea, ret); //} ret.DecodeAttackInfo(p); p.Skip(4); // position if (ret.nSkillID == (int)Skills.NIGHTWALKER_POISON_BOMB) { ret.nGrenadePtX = p.Decode2(); ret.nGrenadePtY = p.Decode2(); } return(ret); }
public static void Handle_SummonedAttack(WvsGameClient c, CInPacket p) { var dwSummonedID = p.Decode4(); var item = c.Character.Field.Summons[dwSummonedID]; if (item is null) { return; } var nSkillID = item.nSkillID; p.Skip(8); // crc p.Decode4(); // get_update_time() p.Skip(8); // crc var nActionAndDir = p.Decode1(); var nAction = nActionAndDir & 0x7F; var bLeft = ((nAction >> 7) & 1) > 0; p.Skip(8); // crc var nMobCount = p.Decode1(); if (nSkillID == (int)Skills.MECHANIC_TESLA_COIL && nAction != 0x10) { var adwTeslaFamily = p.DecodeIntArray(3); } p.Skip(8); // mob pos, summon pos p.Skip(4); var deadMobs = new List <int>(); var aSAI = new List <SummonAttackInfo>(); for (var i = 0; i < nMobCount; i++) { var dwMobID = p.Decode4(); var pMob = item.Field.Mobs[dwMobID]; var dwTemplateID = p.Decode4(); var nHitAction = p.Decode1(); var nForeAction = p.Decode1(); var nFrameIdx = p.Decode1(); var nCalcDamageStatIndex = p.Decode1(); p.Skip(8); // position var tDelay = p.Decode2(); var nDamage = p.Decode4(); if (nDamage <= 0) { continue; } if (pMob is null || pMob.Dead || pMob.Template.Invincible) { continue; // this happens naturally when two summons attack the same mob at the same time } aSAI.Add(new SummonAttackInfo { dwMobID = dwMobID, nHitAction = nHitAction, nDamage = nDamage }); if (pMob.Damage(c.Character, nDamage, tDelay)) { deadMobs.Add(dwMobID); } } item.Field.Broadcast(CPacket.CSummonedPool.SummonedAttack(item, item.dwId, nActionAndDir, aSAI), c.Character); deadMobs.ForEach(mob => c.Character.Field.Mobs.Remove(mob)); if (nAction == 0x10) { item.nLeaveType = SummonLeaveType.LEAVE_TYPE_SELF_DESTRUCT; item.Field.Summons.Remove(item); } else if (nSkillID == (int)Skills.VALKYRIE_GABIOTA) { item.nLeaveType = SummonLeaveType.LEAVE_TYPE_GABIOTA; item.Field.Summons.Remove(item); } }