Skip to content

Instantly share code, notes, and snippets.

@ValentinFunk
Last active March 24, 2018 16:51
Show Gist options
  • Save ValentinFunk/e343784e15966b1fa8a38544793293e9 to your computer and use it in GitHub Desktop.
Save ValentinFunk/e343784e15966b1fa8a38544793293e9 to your computer and use it in GitHub Desktop.
Making Customizable Weaponry 2.0 Attachments work with Pointshop 2 Tutorial. Full Code: https://github.com/Kamshak/ps2-customizable-weaponry

Generating Pointshop 2 Items programatically

The goal here is to integrate an external system into Pointshop 2 by using items. Since they are already created we only need to map them to PS2 items. For this example Attachments for Customizable Weaponry 2 are used. CW2 offers a system where players can pick attachments in a menu. Players can only pick attachments that they own.

  1. Players should be able to buy attachments in Pointshop2
  2. Players should be given all attachments they own in Pointshop2 so that CW can pick them up
  3. It should be possible to set the price for the attachments individually

0) Prerequisites

I've created two small utilities for you that are very useful for this script.

  1. DTextureItemIcon which allows to use texture ids for item/inv icons easily
  2. Pointshop2.GenerateItemClass to easier bootstrap a new item class

Please see the files below and install them (instructions at the top of each file)

1) Players should be able to buy attachments in Pointshop2

So what we want to do is:

  1. Generate a lot of items that are very similar (they do nothing at all, they just sit in the users inventory to be picked up by another script)
  2. Attachments typically don't get removed, removing them is a rare operation
  3. Once all attachments have been created, there is no point in creating more items for them (i.e. if we have created the Silencer item we won't create a second Silencer item).
  4. It would be nice to have an in game editor for prices

There are two ways you could go about this:

  1. Generate a lua defined item for each attachment
  2. Generate the item classes from lua

I'd go with choice 2) which checks all of the boxes except for b) and d). Removing an item would mean removing it from all inventories first, this is possible through lua but you have to remember to do it.

Generating items through lua

The idea here is to generate an item class for each attachment (remember: Item Class = Shop Item). Generating a new item class is as easy as this:

-- the method is: GenerateItemClass( className, baseClassName, addonName )
local item = Pointshop2.GenerateItemClass( 'my_custom_class', 'base_pointshop_item', 'CW 2.0 Support' )
item.cwAttachmentId = 'md_bipod'

Since we want all attachments to use material icons and share some properties let's create a new base class. We then need to modify the snippet above to use our new base

addons/ps2-customizable-weaponry/lua/kinv/items/pointshop/sh_base_cw_attachment.lua

ITEM.baseClass    = "base_pointshop_item"
ITEM.texture = 0
ITEM.cwAttachmentId = "not set"

-- Returns a Derma Control Name used to create the shop icon in normal mode
function ITEM.static:GetPointshopIconControl( )
    return "DPointshopTextureIcon"
end

-- Returns a Derma Control Name used to create the shop icon in lowend mode
function ITEM.static:GetPointshopLowendIconControl( )
    return "DPointshopTextureIcon"
end

-- Returns a derma icon that used in the inventory
function ITEM:getIcon( )
    self.icon = vgui.Create( "DPointshopTextureInvIcon" )
    self.icon:SetItem( self )
    return self.icon
end

-- Returns a 4x4 dimensioned icon
function ITEM.static.GetPointshopIconDimensions( )
    return Pointshop2.GenerateIconSize( 4, 4 )
end

Then to generate an attachment item we would do:

local bipodItem = Pointshop2.GenerateItemClass( 'my_custom_class', 'base_cw_attachment', 'CW 2.0 Support' )
bipodItem.PrintName = 'Harris Bipod'
bipodItem.Description = 'Decreases recoil by 70%\nGreatly increases hip fire accuracy'
bipodItem.static.Price = {
	points = 100,
	premiumPoints = 1000
}

bipodItem.cwAttachmentId = 'md_bipod'

We can do this for every attachment now which would be the same as creating a file for each item. That's really annoying and we can do better:

function generateCwClasses( )
    for cwAttachmentName, cwAttachmentTable in pairs(CustomizableWeaponry.registeredAttachmentsSKey) do
        -- Generate the class
        local className = 'cw_' .. cwAttachmentName
        local ps2Item = Pointshop2.GenerateItemClass( className, 'base_cw_attachment', 'CW 2.0 Support' )
        -- Set the variables
        ps2Item.PrintName = cwAttachmentTable.displayName
        ps2Item.cwAttachmentName = cwAttachmentName
        ps2Item.texture = cwAttachmentTable.displayIcon
        -- Put the stats description into a single string
        local description = LibK._(cwAttachmentTable.description or {}):chain():pluck("t"):join('\n'):value()
        description = description .. '\n\nYou don\'t need to equip this item. Once bought use the CW menu to attach it to your weapon.'
        ps2Item.Description = description
    
        KInventory.Items[className] = ps2Item
        KLogf(4, "Generated item KInventory.Items.%s for CW2.0 Attachment %s", className, cwAttachmentName )
    end
end

if SERVER then
    Pointshop2.ModuleItemsLoadedPromise:Then( generateCwClasses )
else
    hook.Add( "PS2_ModulesLoaded", "generateClasses", generateCwClasses )
end

2) Players should be given all attachments they own in Pointshop2 so that CW can pick them

