모듈:Metadata: 두 판 사이의 차이

리버티게임, 모두가 만들어가는 자유로운 게임
imported>Hsl0
편집 요약 없음
편집 요약 없음
 
(사용자 3명의 중간 판 33개는 보이지 않습니다)
1번째 줄: 1번째 줄:
local p = {} -- 패키지
local p = {} -- 패키지
local get = {}


-- 상수 시작
-- 상수 시작
local DEFAULT_IMG = {
local DEFAULT_IMG = {
progress = "[[파일:Progress unknown.svg|완성도 정도를 입력하세요|링크=|16px]]",
progress = "[[파일:Progress unknown.svg|완성도 정도를 입력하세요|링크=|16px]]",
openness = "[[파일:Crystal Clear action info.svg|17px|개방성 정도를 입력하세요|링크=]]",
editpolicy = "[[파일:Crystal Clear action info.svg|17px|개방성 정도를 입력하세요|링크=]]",
tech = "[[파일:Tech unknown.svg|게임 구조를 입력하세요|링크=]]",
rating = "[[파일:GRAC Square Template.svg|16px|게임 제작자가 이용가 등급 판정을 하지 않았습니다|링크=]]"
rating = "[[파일:GRAC Square Template.svg|16px|게임 제작자가 이용가 등급 판정을 하지 않았습니다|링크=]]"
}
}
20번째 줄: 20번째 줄:
local ABANDON = {} --Symbol
local ABANDON = {} --Symbol


local OPENNESS_IMG = {
local EDITPOLICY_IMG = {
[false] = "[[파일:Crystal Clear action editdelete.png|17px|편집 금지|링크=]]",
open = "[[파일:Crystal Clear app clean.png|17px|편집 가능|링크=]]",
partial = "[[파일:Crystal Clear bot on trial2.png|17px|규칙에 따라 편집 가능|링크=]]",
limited = "[[파일:Crystal Clear bot on trial2.png|17px|규칙에 따라 편집 가능|링크=]]",
[true] = "[[파일:Crystal Clear app clean.png|17px|편집 가능|링크=]]",
closed = "[[파일:Crystal Clear action editdelete.png|17px|편집 금지|링크=]]",
[ABANDON] = "[[파일:Crystal Clear app logout.png|17px|버려진 게임|링크=]]"
[ABANDON] = "[[파일:Crystal Clear app logout.png|17px|버려진 게임|링크=]]"
}
}


local TECH_IMG = {
local PLATFORM_IMG = {
link = "[[파일:Tech link.svg|링크|링크=]]",
web = "[[파일:Feather-location-globe.svg|16px|웹|링크=|class=gameicon-platform-web]]",
cgi = "[[파일:Tech CGI.svg|CGI|링크=]]",
windows = "[[파일:Windows logo - 2021 (Black).svg|16px|윈도우|링크=|class=gameicon-platform-windows]]",
windows = "[[파일:Windows logo - 2021.svg|16px|윈도우|링크=]]",
linux = "[[파일:Tux Mono.svg|16px|리눅스|링크=|class=gameicon-platform-linux]]",
db = "[[파일:Tech DB.svg|DB|링크=]]",
macos = "[[파일:MacOS logo.svg|16px|맥 OS|링크=|class=gameicon-platform-macos]]",
javascript = "[[파일:Tech JS.svg|16px|자바스크립트|링크=]]",
android = "[[파일:Cib-android (CoreUI Icons v1.0.0).svg|16px|안드로이드|링크=|class=gameicon-platform-android]]",
lua = "[[파일:Tech Lua.svg|16px|루아|링크=]]",
other = "[[파일:OOjs UI icon help-ltr.svg|16px|기타|링크=|class=gameicon-platform-other]]"
other = "[[파일:Tech other.svg|기타|링크=]]"
}
}


49번째 줄: 48번째 줄:
local title = mw.title.new(user, 'User')
local title = mw.title.new(user, 'User')
return '[[' .. tostring(title) .. '|' .. (display or title.subpageText) .. ']]'
return '[[' .. tostring(title) .. '|' .. (display or title.subpageText) .. ']]'
end
function platformIcons(platforms)
local icons = mw.html.create('span'):addClass('gameicon-platform')
local other = false
if type(platforms) == 'string' then
platforms = {platforms}
end
if not platforms or #platforms <= 0 then
platforms = {'web'}
end
for index, platform in ipairs(platforms) do
if platform == 'other' then
other = true
else
local img = PLATFORM_IMG[platform]
if img then
icons:wikitext(img)
else
other = true
end
end
end
if other then
icons:wikitext(PLATFORM_IMG.other)
end
return icons:done()
end
end


