Module:InventorySlot

From Official Gamemode 4 Wiki
Jump to navigation Jump to search
-- 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