Conveniently there is a hook for this in CW that we can use to tell CW that a player owns an attachment (CW20HasAttachment):

hook.Add("CW20HasAttachment", "PS2_HasCwAttachment", function( ply, attachmentName )
    -- Make CW recognize attachments in the inventory
    -- Return if the player's inventory is not yet loaded
    if not ply.PS2_Inventory then return end
    
    -- Go over the player's entire inv
    local baseCwAttachment = Pointshop2.GetItemClassByName( 'base_cw_attachment' )
    for k, item in pairs( ply.PS2_Inventory:getItems( ) ) do
        -- Check if the current item uses the base_cw_attachment base
        if instanceOf( baseCwAttachment, item ) then
            -- Then check if this is the attachment we're looking for
            if item.class.cwAttachmentName == attachmentName then
                return true
            end
        end
    end

    -- Player doesn't have it, return nothing instead of
    -- false to allow other hooks to run as well
    return
end )

** Now to disable the default CW2.0 give all attachments on spawn **:

for key, attData in ipairs(CustomizableWeaponry.registeredAttachments) do
	game.ConsoleCommand(attData.cvar .. " 0\n")
end

3) It should be possible to set the price for the attachments individually

This one should be fairly easy, just create a table with prices and set them in the loop from step 1. You could also get fancy and solve this using module settings to allow the price to be changed from ingame without using a full-blown item creator.

I can write something up for that later.

img img

-- Create this file in addons\pointshop2\lua\ps2\modules\pointshop2
local function paintCentered(texId, w, h)
surface.SetTexture( texId )
surface.SetDrawColor( 255, 255, 255, 255 )
local tw, th = surface.GetTextureSize( texId )
local draw = math.min( w, h, math.max( tw, th ) )
local x = ( w - draw ) / 2
local y = ( h - draw ) / 2
surface.DrawTexturedRect( x, y, draw, draw )
end
local PANEL = {}
function PANEL:Init( )
self.image = vgui.Create( "DImage", self )
self.image:Dock( FILL )
self.image:SetMouseInputEnabled( false )
self.image:DockMargin( 5, 5, 5, 5 )
end
function PANEL:SetItemClass( itemClass )
self.BaseClass.SetItemClass( self, itemClass )
if itemClass.texture then
function self.image:Paint( w, h )
paintCentered( itemClass.texture, w, h )
end
else
ErrorNoHalt( "Invalid texture on item class " .. tostring( itemClass.name ) )
end
end
function PANEL:SetItem( item )
self:SetItemClass( item.class )
end
derma.DefineControl( "DPointshopTextureIcon", "", PANEL, "DPointshopItemIcon" )
local PANEL = {}
function PANEL:Init( )
self.image = vgui.Create( "DImage", self )
self.image:Dock( FILL )
self.image:SetMouseInputEnabled( false )
self.image:DockMargin( 5, 5, 5, 5 )
end
function PANEL:SetItem( item )
self.BaseClass.SetItem( self, item )
if item.class.texture then
function self.image:Paint( w, h )
paintCentered( item.class.texture, w, h )
end
else
ErrorNoHalt( "Invalid texture on item class " .. tostring( item.class.name ) )
end
end
derma.DefineControl( "DPointshopTextureInvIcon", "", PANEL, "DPointshopInventoryItemIcon" )
-- Copy and paste this function to the bottom of addons\pointshop2\lua\ps2\shared\sh_0_pointshop2.lua
-- It will be added in the next version
function Pointshop2.GenerateItemClass( className, baseClassName, moduleName )
if KInventory.Items[className] then
LibK.GLib.Error("Class " .. className .. " is already registered with Pointshop 2")
end
local baseClass = KInventory.Items[baseClassName]
if not baseClass then
LibK.GLib.Error("Class " .. baseClassName .. " is not registered with Pointshop 2")
end
local newClass = class( 'KInventory.Items' .. className, baseClass )
newClass.static.className = className
-- For the error hint when trying to edit it
newClass.static.originFilePath = LibK.GLib and LibK.GLib.Lua and ( LibK.GLib.Lua.StackTrace().RawFrames[2].source .. '(' .. moduleName .. ')' ) or moduleName
newClass.static.isBase = false
newClass.static.UUID = className
return newClass
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment