Module:InventorySlot

From Official Gamemode 4 Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
-- Based on work by the MC wiki: https://minecraft.gamepedia.com/

local UISlot = {}

local sprite = require([[Module:Sprite]])
local Sprites = {
	minecraft = sprite:new("inventory"),
	gm4 = sprite:new("gm4"),
	effect = sprite:new("effect"),
	trophy = sprite:new("trophy"),
	gm4RP = sprite:new("gm4RP")
}

function UISlot:new(item, large)
	o = {}
	setmetatable(o, {__index = self})
	o:SetItem(item)
	o.__large = large
	return o
end

-- Sets the item of this UISlot. If 'raw html' (=first char == '<') then
--			this will return said html wrapped instead of doing a item lookup
function UISlot:SetItem(item)
	if string.sub(item or "",1,1) == '<' then
		-- If the item is already html just save that
		self.__html = item
		return self
	end

	self.__sprite = UISlot.Decode(item)



	return self
end

function UISlot.Decode(item)
	if not(item) then return nil end
	item = mw.text.trim(item)
	if item == "" then item = nil end
	if item then
		-- split at ':'
		local t={}
		for str in string.gmatch(item, "([^:]+)") do
			table.insert(t, str)
		end
		if #t == 1 then
			return Sprites.minecraft:Item(item,"minecraft")
		elseif #t == 2 then
			local spr = Sprites[mw.text.trim(t[1])]
			if spr then
				return spr:Item(mw.text.trim(t[2]), mw.text.trim(t[1]))
			else
				error("Unknown namespace: " .. mw.text.trim(t[1]))
			end
		else
			error("Got unexpected amount of ':', namely:" .. (#t - 1))
		end

	else
		return nil
	end
end

-- Append the HTML for an item
function UISlot.Item(item, large, html)
	return UISlot:new(item, large):GenerateHTML(html)
end

-- Generate the HTML for a grid of items
function UISlot.Call(items, width, height, size)
	local w = width or 1
	local h = height or 1
	local grid = {}
	local large = size
	for i = 1,h do
		grid[i] = {}
		for j = 1,w do
			grid[i][j] = mw.text.trim(items[j+(i-1)*w] or "")
		end
	end
	return UISlot.Grid(grid, large)
end

-- Convert a 2d list of itemnames to an HTML grid
function UISlot.Grid(items, large)
	local root = mw.html.create( "table" ):addClass("invgrid")
	for i = 1, #items do
		local row = root:tag("tr"):addClass("invrow")
		for j = 1, #items[i] do
			UISlot.Item(items[i][j], large, row)
		end
	end
	return root
end

function UISlot:GenerateHTML (html)
	local slot
	if html then
		slot = html:tag("td")
	else
		slot = mw.html.create("div")
	end
	slot:addClass( "invslot" )

	if self.__large then
		slot:addClass( "invslot-large")
	end

	if self.__html then
		slot:wikitext(self.__html)
	end

	if self.__sprite then
		self.__sprite:GenerateHTML(slot)
	end
	return slot
end


-- Well Template:InventorySlot does not use any of the above code, so I'm going
-- to start brand new from here, with the Minecraft code
-- Based on work by the Minecraft wiki, https://minecraft.wiki


local i18n = {
	filename = 'Invicon $1',
	moduleAliases = [[Module:InventorySlot/Aliases]],
	prefixes = {
		any = 'Any',
		matching = 'Matching',
		damaged = 'Damaged',
		unwaxed = 'Unwaxed',
	},
}

local aliases = mw.loadData( i18n.moduleAliases )

--[[Merges a list, or inserts a string
	or table into a table
--]]
local function mergeList( parentTable, content )
	local i = #parentTable + 1
	if content[1] then
		-- Merge list into table
		for _, v in ipairs( content ) do
			parentTable[i] = v
			i = i + 1
		end
	else
		-- Add strings or tables to table
		parentTable[i] = content
	end
end

local function makeItem( args )
	local item = mw.html.create( 'span' ):addClass( 'invslot-item' )
	if args.imgclass then
		item:addClass( args.imgclass )
	end
	if frame.name == '' then
		return item
	end

	local title = frame.title or mw.text.trim( args.title or '' )
	local name = frame.name or ''
	local count = frame.count
	local description = frame.text
	local img
	
	if name:match( '%.gif$' ) or name:match( '%.png$' ) then
		img = file:gsub( '%$1', name )
		-- Remove file extension from name
		name = il8n.filename:sub( 0, -5 )
	else
		-- Fall back to an individual image if the sprite is lacking
		img = i18n.filename:gsub( '%$1', name .. '.png' )
	end

	local link = args.link or ''
	if link == '' then
		link = name:gsub( '^' .. i18n.prefixes.damaged .. ' ', '' )
		for _, suffix in pairs( i18n.suffixes ) do
			link = link:gsub( ' ' .. suffix .. '$', '' )
		end
	elseif link:lower() == 'none' then
		link = nil
	end
	if link == pageName then
		link = nil
	end
	
	local formattedTitle
	local plainTitle
	if title == '' then
		plainTitle = name
	elseif title:lower() ~= 'none' then
		plainTitle = title:gsub( '\\\\', '&#92;' ):gsub( '\\&', '&#38;' )
		
		local formatPattern = '&[0-9a-fk-or]'
		if plainTitle:match( formatPattern ) then
			formattedTitle = title
			plainTitle = plainTitle:gsub( formatPattern, '' )
		end
		
		if plainTitle == '' then
			plainTitle = name
		else
			plainTitle = plainTitle:gsub( '&#92;', '\\' ):gsub( '&#38;', '&' )
		end
	elseif link then
		formattedTitle = ''
	end
	
	item:attr{
		['data-title'] = formattedTitle,
		['data-lore'] = description
	}
	
	item:addClass( 'invslot-item-image' )
		:wikitext( '[[File:', img, '|32x32px|link=', link or '', '|alt=', altText, '|', escapedTitle, ']]' )
	
	if count and count > 1 and count < 1000 then
		if link then
			item:wikitext( '[[', link, '|' )
		end
		local number = item
		:tag( 'span' )
			:addClass( 'invslot-stacksize' )
			:attr{ title = plainTitle }
			:wikitext( count )
		if link then
			item:wikitext( ']]' )
		end
	end
	
	return item
end

function UISlot.slot( f )
	local args = f.args or f
	-- not really sure what this does
	local args = f.args or f
	if f == mw.getCurrentFrame() and args[1] == nil then
		args = f:getParent().args
	end
	
	if not args.parsed then
		args[1] = mw.text.trim( args[1] or '' )
	end

	-- I see.  This frames is where you parse the semicolon separated list
	-- and enable cycling, in addition to all of the extra data that you
	-- need for items.  We'll need to investigate whether or not we'll need
	-- this?  Like it'll be useful for vanilla items, but not for any of our
	-- recipes, since we don't need to cycle any of our custom items in recipes.
	-- .... I say that, then realize Zauber armor cycles.  But tbf, it's all
	-- premade from aliases, and does not need custom in-place data
	local frames
	if args.parsed then
		frames = args[1]
	elseif args[1] ~= '' then
		-- Not using moddata, but that kinda makes me think we could use the
		-- module namespaces from automatically imported items... hmm...
		frames = UISlot:parseFrameText ( args[1], false )
	end
	
	local body = mw.html.create( 'span' ):addClass( 'invslot' ):css{ ['vertical-align'] = args.align }
	if animated then body:addClass( 'animated' ) end
	if args.class then body:addClass( args.class ) end
	if args.style then body:cssText( args.style ) end

	if not frames then return tostring ( body) end
	
	-- Figure out cycling here
	for i, frame in ipairs( frames ) do
		local item
		-- how?
		
		item = makeItem( frame, i, args)
		body:node( item )

		if i == activeFrame and animated then
			item:addClass( 'animated-active' )
		end
	end
	return tostring( body ) 
end

function UISlot.parseFrameText ( framesText, aliasReference )
	local frames = {}
	local expandedAliases
	
	-- wow so much code for cycling that is being skipped here
	-- this line is definitely wrong, but we'll see if this works as a
	-- single line hack
	local frameText = framesText
	
	local frame = UISlot.makeFrame( frameText )
	local newFrame = frame
	
	-- or gm4 ?
	if aliases then
		local id = frame.name
		if frame.mod and frame.mod == 'gm4' then
			-- load gm4 aliases, I guess?
		end
		
		-- Figure out how to load and access the gm4 alias
		local alias = aliases or aliases[id]
		if alias then
			newFrame = UISlot.getAlias ( alias, frame )
			-- lots of other code that we need to do when we accept cycle lists
		end
		mergeList( frames, newFrame )
	end
	
	return frames
end

function UISlot.getAlias( aliasFrames, parentFrame )
	
	if type( aliasFrames ) == 'string' then
		local expandedFrame = mw.clone ( parentFrame )
		expandedFrame.name = aliasFrames
		return { expandedFrame }
	end
	
	if aliasFrames.name then
		aliasFrames = { aliasFrames }
	end
	
	for i, aliasFrame in ipairs( aliasFrames ) do
		local expandedFrame
		if type( aliasFrame ) == 'string' then
			expandedFrame = { name = aliasFrame }
		else
			expandedFrame = cloneTable( aliasFrame )
		end
		expandedFrame.title = parentFrame.title or expandedFrame.title
		expandedFrame.mod = parentFrame.mod or expandedFrame.mod
		expandedFrame.num = parentFrame.num or expandedFrame.num
		expandedFrame.text = parentFrame.text or expandedFrame.text
		
		expandedFrames[i] = expandedFrame
	end
	
	return expandedFrames
end


function UISlot.makeFrame( frameText )
	local frame = {}
	
	frame.title = frameText:match( '^%[([^%]]+)%]' )

	frame.mod = frameText:match( '([^:%]]+):' ) or mod
	local vanilla = { v = 1, vanilla = 1, mc = 1, minecraft = 1 }
	if frame.mod and vanilla[mw.ustring.lower( frame.mod )] or frame.mod == '' then
		frame.mod = nil
	end
	
	local nameStart = ( frameText:find( ':' ) or frameText:find( '%]' ) or 0 ) + 1
	if nameStart - 1 == #frameText then
		nameStart = 1
	end
	
	-- Come back to this later.  If we are going to pull names after colons,
	-- then we know whether it's vanilla or gm4.  Should maybe use the "mod"
	-- stuff at some point, but I'd imagine above is where you'd pull and find
	frame.name = frameText:sub( nameStart, ( frameText:find( '[,%[]', nameStart ) or 0 ) - 1 )
	

	frame.text = frameText:match( '%[([^%]]+)%]$' )
	
	return frame
end

return UISlot