function p.icon(frame)
function p.icon(frame)
local title = frame.args[1]
local title = frame.args[1]
local doc = mw.title.new(title .. '/game.json')
local doc = p.resolve(title)
if not doc.exists then
if not doc.exists then
65번째 줄: 97번째 줄:
-- status
-- status
local openness = data.openness
local editpolicy = data.editpolicy
if data.abandon then
if data.abandon then
openness = ABANDON
editpolicy = ABANDON
end
end
local status = icon:tag('span'):addClass('gameicon-status')
local status = icon:tag('span'):addClass('gameicon-status')
                :wikitext(data.progress ~= nil and PROGRESS_IMG[data.progress] or DEFAULT_IMG.progress)
                :wikitext(data.progress ~= nil and PROGRESS_IMG[data.progress] or DEFAULT_IMG.progress)
                :wikitext(openness ~= nil and OPENNESS_IMG[openness] or DEFAULT_IMG.openness)
                :wikitext(editpolicy ~= nil and EDITPOLICY_IMG[editpolicy] or DEFAULT_IMG.editpolicy)
                :wikitext(data.tech and TECH_IMG[data.tech] or DEFAULT_IMG.tech)
                :node(platformIcons(data.platform))
                :wikitext(data.rating and RATING_IMG[data.rating] or DEFAULT_IMG.rating)
                :wikitext(data.rating and RATING_IMG[get.rating(data.rating)] or DEFAULT_IMG.rating)
                     :attr{
                     :attr{
                         ['data-progress'] = data.progress,
                         ['data-progress'] = data.progress,
                         ['data-openness'] = openness == ABANDON and "true" or tostring(openness),
                         ['data-editpolicy'] = editpolicy == ABANDON and "true" or tostring(editpolicy),
                         ['data-tech'] = data.tech,
                         ['data-rating'] = get.rating(data.rating)
                        ['data-rating'] = data.rating
                     }
                     }
     if data.abandon then
     if data.abandon then
134번째 줄: 165번째 줄:


function getData(source)
function getData(source)
local title = mw.title.new(rel2abs(source))
local title = p.resolve(rel2abs(source))
local result, data = pcall(mw.text.jsonDecode, title and title:getContent() or source)
local result, data = pcall(mw.text.jsonDecode, title and title:getContent() or source)
150번째 줄: 181번째 줄:
local contributors = type(data.contributor) == 'string' and {data.contributor} or data.contributor
local contributors = type(data.contributor) == 'string' and {data.contributor} or data.contributor
for index, author in ipairs(authors) do
if authors then
if mw.title.equals(user, mw.title.new(author, 'User')) then
for index, author in ipairs(authors) do
return 'author'
if mw.title.equals(user, mw.title.new(author, 'User')) then
return 'author'
end
end
end
else
error('유효하지 않은 메타데이터: 제작자(author)가 없음', 2)
end
end
for index, contributor in ipairs(contributors) do
if contributors then
if mw.title.equals(user, mw.title.new(contributor, 'User')) then
for index, contributor in ipairs(contributors) do
return 'contributor'
if mw.title.equals(user, mw.title.new(contributor, 'User')) then
return 'contributor'
end
end
end
end
end
end
function p.resolve(location)
local title = mw.title.new(location).subjectPageTitle
-- 넘겨주기 대상 탐색
while title.redirectTarget do
title = title.redirectTarget
location = title.fullText
end
local parts = mw.text.split(title.fullText, '/')
local size = #parts
-- 상위 문서 탐색
for index = 0, size - 1 do
local pagename = table.concat(parts, '/', 1, size - index)
-- 현재 상위 문서 이름이 game.json이거나 하위 문서로 game.json을 갖는지 확인
if string.find(pagename, '/game%.json$') == nil then
local title = mw.title.new(pagename .. '/game.json')
if title.exists then
return title
end
end
end
end
function p.root(location)
local meta = p.resolve(location)
return meta and meta.basePageTitle or mw.title.new(location).rootPageTitle
end
function p.subpage(location, path)
local namespace = select(3, path:find('^([^:/]+):'))
local root = p.root(location)
if mw.site.namespaces[namespace].id == 1 then -- namespace = 토론
root = root.talkPageTitle
path = mw.ustring.sub(path, mw.ustring.len(namespace) + 2)
end
return root:subPageTitle(select(3, tostring(path):find('^/?(.+)$')))
end
function getRelPage(key)
return function(data, title)
return p.subpage(title, data[key])
end
end
function p.get(frame)
local data = getData(frame.args[1])
local prop = frame.args[2]
if not prop then
error('속성을 지정하지 않았습니다', 2)
end
if get[prop] then
return get[prop](data[prop], p.root(frame.args[1]))
else
return data[prop]
end
end
p['if'] = function (frame)
local data = getData(frame.args[1])
local prop = frame.args[2]
local t, e = frame.args[3], frame.args[4]
if not prop then
error('속성을 지정하지 않았습니다', 2)
end
if get[prop] then
return get[prop](data[prop]) and t or e
else
return data[prop] and t or e
end
end
function get.rating(data)
return data and (data.grac and data.grac.age or data.libertygame and data.libertygame.age) or nil
end
end


