Module:OmegaMap: Difference between revisions

From Omega Wiki
Jump to navigation Jump to search
更新双语主页与 LOCUS 地图,统一草稿入口和版本说明
补全LOCUS地点及任务人物关联:temples
 
(2 intermediate revisions by the same user not shown)
Line 99: Line 99:


local remakeLocations = {
local remakeLocations = {
   ['%'] = { target="Draft:Archmage's Castle", label="Archmage's Castle", zh='大法师城堡', char='%', class='grey' },
   ['%'] = { target="Draft:Archmage's Castle", label="Archmage's Castle", zh='大法师城堡', char='%', class='grey', article=true },
   ['|'] = { target='Draft:Star Peak', label='Star Peak', zh='星辰峰', char='|', class='light-blue' },
   ['|'] = { target='Draft:Star Peak', label='Star Peak', zh='星辰峰', char='|', class='light-blue', article=true },
   ['*'] = { target='Draft:Goblin Caves', label='Goblin Caves', zh='地精洞窟', char='*', class='black-on-brown' },
   ['*'] = { target='Draft:Goblin Caves', label='Goblin Caves', zh='地精洞窟', char='*', class='black-on-brown', article=true },
   ['!'] = { target='Draft:HellWell Volcano', label='HellWell Volcano', zh='地狱井火山', char='!', class='red' },
   ['!'] = { target='Draft:HellWell Volcano', label='HellWell Volcano', zh='地狱井火山', char='!', class='red', article=true },
   ['$'] = { target="Draft:Dragon's Lair", label="Dragon's Lair", zh='龙穴', char='$', class='brown' },
   ['$'] = { target="Draft:Dragon's Lair", label="Dragon's Lair", zh='龙穴', char='$', class='brown', article=true },
   ['&'] = { target='Draft:Magic Isle', label='Magic Isle', zh='魔法岛', char='&', class='purple' },
   ['&'] = { target='Draft:Magic Isle', label='Magic Isle', zh='魔法岛', char='&', class='purple', article=true },
   ['O'] = { target='Draft:Rampart', label='Rampart', zh='兰帕特', char='O', class='white', article=true },
   ['O'] = { target='Draft:Rampart', label='Rampart', zh='兰帕特', char='O', class='white', article=true },
   ['a'] = { target='Draft:Village 1', label='Village 1', zh='村庄 1', char='o', class='white' },
   ['a'] = { target='Draft:Star View', label='Star View', zh='村庄 1', char='o', class='white', article=true },
   ['b'] = { target='Draft:Village 2', label='Village 2', zh='村庄 2', char='o', class='white' },
   ['b'] = { target='Draft:Woodmere', label='Woodmere', zh='村庄 2', char='o', class='white', article=true },
   ['c'] = { target='Draft:Village 3', label='Village 3', zh='村庄 3', char='o', class='white' },
   ['c'] = { target='Draft:Stormwatch', label='Stormwatch', zh='村庄 3', char='o', class='white', article=true },
   ['d'] = { target='Draft:Village 4', label='Village 4', zh='村庄 4', char='o', class='white' },
   ['d'] = { target='Draft:Thaumaris', label='Thaumaris', zh='村庄 4', char='o', class='white', article=true },
   ['e'] = { target='Draft:Village 5', label='Village 5', zh='村庄 5', char='o', class='white' },
   ['e'] = { target='Draft:Skorch', label='Skorch', zh='村庄 5', char='o', class='white', article=true },
   ['f'] = { target='Draft:Village 6', label='Village 6', zh='村庄 6', char='o', class='white' },
   ['f'] = { target='Draft:Whorfen', label='Whorfen', zh='村庄 6', char='o', class='white', article=true },
   ['1'] = { target='Draft:Temple of Odin', label='Temple of Odin', zh='奥丁神殿', char='X', class='brown' },
   ['1'] = { target='Draft:Temple of Odin', label='Temple of Odin', zh='奥丁神殿', char='X', class='brown', article=true },
   ['2'] = { target='Draft:Temple of Set', label='Temple of Set', zh='赛特神殿', char='X', class='brown' },
   ['2'] = { target='Draft:Temple of Set', label='Temple of Set', zh='赛特神殿', char='X', class='brown', article=true },
   ['3'] = { target='Draft:Temple of Athena', label='Temple of Athena', zh='雅典娜神殿', char='X', class='brown' },
   ['3'] = { target='Draft:Temple of Athena', label='Temple of Athena', zh='雅典娜神殿', char='X', class='brown', article=true },
   ['4'] = { target='Draft:Temple of Hecate', label='Temple of Hecate', zh='赫卡忒神殿', char='X', class='brown' },
   ['4'] = { target='Draft:Temple of Hecate', label='Temple of Hecate', zh='赫卡忒神殿', char='X', class='brown', article=true },
   ['5'] = { target='Draft:Temple of Druidism', label='Temple of Druidism', zh='德鲁伊神殿', char='X', class='brown' },
   ['5'] = { target='Draft:Temple of Druidism', label='Temple of Druidism', zh='德鲁伊神殿', char='X', class='brown', article=true },
   ['6'] = { target='Draft:Temple of Destiny', label='Temple of Destiny', zh='命运神殿', char='X', class='brown' },
   ['6'] = { target='Draft:Temple of Destiny', label='Temple of Destiny', zh='命运神殿', char='X', class='brown', article=true },
}
}


