public static void HandleSummonAttack(Character chr, Packet packet) { if (!ParseAttackData(chr, packet, out AttackData ad, AttackTypes.Summon)) { return; } var summonId = ad.SummonID; if (chr.Summons.GetSummon(summonId, out var summon)) { //SendMagicAttack(chr, ad); SendSummonAttack(chr, summon, ad); var totalDamage = 0; foreach (var ai in ad.Attacks) { try { var mob = chr.Field.GetMob(ai.MobMapId); if (mob != null) { foreach (int amount in ai.Damages) { totalDamage += amount; mob.GiveDamage(chr, amount); } var dead = mob.CheckDead(mob.Position, ai.HitDelay, chr.PrimaryStats.BuffMesoUP.N); if (!dead) { switch (summon.SkillId) { case Constants.Ranger.Skills.SilverHawk: case Constants.Sniper.Skills.GoldenEagle: var sld = CharacterSkills.GetSkillLevelData(summon.SkillId, summon.SkillLevel); if (!mob.IsBoss && totalDamage > 0 && Rand32.NextBetween(0, 100) < sld.Property) { int stunTime = ai.HitDelay + 4000; long expireTime = MasterThread.CurrentTime + stunTime; var stat = mob.Status.BuffStun.Set(summon.SkillId, -1, expireTime); MobPacket.SendMobStatsTempSet(mob, ai.HitDelay, stat); } break; } } } } catch (Exception ex) { Program.MainForm.LogAppend(ex.ToString()); } } } }
public void AddEXP(uint value, bool IsLastHit = false, bool Quest = false) { if (value == 0 || PrimaryStats.Level >= 200 || PrimaryStats.HP <= 0) { return; } var amount = (int)(value > Int32.MaxValue ? Int32.MaxValue : value); var amnt = (uint)(PrimaryStats.EXP + amount); CharacterStatsPacket.SendGainEXP(this, amount, IsLastHit, Quest); var level = PrimaryStats.Level; var save = false; var expRequired = Constants.GetLevelEXP(PrimaryStats.Level); if (amnt >= expRequired) { short apgain = 0; short spgain = 0; short mpgain = 0; short hpgain = 0; var job = (short)(PrimaryStats.Job / 100); var intt = PrimaryStats.GetIntAddition(true); amnt -= (uint)expRequired; level++; // Update EXP required... expRequired = Constants.GetLevelEXP(level); apgain += Constants.ApPerLevel; hpgain += RNG.Range.generate( Constants.HpMpFormulaArguments[job, 0, (int)Constants.HpMpFormulaFields.HPMin], Constants.HpMpFormulaArguments[job, 0, (int)Constants.HpMpFormulaFields.HPMax], true ); mpgain += RNG.Range.generate( Constants.HpMpFormulaArguments[job, 0, (int)Constants.HpMpFormulaFields.MPMin], Constants.HpMpFormulaArguments[job, 0, (int)Constants.HpMpFormulaFields.MPMax], true ); // Additional buffing through INT stats mpgain += (short)( intt * Constants.HpMpFormulaArguments[job, 0, (int)Constants.HpMpFormulaFields.MPIntStatMultiplier] / 200 ); var improvedMaxHpIncreaseLvl = Skills.GetSkillLevel(Constants.Swordsman.Skills.ImprovedMaxHpIncrease); if (improvedMaxHpIncreaseLvl > 0) { hpgain += CharacterSkills.GetSkillLevelData(Constants.Swordsman.Skills.ImprovedMaxHpIncrease, improvedMaxHpIncreaseLvl).XValue; } var improvedMaxMpIncreaseLvl = Skills.GetSkillLevel(Constants.Magician.Skills.ImprovedMaxMpIncrease); if (improvedMaxMpIncreaseLvl > 0) { mpgain += CharacterSkills.GetSkillLevelData(Constants.Magician.Skills.ImprovedMaxMpIncrease, improvedMaxMpIncreaseLvl).XValue; } if (PrimaryStats.Job != 0) { spgain = Constants.SpPerLevel; } if (level >= 200) { amnt = 0; // TODO: Announce max level! } // Overflow? lets reduce it if (amnt >= expRequired) { amnt = (uint)(expRequired - 1); } _levelLog.Info(new LevelLogRecord { level = level, posX = Position.X, posY = Position.Y, }); ModifyMaxHP(hpgain); ModifyMaxMP(mpgain); SetLevel(level); AddAP(apgain); AddSP(spgain); ModifyHP(PrimaryStats.GetMaxHP(false)); ModifyMP(PrimaryStats.GetMaxMP(false)); save = true; } PrimaryStats.EXP = (int)amnt; // Calculate savepoints var stepOfSave = CalculateSaveStep(); var curDateTime = MasterThread.CurrentDate; if (!save) { if (lastSaveStep != stepOfSave) { var levelTimeSpan = curDateTime - LastSavepoint; if (levelTimeSpan.TotalSeconds >= 30) { _characterLog.Debug( $"Saving because user reached save threshold. Current {stepOfSave} last {lastSaveStep}"); save = true; LastSavepoint = curDateTime; } else { AssertForHack( levelTimeSpan.TotalSeconds < 20, $"Getting fast EXP ({levelTimeSpan.TotalSeconds} seconds since last savepoint)", levelTimeSpan.TotalSeconds < 15 ); } _characterLog.Debug( new SavepointLogRecord { level = PrimaryStats.Level, posX = Position.X, posY = Position.Y, totalMillisBetween = (int)levelTimeSpan.TotalMilliseconds, blocked = save == false } ); lastSaveStep = stepOfSave; } } else { lastSaveStep = stepOfSave; } if (save) { LastSavepoint = curDateTime; Save(); } CharacterStatsPacket.SendStatChange(this, (uint)CharacterStatsPacket.StatFlags.Exp, PrimaryStats.EXP); }
public LoadFailReasons Load(string IP) { var imitateId = RedisBackend.Instance.GetImitateID(ID); var imitating = imitateId.HasValue; var originalId = ID; if (imitating) { ID = imitateId.Value; _characterLog.Debug($"Loading character {ID} from IP {IP}... (IMITATION from ID {originalId})"); } else { _characterLog.Debug($"Loading character {ID} from IP {IP}..."); } // Initial load using (var data = (MySqlDataReader)Server.Instance.CharacterDatabase.RunQuery( "SELECT " + "characters.*, users.admin, users.superadmin, users.donator, users.beta, users.last_ip, users.online " + "FROM characters " + "LEFT JOIN users ON users.id = characters.userid " + "WHERE characters.id = @id", "@id", originalId)) { if (!data.Read()) { _characterLog.Debug("Loading failed: unknown character."); return(LoadFailReasons.UnknownCharacter); } if (data.GetString("last_ip") != IP && !imitating) { #if DEBUG Program.MainForm.LogAppend("Allowed player " + this.ID + " to log in from different IP because source is running in debug mode!"); #else _characterLog.Debug("Loading failed: not from previous IP."); return(LoadFailReasons.NotFromPreviousIP); #endif } UserID = data.GetInt32("userid"); Name = data.GetString("name"); GMLevel = data.GetByte("admin"); Donator = data.GetBoolean("donator"); BetaPlayer = data.GetBoolean("beta"); if (imitating) { ImitatorName = Name; } else { ImitatorName = null; } } var tmpUserId = UserID; using (var data = (MySqlDataReader)Server.Instance.CharacterDatabase.RunQuery( "SELECT " + "characters.*, users.last_ip, users.online, users.quiet_ban_expire, users.quiet_ban_reason " + "FROM characters " + "LEFT JOIN users ON users.id = characters.userid " + "WHERE characters.id = @id", "@id", ID)) { if (!data.Read()) { _characterLog.Debug("Loading failed: unknown character."); if (imitating) { // Reset! RedisBackend.Instance.SetImitateID(originalId, 0); } return(LoadFailReasons.UnknownCharacter); } UserID = data.GetInt32("userid"); // For cashitem loading Name = data.GetString("name"); Gender = data.GetByte("gender"); Skin = data.GetByte("skin"); Hair = data.GetInt32("hair"); Face = data.GetInt32("eyes"); PetCashId = data.GetInt64("pet_cash_id"); MutedUntil = data.GetDateTime("quiet_ban_expire"); MuteReason = data.GetByte("quiet_ban_reason"); LastSavepoint = data.GetDateTime("last_savepoint"); LastPlayTimeSave = MasterThread.CurrentTime; var _mapId = data.GetInt32("map"); Map field; if (!DataProvider.Maps.TryGetValue(_mapId, out field)) { Program.MainForm.LogAppend( "The map of {0} is not valid (nonexistant)! Map was {1}. Returning to 0", ID, _mapId); field = DataProvider.Maps[0]; MapPosition = 0; } Field = field; // Push back player when there's a forced return value if (field.ForcedReturn != Constants.InvalidMap) { _mapId = field.ForcedReturn; if (!DataProvider.Maps.TryGetValue(_mapId, out field)) { Program.MainForm.LogAppend( "The map of {0} is not valid (nonexistant)! Map was {1}. Returning to 0", ID, _mapId); // Note: using Field here Field = DataProvider.Maps[0]; } else { Field = DataProvider.Maps[_mapId]; } MapPosition = 0; } else { MapPosition = (byte)data.GetInt16("pos"); } // Select portal to spawn on. { Portal portal = Field.SpawnPoints.Find(x => x.ID == MapPosition); if (portal == null) { portal = Field.GetRandomStartPoint(); } Position = new Pos(portal.X, portal.Y); } Stance = 0; Foothold = 0; CalcDamageRandomizer = new Rand32(); RndActionRandomizer = new Rand32(); PrimaryStats = new CharacterPrimaryStats(this) { Level = data.GetByte("level"), Job = data.GetInt16("job"), Str = data.GetInt16("str"), Dex = data.GetInt16("dex"), Int = data.GetInt16("int"), Luk = data.GetInt16("luk"), HP = data.GetInt16("chp"), MaxHP = data.GetInt16("mhp"), MP = data.GetInt16("cmp"), MaxMP = data.GetInt16("mmp"), AP = data.GetInt16("ap"), SP = data.GetInt16("sp"), EXP = data.GetInt32("exp"), Fame = data.GetInt16("fame"), BuddyListCapacity = data.GetInt32("buddylist_size") }; // Make sure we don't update too many times lastSaveStep = CalculateSaveStep(); } Inventory = new CharacterInventory(this); Inventory.LoadInventory(); UserID = tmpUserId; Ring.LoadRings(this); Skills = new CharacterSkills(this); Skills.LoadSkills(); Storage = new CharacterStorage(this); Storage.Load(); Buffs = new CharacterBuffs(this); Summons = new CharacterSummons(this); Quests = new CharacterQuests(this); Quests.LoadQuests(); Variables = new CharacterVariables(this); Variables.Load(); GameStats = new CharacterGameStats(this); GameStats.Load(); Wishlist = new List <int>(); using (var data = (MySqlDataReader)Server.Instance.CharacterDatabase.RunQuery("SELECT serial FROM character_wishlist WHERE charid = " + ID)) { while (data.Read()) { Wishlist.Add(data.GetInt32(0)); } } // Loading done, switch back ID ID = originalId; InitDamageLog(); SetIncExpRate(); var muteTimeSpan = RedisBackend.Instance.GetCharacterMuteTime(ID); if (muteTimeSpan.HasValue) { HacklogMuted = MasterThread.CurrentDate.Add(muteTimeSpan.Value); } else { HacklogMuted = DateTime.MinValue; } Undercover = RedisBackend.Instance.IsUndercover(ID); RedisBackend.Instance.SetPlayerOnline( UserID, Server.Instance.GetOnlineId() ); _characterLog.Debug("Loaded!"); return(LoadFailReasons.None); }
public static void HandleStats(Character chr, Packet packet) { uint flag = packet.ReadUInt(); if (chr.AssertForHack(chr.PrimaryStats.AP <= 0, "Trying to use AP, but nothing left.")) { InventoryPacket.NoChange(chr); return; } short jobTrack = Constants.getJobTrack(chr.PrimaryStats.Job); switch ((StatFlags)flag) { case StatFlags.Str: { if (chr.PrimaryStats.Str >= Constants.MaxStat) { InventoryPacket.NoChange(chr); return; } chr.AddStr(1); break; } case StatFlags.Dex: { if (chr.PrimaryStats.Dex >= Constants.MaxStat) { InventoryPacket.NoChange(chr); return; } chr.AddDex(1); break; } case StatFlags.Int: { if (chr.PrimaryStats.Int >= Constants.MaxStat) { InventoryPacket.NoChange(chr); return; } chr.AddInt(1); break; } case StatFlags.Luk: { if (chr.PrimaryStats.Luk >= Constants.MaxStat) { InventoryPacket.NoChange(chr); return; } chr.AddLuk(1); break; } case StatFlags.MaxHp: { if (chr.PrimaryStats.MaxHP >= Constants.MaxMaxHp) { InventoryPacket.NoChange(chr); return; } short hpGain = 0; hpGain += RNG.Range.generate( Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.HPMin], Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.HPMax], true ); byte improvedMaxHpIncreaseLvl = chr.Skills.GetSkillLevel(Constants.Swordsman.Skills.ImprovedMaxHpIncrease); if (improvedMaxHpIncreaseLvl > 0) { hpGain += CharacterSkills.GetSkillLevelData(Constants.Swordsman.Skills.ImprovedMaxHpIncrease, improvedMaxHpIncreaseLvl).XValue; } chr.ModifyMaxHP(hpGain); break; } case StatFlags.MaxMp: { if (chr.PrimaryStats.MaxMP >= Constants.MaxMaxMp) { InventoryPacket.NoChange(chr); return; } short mpGain = 0; short intt = chr.PrimaryStats.GetIntAddition(true); mpGain += RNG.Range.generate( Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPMin], Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPMax], true ); // Additional buffing through INT stats mpGain += (short)( intt * Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPIntStatMultiplier] / 200 ); byte improvedMaxMpIncreaseLvl = chr.Skills.GetSkillLevel(Constants.Magician.Skills.ImprovedMaxMpIncrease); if (improvedMaxMpIncreaseLvl > 0) { mpGain += CharacterSkills.GetSkillLevelData(Constants.Magician.Skills.ImprovedMaxMpIncrease, improvedMaxMpIncreaseLvl).XValue; } chr.ModifyMaxMP(mpGain); break; } default: { Program.MainForm.LogAppend("Unknown type {0:X4}", flag); break; } } chr.AddAP(-1, true); chr.PrimaryStats.CalculateAdditions(false, false); }