return p
return p

2024년 11월 11일 (월) 17:20 기준 최신판


모듈 설명문서[보기] [편집] [역사] [새로 고침]

게임 메타데이터를 다루는 모듈입니다.

icon

게임 메타데이터를 바탕으로 게임아이콘을 만듭니다.

{{#invoke:metadata|icon|(게임 문서)}}

get

게임 메타데이터의 특정 속성을 불러옵니다. 이때, 원본 값을 그대로 불러오는 것이 아닌 활용하기 편한 형태로 불러오는 기능입니다. 일반적으로는 원본 값이지만, 특별히 수정된 경우 가공된 값이 불러와집니다.

{{#invoke:metadata|get|(게임 문서)|(속성)}}

rating

게임 등급을 불러옵니다. 리버티게임 등급 분류와 게임물관리위원회 등급 분류를 구별하지 않고 바로 등급만 가져옵니다.

{{#invoke:metadata|get|(게임 문서)|rating}}

if

게임 메타데이터의 특정 속성의 존재 여부를 확인하고, 있을 때와 없을 때 나올 내용을 구분합니다.

{{#invoke:metadata|if|(게임 문서)|(속성)|(있을 때)|(없을 때)}}

role

특정 사용자가 게임의 제작자인지, 기여자(조력자)인지, 이에 해당되지 않는 지 확인합니다. 제작자일 경우 author가, 기여자일 경우 contributor가 반환됩니다.

{{#invoke:metadata|role|(게임 문서)|(사용자 이름)}}

resolve

게임 메타데이터가 있는 문서를 찾습니다. 대상 문서의 하위 문서에 game.json이 없다면 그 상위 문서에서 찾고, 없으면 반복하며 있으면 찾은 game.json 문서에서 멈춥니다. 대상 문서가 넘겨주기 문서인 경우 넘어가는 문서를 기준으로 찾습니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 nil이 반환됩니다.

p.resolve(title)

root

게임의 최상위 문서를 찾습니다. 정확히는 게임 메타데이터(game.json)을 하위 문서로 갖는 문서를 찾습니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 루트 페이지가 반환됩니다.

p.root(title)

subpage

게임의 최상위 문서 바로 아래에 있는 하위 문서를 찾습니다. 첫번째 인수는 현재 위치 및 최상위 문서입니다. 현재 위치를 넣으면 root 함수를 통해 알아서 최상위 문서를 찾아줍니다. 두번째 인수는 하위 문서의 상대적 경로입니다. 예를 들어, 어떤 게임/하위라는 문서를 가리키고 싶을 경우 두번째 인수에 /하위만 입력하면 절대 경로인 어떤 게임/하위를 반환합니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 nil이 반환됩니다.

p.subpage(title, path)


local p = {} -- 패키지
local get = {}

-- 상수 시작
local DEFAULT_IMG = {
	progress = "[[파일:Progress unknown.svg|완성도 정도를 입력하세요|링크=|16px]]",
	editpolicy = "[[파일:Crystal Clear action info.svg|17px|개방성 정도를 입력하세요|링크=]]",
	rating = "[[파일:GRAC Square Template.svg|16px|게임 제작자가 이용가 등급 판정을 하지 않았습니다|링크=]]"
}

local PROGRESS_IMG = {
	[0] = "[[파일:Progress base.svg|변경하지 말아야 하는 공사중인 게임|링크=|16px]]",
	[1] = "[[파일:Progress low.svg|조금 완성된 게임|링크=|16px]]",
	[2] = "[[파일:Progress medium.svg|중간 정도 완성된 게임|링크=|16px]]",
	[3] = "[[파일:Progress high.svg|거의 완성된 게임|링크=|16px]]",
	[4] = "[[파일:Progress full.svg|완성되었지만 추가할 수 있는 게임|링크=|16px]]",
	[5] = "[[파일:백괴게임 완성도 7단계.svg|완성되어 변경하지 말아야 하는 게임|링크=|16px]]"
}

local ABANDON = {} --Symbol

local EDITPOLICY_IMG = {
	open = "[[파일:Crystal Clear app clean.png|17px|편집 가능|링크=]]",
	limited = "[[파일:Crystal Clear bot on trial2.png|17px|규칙에 따라 편집 가능|링크=]]",
	closed = "[[파일:Crystal Clear action editdelete.png|17px|편집 금지|링크=]]",
	[ABANDON] = "[[파일:Crystal Clear app logout.png|17px|버려진 게임|링크=]]"
}

local PLATFORM_IMG = {
	web = "[[파일:Feather-location-globe.svg|16px|웹|링크=|class=gameicon-platform-web]]",
	windows = "[[파일:Windows logo - 2021 (Black).svg|16px|윈도우|링크=|class=gameicon-platform-windows]]",
	linux = "[[파일:Tux Mono.svg|16px|리눅스|링크=|class=gameicon-platform-linux]]",
	macos = "[[파일:MacOS logo.svg|16px|맥 OS|링크=|class=gameicon-platform-macos]]",
	android = "[[파일:Cib-android (CoreUI Icons v1.0.0).svg|16px|안드로이드|링크=|class=gameicon-platform-android]]",
	other = "[[파일:OOjs UI icon help-ltr.svg|16px|기타|링크=|class=gameicon-platform-other]]"
}

local RATING_IMG = {
	[18] = "[[파일:GRAC 18 Square.svg|16px|청소년 이용불가|링크=]]",
	[15] = "[[파일:GRAC 15 Square.svg|16px|15세 이용가|링크=]]",
	[12] = "[[파일:GRAC 12 Square.svg|16px|12세 이용가|링크=]]",
	all = "[[파일:GRAC All Square.svg|16px|전체 이용가|링크=]]",
	test  = "[[파일:GRAC Test Square.svg|16px|평가하고 있는 중입니다|링크=]]"
}
-- 상수 끝 코드 시작

function userLink(user, display)
	local title = mw.title.new(user, 'User')
	return '[[' .. tostring(title) .. '|' .. (display or title.subpageText) .. ']]'
end

function platformIcons(platforms)
	local icons = mw.html.create('span'):addClass('gameicon-platform')
	local other = false
	
	if type(platforms) == 'string' then
		platforms = {platforms}
	end
	
	if not platforms or #platforms <= 0 then
		platforms = {'web'}
	end
	
	for index, platform in ipairs(platforms) do
		if platform == 'other' then
			other = true
		else
			local img = PLATFORM_IMG[platform]
			
			if img then
				icons:wikitext(img)
			else
				other = true
			end
		end
	end
	
	if other then
		icons:wikitext(PLATFORM_IMG.other)
	end
	
	return icons:done()
end

function p.icon(frame)
	local title = frame.args[1]
	local doc = p.resolve(title)
	
	if not doc.exists then
		return mw.html.create('span'):addClass('error')
									 :wikitext('[[' .. title .. '/game.json]] 문서가 존재하지 않습니다. 메타데이터 문서를 만들어 주세요.')
									 :done()
	end
	
	local data = mw.text.jsonDecode(doc:getContent())
	local icon = mw.html.create('span'):addClass('gameicon')
	
	-- status
	local editpolicy = data.editpolicy
	if data.abandon then
		editpolicy = ABANDON
	end
	
	local status = icon:tag('span'):addClass('gameicon-status')
	                :wikitext(data.progress ~= nil and PROGRESS_IMG[data.progress] or DEFAULT_IMG.progress)
	                :wikitext(editpolicy ~= nil and EDITPOLICY_IMG[editpolicy] or DEFAULT_IMG.editpolicy)
	                :node(platformIcons(data.platform))
	                :wikitext(data.rating and RATING_IMG[get.rating(data.rating)] or DEFAULT_IMG.rating)
                    :attr{
                        ['data-progress'] = data.progress,
                        ['data-editpolicy'] = editpolicy == ABANDON and "true" or tostring(editpolicy),
                        ['data-rating'] = get.rating(data.rating)
                    }
    if data.abandon then
    	status:attr('data-abandon', '')
    end
    
    -- name
    icon:tag('span'):addClass('gameicon-name')
    				:wikitext('[[' .. title .. '|' .. (data.name or title) .. ']]')
    
    -- maker
    local contributor
    if data.contributor then
    	contributor = ""
    	
    	if type(data.contributor) == 'string' then
    		data.contributor = {data.contributor}
    	end
    	
    	for index, name in pairs(data.contributor) do
    		contributor = contributor .. userLink(name) .. ", "
    	end
    	
    	contributor = contributor:sub(1, -3) .. " 사용자께서 도와주셨습니다."
    end
    
    local author
    if data.author then
	    if type(data.author) == 'table' then
	    	author = ""
	    	
	    	for index, name in pairs(data.author) do
	    		author = author .. userLink(name) .. ", "
	    	end
	    	
	    	author = author:sub(1, -2)
	    else
	    	author = userLink(data.author)
	    end
	    
	    icon:tag('span'):addClass('gameicon-maker')
	    				:wikitext(contributor and frame:expandTemplate{
	    					title = '툴팁',
	    					args = {author, contributor, sym = 1, class='messagebox gameicon-contributor'}
	    				} or author)
	end
	
	return icon:allDone()
end

function rel2abs(rel, base)
	return mw.getCurrentFrame():callParserFunction('#rel2abs', rel, base)
end

function getData(source)
	local title = p.resolve(rel2abs(source))
	local result, data = pcall(mw.text.jsonDecode, title and title:getContent() or source)
	
	if result then
		return data
	else
		error('유효하지 않은 게임 메타데이터', 2)
	end
end

function p.role(frame)
	local data = getData(frame.args[1])
	local user = mw.title.new(frame.args[2], 'User')
	local authors = type(data.author) == 'string' and {data.author} or data.author
	local contributors = type(data.contributor) == 'string' and {data.contributor} or data.contributor
	
	if authors then
		for index, author in ipairs(authors) do
			if mw.title.equals(user, mw.title.new(author, 'User')) then
				return 'author'
			end
		end
	else
		error('유효하지 않은 메타데이터: 제작자(author)가 없음', 2)
	end
	
	if contributors then
		for index, contributor in ipairs(contributors) do
			if mw.title.equals(user, mw.title.new(contributor, 'User')) then
				return 'contributor'
			end
		end
	end
end

function p.resolve(location)
	local title = mw.title.new(location).subjectPageTitle
	
	-- 넘겨주기 대상 탐색
	while title.redirectTarget do
		title = title.redirectTarget
		location = title.fullText
	end
	
	local parts = mw.text.split(title.fullText, '/')
	local size = #parts
	
	-- 상위 문서 탐색
	for index = 0, size - 1 do
		local pagename = table.concat(parts, '/', 1, size - index)
		
		-- 현재 상위 문서 이름이 game.json이거나 하위 문서로 game.json을 갖는지 확인
		if string.find(pagename, '/game%.json$') == nil then
			local title = mw.title.new(pagename .. '/game.json')
			
			if title.exists then
				return title
			end
		end
	end
end

function p.root(location)
	local meta = p.resolve(location)
	return meta and meta.basePageTitle or mw.title.new(location).rootPageTitle
end

function p.subpage(location, path)
	local namespace = select(3, path:find('^([^:/]+):'))
	local root = p.root(location)
	
	if mw.site.namespaces[namespace].id == 1 then -- namespace = 토론
		root = root.talkPageTitle
		path = mw.ustring.sub(path, mw.ustring.len(namespace) + 2)
	end
	
	return root:subPageTitle(select(3, tostring(path):find('^/?(.+)$')))
end

function getRelPage(key)
	return function(data, title)
		return p.subpage(title, data[key])
	end
end

function p.get(frame)
	local data = getData(frame.args[1])
	local prop = frame.args[2]
	
	if not prop then
		error('속성을 지정하지 않았습니다', 2)
	end
	
	if get[prop] then
		return get[prop](data[prop], p.root(frame.args[1]))
	else
		return data[prop]
	end
end

p['if'] = function (frame)
	local data = getData(frame.args[1])
	local prop = frame.args[2]
	local t, e = frame.args[3], frame.args[4]
	
	if not prop then
		error('속성을 지정하지 않았습니다', 2)
	end
	
	if get[prop] then
		return get[prop](data[prop]) and t or e
	else
		return data[prop] and t or e
	end
end

function get.rating(data)
	return data and (data.grac and data.grac.age or data.libertygame and data.libertygame.age) or nil
end

return p