2012-01-01 04:01:18 +01:00
local GetTime = QuestHelper_GetTime
2010-11-08 14:28:59 +01:00
QuestHelper_File [ " objective.lua " ] = " 4.0.1.$svnversion$ "
2010-10-24 23:17:33 +02:00
QuestHelper_Loadtime [ " objective.lua " ] = GetTime ( )
local UserIgnored = {
name = " user_manual_ignored " ,
no_disable = true ,
friendly_reason = QHText ( " FILTERED_USER " ) ,
AddException = function ( self , node )
QH_Route_UnignoreNode ( node , self ) -- there isn't really state with this one
end
}
function QuestHelper : AddObjectiveOptionsToMenu ( obj , menu )
local submenu = self : CreateMenu ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local pri = ( QH_Route_GetClusterPriority ( obj.cluster ) or 0 ) + 3
for i = 1 , 5 do
local name = QHText ( " PRIORITY " .. i )
local item = self : CreateMenuItem ( submenu , name )
local tex
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if pri == i then
tex = self : CreateIconTexture ( item , 10 )
else
tex = self : CreateIconTexture ( item , 12 )
tex : SetVertexColor ( 1 , 1 , 1 , 0 )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
item : AddTexture ( tex , true )
item : SetFunction ( QH_Route_SetClusterPriority , obj.cluster , i - 3 )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
self : CreateMenuItem ( menu , QHText ( " PRIORITY " ) ) : SetSubmenu ( submenu )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--[[if self.sharing then
submenu = self : CreateMenu ( )
local item = self : CreateMenuItem ( submenu , QHText ( " SHARING_ENABLE " ) )
local tex = self : CreateIconTexture ( item , 10 )
if not obj.want_share then tex : SetVertexColor ( 1 , 1 , 1 , 0 ) end
item : AddTexture ( tex , true )
item : SetFunction ( obj.Share , obj )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local item = self : CreateMenuItem ( submenu , QHText ( " SHARING_DISABLE " ) )
local tex = self : CreateIconTexture ( item , 10 )
if obj.want_share then tex : SetVertexColor ( 1 , 1 , 1 , 0 ) end
item : AddTexture ( tex , true )
item : SetFunction ( obj.Unshare , obj )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
self : CreateMenuItem ( menu , QHText ( " SHARING " ) ) : SetSubmenu ( submenu )
end ] ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--self:CreateMenuItem(menu, "(No options available)")
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not obj.map_suppress_ignore then
self : CreateMenuItem ( menu , QHText ( " IGNORE " ) ) : SetFunction ( function ( ) QH_Route_IgnoreCluster ( obj.cluster , UserIgnored ) end ) -- There is probably a nasty race condition here. I'm not entirely happy about it.
end
if obj.map_custom_menu then
obj.map_custom_menu ( menu )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if obj.cluster and # obj.cluster > 1 and QH_Route_Ignored_Cluster_Active ( obj.cluster ) > 1 then
self : CreateMenuItem ( menu , QHText ( " IGNORE_LOCATION " ) ) : SetFunction ( QH_Route_IgnoreNode , obj , UserIgnored )
end
end
do return end
local function ObjectiveCouldBeFirst ( self )
if ( self.user_ignore == nil and self.auto_ignore ) or self.user_ignore then
return false
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , j in pairs ( self.after ) do
if i.watched then
return false
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return true
end
local function DefaultObjectiveKnown ( self )
if self.user_ignore == nil then
if ( self.filter_zone and QuestHelper_Pref.filter_zone ) or
( self.filter_done and QuestHelper_Pref.filter_done ) or
( self.filter_level and QuestHelper_Pref.filter_level ) or
( self.filter_blocked and QuestHelper_Pref.filter_blocked ) or
( self.filter_watched and QuestHelper_Pref.filter_watched ) then
return false
end
elseif self.user_ignore then
return false
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , j in pairs ( self.after ) do
if i.watched and not i : Known ( ) then -- Need to know how to do everything before this objective.
return false
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return true
end
local function ObjectiveReason ( self , short )
local reason , rc = nil , 0
if self.reasons then
for r , c in pairs ( self.reasons ) do
if not reason or c > rc or ( c == rc and r > reason ) then
reason , rc = r , c
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not reason then reason = " Do some extremely secret unspecified something. " end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not short and self.pos and self.pos [ 6 ] then
reason = reason .. " \n " .. self.pos [ 6 ]
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return reason
end
local function Uses ( self , obj , text )
if self == obj then return end -- You cannot use yourself. A purse is not food.
local uses , used = self.uses , obj.used
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not uses then
uses = QuestHelper : CreateTable ( " uses " )
self.uses = uses
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not used then
used = QuestHelper : CreateTable ( " used " )
obj.used = used
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not uses [ obj ] then
uses [ obj ] = true
used [ self ] = text
obj : MarkUsed ( )
end
end
local function DoMarkUsed ( self )
-- Objectives should call 'self:Uses(objective, text)' to mark objectives they use by don't directly depend on.
-- This information is used in tooltips.
-- text is passed to QHFormat with the name of the objective being used.
end
local function MarkUsed ( self )
if not self.marked_used then
self.marked_used = 1
self : DoMarkUsed ( )
else
self.marked_used = self.marked_used + 1
end
end
local function MarkUnused ( self )
--[[ assert(self.marked_used) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.marked_used == 1 then
local uses = self.uses
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if uses then
for obj in pairs ( uses ) do
obj.used [ self ] = nil
obj : MarkUnused ( )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
QuestHelper : ReleaseTable ( uses )
self.uses = nil
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.used then
--[[ assert(not next(self.used)) ]]
QuestHelper : ReleaseTable ( self.used )
self.used = nil
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
self.marked_used = nil
else
self.marked_used = self.marked_used - 1
end
end
local function DummyObjectiveKnown ( self )
return ( self.o . pos or self.fb . pos ) and DefaultObjectiveKnown ( self )
end
local function ItemKnown ( self )
if not DefaultObjectiveKnown ( self ) then return false end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . vendor then
for i , npc in ipairs ( self.o . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) and n : Known ( ) then
return true
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . vendor then
for i , npc in ipairs ( self.fb . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) and n : Known ( ) then
return true
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . pos or self.fb . pos then
return true
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . drop then for monster in pairs ( self.o . drop ) do
if self.qh : GetObjective ( " monster " , monster ) : Known ( ) then
return true
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . drop then for monster in pairs ( self.fb . drop ) do
if self.qh : GetObjective ( " monster " , monster ) : Known ( ) then
return true
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . contained then for item in pairs ( self.o . contained ) do
if self.qh : GetObjective ( " item " , item ) : Known ( ) then
return true
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . contained then for item in pairs ( self.fb . contained ) do
if self.qh : GetObjective ( " item " , item ) : Known ( ) then
return true
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.quest then
local item = self.quest . o.item
item = item and item [ self.obj ]
2013-01-04 02:03:20 +01:00
if item then
2010-10-24 23:17:33 +02:00
if item.pos then
return true
end
if item.drop then
for monster in pairs ( item.drop ) do
if self.qh : GetObjective ( " monster " , monster ) : Known ( ) then
return true
end
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
item = self.quest . fb.item
item = item and item [ self.obj ]
2013-01-04 02:03:20 +01:00
if item then
2010-10-24 23:17:33 +02:00
if item.pos then
return true
end
if item.drop then
for monster in pairs ( item.drop ) do
if self.qh : GetObjective ( " monster " , monster ) : Known ( ) then
return true
end
end
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return false
end
local function ObjectiveAppendPositions ( self , objective , weight , why , restrict )
local high = 0
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . pos then for i , p in ipairs ( self.o . pos ) do
high = math.max ( high , p [ 4 ] )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . pos then for i , p in ipairs ( self.fb . pos ) do
high = math.max ( high , p [ 4 ] )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
high = weight / high
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . pos then for i , p in ipairs ( self.o . pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] * high , why )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . pos then for i , p in ipairs ( self.fb . pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] * high , why )
end
end end
end
local function ObjectivePrepareRouting ( self , anywhere )
self.setup_count = self.setup_count + 1
if not self.setup then
--[[ assert(not self.d) ]]
--[[ assert(not self.p) ]]
--[[ assert(not self.nm) ]]
--[[ assert(not self.nm2) ]]
--[[ assert(not self.nl) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
self.d = QuestHelper : CreateTable ( " objective.d " )
self.p = QuestHelper : CreateTable ( " objective.p " )
self.nm = QuestHelper : CreateTable ( " objective.nm " )
self.nm2 = QuestHelper : CreateTable ( " objective.nm2 " )
self.nl = QuestHelper : CreateTable ( " objective.nl " )
self.distance_cache = QuestHelper : CreateTable ( " objective.distance_cache " )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not anywhere then
self : AppendPositions ( self , 1 , nil , true )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not next ( self.p ) then
QuestHelper : TextOut ( QHFormat ( " INACCESSIBLE_OBJ " , self.obj or " whatever it was you just requested " ) )
anywhere = true
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if anywhere then
self : AppendPositions ( self , 1 , nil , false )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
self : FinishAddLoc ( args )
end
end
local function ItemAppendPositions ( self , objective , weight , why , restrict )
why2 = why and why .. " \n " or " "
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . vendor then for i , npc in ipairs ( self.o . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) then
n : AppendPositions ( objective , 1 , why2 .. QHFormat ( " OBJECTIVE_PURCHASE " , npc ) , restrict )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . vendor then for i , npc in ipairs ( self.fb . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) then
n : AppendPositions ( objective , 1 , why2 .. QHFormat ( " OBJECTIVE_PURCHASE " , npc ) , restrict )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if next ( objective.p , nil ) then
-- If we have points from vendors, then always use vendors. I don't want it telling you to killing the
-- towns people just because you had to talk to them anyway, and it saves walking to the store.
return
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . drop then for monster , count in pairs ( self.o . drop ) do
local m = self.qh : GetObjective ( " monster " , monster )
m : AppendPositions ( objective , m.o . looted and count / m.o . looted or 1 , why2 .. QHFormat ( " OBJECTIVE_SLAY " , monster ) , restrict )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . drop then for monster , count in pairs ( self.fb . drop ) do
local m = self.qh : GetObjective ( " monster " , monster )
m : AppendPositions ( objective , m.fb . looted and count / m.fb . looted or 1 , why2 .. QHFormat ( " OBJECTIVE_SLAY " , monster ) , restrict )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . contained then for item , count in pairs ( self.o . contained ) do
local i = self.qh : GetObjective ( " item " , item )
i : AppendPositions ( objective , i.o . opened and count / i.o . opened or 1 , why2 .. QHFormat ( " OBJECTIVE_LOOT " , item ) , restrict )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . contained then for item , count in pairs ( self.fb . contained ) do
local i = self.qh : GetObjective ( " item " , item )
i : AppendPositions ( objective , i.fb . opened and count / i.fb . opened or 1 , why2 .. QHFormat ( " OBJECTIVE_LOOT " , item ) , restrict )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . pos then for i , p in ipairs ( self.o . pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , why )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . pos then for i , p in ipairs ( self.fb . pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , why )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.quest then
local item_list = self.quest . o.item
if item_list then
local data = item_list [ self.obj ]
if data and data.drop then
for monster , count in pairs ( data.drop ) do
local m = self.qh : GetObjective ( " monster " , monster )
m : AppendPositions ( objective , m.o . looted and count / m.o . looted or 1 , why2 .. QHFormat ( " OBJECTIVE_SLAY " , monster ) , restrict )
end
elseif data and data.pos then
for i , p in ipairs ( data.pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , why )
end
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
item_list = self.quest . fb.item
2013-01-04 02:03:20 +01:00
if item_list then
2010-10-24 23:17:33 +02:00
local data = item_list [ self.obj ]
if data and data.drop then
for monster , count in pairs ( data.drop ) do
local m = self.qh : GetObjective ( " monster " , monster )
m : AppendPositions ( objective , m.fb . looted and count / m.fb . looted or 1 , why2 .. QHFormat ( " OBJECTIVE_SLAY " , monster ) , restrict )
end
elseif data and data.pos then
for i , p in ipairs ( data.pos ) do
if not restrict or not self.qh : Disallowed ( p [ 1 ] ) then
objective : AddLoc ( p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , why )
end
end
end
end
end
end
local function ItemDoMarkUsed ( self )
if self.o . vendor then for i , npc in ipairs ( self.o . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) then
self : Uses ( n , " TOOLTIP_PURCHASE " )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . vendor then for i , npc in ipairs ( self.fb . vendor ) do
local n = self.qh : GetObjective ( " monster " , npc )
local faction = n.o . faction or n.fb . faction
if ( not faction or faction == self.qh . faction ) then
self : Uses ( n , " TOOLTIP_PURCHASE " )
end
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . drop then for monster , count in pairs ( self.o . drop ) do
self : Uses ( self.qh : GetObjective ( " monster " , monster ) , " TOOLTIP_SLAY " )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . drop then for monster , count in pairs ( self.fb . drop ) do
self : Uses ( self.qh : GetObjective ( " monster " , monster ) , " TOOLTIP_SLAY " )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.o . contained then for item , count in pairs ( self.o . contained ) do
self : Uses ( self.qh : GetObjective ( " item " , item ) , " TOOLTIP_LOOT " )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.fb . contained then for item , count in pairs ( self.fb . contained ) do
self : Uses ( self.qh : GetObjective ( " item " , item ) , " TOOLTIP_LOOT " )
end end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.quest then
local item_list = self.quest . o.item
if item_list then
local data = item_list [ self.obj ]
if data and data.drop then
for monster , count in pairs ( data.drop ) do
self : Uses ( self.qh : GetObjective ( " monster " , monster ) , " TOOLTIP_SLAY " )
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
item_list = self.quest . fb.item
2013-01-04 02:03:20 +01:00
if item_list then
2010-10-24 23:17:33 +02:00
local data = item_list [ self.obj ]
if data and data.drop then
for monster , count in pairs ( data.drop ) do
self : Uses ( self.qh : GetObjective ( " monster " , monster ) , " TOOLTIP_SLAY " )
end
end
end
end
end
---------------
2013-01-04 02:03:20 +01:00
-- NEEDS CONVERSION FOR Map, Floor.
2010-10-24 23:17:33 +02:00
local function AddLoc ( self , index , x , y , w , why )
--[[ assert(not self.setup) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if w > 0 then
local pair = QuestHelper_ZoneLookup [ index ]
2011-03-05 00:26:38 +01:00
QuestHelper : Assert ( pair , " Index does not exist... it is probably 26 or 38 and the value really is... ' " .. tostring ( index ) .. " ' " )
2010-10-24 23:17:33 +02:00
if not pair then return end -- that zone doesn't exist! We require more vespene gas. Not enough rage!
local c , z = pair [ 1 ] , pair [ 2 ]
x , y = self.qh . Astrolabe : TranslateWorldMapPosition ( c , z , x , y , c , 0 )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
x = x * self.qh . continent_scales_x [ c ]
y = y * self.qh . continent_scales_y [ c ]
local list = self.qh . zone_nodes [ index ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local points = self.p [ list ]
if not points then
points = QuestHelper : CreateTable ( " objective.p[zone] (objective nodes per-zone) " )
self.p [ list ] = points
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , p in pairs ( points ) do
local u , v = x - p [ 3 ] , y - p [ 4 ]
if u * u + v * v < 25 then -- Combine points within a threshold of 5 seconds travel time.
p [ 3 ] = ( p [ 3 ] * p [ 5 ] + x * w ) / ( p [ 5 ] + w )
p [ 4 ] = ( p [ 4 ] * p [ 5 ] + y * w ) / ( p [ 5 ] + w )
p [ 5 ] = p [ 5 ] + w
if w > p [ 7 ] then
p [ 6 ] , p [ 7 ] = why , w
end
return
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local new = QuestHelper : CreateTable ( " objective.p[zone] (possible objective node) " )
new [ 1 ] , new [ 2 ] , new [ 3 ] , new [ 4 ] , new [ 5 ] , new [ 6 ] , new [ 7 ] = list , nil , x , y , w , why , w
table.insert ( points , new )
end
end
local function FinishAddLoc ( self , args )
local mx = 0
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for z , pl in pairs ( self.p ) do
for i , p in ipairs ( pl ) do
if p [ 5 ] > mx then
self.location = p
mx = p [ 5 ]
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not self.zones then
-- Not using CreateTable, because it will not be released when routing is complete.
self.zones = { }
else
-- We could remove the already known zones, but I'm operating under the assumtion that locations will only be added,
-- not removed, so this isn't necessary.
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- Remove probably useless locations.
for z , pl in pairs ( self.p ) do
local remove_zone = true
local i = 1
while i <= # pl do
if pl [ i ] [ 5 ] < mx * 0.2 then
QuestHelper : ReleaseTable ( pl [ i ] )
table.remove ( pl , i )
else
remove_zone = false
i = i + 1
end
end
if remove_zone then
QuestHelper : ReleaseTable ( self.p [ z ] )
self.p [ z ] = nil
else
self.zones [ z.i ] = true
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local node_map = self.nm
local node_list = self.nl
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for list , pl in pairs ( self.p ) do
local dist = self.d [ list ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--[[ assert(not dist) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not dist then
dist = QuestHelper : CreateTable ( " self.d[list] " )
self.d [ list ] = dist
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , point in ipairs ( pl ) do
point [ 5 ] = mx / point [ 5 ] -- Will become 1 for the most desired location, and become larger and larger for less desireable locations.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
point [ 2 ] = QuestHelper : CreateTable ( " possible objective node to zone edge cache " )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , node in ipairs ( list ) do
QuestHelper : Assert ( type ( point [ 3 ] ) == " number " , string.format ( " p3 %s " , tostring ( point [ 3 ] ) ) )
QuestHelper : Assert ( type ( point [ 4 ] ) == " number " , string.format ( " p4 %s " , tostring ( point [ 4 ] ) ) )
QuestHelper : Assert ( type ( node.x ) == " number " , string.format ( " nx %s " , tostring ( node.x ) ) )
QuestHelper : Assert ( type ( node.y ) == " number " , string.format ( " ny %s " , tostring ( node.y ) ) )
local u , v = point [ 3 ] - node.x , point [ 4 ] - node.y
local d = math.sqrt ( u * u + v * v )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
point [ 2 ] [ i ] = d
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if dist [ i ] then
if d * point [ 5 ] < dist [ i ] [ 1 ] * dist [ i ] [ 2 ] then
dist [ i ] [ 1 ] , dist [ i ] [ 2 ] = d , point [ 5 ]
node_map [ node ] = point
end
else
local pair = QuestHelper : CreateTable ( )
pair [ 1 ] , pair [ 2 ] = d , point [ 5 ]
dist [ i ] = pair
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not node_map [ node ] then
table.insert ( node_list , node )
node_map [ node ] = point
else
u , v = node_map [ node ] [ 3 ] - node.x , node_map [ node ] [ 4 ] - node.y
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if dist [ i ] [ 1 ] * dist [ i ] [ 2 ] < math.sqrt ( u * u + v * v ) * node_map [ node ] [ 5 ] then
node_map [ node ] = point
end
end
end
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- Disabled because we're having some data sanity issues. This should be solved at buildtime, but I'm leery of mucking with the build system right now, so it isn't. Re-enable later.
--if not args or not args.failable then
-- if #node_list == 0 and QuestHelper:IsWrath() then QuestHelper:Error(self.cat.."/"..self.obj..": zero nodes!") end
--end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--[[ assert(not self.setup) ]]
self.setup = true
table.insert ( self.qh . prepared_objectives , self )
end
local function GetPosition ( self )
--[[ assert(self.setup) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return self.location
end
local QH_TESTCACHE = nil -- make this "true" or something if you want to test caching (i.e. recalculate everything, then verify that the cache is valid)
-- Note: Pos is the starting point, the objective is the destination. These are different data formats - "self" can be a set of points.
-- More annotation here, if you're trying to learn the codebase. This function is a more complicated version of QH:ComputeTravelTime, so refer to that for information first before reading this one.
local function ObjectiveTravelTime ( self , pos , nocache )
--[[ assert(self.setup) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- The caching is pretty obvious.
local key , cached
if not nocache then
--[[ assert(pos ~= QuestHelper.pos) ]]
if not pos.key then
pos.key = math.random ( ) .. " "
end
key = pos.key
cached = self.distance_cache [ key ]
if cached then
if not QH_TESTCACHE then
return unpack ( cached )
end
end
end
local graph = self.qh . world_graph
local nl = self.nl
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
graph : PrepareSearch ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- This is quite similar to the same "create nodes for all zone links" in ComputeTravelTime except that it's creating nodes for all zone links for a set of possible destinations. I'm not sure if the weighting is backwards. It might be.
for z , l in pairs ( self.d ) do
for i , n in ipairs ( z ) do
if n.s == 0 then
n.e , n.w = unpack ( l [ i ] )
n.s = 3
elseif n.e * n.w < l [ i ] [ 1 ] * l [ i ] [ 2 ] then
n.e , n.w = unpack ( l [ i ] )
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local d = pos [ 2 ]
for i , n in ipairs ( pos [ 1 ] ) do
graph : AddStartNode ( n , d [ i ] , nl )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local e = graph : DoSearch ( nl )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- d changes datatype here. I hate this codebase. Hell, e probably changes datatype also! yaaaay. what does .nm mean? what does .d mean?
d = e.g + e.e
e = self.nm [ e ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- There's something going on with weighting here that I don't understand
local l = self.p [ pos [ 1 ] ]
if l then
local x , y = pos [ 3 ] , pos [ 4 ]
local score = d * e [ 5 ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , n in ipairs ( l ) do
local u , v = x - n [ 3 ] , y - n [ 4 ]
local d2 = math.sqrt ( u * u + v * v )
local s = d2 * n [ 5 ]
if s < score then
d , e , score = d2 , n , s
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--[[ assert(e) ]]
if not nocache then
--[[ assert( not cached or (cached[1] == d and cached[2] == e)) ]]
if not QH_TESTCACHE or not cached then
local new = self.qh : CreateTable ( )
new [ 1 ] , new [ 2 ] = d , e
self.distance_cache [ key ] = new
self.qh : CacheRegister ( self )
end
else
if self.distance_cache and self.distance_cache [ key ] then
--[[ assert(self.distance_cache[key][1] == d) ]]
end
end
return d , e
end
-- Note: pos1 is the starting point, pos2 is the ending point, the objective is somewhere between them.
-- Yet more annotation! This one is based off ObjectiveTravelTime. Yes, it's nasty that there are three (edit: four) functions with basically the same goal. Have I mentioned this codebase kind of sucks?
local function ObjectiveTravelTime2 ( self , pos1 , pos2 , nocache )
--[[ assert(self.setup) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- caching is pretty simple as usual
local key , cached
if not nocache then
--[[ assert(pos1 ~= QuestHelper.pos) ]]
--[[ assert(pos2 ~= QuestHelper.pos) ]]
-- We don't want to cache distances involving the player's current position, as that would spam the table
if not pos1.key then
pos1.key = math.random ( ) .. " "
end
if not pos2.key then
pos2.key = math.random ( ) .. " "
end
key = pos1.key .. pos2.key
cached = self.distance_cache [ key ]
if cached then
if not QH_TESTCACHE then
return unpack ( cached )
end
end
end
local graph = self.qh . world_graph
local nl = self.nl
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- This is the standard pos1-to-self code that we're used to seeing . . .
graph : PrepareSearch ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for z , l in pairs ( self.d ) do
for i , n in ipairs ( z ) do
if n.s == 0 then
n.e , n.w = unpack ( l [ i ] )
n.s = 3
elseif n.e * n.w < l [ i ] [ 1 ] * l [ i ] [ 2 ] then
n.e , n.w = unpack ( l [ i ] )
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local d = pos1 [ 2 ]
for i , n in ipairs ( pos1 [ 1 ] ) do
graph : AddStartNode ( n , d [ i ] , nl )
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
graph : DoFullSearch ( nl )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
graph : PrepareSearch ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- . . . and here's where it gets wonky
-- Now, we need to figure out how long it takes to get to each node.
for z , point_list in pairs ( self.p ) do
if z == pos1 [ 1 ] then
-- Will also consider min distance.
local x , y = pos1 [ 3 ] , pos1 [ 4 ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , p in ipairs ( point_list ) do
local a , b = p [ 3 ] - x , p [ 4 ] - y
local u , v = p [ 3 ] , p [ 4 ]
local d = math.sqrt ( a * a + b * b )
local w = p [ 5 ]
local score = d * w
for i , n in ipairs ( z ) do
a , b = n.x - u , n.y - v
local bleh = math.sqrt ( a * a + b * b ) + n.g
local s = bleh * w
if s < score then
d , score = bleh , d
end
end
p [ 7 ] = d
end
else
for i , p in ipairs ( point_list ) do
local x , y = p [ 3 ] , p [ 4 ]
local w = p [ 5 ]
local d
local score
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , n in ipairs ( z ) do
local a , b = n.x - x , n.y - y
local d2 = math.sqrt ( a * a + b * b ) + n.g
local s = d2 * w
if not score or s < score then
d , score = d2 , s
end
end
p [ 7 ] = d
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
d = pos2 [ 2 ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for i , n in ipairs ( pos2 [ 1 ] ) do
n.e = d [ i ]
n.s = 3
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local el = pos2 [ 1 ]
local nm = self.nm2
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for z , l in pairs ( self.d ) do
for i , n in ipairs ( z ) do
local x , y = n.x , n.y
local bp
local bg
local bs
for i , p in ipairs ( self.p [ z ] ) do
local a , b = x - p [ 3 ] , y - p [ 4 ]
d = p [ 7 ] + math.sqrt ( a * a + b * b )
s = d * p [ 5 ]
if not bs or s < bs then
bg , bp , bs = d , p , s
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
nm [ n ] = bp
-- Using score instead of distance, because we want nodes we're not really interested in to be less likely to get chosen.
graph : AddStartNode ( n , bs , el )
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local e = graph : DoSearch ( pos2 [ 1 ] )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
d = nm [ e.p ] [ 7 ]
local d2 = e.g + e.e - e.p . g + ( e.p . g / nm [ e.p ] [ 5 ] - nm [ e.p ] [ 7 ] )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
e = nm [ e.p ]
local total = ( d + d2 ) * e [ 5 ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.p [ el ] then
local x , y = pos2 [ 3 ] , pos2 [ 4 ]
for i , p in ipairs ( self.p [ el ] ) do
local a , b = x - p [ 3 ] , y - p [ 4 ]
local c = math.sqrt ( a * a + b * b )
local t = ( p [ 7 ] + c ) * p [ 5 ]
if t < total then
total , d , d2 , e = t , p [ 7 ] , c , p
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- grim stabilization hack, since obviously the numbers it generates are only vaguely based in reality. This should be fixed and removed ASAP (you know, once I figure out WTF this thing is doing)
d = QuestHelper : ComputeTravelTime ( pos1 , e )
d2 = QuestHelper : ComputeTravelTime ( e , pos2 )
--[[ assert(e) ]]
if not nocache then
--[[ assert( not cached or (cached[1] == d and cached[2] == d2 and cached[3] == e)) ]]
if not QH_TESTCACHE or not cached then
local new = self.qh : CreateTable ( " ObjectiveTravelTime2 cache " )
new [ 1 ] , new [ 2 ] , new [ 3 ] = d , d2 , e
self.distance_cache [ key ] = new
self.qh : CacheRegister ( self )
end
else
if self.distance_cache and self.distance_cache [ key ] then
--[[ assert(self.distance_cache[key][1] == d and self.distance_cache[key][2] == d2) ]]
end
end
--[[if pos1 and pos2 then -- Debug code so I can maybe actually fix the problems someday
QuestHelper : TextOut ( " Beginning dumping here " )
local laxa = QuestHelper : ComputeTravelTime ( pos1 , e , true )
if math.abs ( laxa - d ) >= 0.0001 then
QuestHelper : TextOut ( QuestHelper : StringizeTable ( pos1 ) )
QuestHelper : TextOut ( QuestHelper : StringizeRecursive ( pos1 , 2 ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e [ 1 ] ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e [ 2 ] ) )
QuestHelper : TextOut ( QuestHelper : StringizeRecursive ( e [ 1 ] , 2 ) ) ] ]
----[[ QuestHelper:Assert(math.abs(laxa-d) < 0.0001, "Compare: "..laxa.." vs "..d) ]] -- wonky commenting is thanks to the de-assert script, fix later
--[[end
local laxb = QuestHelper : ComputeTravelTime ( e , pos2 , true )
if math.abs ( laxb - d2 ) >= 0.0001 then
QuestHelper : TextOut ( QuestHelper : StringizeTable ( pos2 ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e [ 1 ] ) )
QuestHelper : TextOut ( QuestHelper : StringizeTable ( e [ 2 ] ) )
QuestHelper : TextOut ( QuestHelper : StringizeRecursive ( e [ 1 ] , 2 ) ) ] ]
----[[ QuestHelper:Assert(math.abs(laxa-d) < 0.0001, "Compare: "..laxb.." vs "..d2) ]]
--[[end
end ] ]
return d , d2 , e
end
local function DoneRouting ( self )
--[[ assert(self.setup_count > 0) ]]
--[[ assert(self.setup) ]]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.setup_count == 1 then
self.setup_count = 0
QuestHelper : ReleaseObjectivePathingInfo ( self )
for i , obj in ipairs ( self.qh . prepared_objectives ) do
if o == obj then
table.remove ( self.qh . prepared_objectives , i )
break
end
end
else
self.setup_count = self.setup_count - 1
end
end
local function IsObjectiveWatched ( self )
-- Check if an objective is being watched. Note that this is an external query, not a simple Selector.
local info
if self.cat == " quest " then
info = QuestHelper.quest_log [ self ]
else
info = QuestHelper.quest_log [ self.quest ]
end
if info then
local index = info.index
if index then
if UberQuest then
-- UberQuest has it's own way of tracking quests.
local uq_settings = UberQuest_Config [ UnitName ( " player " ) ]
if uq_settings then
local list = uq_settings.selected
if list then
return list [ GetQuestLogTitle ( index ) ]
end
end
else
return IsQuestWatched ( index )
end
end
end
return false
end
local next_objective_id = 0
local function ObjectiveShare ( self )
self.want_share = true
end
local function ObjectiveUnshare ( self )
self.want_share = false
end
QuestHelper.default_objective_param =
{
CouldBeFirst = ObjectiveCouldBeFirst ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
Uses = Uses ,
DoMarkUsed = DoMarkUsed ,
MarkUsed = MarkUsed ,
MarkUnused = MarkUnused ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
DefaultKnown = DefaultObjectiveKnown ,
Known = DummyObjectiveKnown ,
Reason = ObjectiveReason ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
AppendPositions = ObjectiveAppendPositions ,
PrepareRouting = ObjectivePrepareRouting ,
AddLoc = AddLoc ,
FinishAddLoc = FinishAddLoc ,
DoneRouting = DoneRouting ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
Position = GetPosition ,
TravelTime = ObjectiveTravelTime ,
TravelTime2 = ObjectiveTravelTime2 ,
IsWatched = IsObjectiveWatched ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
Share = ObjectiveShare , -- Invoke to share this objective with your peers.
Unshare = ObjectiveUnshare , -- Invoke to stop sharing this objective.
}
QuestHelper.default_objective_item_param =
{
Known = ItemKnown ,
AppendPositions = ItemAppendPositions ,
DoMarkUsed = ItemDoMarkUsed
}
for key , value in pairs ( QuestHelper.default_objective_param ) do
if not QuestHelper.default_objective_item_param [ key ] then
QuestHelper.default_objective_item_param [ key ] = value
end
end
QuestHelper.default_objective_meta = { __index = QuestHelper.default_objective_param }
QuestHelper.default_objective_item_meta = { __index = QuestHelper.default_objective_item_param }
function QuestHelper : NewObjectiveObject ( )
next_objective_id = next_objective_id + 1
return
setmetatable ( {
qh = self ,
id = next_objective_id ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
want_share = false , -- True if we want this objective shared.
is_sharing = false , -- Set to true if we've told other users about this objective.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
user_ignore = nil , -- When nil, will use filters. Will ignore, when true, always show (if known).
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
priority = 3 , -- A hint as to what priority the quest should have. Should be 1, 2, 3, 4, or 5.
real_priority = 3 , -- This will be set to the priority routing actually decided to assign it.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
setup_count = 0 ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
icon_id = 12 ,
icon_bg = 14 ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
match_zone = false ,
match_level = false ,
match_done = false ,
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
before = { } , -- List of objectives that this objective must appear before.
after = { } , -- List of objectives that this objective must appear after.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- Routing related junk.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--[[ Will be created as needed.
d = nil ,
p = nil ,
nm = nil , -- Maps nodes to their nearest zone/list/x/y position.
nm2 = nil , -- Maps nodes to their nears position, but dynamically set in TravelTime2.
nl = nil , -- List of all the nodes we need to consider.
location = nil , -- Will be set to the best position for the node.
pos = nil , -- Zone node list, distance list, x, y, reason.
sop = nil ] ]
} , QuestHelper.default_objective_meta )
end
local explicit_support_warning_given = false
function QuestHelper : GetObjective ( category , objective )
local objective_list = self.objective_objects [ category ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not objective_list then
objective_list = { }
self.objective_objects [ category ] = objective_list
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local objective_object = objective_list [ objective ]
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not objective_object then
if category == " quest " then
local level , hash , name = string.match ( objective , " ^(%d+)/(%d*)/(.*)$ " )
if not level then
level , name = string.match ( objective , " ^(%d+)/(.*)$ " )
if not level then
name = objective
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if hash == " " then hash = nil end
objective_object = self : GetQuest ( name , tonumber ( level ) , tonumber ( hash ) )
objective_list [ objective ] = objective_object
return objective_object
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective_object = self : NewObjectiveObject ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective_object.cat = category
objective_object.obj = objective
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if category == " item " then
setmetatable ( objective_object , QuestHelper.default_objective_item_meta )
objective_object.icon_id = 2
elseif category == " monster " then
objective_object.icon_id = 1
elseif category == " object " then
objective_object.icon_id = 3
elseif category == " event " then
objective_object.icon_id = 4
elseif category == " loc " then
objective_object.icon_id = 6
elseif category == " reputation " then
objective_object.icon_id = 5
elseif category == " player " then
objective_object.icon_id = 1 -- not ideal, will improve later
else
if not explicit_support_warning_given then
self : TextOut ( " FIXME: Objective type ' " .. category .. " ' for objective ' " .. objective .. " ' isn't explicitly supported yet; hopefully the dummy handler will do something sensible. " )
explicit_support_warning_given = true
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective_list [ objective ] = objective_object
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if category == " loc " then
-- Loc is special, we don't store it, and construct it from the string.
-- Don't have any error checking here, will assume it's correct.
local i
local _ , _ , c , z , x , y = string.find ( objective , " ^(%d+),(%d+),([%d%.]+),([%d%.]+)$ " )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not y then
_ , _ , i , x , y = string.find ( objective , " ^(%d+),([%d%.]+),([%d%.]+)$ " )
else
i = QuestHelper_IndexLookup [ c ] [ z ]
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective_object.o = { pos = { { tonumber ( i ) , tonumber ( x ) , tonumber ( y ) , 1 } } }
objective_object.fb = { }
else
objective_list = QuestHelper_Objectives_Local [ category ]
if not objective_list then
objective_list = { }
QuestHelper_Objectives_Local [ category ] = objective_list
end
objective_object.o = objective_list [ objective ]
if not objective_object.o then
objective_object.o = { }
objective_list [ objective ] = objective_object.o
end
local l = QuestHelper_StaticData [ self.locale ]
if l then
objective_list = l.objective [ category ]
if objective_list then
objective_object.fb = objective_list [ objective ]
end
end
if not objective_object.fb then
objective_object.fb = { }
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- TODO: If we have some other source of information (like LightHeaded) add its data to objective_object.fb
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return objective_object
end
function QuestHelper : AppendObjectivePosition ( objective , i , x , y , w )
if not i then return end -- We don't have a player position. We have a pile of poop. Enjoy your poop.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local pos = objective.o . pos
if not pos then
if objective.o . drop or objective.o . contained then
return -- If it's dropped by a monster, don't record the position we got the item at.
end
objective.o . pos = self : AppendPosition ( { } , i , x , y , w )
else
self : AppendPosition ( pos , i , x , y , w )
end
end
function QuestHelper : AppendObjectiveDrop ( objective , monster , count )
local drop = objective.o . drop
if drop then
drop [ monster ] = ( drop [ monster ] or 0 ) + ( count or 1 )
else
objective.o . drop = { [ monster ] = count or 1 }
objective.o . pos = nil -- If it's dropped by a monster, then forget the position we found it at.
end
end
function QuestHelper : AppendItemObjectiveDrop ( item_object , item_name , monster_name , count )
local quest = self : ItemIsForQuest ( item_object , item_name )
if quest and not item_object.o . vendor and not item_object.o . drop and not item_object.o . pos then
self : AppendQuestDrop ( quest , item_name , monster_name , count )
else
if not item_object.o . drop and not item_object.o . pos then
self : PurgeQuestItem ( item_object , item_name )
end
self : AppendObjectiveDrop ( item_object , monster_name , count )
end
end
function QuestHelper : AppendItemObjectivePosition ( item_object , item_name , i , x , y )
local quest = self : ItemIsForQuest ( item_object , item_name )
if quest and not item_object.o . vendor and not item_object.o . drop and not item_object.o . pos then
self : AppendQuestPosition ( quest , item_name , i , x , y )
else
if not item_object.o . vendor and not item_object.o . drop and not item_object.o . contained and not item_object.o . pos then
-- Just learned that this item doesn't depend on a quest to drop, remove any quest references to it.
self : PurgeQuestItem ( item_object , item_name )
end
self : AppendObjectivePosition ( item_object , i , x , y )
end
end
function QuestHelper : AppendItemObjectiveContainer ( objective , container_name , count )
local container = objective.o . contained
if container then
container [ container_name ] = ( container [ container_name ] or 0 ) + ( count or 1 )
else
objective.o . contained = { [ container_name ] = count or 1 }
objective.o . pos = nil -- Forget the position.
end
end
function QuestHelper : AddObjectiveWatch ( objective , reason )
if not objective.reasons then
objective.reasons = { }
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not next ( objective.reasons , nil ) then
objective.watched = true
objective : MarkUsed ( )
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective.filter_blocked = false
for obj in pairs ( objective.swap_after or objective.after ) do
if obj.watched then
objective.filter_blocked = true
break
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for obj in pairs ( objective.swap_before or objective.before ) do
if obj.watched then
obj.filter_blocked = true
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.to_remove [ objective ] then
self.to_remove [ objective ] = nil
else
self.to_add [ objective ] = true
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective.reasons [ reason ] = ( objective.reasons [ reason ] or 0 ) + 1
end
function QuestHelper : RemoveObjectiveWatch ( objective , reason )
if objective.reasons [ reason ] == 1 then
objective.reasons [ reason ] = nil
if not next ( objective.reasons , nil ) then
objective : MarkUnused ( )
objective.watched = false
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for obj in pairs ( objective.swap_before or objective.before ) do
if obj.watched then
obj.filter_blocked = false
for obj2 in pairs ( obj.swap_after or obj.after ) do
if obj2.watched then
obj.filter_blocked = true
break
end
end
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if self.to_add [ objective ] then
self.to_add [ objective ] = nil
else
self.to_remove [ objective ] = true
end
end
else
objective.reasons [ reason ] = objective.reasons [ reason ] - 1
end
end
function QuestHelper : ObjectiveObjectDependsOn ( objective , needs )
--[[ assert(objective ~= needs) ]] -- If this was true, ObjectiveIsKnown would get in an infinite loop.
-- TODO: Needs sanity checking, especially now that dependencies can be assigned by remote users.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
-- We store the new relationships in objective.swap_[before|after],
-- creating and copying them from objective.[before|after],
-- the routing coroutine will check for those, swap them, and release the originals
-- when it gets to a safe place to do so.
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not ( objective.swap_after or objective.after ) [ needs ] then
if objective.peer then
for u , l in pairs ( objective.peer ) do
-- Make sure other users know that the dependencies for this objective changed.
objective.peer [ u ] = math.min ( l , 1 )
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not objective.swap_after then
objective.swap_after = self : CreateTable ( " swap_after " )
for key , value in pairs ( objective.after ) do objective.swap_after [ key ] = value end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not needs.swap_before then
needs.swap_before = self : CreateTable ( " swap_before " )
for key , value in pairs ( needs.before ) do needs.swap_before [ key ] = value end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if needs.watched then
objective.filter_blocked = true
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
objective.swap_after [ needs ] = true
needs.swap_before [ objective ] = true
end
end
function QuestHelper : IgnoreObjective ( objective )
if self.user_objectives [ objective ] then
self : RemoveObjectiveWatch ( objective , self.user_objectives [ objective ] )
self.user_objectives [ objective ] = nil
else
objective.user_ignore = true
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
--self:ForceRouteUpdate()
end
function QuestHelper : SetObjectivePriority ( objective , level )
level = math.min ( 5 , math.max ( 1 , math.floor ( ( tonumber ( level ) or 3 ) + 0.5 ) ) )
if level ~= objective.priority then
objective.priority = level
if objective.peer then
for u , l in pairs ( objective.peer ) do
-- Peers don't know about this new priority.
objective.peer [ u ] = math.min ( l , 2 )
end
end
--self:ForceRouteUpdate()
end
end
local function CalcObjectivePriority ( obj )
local priority = obj.priority
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
for o in pairs ( obj.before ) do
if o.watched then
priority = math.min ( priority , CalcObjectivePriority ( o ) )
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
return priority
end
local function ApplyBlockPriority ( obj , level )
for o in pairs ( obj.before ) do
if o.watched then
ApplyBlockPriority ( o , level )
end
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if obj.priority < level then QuestHelper : SetObjectivePriority ( obj , level ) end
end
function QuestHelper : SetObjectivePriorityPrompt ( objective , level )
self : SetObjectivePriority ( objective , level )
if CalcObjectivePriority ( objective ) ~= level then
local menu = self : CreateMenu ( )
self : CreateMenuTitle ( menu , QHText ( " IGNORED_PRIORITY_TITLE " ) )
self : CreateMenuItem ( menu , QHText ( " IGNORED_PRIORITY_FIX " ) ) : SetFunction ( ApplyBlockPriority , objective , level )
self : CreateMenuItem ( menu , QHText ( " IGNORED_PRIORITY_IGNORE " ) ) : SetFunction ( self.nop )
menu : ShowAtCursor ( )
end
end
function QuestHelper : SetObjectiveProgress ( objective , user , have , need )
if have and need then
local list = objective.progress
if not list then
list = self : CreateTable ( " objective.progress " )
objective.progress = list
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local user_progress = list [ user ]
if not user_progress then
user_progress = self : CreateTable ( " objective.progress[user] " )
list [ user ] = user_progress
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
local pct = 0
local a , b = tonumber ( have ) , tonumber ( need )
if a and b then
if b ~= 0 then
pct = a / b
elseif a == 0 then
pct = 1
end
elseif a == b then
pct = 1
end
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
user_progress [ 1 ] , user_progress [ 2 ] , user_progress [ 3 ] = have , need , pct
else
if objective.progress then
if objective.progress [ user ] then
self : ReleaseTable ( objective.progress [ user ] )
objective.progress [ user ] = nil
2013-01-04 02:03:20 +01:00
2010-10-24 23:17:33 +02:00
if not next ( objective.progress , nil ) then
self : ReleaseTable ( objective.progress )
objective.progress = nil
end
end
end
end
end