/* Kart Secondcolor by Lach v1.4 */ -- NOTE: This tutorial assumes basic knowledge of WAD/PK3 editing. You will -- need to know how to create markers for new spritesets (WAD) or create -- directories for sprites and scripts (PK3) and similar functions. If you need -- assistance with such tasks, refer to these articles on the SRB2 Wiki: -- - https://wiki.srb2.org/wiki/WAD_file -- - https://wiki.srb2.org/wiki/PK3 -- ...or join the Kart Krew Discord server and ask for help in an appropriate -- channel: -- - https://discordapp.com/invite/WJmqDtN -- ...or DM @Lach#9444 on Discord. SLADE is generally agreed upon to be the -- best WAD/PK3 editor available, for those who are new to this: -- - http://slade.mancubus.net -- Jimita's WAD2PK3 conversion tool may be helpful for those who are familiar -- with WADs but would like their addon in PK3 format: -- - https://mb.srb2.org/showthread.php?t=44181 -------------------------------------------------------------------------------- -- Instructions! -- 1. Import this script into your addon as a new lump. -- 2. Import your secondcolor sprites. These sprites should be a green-mapped -- cutout of the sprite parts you want to change as the secondary color. -- (Every character with a secondcolor has this, so look at one of those -- WADs if you need an example!) -- These sprites are separate from your S_SKIN sprites! They should be -- between S_START and S_END markers (WAD) or in their subdirectory in the -- sprites directory (PK3). -- 3. Name all these sprites exactly as they would be named if they were -- your default sprites, but with a custom 4-letter prefix. For instance, -- if your character uses the prefix PLAY and you pick the prefix BODY, -- then the secondcolor sprite that matches with PLAYM2O8 should be -- BODYM2O8. Try to make this prefix as unique as you can to avoid -- conflicts with other addons! -- (Again, don't be afraid to use others' addons as examples!) -- Then, replace "BODY" below with the prefix you chose. (Keep the quotes!) -- (For those familiar with SOC/Lua - do NOT freeslot this spriteset on -- your own; this script will do it for you, out of both convenience and -- necessity.) local SPRITESET = "BODY" -- 4. Replace "sonic" below with your character's skin name. -- (This is the name paramater in S_SKIN, but in quotes.) local SKIN = "sonic" -- 5. Fill out secondcolors for each color you want a secondcolor for. Any -- color you do not fill out a secondcolor for will use the color set in -- secondcolors.default (see example below). There are also a few special -- terms you can use, for further customization: -- • "opposite": sets the color's secondcolor to SRB2 Kart's built-in -- opposite color for that color (the color used for the background -- of the signpost that appears when the first-place player crosses -- the finish line!) -- -- • "none": a secondcolor overlay will not display at all -- -- • "self": sets the color's secondcolor to itself -- -- Any of these may be helpful to use as a default. I have filled in some -- examples below! Replace them and add new ones as you see fit. -- NOTE: colors with spaces or punctuation markings should be concatenated. local secondcolors = {} -- (this line sets up our table, do not delete it!) secondcolors.default = "opposite" secondcolors.blue = "red" secondcolors.red = "blue" secondcolors.creamsicle = "none" secondcolors.lilac = "robohood" secondcolors.robohood = "red" secondcolors.scarlet = "self" secondcolors.fairy = "gsuper4" secondcolors.super1 = "handheld" -- just a note: supercolors are supported, -- but they don't have opposite colors, so -- any supercolor that is set to "opposite" -- will be set to "self" instead -- 6. By default, this script lets players pick a custom secondcolor for -- characters that support it, using the console command "secondcolor" -- (and splitscreen variants). I give you, as the creator of your addon, -- the option to disable this functionality for your character, if you -- would prefer that players only use the secondcolors you assigned. -- Change the below variable to false if you would like to opt your -- character out of player-selected secondcolors. local ALLOW_CUSTOMIZABLE_SECONDCOLORS = true -- 7. Test out your addon! Once you are satisfied with your color choices, -- you are done. Everything below this paragraph is code, so don't edit it! -- (No, really, please don't. I can't believe I actually had to clarify -- that after I released this. The code is designed to not be executed a -- second time if another addon has already initialized it. Please don't -- ruin other people's setups by shoehorning your own tidbits in.) -- Please do not remove these comments, they will be helpful for creators who -- find this script by looking in your addon. -- If you have any questions, do not hesitate to DM @Lach#9444 on Discord! -- I'm looking forward to seeing your character. <3 -Lach -------------------------------------------------------------------------------- if not kart_secondcolors rawset(_G, "kart_secondcolors", {}) // This is our global table - it contains every secondcolor list for every supported character added to your game! freeslot("MT_KART_SECONDCOLOR") // This is our custom overlay object - it follows your character and displays your secondcolor sprites over them! mobjinfo[MT_KART_SECONDCOLOR] = { radius = mobjinfo[MT_PLAYER].radius, height = mobjinfo[MT_PLAYER].height, flags = MF_NOBLOCKMAP | MF_NOCLIPTHING | MF_DONTENCOREMAP, spawnstate = S_INVISIBLE, dispoffset = 2 } local function setSecondColor(player, arg, num) if not arg CONS_Printf(player, 'Sets a custom secondcolor for supported characters that allow it.\nAccepted arguments:\n\t- Name of a skincolor (such as red)\n\t- Index of a skincolor (such as 15)\n\t- \"none\"/\"off\"/\"clear\" to return to skin-defined secondcolors') return end if netgame if num > 0 CONS_Printf(player, 'You can only change secondcolor for splitscreen players in an offline game.') return end else if num > 0 if players[num] player = players[num] else CONS_Printf(player, 'You can only change secondcolor for splitscreen players that are active.') return end end end if arg == "off" or arg == "clear" player.ksc_secondcolor = 0 return end local n = tonumber(arg) if n ~= nil and n >= 0 and n < MAXTRANSLATIONS player.ksc_secondcolor = n return else n = _G["SKINCOLOR_"..arg:upper()] if n ~= nil player.ksc_secondcolor = n return end end CONS_Printf(player, '\"'..arg..'\" is not a possible value for \"secondcolor.\"') end for i = 4, 1, -1 local s = i - 1 and i or "" COM_AddCommand("secondcolor"..s, function(player, arg) setSecondColor(player, arg, i - 1) end) end addHook("ThinkFrame", do for player in players.iterate if not player.mo then continue end local mo = player.mo if mo.skin ~= player.ksc_skin player.ksc_skin = mo.skin player.ksc_colors = kart_secondcolors[mo.skin] end if player.ksc_colors if not (mo.ksc_overlay and mo.ksc_overlay.valid) mo.ksc_overlay = P_SpawnMobj(mo.x, mo.y, mo.z, MT_KART_SECONDCOLOR) mo.ksc_overlay.target = mo end local overlay = mo.ksc_overlay overlay.scale = mo.scale overlay.angle = player.frameangle overlay.height = mo.height overlay.eflags = mo.eflags & MFE_VERTICALFLIP overlay.state = mo.state overlay.skin = mo.skin overlay.tics = mo.tics overlay.sprite = player.ksc_colors.sprite overlay.frame = mo.frame overlay.colorized = mo.colorized if not (player.ksc_colors.customizable and player.ksc_secondcolor or player.ksc_colors[player.skincolor] ~= 0) overlay.color = 0 elseif mo.colorized overlay.color = mo.color elseif player.ksc_colors.customizable and player.ksc_secondcolor overlay.color = player.ksc_secondcolor else mo.ksc_overlay.color = player.ksc_colors[player.skincolor] or ColorOpposite(player.skincolor) end overlay.flags2 = mo.flags2 & (MF2_DONTDRAW|MF2_SHADOW) | ((overlay.color == 0 or mo.sprite ~= SPR_PLAY) and MF2_DONTDRAW or 0) overlay.momx = 0 overlay.momy = 0 overlay.momz = 0 P_TeleportMove(overlay, mo.x, mo.y, mo.z) if overlay.color if player.kartstuff[k_invincibilitytimer] P_SpawnGhostMobj(overlay).fuse = 4 elseif (player.kartstuff[k_driftboost] or player.kartstuff[k_sneakertimer] or player.kartstuff[k_startboost]) P_SpawnGhostMobj(overlay).fuse = 2 end end end end end) addHook("MobjThinker", function(ghost) if ghost.ksc_checked then return end local sign = ghost.target if ghost.state == S_PLAY_SIGN and sign and sign.valid and sign.target and sign.target.ksc_overlay and sign.target.ksc_overlay.color local mo = sign.target local player = mo.player local overlay = P_SpawnMobj(ghost.x, ghost.y, ghost.z, MT_OVERLAY) overlay.target = ghost overlay.state = S_INVISIBLE overlay.sprite = player.ksc_colors.sprite overlay.frame = S if player.ksc_colors.customizable and player.ksc_secondcolor overlay.color = player.ksc_secondcolor else overlay.color = player.ksc_colors[player.skincolor] or ColorOpposite(player.skincolor) end end ghost.ksc_checked = true end, MT_OVERLAY) addHook("MobjThinker", function(mo) local target = mo.target if not (target and target.valid and kart_secondcolors[target.skin]) P_RemoveMobj(mo) return end mo.momx = target.x - mo.x mo.momy = target.y - mo.y if P_IsObjectOnGround(target) mo.momz = 2*P_GetMobjGravity(mo) else mo.momz = target.z - mo.z end end, MT_KART_SECONDCOLOR) end SKIN = $:lower() // You thought I was using these as constants? hah! kart_secondcolors[SKIN] = {} if secondcolors.default and secondcolors.default:lower() ~= "opposite" and secondcolors.default:lower() ~= "self" if not _G["SKINCOLOR_"..secondcolors.default:upper()] error("Invalid secondcolor set for \'default\'") else local color = _G["SKINCOLOR_"..secondcolors.default:upper()] for i = 0, MAXTRANSLATIONS - 1 kart_secondcolors[SKIN][i] = color end end end secondcolors.default = nil for k, v in pairs(secondcolors) local i = "SKINCOLOR_"..k:upper() if _G[i] ~= nil v = $:lower() if v == "opposite" kart_secondcolors[SKIN][_G[i]] = nil continue end if v == "self" kart_secondcolors[SKIN][_G[i]] = _G[i] continue end v = "SKINCOLOR_"..$:upper() if _G[v] ~= nil kart_secondcolors[SKIN][_G[i]] = _G[v] else error("Invalid secondcolor set for \'"..k.."\'") end else error("Invalid skincolor \'"..k.."\'") end end for i = SKINCOLOR_SUPER1, MAXTRANSLATIONS - 1 if kart_secondcolors[SKIN][i] == nil kart_secondcolors[SKIN][i] = i end end SPRITESET = "SPR_"..$ freeslot(SPRITESET) SPRITESET = _G[$] kart_secondcolors[SKIN].sprite = SPRITESET kart_secondcolors[SKIN].customizable = ALLOW_CUSTOMIZABLE_SECONDCOLORS