/// <inheritdoc/> public void HandlePacket(Player player, Span <byte> packet) { if (packet.Length < 7) { return; } using var loggerScope = player.Logger.BeginScope(this.GetType()); HitRequest message = packet; var currentMap = player.CurrentMap; if (currentMap == null) { player.Logger.LogWarning($"Current player map not set. Possible hacker action. Character name: {player.Name}"); return; } var target = currentMap.GetObject(message.TargetId) as IAttackable; if (target == null) { player.Logger.LogWarning($"Object {message.TargetId} of current player map not found alive. Possible hacker action. Character name: {player.Name}"); } else { this.hitAction.Hit(player, target, message.AttackAnimation, message.LookingDirection.ParseAsDirection()); } }
/// <summary> /// 檢查資料合法性 /// </summary> /// <param name="request"></param> /// <returns></returns> private bool _CheckDataLegality(HitRequest request) { if (request.FishDatas.Any(x => x.FishOdds <= 0)) { _OnHitExceptionEvent.Invoke("FishData.Odds 不可為0"); Singleton <Log> .Instance.WriteInfo("FishData.Odds 不可為0"); return(false); } if (request.FishDatas.Any(x => x.FishStatus < FISH_STATUS.NORMAL || x.FishStatus > FISH_STATUS.FREEZE)) { _OnHitExceptionEvent.Invoke("FishData.FishStatus 無此狀態"); Singleton <Log> .Instance.WriteInfo("FishData.FishStatus 無此狀態"); return(false); } if (request.WeaponData.TotalHits <= 0) { _OnHitExceptionEvent.Invoke("WeaponData.TotalHitOdds 不可為0"); Singleton <Log> .Instance.WriteInfo("WeaponData.TotalHitOdds 不可為0"); return(false); } if (request.WeaponData.WeaponType < WEAPON_TYPE.NORMAL || request.WeaponData.WeaponType > WEAPON_TYPE.FREEZE_BOMB) { _OnHitExceptionEvent.Invoke("WeaponData.WeaponType,無此編號"); Singleton <Log> .Instance.WriteInfo("WeaponData.WeaponType,無此編號"); return(false); } if (request.WeaponData.WeaponBet <= 0 || request.WeaponData.WeaponBet > 1000) { _OnHitExceptionEvent.Invoke("WeaponData.WeaponBet,押注分數錯誤"); Singleton <Log> .Instance.WriteInfo("WeaponData.WeaponBet,押注分數錯誤"); return(false); } if (request.WeaponData.WeaponOdds != 1) { _OnHitExceptionEvent.Invoke("WeaponData.OddsResult,目前只能為1"); Singleton <Log> .Instance.WriteInfo("WeaponData.OddsResult,目前只能為1"); return(false); } if (request.WeaponData.TotalHits != request.FishDatas.Length) { _OnHitExceptionEvent.Invoke("WeaponData.TotalHits,擊中數量不符"); Singleton <Log> .Instance.WriteInfo("WeaponData.TotalHits,擊中數量不符"); return(false); } return(true); }
public static FarmBuffer.BUFFER_BLOCK GetBlock(HitRequest request, int max_bet) { var bet = request.WeaponData.GetTotalBet(); if (bet >= ((750 * max_bet) / 1000)) { return(FarmBuffer.BUFFER_BLOCK.BLOCK_5); } if (bet >= ((500 * max_bet) / 1000)) { return(FarmBuffer.BUFFER_BLOCK.BLOCK_4); } if (bet >= ((250 * max_bet) / 1000)) { return(FarmBuffer.BUFFER_BLOCK.BLOCK_3); } if (bet >= ((100 * max_bet) / 1000)) { return(FarmBuffer.BUFFER_BLOCK.BLOCK_2); } return(FarmBuffer.BUFFER_BLOCK.BLOCK_1); }
private string _MakeRequestLog(HitRequest request) { var weapon = request.WeaponData; string weaponDataLog = string.Format("WeaponData\r\n{0,-7}{1,-32}{2,-7}{3,-7}{4,-7}\r\n{5,-7}{6,-7}{7,-7}{8,-7}{9,-7}\r\n" , "Bullet", "WeaponType", "Bet", "Odds", "Hits" , weapon.BulletId, weapon.WeaponType, weapon.WeaponBet, weapon.WeaponOdds, weapon.TotalHits); var fishTitle = string.Format( "FishData\r\n{0,-7}{1,-32}{2,-32}{3,-7}\r\n", "Id", "FishType", "FishStatus", "Odds"); var fishs = request.FishDatas; return(weaponDataLog + fishTitle + string.Join( "\r\n", fishs.Select( fish_data => string.Format( "{0,-7}{1,-32}{2,-32}{3,-7}", fish_data.FishId, fish_data.FishType, fish_data.FishStatus, fish_data.FishOdds)).ToArray())); }
protected override Task Handle(FillHitRequestEvent e, CancellationToken cancellation) { HitRequest hitRequest = e.HitRequest; hitRequest.Damages = _algorithm.GenerateDamage(hitRequest); return(Task.CompletedTask); }
void IFishStage.Hit(HitRequest request) { var responses = _Formula.TotalRequest(request); _OnTotalHitResponseEvent.Invoke(responses); _MakeLog(request, responses); }
private void _MakeLog(HitRequest request, IEnumerable <HitResponse> responses) { const string formats = "PlayerVisitor:{0}\tStage:{1}\r\n<Request>\r\n{2}\r\n<Response>\r\n{3}"; var log = string.Format( formats, _AccountId, _FishFarmData.FarmId, _MakeRequestLog(request), _MakeResponesLog(responses)); Singleton <Log> .Instance.WriteInfo(log); }
private void _MakeLog(HitRequest request, HitResponse[] responses) { var format = "PlayerVisitor:{0}\tStage:{1}\nRequest:{2}\nResponse:{3}"; var log = string.Format( format, _AccountId, _FishStage, request.ShowMembers(" "), responses.ShowMembers(" ")); Singleton <Log> .Instance.WriteInfo(log); }
/// <summary> /// 魚王倍數 = 所有同種類魚的 total odds /// </summary> /// <param name="request"></param> private void _SetFishKingOdds(HitRequest request) { var fishKings = request.FishDatas.Where(x => x.FishStatus == FISH_STATUS.KING).ToArray(); if (!fishKings.Any()) { return; } foreach (var king in fishKings) { var smallFishs = request.FishDatas.Where(x => x.FishStatus != FISH_STATUS.KING && x.FishType == king.FishType); king.FishOdds += smallFishs.Sum(x => x.FishOdds); } }
void IFishStage.Hit(HitRequest request) { _OnTotalHitResponseEvent( request.FishDatas.Select( requset_fish_data => new HitResponse { DieResult = Random.Instance.NextInt(1, 4) == 1 ? FISH_DETERMINATION.DEATH : FISH_DETERMINATION.SURVIVAL, FishId = requset_fish_data.FishId, WepId = request.WeaponData.BulletId, FeedbackWeapons = new[] { WEAPON_TYPE.INVALID } }).ToArray()); }
void IFishStage.Hit(HitRequest request) { if (!_CheckDataLegality(request)) { return; } _SetFishKingOdds(request); var totalRequest = new ZsHitChecker(_FishFarmData, _FormulaPlayerRecord, _CreateRandoms()).TotalRequest(request); _FormulaFarmRecorder.Save(_FishFarmData); _FormulaPlayerRecorder.Save(_FormulaPlayerRecord); _OnTotalHitResponseEvent.Invoke(totalRequest); _MakeLog(request, totalRequest); }
public static SuPacket GenerateSuPacket(this IBattleEntity entity, HitRequest hit, ushort damages) => new SuPacket { VisualType = entity.Type, VisualId = entity.Id, HitMode = hit.HitMode, Damage = damages, HpPercentage = entity.HpPercentage, // factorise this code with extension PositionX = entity.Position.X, PositionY = entity.Position.X, TargetIsAlive = hit.Target.IsAlive, AttackAnimation = hit.UsedSkill.AttackAnimation, SkillCooldown = hit.UsedSkill.Cooldown, SkillEffect = hit.UsedSkill.Effect, SkillVnum = hit.UsedSkill.Id, SkillTypeMinusOne = hit.UsedSkill.SkillType - 1, TargetVisualType = hit.Target.Type, TargetId = hit.Target.Id };
/// <inheritdoc/> public void HandlePacket(Player player, Span <byte> packet) { if (packet.Length < 7) { return; } using var loggerScope = player.Logger.BeginScope(this.GetType()); HitRequest message = packet; var currentMap = player.CurrentMap; if (currentMap is null) { player.Logger.LogWarning($"Current player map not set. Possible hacker action. Character name: {player.Name}"); return; } if (currentMap.GetObject(message.TargetId) is not IAttackable target) { player.Logger.LogWarning($"Object {message.TargetId} of current player map not found alive. Possible hacker action. Character name: {player.Name}"); }
public override HitResponse[] TotalRequest(HitRequest request) { var block = CalculationBufferBlock.GetBlock(request, _DataVisitor.Farm.MaxBet); _DataVisitor.FocusBufferBlock = block; // 只有第一發才能累積buffer if (request.WeaponData.WeaponType == WEAPON_TYPE.NORMAL && request.WeaponData.TotalHits == 1) { new AccumulationBufferRule(_DataVisitor, request).Run(); new ApproachBaseOddsRule(_DataVisitor).Run(); new AdjustmentAverageRule(_DataVisitor, request).Run(); } new AdjustmentGameLevelRule(_DataVisitor).Run(); new AdjustmentPlayerPhaseRule(_DataVisitor).Run(); return(new DeathRule(_DataVisitor, request).Run()); }
/// <summary> /// Use this extension only if your damages are lower than /// <value>65535</value> /// (ushort limit) /// </summary> /// <param name="entity"></param> /// <param name="hit"></param> /// <returns></returns> public static SuPacket GenerateSuPacket(this IBattleEntity entity, HitRequest hit) => GenerateSuPacket(entity, hit, (ushort)hit.Damages);
public AdjustmentAverageRule(DataVisitor fish_visitor, HitRequest hit_request) { _DataVisitor = fish_visitor; _HitRequest = hit_request; }
/// <summary> /// Handle any kind of Monster interaction /// </summary> internal void MonsterLife() { if (Monster == null) { return; } // handle hit queue HitRequest hitRequest = null; while (HitQueue.TryDequeue(out hitRequest)) { if (IsAlive) { int hitmode = 0; // calculate damage int damage = hitRequest.Session.Character.GenerateDamage(this, hitRequest.Skill, ref hitmode); switch (hitRequest.TargetHitType) { case Domain.TargetHitType.SingleTargetHit: { // Target Hit Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {(hitRequest.SkillEffect)} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case Domain.TargetHitType.SingleTargetHitCombo: { // Taget Hit Combo Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.SkillCombo.Animation} {hitRequest.SkillCombo.Effect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case Domain.TargetHitType.SingleAOETargetHit: { // Target Hit Single AOE switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {(hitRequest.SkillEffect)} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case Domain.TargetHitType.AOETargetHit: { // Target Hit AOE switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {(hitRequest.SkillEffect)} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case Domain.TargetHitType.ZoneHit: { // Zone HIT Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.MapX} {hitRequest.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} 5 {hitRequest.Skill.SkillType - 1}"); break; } case Domain.TargetHitType.SpecialZoneHit: { // Special Zone hit Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)(((float)CurrentHp / (float)Monster.MaxHP) * 100)} {damage} 0 {hitRequest.Skill.SkillType - 1}"); break; } } // generate the kill bonus hitRequest.Session.Character.GenerateKillBonus(this); } else { // monster already has been killed, send cancel hitRequest.Session.SendPacket($"cancel 2 {MapMonsterId}"); } } // Respawn if (!IsAlive && ShouldRespawn.Value) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10) { Respawn(); } return; } // normal movement else if (Target == -1) { Move(); return; } // target following else { ClientSession targetSession = Map.GetSessionByCharacterId(Target); // remove target in some situations if (targetSession == null || targetSession.Character.Invisible || targetSession.Character.Hp <= 0) { RemoveTarget(); return; } NpcMonsterSkill npcMonsterSkill = null; if (_random.Next(10) > 8 && Skills != null) { npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } // check if target is in range if (targetSession != null && !targetSession.Character.InvisibleGm && !targetSession.Character.Invisible && targetSession.Character.Hp > 0 && ((npcMonsterSkill != null && CurrentMp >= npcMonsterSkill.Skill.MpCost && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) < npcMonsterSkill.Skill.Range) || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) <= Monster.BasicRange))) { TargetHit(targetSession, npcMonsterSkill); } else { if (targetSession != null) { FollowTarget(targetSession); } } } }
public override HitResponse[] TotalRequest(HitRequest request) { var hitResponses = new List <HitResponse>(); foreach (var fishData in request.FishDatas) { const int MAX_WEPBET = 10000; const int MAX_WEPODDS = 10000; const short MAX_TOTALHITS = 1000; const short MAX_FISHODDS = 1000; const long gateOffset = 0x0fffffff; if (request.WeaponData.WeaponBet > MAX_WEPBET) { hitResponses.Add(HitTest._Miss(fishData, request.WeaponData)); continue; } if (request.WeaponData.WeaponOdds > MAX_WEPODDS) { hitResponses.Add(HitTest._Miss(fishData, request.WeaponData)); continue; } if (request.WeaponData.TotalHits == 0 || request.WeaponData.TotalHits > MAX_TOTALHITS) { hitResponses.Add(HitTest._Miss(fishData, request.WeaponData)); continue; } if (fishData.FishOdds == 0 || fishData.FishOdds > MAX_FISHODDS) { hitResponses.Add(HitTest._Miss(fishData, request.WeaponData)); } else { long gate = 1000; gate *= gateOffset; gate *= request.WeaponData.WeaponBet; gate /= request.WeaponData.TotalHits; gate /= fishData.FishOdds; gate /= 1000; if (gate > 0x0fffffff) { gate = 0x10000000; } var rValue = _Random.NextLong(0, long.MaxValue); var value = rValue % 0x10000000; if (value < gate) { hitResponses.Add(_Die(fishData, request.WeaponData)); } else { hitResponses.Add(HitTest._Miss(fishData, request.WeaponData)); } } } return(hitResponses.ToArray()); }
protected override async Task Handle(UseSkillEvent e, CancellationToken cancellation) { if (!(e.Sender is IBattleEntity entity)) { return; } IBattleEntity target = e.Target; SkillDto skill = e.Skill; if (!(entity is IPlayerEntity player)) { player = null; } if (e.Skill.MpCost > entity.Mp) //TODO: others check { if (!(player is null)) { await player.SendPacketAsync(target.GenerateTargetCancelPacket(CancelPacketType.InCombatMode)); } return; } List <IBattleEntity> targets = new List <IBattleEntity>(); switch ((SkillTargetType)skill.TargetType) { case SkillTargetType.SingleHit: case SkillTargetType.SingleBuff when skill.HitType == 0: // if too much distance, send cancel packet if (entity.GetDistance(target) > skill.Range + target.BasicArea + 1) { if (!(player is null)) { await player.SendPacketAsync(target.GenerateTargetCancelPacket(CancelPacketType.InCombatMode)); } return; } if (entity.Type == VisualType.Character && target.Type == VisualType.Character && !entity.CurrentMap.IsPvpEnabled && skill.HitType != 1) { if (!(player is null)) { await player.SendPacketAsync(target.GenerateTargetCancelPacket(CancelPacketType.InCombatMode)); } return; } targets.Add(target); break; case SkillTargetType.AOE when skill.HitType == 1: // Target Hit if (skill.TargetRange == 0) { targets.Add(target); goto default; } targets.Add(target); break; case SkillTargetType.AOE when skill.HitType != 1: // Buff switch (skill.HitType) { case 0: case 4: // Apply Buff on himself targets.Add(target); break; case 2: // Apply Buff in range if (skill.TargetRange == 0) { if (!(player is null)) { await player.SendPacketAsync(target.GenerateTargetCancelPacket(CancelPacketType.InCombatMode)); } return; } // apply buff on each entities of type break; } break; default: if (!(player is null)) { await player.SendPacketAsync(target.GenerateTargetCancelPacket(CancelPacketType.InCombatMode)); } targets.Add(target); return; } await entity.CurrentMap.BroadcastAsync(entity.GenerateCtPacket(e.Target, e.Skill)); await entity.DecreaseMp(e.Skill.MpCost); //TODO: Skill Cooldown foreach (IBattleEntity t in targets) { HitRequest hitRequest = await _hitRequestFactory.CreateHitRequest(entity, t, e.Skill); await entity.EmitEventAsync(new FillHitRequestEvent { HitRequest = hitRequest, }); await entity.EmitEventAsync(new ProcessHitRequestEvent { HitRequest = hitRequest }); } }
public DeathRule(DataVisitor visitor, HitRequest request) { _Visitor = visitor; _Request = request; _HitResponses = new List <HitResponse>(); }
protected override async Task Handle(ProcessHitRequestEvent e, CancellationToken cancellation) { HitRequest hitRequest = e.HitRequest; IBattleEntity target = hitRequest.Target; uint givenDamages = 0; await Task.Delay(hitRequest.UsedSkill.CastTime * 100, cancellation); List <SuPacket> packets = new List <SuPacket>(); while (givenDamages != hitRequest.Damages && target.IsAlive) { ushort nextDamages = hitRequest.Damages - givenDamages > ushort.MaxValue ? ushort.MaxValue : (ushort)(hitRequest.Damages - givenDamages); givenDamages += nextDamages; if (target.Hp - nextDamages <= 0) { target.Hp = 0; switch (target) // send death event { case IPlayerEntity player: await player.EmitEventAsync(new PlayerDeathEvent { Killer = hitRequest.Sender }); break; case IMonsterEntity monster: await monster.EmitEventAsync(new MonsterDeathEvent { Killer = hitRequest.Sender }); break; case INpcEntity npc: await npc.EmitEventAsync(new NpcDeathEvent { Killer = hitRequest.Sender }); break; } packets.Add(hitRequest.Sender.GenerateSuPacket(hitRequest, nextDamages)); break; } target.Hp -= nextDamages; packets.Add(hitRequest.Sender.GenerateSuPacket(hitRequest, nextDamages)); } if (!packets.Any()) { packets.Add(hitRequest.Sender.GenerateSuPacket(hitRequest, 0)); } await hitRequest.Sender.CurrentMap.BroadcastAsync(hitRequest.Sender.GenerateEffectPacket(hitRequest.UsedSkill.CastEffect)); await hitRequest.Sender.CurrentMap.BroadcastAsync <SuPacket>(packets); Log.Debug($"[{hitRequest.Sender.Type.ToString()}][{hitRequest.Sender.Id}] ATTACK -> [{hitRequest.Target.Type.ToString()}]({hitRequest.Target.Id}) : {givenDamages} damages"); // sets the new target (for AI) if (hitRequest.Target.Type != VisualType.Character && !hitRequest.Target.HasTarget) { hitRequest.Target.Target = hitRequest.Sender; } foreach (BCardDto bCardDto in e.HitRequest.Bcards) { await _bCardHandlerContainer.Handle(e.HitRequest.Sender, e.HitRequest.Target, bCardDto); } }
public uint GenerateDamage(HitRequest hit) { return(1); }
public override HitResponse[] TotalRequest(HitRequest request) { throw new NotImplementedException(); }
public AccumulationBufferRule(DataVisitor visitor, HitRequest request) { _Visitor = visitor; _Request = request; }
Value <int> IPlayer.Hit(int bullet_id, int[] fishids) { var hasBullet = _PopBullet(bullet_id); if (hasBullet == false) { return(0); } var logFishs = string.Empty; var count = 0; foreach (var fishid in fishids) { if (_PopFish(fishid) == false) { continue; } if (_Requests.ContainsKey(fishid)) { continue; } count++; var fishs = new List <RequsetFishData> { new RequsetFishData { FishId = fishid, FishOdds = 1, FishStatus = FISH_STATUS.NORMAL, FishType = FISH_TYPE.TROPICAL_FISH } }; var weapon = new RequestWeaponData { TotalHits = fishids.Length, WeaponBet = _WeaponBet, BulletId = bullet_id, WeaponOdds = 1, WeaponType = _WeaponType }; var request = new HitRequest(fishs.ToArray(), weapon); _Requests.Add(fishid, request); _FishStage.Hit(request); logFishs += fishid + ","; } if (count == 0) { _PushBullet(bullet_id); } Singleton <Log> .Instance.WriteInfo( string.Format( "all WEAPON_TYPE:{0} , targets:{1} , count:{2}", bullet_id, string.Join(",", (from id in fishids select id.ToString()).ToArray()), fishids.Length)); Singleton <Log> .Instance.WriteInfo( string.Format("requested WEAPON_TYPE:{0} , targets:{1} , count:{2}", bullet_id, logFishs, count)); Singleton <Log> .Instance.WriteInfo( string.Format( "request fishs:{0} count:{1} ", string.Join(",", (from id in _Requests.Keys select id.ToString()).ToArray()), _Requests.Count)); return(count); }
public abstract HitResponse[] TotalRequest(HitRequest request);