From 4aed1cc51fe3e94502d379ae179e8395933d7234 Mon Sep 17 00:00:00 2001 From: "Ryan P. McKinnon" <15917743+mrhoribu@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:34:54 -0400 Subject: [PATCH 1/3] Revert "Revert "feat: add DragonRealms customizations"" --- profanity.rb | 398 ++++++++++++++++++++++++++++++++++++++++- templates/default.xml | 11 +- templates/original.xml | 11 +- templates/tysong.xml | 11 +- ui/exp.rb | 159 ++++++++++++++++ ui/perc.rb | 108 +++++++++++ ui/text.rb | 3 +- 7 files changed, 694 insertions(+), 7 deletions(-) create mode 100644 ui/exp.rb create mode 100644 ui/perc.rb diff --git a/profanity.rb b/profanity.rb index dd12137..626aa8d 100644 --- a/profanity.rb +++ b/profanity.rb @@ -41,6 +41,8 @@ require_relative "./ui/indicator.rb" require_relative "./ui/progress.rb" require_relative "./ui/text.rb" +require_relative "./ui/exp.rb" +require_relative "./ui/perc.rb" require_relative "./plugin/autocomplete.rb" require_relative "./settings/settings.rb" @@ -258,6 +260,16 @@ def add_prompt(window, prompt_text, cmd = "") 'page_up' => 339, 'end' => 360, 'resize' => 410, + 'num_7' => 449, + 'num_8' => 450, + 'num_9' => 451, + 'num_4' => 452, + 'num_5' => 453, + 'num_6' => 454, + 'num_1' => 455, + 'num_2' => 456, + 'num_3' => 457, + 'num_enter' => 459, 'ctrl+delete' => 513, 'alt+down' => 517, 'ctrl+down' => 519, @@ -461,10 +473,15 @@ def get_color_pair_id(fg_code, bg_code) window.layout = [e.attributes['height'], e.attributes['width'], e.attributes['top'], e.attributes['left']] window.scrollok(true) window.max_buffer_size = e.attributes['buffer-size'] || 1000 + window.time_stamp = e.attributes['timestamp'] e.attributes['value'].split(',').each { |str| stream_handler[str] = window } end + elsif e.attributes['class'] == 'exp' + stream_handler['exp'] = ExpWindow.new(height, width - 1, top, left) + elsif e.attributes['class'] == 'percWindow' + stream_handler['percWindow'] = PercWindow.new(height, width - 1, top, left) elsif e.attributes['class'] == 'countdown' if e.attributes['value'] and (window = previous_countdown_handler[e.attributes['value']]) previous_countdown_handler[e.attributes['value']] = nil @@ -1282,7 +1299,7 @@ def get_color_pair_id(fg_code, bg_code) oc[:start] = 0 end - if current_stream.nil? or stream_handler[current_stream] or (current_stream =~ /^(?:death|logons|thoughts|voln|familiar)$/) + if current_stream.nil? or stream_handler[current_stream] or (current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics)$/) SETTINGS_LOCK.synchronize { HIGHLIGHT.each_pair { |regex, colors| pos = 0 @@ -1407,12 +1424,380 @@ def get_color_pair_id(fg_code, bg_code) } line_colors.push(h) end + elsif current_stream == 'exp' + window = stream_handler['exp'] + elsif current_stream == 'percWindow' + window = stream_handler['percWindow'] + all_spells = { + 'Abandoned Heart' => 'ABAN', + 'Absolution' => 'Absolution', + 'Acid Splash' => 'ACS', + 'Aegis of Granite' => 'AEG', + 'Aesandry Darlaeth' => 'AD', + 'Aesrela Everild' => 'AE', + 'Aether Cloak' => 'AC', + 'Aether Wolves' => 'AEWO', + 'Aethrolysis' => 'Aethrolysis', + 'Avren Aevareae' => 'AVA', + 'Aggressive Stance' => 'AGS', + 'Air Bubble' => 'AB', + 'Air Lash' => 'ALA', + "Alamhif's Gift" => 'AG', + "Albreda's Balm" => 'ALB', + "Anther's Call" => 'ANC', + 'Anti-Stun' => 'AS', + "Arbiter's Stylus" => 'ARS', + 'Arc Light' => 'AL', + "Artificer's Eye" => 'ART', + 'Aspects of the All-God' => 'ALL', + "Aspirant's Aegis" => 'AA', + 'Athleticism' => 'Athleticism', + 'Aura Sight' => 'AUS', + 'Aura of Tongues' => 'AOT', + 'Auspice' => 'Auspice', + 'Awaken' => 'Awaken', + 'Awaken Forest' => 'AF', + 'Banner of Truce' => 'BOT', + 'Bear Strength' => 'BES', + 'Beckon the Naga' => 'BTN', + 'Benediction' => 'Benediction', + 'Blend' => 'Blend', + 'Bless' => 'Bless', + 'Blessing of the Fae' => 'BOTF', + 'Bloodthorns' => 'Bloodthorns', + 'Blood Burst' => 'BLB', + 'Blood Staunching' => 'BS', + 'Blufmor Garaen' => 'BG', + 'Blur' => 'Blur', + 'Bond Armaments' => 'BA', + "Braun's Conjecture" => 'BC', + 'Breath of Storms' => 'BOS', + 'Burden' => 'Burden', + 'Burn' => 'Burn', + "Butcher's Eye" => 'BUE', + 'Cage of Light' => 'CoL', + 'Calcified Hide' => 'CH', + 'Call from Beyond' => 'CFB', + 'Calm' => 'Calm', + 'Caress of the Sun' => 'CARE', + 'Carrion Call' => 'CAC', + 'Centering' => 'Centering', + 'Chain Lightning' => 'CL', + 'Cheetah Swiftness' => 'CS', + 'Chill Spirit' => 'CHS', + 'Circle of Sympathy' => 'COS', + 'Clarity' => 'Clarity', + 'Claws of the Cougar' => 'COTC', + 'Clear Vision' => 'CV', + 'Compel' => 'Compel', + 'Compost' => 'Compost', + 'Consume Flesh' => 'CF', + 'Contingency' => 'Contingency', + 'Courage' => 'CO', + 'Crystal Dart' => 'CRD', + "Crusader's Challenge" => 'CRC', + 'Cure Disease' => 'CD', + 'Curse of the Wilds' => 'COTW', + 'Curse of Zachriedek' => 'COZ', + "Damaris' Lullaby" => 'DALU', + 'Dazzle' => 'Dazzle', + 'Deadfall' => 'DF', + "Demrris' Resolve" => 'DMRS', + "Desert's Maelstrom" => 'DEMA', + 'Destiny Cipher' => 'DC', + 'Devitalize' => 'DEVI', + 'Devolve' => 'DE', + 'Devour' => 'Devour', + 'Dispel' => 'Dispel', + 'Distant Gaze' => 'DG', + 'Dinazen Olkar' => 'DO', + 'Divine Armor' => 'DA', + 'Divine Guidance' => 'DIG', + 'Divine Radiance' => 'DR', + "Dragon's Breath" => 'DB', + 'Drought' => 'Drought', + 'Drums of the Snake' => 'DRUM', + 'Ease Burden' => 'EASE', + "Eagle's Cry" => 'EC', + 'Earth Meld' => 'EM', + 'Echoes of Aether' => 'ECHO', + "Eillie's Cry" => 'ECRY', + 'Elision' => 'ELI', + 'Electrostatic Eddy' => 'EE', + "Emuin's Candlelight" => 'EMC', + 'Enrichment' => 'ENRICH', + 'Essence of Yew' => 'EY', + 'Ethereal Fissure' => 'ETF', + 'Ethereal Shield' => 'ES', + 'Eye of Kertigen' => 'EYE', + 'Eyes of the Blind' => 'EOTB', + "Eylhaar's Feast" => 'EF', + "Faenella's Grace" => 'FAE', + 'Failure of the Forge' => 'FOTF', + 'Fire Ball' => 'FB', + 'Fire Rain' => 'FR', + 'Fire Shards' => 'FS', + 'Fire of Ushnish' => 'FOU', + 'Fists of Faenella' => 'FF', + 'Finesse' => 'FIN', + 'Fluoresce' => 'Fluoresce', + 'Flush Poisons' => 'FP', + 'Focus Moonbeam' => 'FM', + "Footman's Strike" => 'FST', + "Forestwalker's Boon" => 'FWB', + 'Fortress of Ice' => 'FOI', + 'Fountain of Creation' => 'FOC', + 'Frostbite' => 'frostbite', + 'Frost Scythe' => 'FRS', + 'Gam Irnan' => 'GI', + 'Gauge Flow' => 'GAF', + 'Gar Zeng' => 'GZ', + 'Geyser' => 'Geyser', + 'Ghost Shroud' => 'GHS', + 'Ghoulflesh' => 'Ghoulflesh', + 'Gift of Life' => 'GOL', + "Glythtide's Gift" => 'GG', + "Glythtide's Joy" => 'GJ', + 'Grizzly Claws' => 'GRIZ', + 'Grounding Field' => 'GF', + 'Guardian Spirit' => 'GS', + 'Halo' => 'HALO', + 'Halt' => 'Halt', + 'Hand of Tenemlor' => 'HOT', + 'Hands of Justice' => 'HOJ', + 'Hands of Lirisa' => 'HOL', + "Harawep's Bonds" => 'HB', + 'Harm Evil' => 'HE', + 'Harm Horde' => 'HH', + 'Harmony' => 'Harmony', + 'Heal' => 'Heal', + 'Heal Scars' => 'HS', + 'Heal Wounds' => 'HW', + 'Heart Link' => 'HL', + 'Heighten Pain' => 'HP', + 'Heroic Strength' => 'HES', + "Hodierna's Lilt" => 'HODI', + 'Holy Warrior' => 'HOW', + 'Horn of the Black Unicorn' => 'HORN', + "Huldah's Pall" => 'HULP', + 'Hydra Hex' => 'HYH', + 'Ice Patch' => 'IP', + 'Icutu Zaharenela' => 'IZ', + "Idon's Theft" => 'IT', + 'Ignite' => 'Ignite', + 'Imbue' => 'Imbue', + 'Innocence' => 'Innocence', + 'Instinct' => 'INST', + 'Invocation of the Spheres' => 'IOTS', + 'Iron Constitution' => 'IC', + 'Iridius Rod' => 'IR', + 'Ivory Mask' => 'IVM', + 'Kura-Silma' => 'KS', + 'Last Gift of Vithwok IV' => 'LGV', + 'Lay Ward' => 'LW', + 'Lethargy' => 'LETHARGY', + 'Lightning Bolt' => 'LB', + 'Locate' => 'Locate', + "Machinist's Touch" => 'MT', + 'Magnetic Ballista' => 'MAB', + 'Major Physical Protection' => 'MAPP', + 'Malediction' => 'Malediction', + 'Manifest Force' => 'MAF', + 'Mantle of Flame' => 'MOF', + 'Mark of Arhat' => 'MOA', + 'Marshal Order' => 'MO', + 'Mask of the Moons' => 'MOM', + 'Mass Rejuvenation' => 'MRE', + "Membrach's Greed" => 'MEG', + 'Memory of Nature' => 'MON', + 'Mental Blast' => 'MB', + 'Mental Focus' => 'MEF', + "Meraud's Cry" => 'MC', + 'Mind Shout' => 'MS', + 'Minor Physical Protection' => 'MPP', + 'Misdirection' => 'MIS', + 'Moonblade' => 'Moonblade', + 'Moongate' => 'MG', + "Murrula's Flames" => 'MF', + 'Naming of Tears' => 'NAME', + 'Necrotic Reconstruction' => 'NR', + 'Nexus' => 'NEXUS', + "Nissa's Binding" => 'NB', + 'Nonchalance' => 'NON', + 'Noumena' => 'NOU', + 'Oath of the Firstborn' => 'OATH', + 'Obfuscation' => 'Obfuscation', + 'Osrel Meraud' => 'OM', + "Paeldryth's Wrath" => 'PW', + 'Paralysis' => 'PARALYSIS', + 'Partial Displacement' => 'PD', + "Perseverance of Peri'el" => 'POP', + 'Persistence of Mana' => 'POM', + 'Petrifying Visions' => 'PV', + "Phelim's Sanction" => 'PS', + "Philosopher's Preservation" => 'PHP', + 'Piercing Gaze' => 'PG', + "Phoenix's Pyre" => 'PYRE', + 'Platinum Hands of Kertigen' => 'PHK', + 'Protection from Evil' => 'PFE', + 'Psychic Shield' => 'PSY', + 'Quicken the Earth' => 'QE', + 'Rage of the Clans' => 'RAGE', + 'Raise Power' => 'RP', + 'Read the Ripples' => 'RtR', + 'Rebuke' => 'REB', + "Redeemer's Pride" => 'REPR', + 'Refractive Field' => 'RF', + 'Refresh' => 'Refresh', + 'Regalia' => 'REGAL', + 'Regenerate' => 'Regenerate', + 'Rejuvenation' => 'REJUV', + 'Rend' => 'rend', + "Researcher's Insight" => 'REI', + 'Resonance' => 'Resonance', + 'Resurrection' => 'REZZ', + 'Revelation' => 'Revelation', + 'Reverse Putrefaction' => 'RPU', + 'Riftal Summons' => 'RS', + 'Righteous Wrath' => 'RW', + 'Rimefang' => 'RIM', + 'Ring of Spears' => 'ROS', + 'Rising Mists' => 'RM', + 'Rite of Contrition' => 'ROC', + 'Rite of Grace' => 'ROG', + 'Rite of Forbearance' => 'ROF', + 'River in the Sky' => 'RITS', + "Rutilor's Edge" => 'RUE', + 'Saesordian Compass' => 'SCO', + 'Sanctify Pattern' => 'SAP', + 'Sanctuary' => 'Sanctuary', + 'Sanyu Lyba' => 'SL', + 'Seal Cambrinth' => 'SEC', + "Seer's Sense" => 'SEER', + 'See the Wind' => 'STW', + 'Senses of the Tiger' => 'SOTT', + "Sentinel's Resolve" => 'SR', + 'Sever Thread' => 'SET', + 'Shadewatch Mirror' => 'SHM', + 'Shadow Servant' => 'SS', + 'Shadowling' => 'Shadowling', + 'Shadows' => 'Shadows', + 'Shadow Web' => 'SHW', + 'Shatter' => 'Shatter', + 'Shear' => 'shear', + 'Shield of Light' => 'SOL', + 'Shift Moonbeam' => 'SM', + 'Shockwave' => 'Shockwave', + 'Siphon Vitality' => 'SV', + 'Skein of Shadows' => 'SKS', + 'Sleep' => 'Sleep', + 'Smite Horde' => 'SMH', + "Soldier's Prayer" => 'SP', + 'Soul Ablaze' => 'SOUL', + 'Soul Attrition' => 'SA', + 'Soul Bonding' => 'SB', + 'Soul Shield' => 'SOS', + 'Soul Sickness' => 'SICK', + 'Sovereign Destiny' => 'SOD', + 'Spite of Dergati' => 'SPIT', + 'Stampede' => 'Stampede', + 'Starcrash' => 'Starcrash', + 'Starlight Sphere' => 'SLS', + 'Stellar Collector' => 'STC', + 'Steps of Vuan' => 'SOV', + 'Stone Strike' => 'STS', + 'Strange Arrow' => 'STRA', + 'Stun Foe' => 'SF', + 'Substratum' => 'Substratum', + 'Sure Footing' => 'SUF', + 'Swarm' => 'Swarm', + 'Swirling Winds' => 'SW', + 'Syamelyo Kuniyo' => 'SK', + 'Tailwind' => 'TW', + 'Tangled Fate' => 'TF', + "Tamsine's Kiss" => 'TK', + 'Telekinetic Shield' => 'TKSH', + 'Telekinetic Storm' => 'TKS', + 'Telekinetic Throw' => 'TKT', + 'Teleport' => 'Teleport', + 'Tenebrous Sense' => 'TS', + "Tezirah's Veil" => 'TV', + 'Thoughtcast' => 'TH', + 'Thunderclap' => 'TC', + 'Tingle' => 'TI', + 'Trabe Chalice' => 'TRC', + 'Tranquility' => 'Tranquility', + 'Tremor' => 'Tremor', + "Truffenyi's Rally" => 'TR', + 'Turmar Illumination' => 'TURI', + 'Uncurse' => 'Uncurse', + 'Universal Solvent' => 'USOL', + 'Unleash' => 'Unleash', + 'Veil of Ice' => 'VOI', + 'Vertigo' => 'Vertigo', + 'Vessel of Salvation' => 'VOS', + 'Vigil' => 'Vigil', + 'Vigor' => 'Vigor', + 'Viscous Solution' => 'VS', + 'Visions of Darkness' => 'VOD', + 'Vitality Healing' => 'VH', + 'Vivisection' => 'Vivisection', + 'Ward Break' => 'WB', + 'Whispers of the Muse' => 'WOTM', + 'Whole Displacement' => 'WD', + 'Will of Winter' => 'WILL', + 'Wisdom of the Pack' => 'WOTP', + 'Wolf Scent' => 'WS', + 'Words of the Wind' => 'WORD', + "Worm's Mist" => 'WORM', + "Y'ntrel Sechra" => 'YS', + 'Zephyr' => 'zephyr' + } + + # Reduce lines a bit + text.sub!(/ (roisaen|roisan)/, '') + text.sub!(/Indefinite/, 'cyclic') + text.sub!(/Khri /, '') + + if text.index('(') + spell_name = text[0..text.index('(') - 2] + # Shorten spell names + text.sub!(/^#{spell_name}/, all_spells[spell_name.strip]) if all_spells.include?(spell_name.strip) + end + + text.strip! + + SETTINGS_LOCK.synchronize do + HIGHLIGHT.each_pair do |regex, colors| + pos = 0 + while (match_data = text.match(regex, pos)) + h = { + start: match_data.begin(0), + end: match_data.end(0), + fg: colors[0], + bg: colors[1], + ul: colors[2] + } + line_colors.push(h) + pos = match_data.end(0) + end + end + end + + line_colors.push( + start: 0, + fg: PRESET[current_stream][0], + bg: PRESET[current_stream][1], + end: text.length + ) + # window.add_string(text, line_colors) + # need_update = true end unless text =~ /^\[server\]: "(?:kill|connect)/ window.add_string(text, line_colors) need_update = true end - elsif current_stream =~ /^(?:death|logons|thoughts|voln|familiar)$/ + elsif current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics)$/ if current_stream =~ /^(?:thoughts|familiar)$/ text = "#{text} (#{Time.now.strftime('%H:%M:%S').sub(/^0/, '')})" if Opts["speech-ts"] end @@ -1670,14 +2055,21 @@ def get_color_pair_id(fg_code, bg_code) end elsif xml =~ /^<(?:pushStream|component|compDef) id=("|')(.*?)\1[^>]*\/?>$/ new_stream = $2 + if new_stream =~ /^exp (\w+\s?\w+?)/ + current_stream = 'exp' + stream_handler['exp'].set_current(Regexp.last_match(1)) if stream_handler['exp'] + else + current_stream = new_stream + end game_text = line.slice!(0, start_pos) handle_game_text.call(game_text) current_stream = new_stream elsif xml =~ /^$/ stream_handler[$1].clear_window if stream_handler[$1] - elsif xml =~ /^' + elsif xml =~ %r{^' game_text = line.slice!(0, start_pos) handle_game_text.call(game_text) + stream_handler['exp'].delete_skill if current_stream == 'exp' and stream_handler['exp'] current_stream = nil elsif xml =~ /^ - + + + + + + + + + + diff --git a/templates/original.xml b/templates/original.xml index dd45ee2..32921bf 100644 --- a/templates/original.xml +++ b/templates/original.xml @@ -43,7 +43,16 @@ - + + + + + + + + + + diff --git a/templates/tysong.xml b/templates/tysong.xml index 40a3fc6..d31885a 100644 --- a/templates/tysong.xml +++ b/templates/tysong.xml @@ -121,7 +121,16 @@ Includes preset highlight colors for merchants/GMs/staff, wizards, sorcerers, em - + + + + + + + + + + diff --git a/ui/exp.rb b/ui/exp.rb new file mode 100644 index 0000000..4136ae9 --- /dev/null +++ b/ui/exp.rb @@ -0,0 +1,159 @@ +class Skill + def initialize(name, ranks, percent, mindstate) + @name = name + @ranks = ranks + @percent = percent + @mindstate = mindstate + end + + def to_s + format('%8s:%5d %2s%% [%2s/34]', @name, @ranks, @percent, @mindstate) + end + + def to_str + format('%8s:%5d %2s%% [%2s/34]', @name, @ranks, @percent, @mindstate) + end +end + +class ExpWindow < Curses::Window + attr_reader :color_stack, :buffer + attr_accessor :scrollbar, :indent_word_wrap, :layout, :time_stamp + + @@list = [] + + def self.list + @@list + end + + def initialize(*args) + @skills = {} + @open = false + @@list.push(self) + super(*args) + end + + def delete_skill + return unless @current_skill + + @skills.delete(@current_skill) + redraw + @current_skill = '' + end + + def set_current(skill) + @current_skill = skill + end + + def add_string(text, _line_colors) + return unless text =~ %r{(.+):\s*(\d+) (\d+)% \[\s*(\d+)/34\]} + + # if text =~ /(\w+(\s\w+)?)<\/d>:\s+(\d+)(?:\s+)(\d{1,2}|100)%\s+\[\s?(\d+)\/34\]/ + name = ::Regexp.last_match(1).strip + ranks = ::Regexp.last_match(2) + percent = ::Regexp.last_match(3) + mindstate = ::Regexp.last_match(4) + + skill = Skill.new(name, ranks, percent, mindstate) + @skills[@current_skill] = skill + redraw + @current_skill = '' + end + + def skill_group_color(skill) + armor = ['Shield', 'Lt Armor', 'Chain', 'Brig', 'Plate', 'Defend', 'Convict'] + + weapon = %w[Parry SE LE 2HE SB LB 2HB Slings Bows Crossbow Staves Polearms LT HT Brawling Offhand Melee Missile + Expert] + + magic = %w[Magic IF IM Attune Arcana TM Aug Debil Util Warding Sorcery Astro Summon Theurgy] + + survival = %w[Evasion Athletic Perc Stealth Locks Thievery FA Outdoors Skinning BS Scouting Than Backstab] + + lore = %w[Forging Eng Outfit Alchemy Enchant Scholar Mech Appraise Perform Tactics BardLore Empathy Trading] + + if armor.include? skill + '00FF00' # green + elsif weapon.include? skill + '00FFFF' # cyan + elsif magic.include? skill + 'FF0000' # red + elsif survival.include? skill + 'FF00FF' # magenta + elsif lore.include? skill + 'FFFF00' # yellow + end + end + + def mindstate_color(mindstate) + if mindstate == 0 + 'FFFFFF' # white + elsif (1..10).member?(mindstate) + '00FFFF' # cyan + elsif (11..20).member?(mindstate) + '00FF00' # green + elsif (21..30).member?(mindstate) + 'FFFF00' # yellow + elsif (31..34).member?(mindstate) + 'FF0000' # red + end + end + + def add_skill(skill, skill_colors = []) + SETTINGS_LOCK.synchronize do + HIGHLIGHT.each_pair do |regex, colors| + pos = 0 + while (match_data = skill.match(regex, pos)) + h = { + start: match_data.begin(0), + end: match_data.end(0), + fg: colors[0], + bg: colors[1], + ul: colors[2] + } + skill_colors.push(h) + pos = match_data.end(0) + end + end + end + + # addstr skill + part = [0, skill.length] + skill_colors.each do |h| + part.push(h[:start]) + part.push(h[:end]) + end + part.uniq! + part.sort! + for i in 0...(part.length - 1) + str = skill[part[i]...part[i + 1]] + color_list = skill_colors.find_all { |h| (h[:start] <= part[i]) and (h[:end] >= part[i + 1]) } + if color_list.empty? + addstr str + noutrefresh + else + color_list = color_list.sort_by { |h| h[:end] - h[:start] } + fg = color_list.map { |h| h[:fg] }.find { |foreground| !foreground.nil? } + bg = color_list.map { |h| h[:bg] }.find { |background| !background.nil? } + ul = color_list.map { |h| h[:ul] == 'true' }.find { |underline| underline } + attron(color_pair(get_color_pair_id(fg, bg)) | (ul ? Curses::A_UNDERLINE : Curses::A_NORMAL)) do + addstr str + noutrefresh + end + end + end + end + + def redraw + clear + setpos(0, 0) + + @skills.sort.each do |_name, skill| + # addstr skill.to_s + "\n" + add_skill(skill.to_s) + # addstr(skill) + addstr("\n") + noutrefresh + end + noutrefresh + end +end diff --git a/ui/perc.rb b/ui/perc.rb new file mode 100644 index 0000000..70be4c0 --- /dev/null +++ b/ui/perc.rb @@ -0,0 +1,108 @@ +class PercWindow < Curses::Window + attr_reader :color_stack, :buffer + attr_accessor :scrollbar, :indent_word_wrap, :layout, :time_stamp + + @@list = [] + + def ExpWindow.list + @@list + end + + def initialize(*args) + @buffer = [] + @buffer_pos = 0 + @max_buffer_size = 250 + @indent_word_wrap = true + @@list.push(self) + super(*args) + end + + def add_line(line, line_colors = []) + part = [0, line.length] + line_colors.each do |h| + part.push(h[:start]) + part.push(h[:end]) + end + part.uniq! + part.sort! + for i in 0...(part.length - 1) + str = line[part[i]...part[i + 1]] + color_list = line_colors.find_all { |h| (h[:start] <= part[i]) and (h[:end] >= part[i + 1]) } + if color_list.empty? + addstr str + "\n" unless str.chomp.empty? + noutrefresh + else + # shortest length highlight takes precedence when multiple highlights cover the same substring + # fixme: allow multiple highlights on a substring when one specifies fg and the other specifies bg + color_list = color_list.sort_by { |h| h[:end] - h[:start] } + fg = color_list.map { |h| h[:fg] }.find { |foreground| !foreground.nil? } + bg = color_list.map { |h| h[:bg] }.find { |background| !background.nil? } + ul = color_list.map { |h| h[:ul] == 'true' }.find { |underline| underline } + attron(color_pair(get_color_pair_id(fg, bg)) | (ul ? Curses::A_UNDERLINE : Curses::A_NORMAL)) do + addstr str + "\n" unless str.chomp.empty? + noutrefresh + end + end + noutrefresh + end + end + + def add_string(string, string_colors = []) + while (line = string.slice!(/^.{2,#{maxx - 1}}(?=\s|$)/)) or (line = string.slice!(0, (maxx - 1))) + line_colors = [] + for h in string_colors + line_colors.push(h.dup) if h[:start] < line.length + h[:end] -= line.length + h[:start] = [(h[:start] - line.length), 0].max + end + string_colors.delete_if { |highlight| highlight[:end] < 0 } + line_colors.each { |highlight| highlight[:end] = [highlight[:end], line.length].min } + @buffer.unshift([line, line_colors]) + @buffer.pop if @buffer.length > @max_buffer_size + if @buffer_pos == 0 + add_line(line, line_colors) + # addstr "\n" + else + @buffer_pos += 1 + scroll(1) if @buffer_pos > (@max_buffer_size - maxy) + update_scrollbar + end + break if string.chomp.empty? + + if @indent_word_wrap + if string[0, 1] == ' ' + string = " #{string}" + string_colors.each do |highlight| + highlight[:end] += 1 + # Never let the highlighting hang off the edge -- it looks weird + highlight[:start] += highlight[:start] == 0 ? 2 : 1 + end + else + string = "#{string}" + string_colors.each do |highlight| + highlight[:end] += 2 + highlight[:start] += 2 + end + end + elsif string[0, 1] == ' ' + string = string[1, string.length] + string_colors.each do |highlight| + highlight[:end] -= 1 + highlight[:start] -= 1 + end + end + end + end + + def redraw + clear + setpos(0, 0) + noutrefresh + end + + def clear_window + clear + setpos(0, 0) + noutrefresh + end +end diff --git a/ui/text.rb b/ui/text.rb index fa77a54..8863d3e 100644 --- a/ui/text.rb +++ b/ui/text.rb @@ -2,7 +2,7 @@ class TextWindow < Curses::Window attr_reader :color_stack, :buffer - attr_accessor :scrollbar, :indent_word_wrap, :layout + attr_accessor :scrollbar, :indent_word_wrap, :layout, :time_stamp @@list = Array.new @@ -57,6 +57,7 @@ def add_string(string, string_colors = Array.new) # # word wrap string, split highlights if needed so each wrapped line is independent, update buffer, update window if needed # + string += " [#{Time.now.hour.to_s.rjust(2,'0')}:#{Time.now.min.to_s.rjust(2,'0')}]" if @time_stamp && string && !string.chomp.empty? while (line = string.slice!(/^.{2,#{maxx - 1}}(?=\s|$)/)) or (line = string.slice!(0, (maxx - 1))) line_colors = Array.new for h in string_colors From 1c06eca1abd7b2884163403ba95eb7bd6ac1fabe Mon Sep 17 00:00:00 2001 From: "Ryan P. McKinnon" <15917743+mrhoribu@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:37:01 -0400 Subject: [PATCH 2/3] robocop cleanup --- ui/text.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/text.rb b/ui/text.rb index 8863d3e..7af0c37 100644 --- a/ui/text.rb +++ b/ui/text.rb @@ -57,7 +57,7 @@ def add_string(string, string_colors = Array.new) # # word wrap string, split highlights if needed so each wrapped line is independent, update buffer, update window if needed # - string += " [#{Time.now.hour.to_s.rjust(2,'0')}:#{Time.now.min.to_s.rjust(2,'0')}]" if @time_stamp && string && !string.chomp.empty? + string += " [#{Time.now.hour.to_s.rjust(2, '0')}:#{Time.now.min.to_s.rjust(2, '0')}]" if @time_stamp && string && !string.chomp.empty? while (line = string.slice!(/^.{2,#{maxx - 1}}(?=\s|$)/)) or (line = string.slice!(0, (maxx - 1))) line_colors = Array.new for h in string_colors From 20b6a075fcecbedc53b8b7990dcb89c84d2ec0ab Mon Sep 17 00:00:00 2001 From: "Ryan P. McKinnon" <15917743+mrhoribu@users.noreply.github.com> Date: Wed, 8 May 2024 12:11:00 -0400 Subject: [PATCH 3/3] Update profanity.rb add charprofile stream support --- profanity.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profanity.rb b/profanity.rb index 626aa8d..b25e23d 100644 --- a/profanity.rb +++ b/profanity.rb @@ -1299,7 +1299,7 @@ def get_color_pair_id(fg_code, bg_code) oc[:start] = 0 end - if current_stream.nil? or stream_handler[current_stream] or (current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics)$/) + if current_stream.nil? or stream_handler[current_stream] or (current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics|charprofile)$/) SETTINGS_LOCK.synchronize { HIGHLIGHT.each_pair { |regex, colors| pos = 0 @@ -1797,7 +1797,7 @@ def get_color_pair_id(fg_code, bg_code) window.add_string(text, line_colors) need_update = true end - elsif current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics)$/ + elsif current_stream =~ /^(?:death|logons|thoughts|voln|familiar|assess|ooc|shopWindow|combat|moonWindow|atmospherics|charprofile)$/ if current_stream =~ /^(?:thoughts|familiar)$/ text = "#{text} (#{Time.now.strftime('%H:%M:%S').sub(/^0/, '')})" if Opts["speech-ts"] end