public Buff(PlayerInfo playerInfo, PlayerAbnormals buffs, Entity currentBoss) { InitializeComponent(); _header = new EnduranceDebuffHeader(); ContentWidth = 1020; Update(playerInfo, buffs, currentBoss); }
public void Repaint(PlayerInfo playerInfo, PlayerAbnormals buffs, long totalDamage, long firstHit, long lastHit, Entity currentBoss, bool timedEncounter) { PlayerInfo = playerInfo; _buffs = buffs; _currentBoss = currentBoss; _firstHit = firstHit; _lastHit = lastHit; _timedEncounter = timedEncounter; LabelDps.Content = GlobalDps; LabelDps.ToolTip = "Individual dps: " + Dps; LabelCritRate.Content = CritRate; var intervalTimespan = TimeSpan.FromSeconds(playerInfo.Dealt.Interval(_currentBoss)); LabelCritRate.ToolTip = "Hits received: " + HitReceived + " - Damage received: " + DamageReceived + " - Fight Duration: " + intervalTimespan.ToString(@"mm\:ss"); LabelDamagePart.Content = DamagePart(totalDamage); LabelDamagePart.ToolTip = "Damage done: " + Damage; var skills = Skills(_timedEncounter); var allskills = AllSkills(_timedEncounter); _windowSkill?.Update(skills, allskills, playerInfo, _buffs, _currentBoss, _timedEncounter, firstHit, lastHit); DpsIndicator.Width = 265 * PlayerInfo.Dealt.DamageFraction(_currentBoss, totalDamage, _timedEncounter) / 100; }
public Buff(PlayerDamageDealt playerDamageDealt, PlayerAbnormals buffs, EntityInformation entityInformation) { InitializeComponent(); _header = new EnduranceDebuffHeader(); ContentWidth = 1020; EnduranceAbnormality.Items.Clear(); EnduranceAbnormality.Items.Add(_header); var counter = 0; foreach (var abnormality in buffs.Times.Where(x => x.Value.Duration(playerDamageDealt.BeginTime, playerDamageDealt.EndTime) > 0)) { EnduranceDebuff abnormalityUi; if (_enduranceDebuffsList.Count > counter) { abnormalityUi = _enduranceDebuffsList[counter]; } else { abnormalityUi = new EnduranceDebuff(); _enduranceDebuffsList.Add(abnormalityUi); } abnormalityUi.Update(abnormality.Key, abnormality.Value, playerDamageDealt.BeginTime, playerDamageDealt.EndTime); EnduranceAbnormality.Items.Add(abnormalityUi); counter++; } }
public void Update(PlayerInfo playerInfo, PlayerAbnormals buffs, Entity currentBoss) { EnduranceAbnormality.Items.Clear(); EnduranceAbnormality.Items.Add(_header); var counter = 0; foreach (var abnormality in buffs.Times) { EnduranceDebuff abnormalityUi; if (_enduranceDebuffsList.Count > counter) { abnormalityUi = _enduranceDebuffsList[counter]; } else { abnormalityUi = new EnduranceDebuff(); _enduranceDebuffsList.Add(abnormalityUi); } abnormalityUi.Update(abnormality.Key, abnormality.Value, playerInfo.Dealt.FirstHit(currentBoss), playerInfo.Dealt.LastHit(currentBoss)); EnduranceAbnormality.Items.Add(abnormalityUi); counter++; } }
public Skills(PlayerStats parent, PlayerDamageDealt playerDamageDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs, bool timedEncounter) { InitializeComponent(); _parent = parent; CloseWindow.Source = BasicTeraData.Instance.ImageDatabase.Close.Source; BackgroundColor.Opacity = BasicTeraData.Instance.WindowData.SkillWindowOpacity; Update(playerDamageDealt, entityInformation, skills, buffs, timedEncounter); }
public Skills(PlayerStats parent, PlayerDamageDealt playerDamageDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs, bool timedEncounter) { Owner = GetWindow(parent); InitializeComponent(); _parent = parent; ClassImage.Source = ClassIcons.Instance.GetImage(playerDamageDealt.Source.Class).Source; //BasicTeraData.Instance.ImageDatabase.Close.Source; Update(playerDamageDealt, entityInformation, skills, buffs, timedEncounter); }
public void Update(Dictionary <long, Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats> > timedSkills, Dictionary <long, Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats> > timedAllSkills, PlayerInfo playerinfo, PlayerAbnormals buffs, Entity currentBoss, bool timedEncounter, long firstHit, long lastHit) { var lastTick = (lastHit + 1) * TimeSpan.TicksPerSecond - 1; var firstTick = firstHit * TimeSpan.TicksPerSecond; var death = buffs.Death; if (death == null) { DeathCounter.Content = 0; DeathDuration.Content = "0s"; } else { DeathCounter.Content = death.Count(firstTick, lastTick); var duration = death.Duration(firstTick, lastTick); var interval = TimeSpan.FromTicks(duration); DeathDuration.Content = interval.ToString(@"mm\:ss"); } var aggro = buffs.Aggro(currentBoss?.NpcE); if (aggro == null) { AggroCounter.Content = 0; AggroDuration.Content = "0s"; } else { AggroCounter.Content = aggro.Count(firstTick, lastTick); var duration = aggro.Duration(firstTick, lastTick); var interval = TimeSpan.FromTicks(duration); AggroDuration.Content = interval.ToString(@"mm\:ss"); } var skills = NoTimedSkills(timedSkills); var allSkills = NoTimedSkills(timedAllSkills); if (_skillDps == null) { _skillDps = new SkillsDetail(skills, SkillsDetail.Type.Dps, currentBoss, timedEncounter); _skillHeal = new SkillsDetail(allSkills, SkillsDetail.Type.Heal, currentBoss, timedEncounter); _skillMana = new SkillsDetail(allSkills, SkillsDetail.Type.Mana, currentBoss, timedEncounter); _buff = new Buff(playerinfo, buffs, currentBoss); } else { _skillDps.Update(skills, currentBoss, timedEncounter); _skillHeal.Update(new Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats>(allSkills), currentBoss, timedEncounter); _skillMana.Update(new Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats>(allSkills), currentBoss, timedEncounter); _buff.Update(playerinfo, buffs, currentBoss); } HealPanel.Content = _skillHeal; DpsPanel.Content = _skillDps; ManaPanel.Content = _skillMana; BuffPanel.Content = _buff; }
public PlayerStats(PlayerInfo playerInfo, PlayerAbnormals buffs) { InitializeComponent(); PlayerInfo = playerInfo; _buffs = buffs; Image = ClassIcons.Instance.GetImage(PlayerInfo.Class).Source; Class.Source = Image; LabelName.Content = PlayerName; LabelName.ToolTip = playerInfo.Player.FullName; }
public Skills(Dictionary <long, Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats> > timedSkills, Dictionary <long, Dictionary <DamageMeter.Skills.Skill.Skill, SkillStats> > timedAllSkills, PlayerStats parent, PlayerInfo playerInfo, PlayerAbnormals buffs, Entity currentBoss, bool timedEncounter, long firstHit, long lastHit) { InitializeComponent(); TabControl.SelectionChanged += TabControlOnSelectionChanged; _parent = parent; CloseWindow.Source = BasicTeraData.Instance.ImageDatabase.Close.Source; BackgroundColor.Opacity = BasicTeraData.Instance.WindowData.SkillWindowOpacity; Update(timedSkills, timedAllSkills, playerInfo, buffs, currentBoss, timedEncounter, firstHit, lastHit); }
public PlayerStats(PlayerDamageDealt playerDamageDealt, PlayerHealDealt playeHealDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs) { InitializeComponent(); PlayerDamageDealt = playerDamageDealt; PlayerHealDealt = playeHealDealt; EntityInformation = entityInformation; Skills = skills; _buffs = buffs; Image = ClassIcons.Instance.GetImage(PlayerDamageDealt.Source.Class).Source; Class.Source = Image; LabelName.Content = PlayerName; LabelName.ToolTip = PlayerDamageDealt.Source.FullName; }
public void Repaint(PlayerDamageDealt playerDamageDealt, PlayerHealDealt playerHealDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs, bool timedEncounter) { PlayerHealDealt = playerHealDealt; EntityInformation = entityInformation; PlayerDamageDealt = playerDamageDealt; _buffs = buffs; _timedEncounter = timedEncounter; Skills = skills; LabelDps.Content = GlobalDps; LabelDps.ToolTip = $"{LP.Individual_dps}: {Dps}"; LabelCritRate.Content = PlayerDamageDealt.Source.IsHealer && BasicTeraData.Instance.WindowData.ShowHealCrit ? CritRateHeal : BasicTeraData.Instance.WindowData.ShowCritDamageRate ? CritDamageRate : CritRate; var intervalTimespan = TimeSpan.FromSeconds(PlayerDamageDealt.Interval / TimeSpan.TicksPerSecond); LabelCritRate.ToolTip = $"{LP.Fight_Duration}: {intervalTimespan.ToString(@"mm\:ss")}"; LabelCritRate.Foreground = PlayerDamageDealt.Source.IsHealer && BasicTeraData.Instance.WindowData.ShowHealCrit ? Brushes.LawnGreen : BasicTeraData.Instance.WindowData.ShowCritDamageRate ? Brushes.Orange : Brushes.LightCoral; LabelDamagePart.Content = DamagePart; LabelDamagePart.ToolTip = $"{LP.Damage_done}: {Damage}"; _windowSkill?.Update(PlayerDamageDealt, EntityInformation, Skills, _buffs, _timedEncounter); Spacer.Width = 0; GridStats.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var SGrid = ((MainWindow)((FrameworkElement)((FrameworkElement)((FrameworkElement)Parent).Parent).Parent).Parent).SGrid; var EGrid = ((MainWindow)((FrameworkElement)((FrameworkElement)((FrameworkElement)Parent).Parent).Parent).Parent).EGrid; SGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var mainWidth = SGrid.DesiredSize.Width; Spacer.Width = mainWidth > GridStats.DesiredSize.Width ? mainWidth - GridStats.DesiredSize.Width : 0; var an = new DoubleAnimation(DpsIndicator.ActualWidth, EntityInformation.TotalDamage == 0 ? mainWidth : mainWidth * PlayerDamageDealt.Amount / EntityInformation.TotalDamage, TimeSpan.FromMilliseconds(400)) { EasingFunction = new QuadraticEase() }; DpsIndicator.BeginAnimation(WidthProperty, an); EGrid.MaxWidth = Math.Max(mainWidth, GridStats.DesiredSize.Width); }
public PlayerStats(PlayerDamageDealt playerDamageDealt, PlayerHealDealt playeHealDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs) { InitializeComponent(); PlayerDamageDealt = playerDamageDealt; PlayerHealDealt = playeHealDealt; EntityInformation = entityInformation; Skills = skills; _buffs = buffs; Image = ClassIcons.Instance.GetImage(PlayerDamageDealt.Source.Class).Source; Class.Source = Image; LabelName.Content = PlayerName; LabelName.ToolTip = PlayerDamageDealt.Source.FullName; if (playerDamageDealt.Source.Class != PlayerClass.Common) { LabelLevel.Content = playerDamageDealt.Source.Level; } var isMe = PlayerDamageDealt.Source.User.Id.Id == PacketProcessor.Instance.PlayerTracker.Me().User.Id.Id; var meCol100 = BasicTeraData.Instance.WindowData.PlayerColor; var meCol30 = Color.FromArgb((byte)(meCol100.A == 0 ? 0 : 60), meCol100.R, meCol100.G, meCol100.B); var meCol0 = Color.FromArgb(0, meCol100.R, meCol100.G, meCol100.B); var dpsCol100 = BasicTeraData.Instance.WindowData.DpsColor; var dpsCol30 = Color.FromArgb((byte)(dpsCol100.A == 0 ? 0 : 60), dpsCol100.R, dpsCol100.G, dpsCol100.B); var dpsCol0 = Color.FromArgb(0, dpsCol100.R, dpsCol100.G, dpsCol100.B); var tankCol100 = BasicTeraData.Instance.WindowData.TankColor; var tankCol30 = Color.FromArgb((byte)(tankCol100.A == 0 ? 0 : 60), tankCol100.R, tankCol100.G, tankCol100.B); var tankCol0 = Color.FromArgb(0, tankCol100.R, tankCol100.G, tankCol100.B); var healCol100 = BasicTeraData.Instance.WindowData.HealerColor; var healCol30 = Color.FromArgb((byte)(healCol100.A == 0 ? 0 : 60), healCol100.R, healCol100.G, healCol100.B); var healCol0 = Color.FromArgb(0, healCol100.R, healCol100.G, healCol100.B); var unkCol100 = Color.FromArgb(255, 200, 200, 200); var unkCol30 = Color.FromArgb(60, 200, 200, 200); var unkCol0 = Color.FromArgb(0, 200, 200, 200); if (playerDamageDealt != null) { switch (playerDamageDealt.Source.Class) { case Tera.Game.PlayerClass.Warrior: case Tera.Game.PlayerClass.Slayer: case Tera.Game.PlayerClass.Berserker: case Tera.Game.PlayerClass.Sorcerer: case Tera.Game.PlayerClass.Archer: case Tera.Game.PlayerClass.Reaper: case Tera.Game.PlayerClass.Gunner: case Tera.Game.PlayerClass.Ninja: case Tera.Game.PlayerClass.Valkyrie: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(isMe? meCol30 : dpsCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(isMe? meCol0 : dpsCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(isMe ? meCol100 : dpsCol100); break; case Tera.Game.PlayerClass.Priest: case Tera.Game.PlayerClass.Mystic: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(isMe? meCol30 :healCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(isMe ? meCol0 : healCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(isMe ? meCol100 : healCol100); break; case Tera.Game.PlayerClass.Brawler: case Tera.Game.PlayerClass.Lancer: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(isMe? meCol30 :tankCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(isMe ? meCol0 : tankCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(isMe ? meCol100 : tankCol100); break; case Tera.Game.PlayerClass.Common: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(unkCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(unkCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(unkCol100); break; } } }
public void Repaint(PlayerDamageDealt playerDamageDealt, PlayerHealDealt playerHealDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs, bool timedEncounter) { PlayerHealDealt = playerHealDealt; EntityInformation = entityInformation; PlayerDamageDealt = playerDamageDealt; _buffs = buffs; _timedEncounter = timedEncounter; Skills = skills; LabelDps.Content = GlobalDps; LabelDps.ToolTip = $"{LP.Individual_dps}: {Dps}"; LabelCritRate.Content = PlayerDamageDealt.Source.IsHealer && BasicTeraData.Instance.WindowData.ShowHealCrit ? CritRateHeal : CritRate; var intervalTimespan = TimeSpan.FromSeconds(PlayerDamageDealt.Interval/TimeSpan.TicksPerSecond); LabelCritRate.ToolTip = $"{LP.Fight_Duration}: {intervalTimespan.ToString(@"mm\:ss")}"; LabelCritRate.Foreground = PlayerDamageDealt.Source.IsHealer && BasicTeraData.Instance.WindowData.ShowHealCrit ? Brushes.LawnGreen : Brushes.LightCoral; LabelDamagePart.Content = DamagePart; LabelDamagePart.ToolTip = $"{LP.Damage_done}: {Damage}"; _windowSkill?.Update(PlayerDamageDealt, EntityInformation, Skills, _buffs, _timedEncounter); Spacer.Width = 0; GridStats.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var SGrid = ((DamageMeter.UI.MainWindow)((System.Windows.FrameworkElement) ((System.Windows.FrameworkElement) ((System.Windows.FrameworkElement) this.Parent).Parent).Parent).Parent) .SGrid; var EGrid = ((DamageMeter.UI.MainWindow)((System.Windows.FrameworkElement) ((System.Windows.FrameworkElement)((System.Windows.FrameworkElement)this.Parent).Parent).Parent).Parent) .EGrid; SGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var mainWidth = SGrid.DesiredSize.Width; Spacer.Width = mainWidth > GridStats.DesiredSize.Width ? mainWidth - GridStats.DesiredSize.Width : 0; DpsIndicator.Width = EntityInformation.TotalDamage == 0 ? mainWidth : mainWidth * PlayerDamageDealt.Amount/EntityInformation.TotalDamage; EGrid.MaxWidth = Math.Max(mainWidth, GridStats.DesiredSize.Width); }
public void Update(PlayerDamageDealt playerDamageDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs, bool timedEncounter) { if (_skills == null || skills != null) { _skills = skills; var death = buffs.Death; if (death == null) { DeathCounter.Content = 0; DeathDuration.Content = "0" + LP.Seconds; } else { DeathCounter.Content = death.Count(entityInformation.BeginTime, entityInformation.EndTime); var duration = death.Duration(entityInformation.BeginTime, entityInformation.EndTime); var interval = TimeSpan.FromTicks(duration); DeathDuration.Content = interval.ToString(@"mm\:ss"); } var aggro = buffs.Aggro(entityInformation.Entity); if (aggro == null) { AggroCounter.Content = 0; AggroDuration.Content = "0" + LP.Seconds; } else { AggroCounter.Content = aggro.Count(entityInformation.BeginTime, entityInformation.EndTime); var duration = aggro.Duration(entityInformation.BeginTime, entityInformation.EndTime); var interval = TimeSpan.FromTicks(duration); AggroDuration.Content = interval.ToString(@"mm\:ss"); } //return; //var tabItem = (TabItem) TabControl.SelectedItem; //if (tabItem == null) //{ // TabControl.SelectedIndex = 0; // tabItem = (TabItem) TabControl.SelectedItem; //} //switch (tabItem.Name) //{ // case "DpsPanel": // if (_skillDps == null) // { _skillDps = new SkillsDetail( SkillAggregate.GetAggregate(playerDamageDealt, entityInformation.Entity, _skills, timedEncounter, Database.Database.Type.Damage), Database.Database.Type.Damage); //} DpsPanel.Content = _skillDps; // return; //case "HealPanel": // if (_skillHeal == null) // { _skillHeal = new SkillsDetail( SkillAggregate.GetAggregate(playerDamageDealt, entityInformation.Entity, _skills, timedEncounter, Database.Database.Type.Heal), Database.Database.Type.Heal); // } HealPanel.Content = _skillHeal; // return; //case "ManaPanel": // if (_skillMana == null) // { _skillMana = new SkillsDetail( SkillAggregate.GetAggregate(playerDamageDealt, entityInformation.Entity, _skills, timedEncounter, Database.Database.Type.Mana), Database.Database.Type.Mana); // } ManaPanel.Content = _skillMana; // return; //case "BuffPanel": // if (_buff == null) // { _buff = new Buff(playerDamageDealt, buffs, entityInformation); // } BuffPanel.Content = _buff; // return; //case "SkillsDealtPanel": // if (_skillDealtLog == null) // { _skillDealtLog = new SkillsLog( _skills?.GetSkillsDealt(playerDamageDealt.Source.User, entityInformation.Entity, timedEncounter), false); // } SkillsDealtPanel.Content = _skillDealtLog; // return; //case "SkillsReceivedPanel": // if (_skillReceivedLog == null) // { _skillReceivedLog = new SkillsLog( _skills?.GetSkillsReceived(playerDamageDealt.Source.User, timedEncounter), true); // } SkillsReceivedPanel.Content = _skillReceivedLog; //return; } }
public PlayerStats(PlayerDamageDealt playerDamageDealt, PlayerHealDealt playeHealDealt, EntityInformation entityInformation, Database.Structures.Skills skills, PlayerAbnormals buffs) { InitializeComponent(); PlayerDamageDealt = playerDamageDealt; PlayerHealDealt = playeHealDealt; EntityInformation = entityInformation; Skills = skills; _buffs = buffs; Image = ClassIcons.Instance.GetImage(PlayerDamageDealt.Source.Class).Source; Class.Source = Image; LabelName.Content = PlayerName; LabelName.ToolTip = PlayerDamageDealt.Source.FullName; var dpsCol100 = Color.FromArgb(255, 244, 164, 66); var dpsCol30 = Color.FromArgb(60, 244, 164, 66); var dpsCol0 = Color.FromArgb(0, 244, 164, 66); var tankCol100 = Color.FromArgb(255, 68, 178, 252); var tankCol30 = Color.FromArgb(60, 68, 178, 252); var tankCol0 = Color.FromArgb(0, 68, 178, 252); var healCol100 = Color.FromArgb(255, 59, 226, 75); var healCol30 = Color.FromArgb(60, 59, 226, 75); var healCol0 = Color.FromArgb(0, 59, 226, 75); var unkCol100 = Color.FromArgb(255, 200, 200, 200); var unkCol30 = Color.FromArgb(60, 200, 200, 200); var unkCol0 = Color.FromArgb(0, 200, 200, 200); if (playerDamageDealt != null) { switch (playerDamageDealt.Source.Class) { case Tera.Game.PlayerClass.Warrior: case Tera.Game.PlayerClass.Slayer: case Tera.Game.PlayerClass.Berserker: case Tera.Game.PlayerClass.Sorcerer: case Tera.Game.PlayerClass.Archer: case Tera.Game.PlayerClass.Reaper: case Tera.Game.PlayerClass.Gunner: case Tera.Game.PlayerClass.Ninja: case Tera.Game.PlayerClass.Valkyrie: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(dpsCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(dpsCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(dpsCol100); break; case Tera.Game.PlayerClass.Priest: case Tera.Game.PlayerClass.Mystic: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(healCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(healCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(healCol100); break; case Tera.Game.PlayerClass.Brawler: case Tera.Game.PlayerClass.Lancer: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(tankCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(tankCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(tankCol100); break; case Tera.Game.PlayerClass.Common: (DpsIndicator.Background as LinearGradientBrush).GradientStops[0] = new GradientStop(unkCol30, 1); (DpsIndicator.Background as LinearGradientBrush).GradientStops[1] = new GradientStop(unkCol0, 0); DpsIndicator.BorderBrush = new SolidColorBrush(unkCol100); break; } } }