Line 236: Line 236:
   if map.draft then
   if map.draft then
     local note = language == 'zh'
     local note = language == 'zh'
       and '地点索引:Rampart 链接独立草稿;其余地点链接 LOCUS 内的简介。'
       and '地点索引:链接各地点的独立草稿。'
       or 'Location index: Rampart links to its draft article; other destinations link to summaries in LOCUS.'
       or 'Location index: links lead to individual draft articles.'
     directory:tag('p')
     directory:tag('p')
       :addClass('omega-map-draft-note')
       :addClass('omega-map-draft-note')

Latest revision as of 00:41, 10 September 2026

Documentation for this module may be created at Module:OmegaMap/doc

local p = {}

-- OMEGA-SOURCE commit=009707d17482b977061a6223b9de63eac4d368c3
-- staging/remake/winomega/country.c:8-113 decrypts and loads the 64x64 map.
-- staging/remake/winomega/defs.h:687-707 defines terrain glyphs and colors.
-- staging/remake/winomega/lib/country.dat contains the encrypted map cells.
local remakeSource = [=[^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^%^^^^^^^^^^^^^---^^^^^^^^^^^^^^^^((((((------^^^^^^^^^~~^^
++^^~^^v^^^---------------((((((((((((((((((--------^^^^^^~~~~~^
+++^^~^^v^^------------(((((((((((((((((((((((((------^^^~~~~|~^
++++^^~^^v^------((((((((((((((^^^((((((((((------------^^^~v~~^
++++^~^^v^^----------((((((((((^5^((((((((-------.......a~~^~~~^
++++^^~d^^--------------(((((((^(^(((((---------.------~~^^^^^^^
+++----~.......-----------((((((((((-----------.------~~--^^^^^^
++++--~--------.......------((((((((((((------.-------~^^^^^^^^^
+++++++---------------.......(((((((((-------.--------~~--^^^^^^
+++++++----------------------......-.........--------~~-----^^^^
+++++++++--------------((((((((((((b(((((((------^^3~~------^^^^
++++++++++++---------------(((((((.(((((((------^^^~~-------^^^^
++++++++++-------((((((((((((((((.(((((((((((((^^^-~~-------^^^^
+++++++----------------(((((((((.(((((((((---^^^----~~------^^^^
++++++++++++---------------((((.(((((((((--^^^-----~~---^^^^^^^^
++++++++++++++++--------------.--------^^^^^------~~---^^^^^^^^^
+++++++++++++++++++----------.------^^^^---------~~---^^^^^^^^^^
++++++++++++++++++++++++----.---v^^^^^^^^-((((--~~----------^^^^
+++++++++++++++++++++++++++O--^^^v^^^((((((((((((~~---^^^^^^^^^^
+++++++++++++-------------.----^^^v^(((((((((((((~~(----^^^^^^^^
++++++++++---------------.-^^^^^^(((((((((((((((~~((--------^^^^
++++++++++++++++++++++...-^*^^^^(((((((((((((((~~((((((-----^^^^
++++++++++++++++++-...-----^^^-(((((((((((((((~~(((((((((---^^^^
+++++++++++++-----.------^^^^((((((((((((((((~~(((((((((----^^^^
++++++++++-------.-----^^^^--((((((((((((((~~~(((((((((-----^^^^
++++++++++++++++.------^^^-------(((((((((~~(((((((((((((---^^^^
+++++++++++++++.------^^^---((((((((((((((~~(((((((--------^^^^^
+++++++++++++++.-----^^^(((((((((((((((~~~~(((((((-------^^^^^^^
+++++++++++++--.------vvv(((((((((((~~~~(((((((((-------^^^"^^^^
+++++++++++++++.-----^1^-------((~~~~(((((((((((((((-^^^^^""^^^^
+++++++++++++++.------^^^-----((~~~((((((((((((((((-^^^"""""^^^^
++++++++++-----.----^^^------(((((~~(((((((((---^^^^""""""""^^^^
++++++++-------.----^^^-(((((((((((~((((((((((((--^"""""""^^^^^^
++++++---------.-----^^^(((((((((((~~((((((((((--^^^""""""""$^^^
+++++++++++++++.----^^-(((((((((((~~((((((((((--^^^"""""""""^^^^
+++++++++++++++.-----^^^^((((((((~~(((((((((-------""""""""^^^^^
++++++++++++---.----^^^(((((((~~~~(((((((-----^^""""""""""""^^^^
+++++++++++++++.---^^^-(((~~~~~(((((((------^^^"""""""""""""^^^^
+++++++++++++++.---^^^^~~~~(((((----------^^^"""""""""""""""^^^^
++++++++++c....-...~~~~~-((((((((((-----^^^^""""""""""""""""^^^^
++++++++++++++~~~~~.f==-------------^^^^""""""""""""""""^^^^^^^^
++++++++++++=====~==~======----^^^^^^^^^"""""""""""""""""""^^^^^
+++++++++++=====~====~====---------------e""""""""""""""^^^^^^^^
+++++++++++++++~====~====-^^-------^^^^^^"""""""""""""""""""^^^^
+++++++++++++++====~====-^^------^^^"""""""""2"""""""""""---^^^^
+++++++++++++=====~4~===---^-----^^"""""""""""---^^^^-------^^^^
++++++++++=======~==~====---------^^"""""""""--^^-----------^^^^
+++++++++++++===~====~====--^--------""""""---^^-)))-^^-----^^^^
++++++++++++===~======~===---^^^^------------^^~)))))--^^---^^^^
+++++++++++++++======~======-----^^^------^----)~)))))--^^^-^^^^
+++++++++++++++=====~===^=======--~~~^^^-^---))))~))))))----^^^^
++++++++++++++=====~=============~~=~~~~^^))))))))~))))))))-^^^^
+++++++++++++++===~=~~~~~~=====~~~~====^^^^)))))))~)))))))^^^^^^
++++&++++++++++==~========~~~~~~==~~~~===^^^)))))))~))))))))^^^^
+++++=+++++++===~=~=========~===~~===~~~~~~^^)))))))~)))))^^^^^^
++++++=++++++++~===~===^===~===~=====~====^^^^)))))))~))))))^^^^
+++++++=+++++++====~~=====~===^=======~====^^)))))))))~)))))^^^+
++++++++=+========~==~===~===^!^=======~===^^^))+++)))~)))^^^^++
+++++++++=++=====~====~=~~====^=======~=~=^^^)))+6+))~)))^^^^+++
++++++++++++====~======~==~++++======~===~==^^))+++))~^^^^^+++++
++++++++++++++++========~++++++++===~====~^^^^^^^+^^^^~^^+++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]=]

local remakeRows = mw.text.split(remakeSource, '\n', true)
assert(#remakeRows == 64, 'Omega 0.90.4 country map must have 64 rows')
for rowNumber, row in ipairs(remakeRows) do
  assert(#row == 64, 'Omega 0.90.4 country map row ' .. rowNumber .. ' must have 64 columns')
end

local terrain = {
  ['-'] = { char='-', class='light-green' },
  ['_'] = { char='_', class='grey' },
  ['.'] = { char='.', class='brown' },
  ['^'] = { char='^', class='grey' },
  ['v'] = { char='v', class='brown' },
  ['~'] = { char='~', class='blue' },
  ['O'] = { char='O', class='white' },
  ['('] = { char='(', class='light-green' },
  [')'] = { char=')', class='green' },
  ['='] = { char='=', class='green' },
  ['!'] = { char='!', class='red' },
  ['%'] = { char='%', class='grey' },
  ['X'] = { char='X', class='brown' },
  ['*'] = { char='*', class='black-on-brown' },
  ['"'] = { char='"', class='yellow' },
  ['+'] = { char='+', class='light-purple' },
  ['|'] = { char='|', class='light-blue' },
  ['$'] = { char='$', class='brown' },
  ['&'] = { char='&', class='purple' },
}

local remakeLocations = {
  ['%'] = { target="Draft:Archmage's Castle", label="Archmage's Castle", zh='大法师城堡', char='%', class='grey', article=true },
  ['|'] = { target='Draft:Star Peak', label='Star Peak', zh='星辰峰', char='|', class='light-blue', article=true },
  ['*'] = { target='Draft:Goblin Caves', label='Goblin Caves', zh='地精洞窟', char='*', class='black-on-brown', article=true },
  ['!'] = { target='Draft:HellWell Volcano', label='HellWell Volcano', zh='地狱井火山', char='!', class='red', article=true },
  ['$'] = { target="Draft:Dragon's Lair", label="Dragon's Lair", zh='龙穴', char='$', class='brown', article=true },
  ['&'] = { target='Draft:Magic Isle', label='Magic Isle', zh='魔法岛', char='&', class='purple', article=true },
  ['O'] = { target='Draft:Rampart', label='Rampart', zh='兰帕特', char='O', class='white', article=true },
  ['a'] = { target='Draft:Star View', label='Star View', zh='村庄 1', char='o', class='white', article=true },
  ['b'] = { target='Draft:Woodmere', label='Woodmere', zh='村庄 2', char='o', class='white', article=true },
  ['c'] = { target='Draft:Stormwatch', label='Stormwatch', zh='村庄 3', char='o', class='white', article=true },
  ['d'] = { target='Draft:Thaumaris', label='Thaumaris', zh='村庄 4', char='o', class='white', article=true },
  ['e'] = { target='Draft:Skorch', label='Skorch', zh='村庄 5', char='o', class='white', article=true },
  ['f'] = { target='Draft:Whorfen', label='Whorfen', zh='村庄 6', char='o', class='white', article=true },
  ['1'] = { target='Draft:Temple of Odin', label='Temple of Odin', zh='奥丁神殿', char='X', class='brown', article=true },
  ['2'] = { target='Draft:Temple of Set', label='Temple of Set', zh='赛特神殿', char='X', class='brown', article=true },
  ['3'] = { target='Draft:Temple of Athena', label='Temple of Athena', zh='雅典娜神殿', char='X', class='brown', article=true },
  ['4'] = { target='Draft:Temple of Hecate', label='Temple of Hecate', zh='赫卡忒神殿', char='X', class='brown', article=true },
  ['5'] = { target='Draft:Temple of Druidism', label='Temple of Druidism', zh='德鲁伊神殿', char='X', class='brown', article=true },
  ['6'] = { target='Draft:Temple of Destiny', label='Temple of Destiny', zh='命运神殿', char='X', class='brown', article=true },
}

local maps = {
  remake = {
    rows=remakeRows,
    terrain=terrain,
    locations=remakeLocations,
    ariaEn='Omega 0.90.4 countryside map',
    ariaZh='Omega 0.90.4 世界地图',
    draft=true,
  },
}

-- Only destinations with an independent article leave the LOCUS summaries.
for _, location in pairs(remakeLocations) do
  if not location.article then
    location.target = 'Draft:LOCUS#' .. location.label
  end
end

-- The offline bitmap generator executes this module and reads the same data.
p.data = maps.remake

local function appendCell(rowNode, text, className)
  local cell = rowNode:tag('span')
  if className then
    cell:addClass('omega-color-' .. className)
  end
  cell:wikitext(mw.text.nowiki(text))
end

function p.render(frame)
  local edition = frame.args.edition or (mw.title.getCurrentTitle().namespace == 3002 and 'classic' or 'remake')
  local language = frame.args.lang == 'zh' and 'zh' or 'en'
  local map = maps[edition]
  if not map then return '' end
  local thumbnail = frame.args.mode == 'thumbnail'
  local root = mw.html.create('div')
    :addClass(thumbnail and 'omega-map-overview' or 'omega-map-layout')
    :attr('lang', language == 'zh' and 'zh-Hans' or 'en')
  if thumbnail then
    local preview = root:tag('div'):addClass('omega-map-preview')
    local image = mw.html.create('span'):addClass('omega-map-thumbnail')
      :attr('role', 'img')
      :attr('aria-label', language == 'zh' and map.ariaZh or map.ariaEn)
    image:tag('span'):addClass('omega-map-thumbnail-label'):wikitext('LOCUS')
    preview:wikitext('[[Draft:LOCUS|' .. tostring(image) .. ']]')
    preview:tag('p'):addClass('omega-map-detail-link'):wikitext(language == 'zh'
      and '[[Draft:LOCUS|LOCUS]]:[[Draft:LOCUS|详细地图]] · Omega 0.90.4'
      or '[[Draft:LOCUS|LOCUS]]: [[Draft:LOCUS|Detailed map]] · Omega 0.90.4')
  else
    root:attr('tabindex', '0'):attr('role', 'region')
      :attr('aria-label', language == 'zh' and map.ariaZh or map.ariaEn)
  end
  local mapNode = (thumbnail and mw.html.create('div') or root:tag('div'))
    :addClass('omega-map')
    :attr('role', 'group')
    :attr('aria-label', language == 'zh' and map.ariaZh or map.ariaEn)
  local locations = {}

  for rowIndex, row in ipairs(map.rows) do
    local rowNode = mapNode:tag('div'):addClass('omega-map-row')
    local pendingClass = nil
    local pendingText = ''

    local function flushPending()
      if pendingText ~= '' then
        appendCell(rowNode, pendingText, pendingClass)
        pendingClass = nil
        pendingText = ''
      end
    end

    for col = 1, #row do
      local sourceChar = row:sub(col, col)
      local location = map.locations[sourceChar]
      if location then
        flushPending()
        local x = col - 1
        local y = rowIndex - 1
        local displayName = language == 'zh'
          and location.zh .. ' (' .. location.label .. ')'
          or location.label
        local title = displayName .. ' (x=' .. x .. ', y=' .. y .. ')'
        local marker = mw.html.create('span')
          :addClass('omega-color-' .. location.class)
          :attr('title', title)
          :attr('aria-label', title)
          :wikitext(mw.text.nowiki(location.char))
        rowNode:wikitext('[[' .. location.target .. '|' .. tostring(marker) .. ']]')
        locations[#locations + 1] = {
          target=location.target,
          label=location.label,
          zh=location.zh,
          x=x,
          y=y,
          article=location.article,
        }
      else
        local cell = map.terrain[sourceChar] or { char=sourceChar, class=nil }
        if pendingClass ~= cell.class then
          flushPending()
          pendingClass = cell.class
        end
        pendingText = pendingText .. cell.char
      end
    end
    flushPending()
  end

  local directory = root:tag('div')
    :addClass('omega-map-directory')
    :attr('role', 'navigation')
    :attr('aria-label', language == 'zh'
      and (map.draft and 'Omega 0.90.4 地点草稿' or '地点')
      or (map.draft and 'Draft locations from Omega 0.90.4' or 'Locations'))
  if map.draft then
    local note = language == 'zh'
      and '地点索引:链接各地点的独立草稿。'
      or 'Location index: links lead to individual draft articles.'
    directory:tag('p')
      :addClass('omega-map-draft-note')
      :wikitext(note)
  end
  local list = directory:tag('ol')
  for _, location in ipairs(locations) do
    local item = list:tag('li')
    local displayName = language == 'zh'
      and location.zh .. ' (' .. location.label .. ')'
      or location.label
    item:wikitext('[[' .. location.target .. '|' .. mw.text.nowiki(displayName) .. ']]')
    item:wikitext(' ')
    item:tag('span')
      :addClass('omega-map-coordinates')
      :wikitext('(x=' .. location.x .. ', y=' .. location.y .. ')')
    if location.article then
      item:tag('span'):addClass('omega-map-status'):wikitext(language == 'zh' and ' 草稿' or ' draft')
    end
  end

  return tostring(root)
end

return p