commit 6eb9f005df4e4c8d5475e01e557b8846a19b8355 Author: Manuel Simon Hirsig Date: Mon Jan 25 16:37:17 2016 +0100 initial commit diff --git a/Event_wardrum_ogre.ogg b/Event_wardrum_ogre.ogg new file mode 100644 index 0000000..bae6d94 Binary files /dev/null and b/Event_wardrum_ogre.ogg differ diff --git a/UI-Achievement-Alert-Glow.blp b/UI-Achievement-Alert-Glow.blp new file mode 100644 index 0000000..4e82b37 Binary files /dev/null and b/UI-Achievement-Alert-Glow.blp differ diff --git a/UI-Achievement-Parchment-Horizontal.blp b/UI-Achievement-Parchment-Horizontal.blp new file mode 100644 index 0000000..10b8c1d Binary files /dev/null and b/UI-Achievement-Parchment-Horizontal.blp differ diff --git a/UI-Achievement-Title.blp b/UI-Achievement-Title.blp new file mode 100644 index 0000000..bbc2e00 Binary files /dev/null and b/UI-Achievement-Title.blp differ diff --git a/gruntling_horn_bb.ogg b/gruntling_horn_bb.ogg new file mode 100644 index 0000000..5b47809 Binary files /dev/null and b/gruntling_horn_bb.ogg differ diff --git a/npcscan.lua b/npcscan.lua new file mode 100644 index 0000000..aeeb9a9 --- /dev/null +++ b/npcscan.lua @@ -0,0 +1,521 @@ +local version = '1.0' +local author = 'shirsig' + +local BROWN = { .7, .15, .05 } +local YELLOW = { 1, 1, .15 } +local CHECK_INTERVAL = .5 + +npcscan_targets = {} + +npcscan = {} + + +function npcscan.without_errors(f) + local orig = UIErrorsFrame.AddMessage + UIErrorsFrame.AddMessage = Aux.util.pass + f() + UIErrorsFrame.AddMessage = orig +end + +do + local last_played + + function npcscan.play_sound() + if not last_played or GetTime() - last_played > 10 then -- 8 + PlaySoundFile('Interface\\AddOns\\npcscan\\Event_wardrum_ogre.ogg', 'Master') + PlaySoundFile('Interface\\AddOns\\npcscan\\scourge_horn.ogg', 'Master') + last_played = GetTime() + end + end +end + +function npcscan.check_for_targets() + for name, _ in pairs(npcscan_targets) do + -- local original_target = UnitName('target') + if npcscan.target(name) then + -- if UnitPlayerControlled('target') then + -- if original_target then + -- TargetByName(original_target, true) + -- end + -- else + npcscan.remove_target(UnitName('target')) + npcscan.play_sound() + if npcscan.flash.animation:playing() then + npcscan.flash.animation:stop_after(3) + else + npcscan.flash:reset() + npcscan.flash.animation:play() + end + npcscan.button:set_target() + -- end + end + end +end + +function npcscan.target(name) + TargetByName(name, true) + return UnitName('target') and strupper(UnitName('target')) == name +end + +function npcscan.on_event() + if event == 'VARIABLES_LOADED' then + do + local flash = CreateFrame('Frame') + npcscan.flash = flash + flash:Show() + flash:SetAllPoints() + flash:SetAlpha(0) + flash:SetFrameStrata('FULLSCREEN_DIALOG') + + local texture = flash:CreateTexture() + texture:SetBlendMode('ADD') + texture:SetAllPoints() + texture:SetTexture([[Interface\FullScreenTextures\LowHealth]]) + + function flash:reset() + self:SetAlpha(0) + end + + local screen_flash_in = npcscan.alpha_animation(flash, 1, .5) + local screen_flash_out = npcscan.alpha_animation(flash, -1, .5) + local screen_flash_delay = npcscan.delay(.5) + + flash.animation = (screen_flash_in..screen_flash_delay..screen_flash_out) * 3 + end + + local button = CreateFrame('Button', 'npcscan_button', UIParent) + button:Hide() + npcscan.button = button + button:SetPoint('BOTTOM', UIParent, 0, 128) + button:SetWidth(150) + button:SetHeight(42) + button:SetScale(1.25) + button:SetFrameStrata('FULLSCREEN_DIALOG') + button:SetNormalTexture([[Interface\AddOns\npcscan\UI-Achievement-Parchment-Horizontal]]) + button:SetBackdrop{ + tile = true, + edgeSize = 16, + edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], + } + button:SetBackdropBorderColor(unpack(BROWN)) + button:SetScript('OnEnter', function() + this:SetBackdropBorderColor(unpack(YELLOW)) + end) + button:SetScript('OnLeave', function() + this:SetBackdropBorderColor(unpack(BROWN)) + end) + button:SetScript('OnClick', function() + TargetByName(this:GetText(), true) + end) + function button:set_target() + self:SetText(UnitName('target')) + + self.model:reset() + self.model:SetUnit('target') + + self:Show() + self.glow.animation:stop() + self.shine.animation:stop() + self.glow:reset() + self.shine:reset() + self.glow.animation:play() + self.shine.animation:play() + end + + do + local background = button:GetNormalTexture() + background:SetDrawLayer('BACKGROUND') + background:ClearAllPoints() + background:SetPoint('BOTTOMLEFT', 3, 3) + background:SetPoint('TOPRIGHT', -3, -3) + background:SetTexCoord(0, 1, 0, 0.25) + end + + do + local title_background = button:CreateTexture(nil, 'BORDER') + title_background:SetTexture([[Interface\AddOns\npcscan\UI-Achievement-Title]]) + title_background:SetPoint('TOPRIGHT', -5, -5) + title_background:SetPoint('LEFT', 5, 0) + title_background:SetHeight(18) + title_background:SetTexCoord(0, .9765625, 0, .3125) + title_background:SetAlpha(.8) + + local title = button:CreateFontString(nil, 'OVERLAY', 'GameFontHighlightMedium') + title:SetPoint('TOPLEFT', title_background, 0, 0) + title:SetPoint('RIGHT', title_background) + button:SetFontString(title) + + local subtitle = button:CreateFontString(nil, 'OVERLAY', 'GameFontBlackTiny') + subtitle:SetPoint('TOPLEFT', title, 'BOTTOMLEFT', 0, -4) + subtitle:SetPoint('RIGHT', title ) + subtitle:SetText('NPC Found!') + end + + do + local model = CreateFrame('PlayerModel', nil, button) + button.model = model + model:SetPoint('BOTTOMLEFT', button, 'TOPLEFT', 0, -4) + model:SetPoint('RIGHT', 0, 0) + model:SetHeight(button:GetWidth() * .6) + + do + local last_update, delay + function model:on_update() + this:SetFacing(this:GetFacing() + (GetTime() - last_update) * math.pi / 4) + last_update = GetTime() + end + + function model:on_update_model() + if delay > 0 then + delay = delay - 1 + return + end + + this:SetScript('OnUpdateModel', nil) + this:SetScript('OnUpdate', this.on_update) + this:SetModelScale(.75) + this:SetAlpha(1) + last_update = GetTime() + end + + function model:reset() + self:SetAlpha(0) + self:SetFacing(0) + self:SetModelScale(1) + self:ClearModel() + self:SetScript('OnUpdate', nil) + self:SetScript("OnUpdateModel", self.on_update_model) + delay = 10 -- to prevent scaling bugs + end + end + end + + do + local close = CreateFrame('Button', nil, button, 'UIPanelCloseButton') + close:SetPoint('TOPRIGHT', 0, 0) + close:SetWidth(32) + close:SetHeight(32) + close:SetScale(.8) + close:SetHitRectInsets(8, 8, 8, 8) + end + + do + local glow = button:CreateTexture(nil, 'OVERLAY') + button.glow = glow + glow:SetPoint('CENTER', button, 'CENTER') + glow:SetWidth(400 / 300 * button:GetWidth()) + glow:SetHeight(171 / 70 * button:GetHeight()) + glow:SetTexture([[Interface\AddOns\npcscan\UI-Achievement-Alert-Glow]]) + glow:SetBlendMode('ADD') + glow:SetTexCoord(0, .78125, 0, .66796875) + glow:SetAlpha(0) + + function glow:reset() + self:SetAlpha(0) + end + + local glow_in = npcscan.alpha_animation(glow, 1, .2) + local glow_out = npcscan.alpha_animation(glow, -1, .5) + + glow.animation = glow_in..glow_out + end + + do + local shine = button:CreateTexture(nil, 'ARTWORK') + button.shine = shine + shine:SetPoint('TOPLEFT', button, 0, 8) + shine:SetWidth(67 / 300 * button:GetWidth()) + shine:SetHeight(1.28 * button:GetHeight()) + shine:SetTexture([[Interface\AddOns\npcscan\UI-Achievement-Alert-Glow]]) + shine:SetBlendMode('ADD') + shine:SetTexCoord(.78125, .912109375, 0, .28125) + shine:SetAlpha(0) + + function shine:reset() + self:SetAlpha(0) + self:SetPoint('TOPLEFT', button, 0, 8) + end + + local shine_delay1 = npcscan.delay(.3) + local shine_in = npcscan.alpha_animation(shine, 1, 0) + local shine_delay2 = npcscan.delay(.2) + local shine_move = npcscan.translation_animation(shine, button:GetWidth() - shine:GetWidth() + 8, 0, .4) + local shine_out = npcscan.alpha_animation(shine, -1, .2) + + shine.animation = (shine_delay1..shine_in)..(shine_move + (shine_delay2..shine_out)) + end + end +end + +do + npcscan.last_check = GetTime() + function npcscan.on_update() + if GetTime() - npcscan.last_check >= CHECK_INTERVAL then + npcscan.last_check = GetTime() + npcscan.without_errors(npcscan.check_for_targets) + end + end +end + +function npcscan.log(msg) + if DEFAULT_CHAT_FRAME then + DEFAULT_CHAT_FRAME:AddMessage('[npcscan]: '..msg, 1, 1, 0) + end +end + +-- Slash commands + +function npcscan.sorted_targets() + local sorted_targets = {} + for key, _ in pairs(npcscan_targets) do + tinsert(sorted_targets, key) + end + sort(sorted_targets, function(key1, key2) return key1 < key2 end) + return sorted_targets +end + +function npcscan.add_target(name) + local key = strupper(name) + if key ~= '' and not npcscan_targets[key] then + npcscan_targets[key] = true + npcscan.log(key..' (added)') + end +end + +function npcscan.remove_target(name) + local key = strupper(name) + if npcscan_targets[key] then + npcscan_targets[key] = nil + npcscan.log(key..' (removed)') + end +end + +SLASH_NPCSCAN1 = '/npcscan' +function SlashCmdList.NPCSCAN(parameter) + local _, _, command, name = strfind(parameter, '%s*(%a*)%s*(.*)') + + if command == '' then + for _, key in ipairs(npcscan.sorted_targets()) do + npcscan.log(key) + end + elseif command == 'add' then + npcscan.add_target(name) + elseif command == 'rem' then + npcscan.remove_target(name) + end +end + +-- Animation framework + +function npcscan.translation_animation(target, x_change, y_change, duration) + return npcscan.animation( + {x_change, y_change}, + duration, + function() + local _, _, _, original_x, original_y = target:GetPoint() + return original_x, original_y + end, + function(x, y) + local point, relative_to, relative_point = target:GetPoint() + target:SetPoint( + point, + relative_to, + relative_point, + x, + y + ) + end + ) +end + +function npcscan.alpha_animation(target, change, duration) + return npcscan.animation( + {change}, + duration, + function() + return target:GetAlpha() + end, + function(alpha) + target:SetAlpha(alpha) + end + ) +end + +function npcscan.delay(t) + return npcscan.animation({}, t, function() end, function() end) +end + +do + local animation_metamethods = {} + + local mul_mt = { __index = {} } + local add_mt = { __index = {} } + local concat_mt = { __index = {} } + local animation_mt = { __index = {} } + + + function animation_metamethods.__mul(animation, repetitions) + local self = { + _frame = CreateFrame('Frame'), + _repetitions_left = 0, + _repetitions = repetitions, + _animation = animation, + } + setmetatable(self, mul_mt) + + self._frame:SetScript('OnUpdate', function() + self:_on_update() + end) + + return self + end + function animation_metamethods.__add(lhs, rhs) + local self = {} + setmetatable(self, add_mt) + + self._animations = {lhs, rhs} + + return self + end + function animation_metamethods.__concat(lhs, rhs) + local self = { _frame = CreateFrame('Frame') } + setmetatable(self, concat_mt) + + self._animations = {lhs, rhs} + + self._frame:SetScript('OnUpdate', function() + self:_on_update() + end) + + return self + end + + + for _, mt in ipairs({ mul_mt, add_mt, concat_mt, animation_mt }) do + for key, metamethod in pairs(animation_metamethods) do + mt[key] = metamethod + end + end + + + function mul_mt.__index:play() + self._repetitions_left = self._repetitions + end + function mul_mt.__index:playing() + return self._repetitions_left > 0 + end + function mul_mt.__index:stop() + self._repetitions_left = 0 + end + function mul_mt.__index:stop_after(repetitions) + self._repetitions_left = self._repetitions + end + function mul_mt.__index:_on_update() + if self._repetitions_left > 0 then + if not self._animation_started then + self._animation:play() + self._animation_started = true + elseif not self._animation:playing() then + self._repetitions_left = self._repetitions_left - 1 + self._animation_started = false + end + end + end + + + function add_mt.__index:play() + for _, animation in ipairs(self._animations) do + animation:play() + end + end + function add_mt.__index:playing() + for _, animation in ipairs(self._animations) do + if animation:playing() then + return true + end + end + end + function add_mt.__index:stop() + for _, animation in ipairs(self._animations) do + animation:stop() + end + end + + + function concat_mt.__index:play() + self._index = 1 + end + function concat_mt.__index:playing() + return self._index ~= nil + end + function concat_mt.__index:stop() + if self._index and self._animations[self._index] then + self._animations[self._index]:stop() + end + self._index = nil + end + function concat_mt.__index:_on_update() + if self._index then + if not self._animations[self._index] then + self._index = nil + elseif not self._animation_started then + self._animations[self._index]:play() + self._animation_started = true + elseif not self._animations[self._index]:playing() then + self._index = self._index + 1 + self._animation_started = false + end + end + end + + + function animation_mt.__index:play() + self._t0 = GetTime() + self._original_values = { self._getter() } + end + function animation_mt.__index:stop() + self._t0 = nil + end + function animation_mt.__index:playing() + return self._t0 ~= nil + end + function animation_mt.__index:_current_changes(factor) + local values = {} + for i, change in ipairs(self._changes) do + tinsert(values, self._original_values[i] + change * factor) + end + return unpack(values) + end + function animation_mt.__index:_on_update(factor) + if self._t0 then + local progress = GetTime() - self._t0 + if progress >= self._duration then + self._callback(self:_current_changes(1)) + self._t0 = nil + else + self._callback(self:_current_changes(progress / self._duration)) + end + end + end + + + function npcscan.animation(changes, duration, getter, callback) + + local self = { + _frame = CreateFrame('Frame'), + _changes = changes, + _duration = duration, + _getter = getter, + _callback = callback, + } + setmetatable(self, animation_mt) + + self._frame:SetScript('OnUpdate', function() + self:_on_update() + end) + + return self + end +end + +-- building the frame structure \ No newline at end of file diff --git a/npcscan.toc b/npcscan.toc new file mode 100644 index 0000000..6c76bdd --- /dev/null +++ b/npcscan.toc @@ -0,0 +1,4 @@ +## Interface: 11200 +## Title: npcscan +## SavedVariables: npcscan_targets +npcscan.xml \ No newline at end of file diff --git a/npcscan.xml b/npcscan.xml new file mode 100644 index 0000000..7e660a6 --- /dev/null +++ b/npcscan.xml @@ -0,0 +1,36 @@ + +