Intravenous 2

Intravenous 2

32 ratings
The Ultimate Modding Guide
By Mambo! and 4 collaborators
"By the community, for the community"

A tutorial that covers all of your modding requirements! From the basic stuff to the more complex. Full customization and importation for the ultimate replayability!

NOT FINISHED YET! This guide is constantly being updated as more modding information becomes available
Next to add: campaign main menu art
4
2
2
   
Award
Favorite
Favorited
Unfavorite
Before we start...
As the title says...

BEFORE WE START, Modding this game is not too hard if its gonna be basic stuff like adding some weapons that just have value tweaks.. adding your own objects and portraits, and even soundtracks!

However if you want to do more than that, such as enter unique scripts and all of that stuff, basic LUA knowledge IS required!

Also, if you are not in the discord server, PLEASE JOIN it as we have a modding community that is more than eager to help you at what you are doing!




Creating the folder where we will make our mods.



Step 1, the most important step, we need to make a mod folder.

A lot of people were confused with the old guide section about this but this time I will explain it in detail.


First off. Go to your games directory.

If you got the game off of steam it is usually the following:

PC > Local Disc > Program Files (x86) > Steam > steamapps > common > Intravenous 2


Now, your IV2 folder should look like this:



As you can see, there is Mods and Mods Staging folders.
By base, your game does not have these, so please MAKE THEM!

  • Mods is where we will create our own mods or even put mods made by others so we can play them (if they are not on steam or if you didn't download the game from steam.)

  • Mods staging (Steam only) is where we put our finished mods and upload them onto the steam workshop through the in-games "Modding" section



Making your own mod folder



So let's say you wanna make a mod...

First off what you need to do is make a folder inside of the mods folder and name it however you wanna call your mod. For example I made a mod called "Borderlands Arsenal".
After that you will want to make a "files" folder inside of your own mod folder.

It should look like this:



This is our base of operation. But alongside it we need to add a "main.lua" file inside. That will be the heart of our code, as without that single file, the game can go to ♥♥♥♥ because your mod wont work and then you will attack me THIS GUIDE BADD!1!!

Now, the rest is on you, do you wanna keep it organised, with everything being directed into proper folders and so on (so you can easily find anything whenever you need to), OR if you will keep it all messy, under the same folder or even without them and so on.


Example of a tidy, organised mod:


Example of a messy mod:


In case you are using folders to organise everything, PLEASE DONT FORGET TO WRITE DOWN PROPER DIRECTORY!

For example an image in the textures folder will require of you to write down "textures/image.png" for the object importing.


This is it. You have now created your own mod folder. You are ready to..

Enter the guide.
A Briefing on Spritesheets and Parsing
As stated before, this guide has been written and expanded over time as new knowledge is learned about the game and the modding process. Some sections of the guide have been written at different time periods before some information was known. In addition to that, this guide is organized by category, not by recency. This sometimes creates a confusing reading experience, where something is taught or explained in a later section of the guide, and an earlier section of the guide assumes you already know this information, because it was written and added at a later date. It's sort of a non-linear reading experience.

To help with this, this section is here right at the start to teach you all the complicated stuff you'll need going forwards. Most sections are pretty self contained with its teachings, but one thing that you're expected to know pretty early on is how Spritesheets and Parsing works.

Included in this guide's original debut is the section about making custom dialogue portraits, which teaches you how to make a sprite and how to parse it. But if that's the case, why would I need a pre-amble telling me how it all works again? While that first section gives you all the numbers and the code you need for it to work, it doesn't explain HOW the code works, or how to apply it to a spritesheet with more than one sprite on it. So let's do that.

A sprite sheet is a large file with multiple sprites on it, meant to be displayed individually as separate objects, or in an animation. Creating a spritesheet is pretty self explanatory, but somethings you should keep in mind are:
- All sprites are .png files. Any other file type won't work. Your art or image editing program can probably export png files, but just make sure that it does before going forward
- Your first sprite should always start in the top left corner, where the edges of the sprite touch the top and left borders of the canvas. (This isn't mandatory, but it makes things much more convenient, as you're about to read)
- You should keep a one pixel gap in between each of your sprites. Imagine a box around your sprite (if your sprite isn't already a perfect rectangle) and make sure no other sprites are inside that box.

Now for parsing. On its own, a spritesheet is just a large image with a bunch of smaller images on it. But the game just sees one big image, it has no idea there are smaller sprites on the image. Parsing is how we tell the game which part of the spritesheet to use. Parsing is done through a regular text document (not a .lua file). This is how it works:

First of all, the text document and the .png file should have the same name. This is so the game knows which text file to use on which image. Inside the text file, you'll use this formula:

sprite_id = x y X Y

The "sprite_id" is whatever you want to call the sprite. You will use it later in your code, so make sure it's short but descriptive. And use an underscore instead of a space if it has multiple words (this goes for any code you'll write). If you only have one sprite on your sheet, you can name it the same as the file name.

The first lowercase x and y are the coordinates of the top left corner of your sprite. All of your sprite's coordinates are in relation to the top left corner of the canvas. We'll call this the origin for this section of the guide. So if you put a sprite in the top left corner of the canvas, your first two numbers would be 0 and 0.

So, so far our code would be:

sprite_id = 0 0 X Y

The uppercase X and Y are the length and width of your sprite from the top left corner. So if you had a sprite that was 20px long and 30px high, your code would look like this:

sprite_id = 0 0 20 30

Let's see an example for all the visual learners out there:


Here's a spritesheet I made for my mod. As you'll read in the next section, dialogue portrait sprites are 78px long and 87 px tall. Here's what the code for these four would look like:

alexa_smile = 0 0 78 87
alexa_frown = 79 0 78 87
ceo_pucker = 0 88 78 87
ceo_aghast = 79 88 78 87

Note how both alexa_smile and ceo_pucker both start with 0, since they're both touching the left border of the canvas, i.e. they're 0 pixels to the right of the origin. ceo_pucker starts with 0 88, meaning that it starts 0 pixels to the right and 88 pixels down from the origin.

Notice how the last two numbers for all four lines are 78 87. That's because they're all the same size, 78px wide and 87px tall. Don't get confused: You're not getting the coordinate of the bottom right of the sprite; you're just measuring the distance from the top left to the bottom right.

So in this case, it would be incorrect to parse ceo_pucker like:

ceo_pucker = 0 88 78 175

Because even though the coordinate of the bottom right of the sprite is 78 175, the code is meant to simply measure the distance from the top left of the sprite to the bottom right. The last two numbers are just how wide and tall your sprite is

One last thing, why do the ceo sprites start with numbers that are one higher than its width and length, 79 and 88? Remember, the first two numbers are the coordinates for the top left of the sprite. The reason they're one higher is because of the one pixel gap we put between each sprite! So make sure to account for that when you're parsing.

And that's it! You just have to do that for each one of your sprites. Each... and every... one... of them...


This game has a lot of sprites. And yes, you have to manually parse each and every on of them. It is quite time consuming. But very rewarding to see your sprites and animations inside the game. So get out there and start spriting! Show the world your twisted creations!
Custom Portraits




SO! Lets kick it off with custom portraits.

PORTRAIT IMAGES MUST BE 78x87 AND IN .png FORMAT!

Also if you are making multiple portraits, I suggest downloading a sheet of portraits and replacing them with yours, it makes the directory sooooo much easier.

To keep it simple, we will make a portrait of Hatsune Miku (I honestly don't know why I chose her)

You can do this in whichever way you want but this is the simplest way to me.

First off, I suggest downloading Pixel Studio (free)
Link to PS: https://store.steampowered.com/app/1204050/Pixel_Studio__pixel_art_editor/

Second, Turn it on and open a new project with the dimensions 78x87 (WIDTH x HEIGHT) under simple template.

Now go to a colour picker, and save this colour (IV2 Portrait Background Colour)



(If images dont load, its R19G20B29 )



Fill the image with that colour and then make a second layer (layer button highlighted green)



After you are on the second layer, IMPORT YOUR CHARACTER OF CHOICE/PORTRAIT (add image button)



Go to "from device" and then choose "open from files"


If the image is bigger than 78x87, its not gonna fit, BUT, we will make it fit, because this editor offers us the ability to drag the image and shrink it and make it fit our dimensions!
Images for example:

  • After you upload your oversized photo:

  • Dragging the image to its corner so we can access the shrinking:

  • The result of fitting the image into the frame:



    After you properly set the image of your choice, we will now erase the background (unless your image was transparent).

    The finishing product should look like this, with black background (optional but looks better):


    Now, we will save the portrait with a custom name (no space, instead use _ ), and transfer it to our "textures" folder

    Alongside our portrait, add a .txt file (normal text document) and name it same as the portrait file.
    example:

    In that text document write down the following:

    portrait_"name" = 0 0 78 87


    This is used so the game can take the coordinates of the portrait (better for portrait sheets but still necessary, as portraits wont work without this)
    example:


    After all of that was done, open "main.lua" file and add the following code:

    spritesheetParser:parse("textures/FILENAME") - This is used so the game can register the portrait(s) into its textures.
    table.insert(dialogueHandler.portraitList, "portrait_NAME YOU TYPED IN TEXT DOCUMENT") - This reads the text documents portrait list and uses the coordinates you input to cut out the portrait

    example of how your main.lua should now look:



    And thats it with the portraits! Now when you open up the game and go into the map editor, the portrait should be there! However, our dialogue name for Hatsune is not there, and thats what we will do next.

Dialogue Names



This is suuuuper simple.

Open up the "main.lua" file and type in the following code:

dialogueHandler:addCharacterName('name', "name", 'Name')

just switch the name with the name of your character.
for us its Hatsune, so it should look like this:



So! the dialogue name is done, and the best part about it? Super simple, you don't need a portrait, just copy paste the code and the name.

Next on, we will be moving to the soundtrack.
Dialogue Choices (Cutscenes)



Ever wondered how to do those multiple choice dialog cutscenes in IV2, and wanted to implement it in your map? Well you can, and in this section I will show you how.

First before I go any further, I want to shout out AVO from the Explosive Squat Games discord server for helping me out with this.

Step 1:
Make sure you have a map already made. If you want to test this out to figure out how it works. Just make a blank map for this example.

Step 2:
Set up 3 cut-scenes. One cut-scene for the start of the cut-scene which will lead into the multiple choice question, and the other two for the branching options, for simplicity sake lets just call them option A and option B. I would go over how to make cutscenes if you are unfamiliar with them, but that is outside the scope of this tutorial, and I will just assume you are familiar enough with how the cutscene editor works.

Step 3:
When your map is ready, export the map. When it’s exported, it should be in the intravenous 2 folder under: $Username$/Appdata/Roaming/intravenous2/levels_workshop

The reason we did this was so that alongside the map file, it would also generate the main.lua file for the map.

Step 4:
Writing the lua code for the branching options for your cutscene.

Below is an example of the Lua code I use for this purpose which also has notations next to the lines of importance to explain what does what:

dialogueHandler.registerQuestion({ -- This line of code here will register a new dialogue ID without adding it in within the map editor. So whatever ID you add for this piece of dialogue, make sure it is not the same as a dialogue ID setup within the editor. answers = nil, id = "enter_dialogue_ID_here", -- this will be the ID for the dialogue that will be registered within the game to be used in a cutscene. text = nil, image = "portrait_unknown", -- this will be for the portrait used when the dialogue starts. nameID = "unknown", -- this is the ID for the name that will be used when the dialogue starts. text = _T("enter_dialogue_ID_here_text", "example text for the dialogue"), answers = { -- enter answers you want the player to be able to choose "1_choice_example1", -- these are the id's for the choices you can make. If you want to add more choices, just make sure to add more. "2_choice_example2" } }) dialogueHandler.registerAnswer({ cutsceneID = "example_cutscene", -- enter the name of the cutscene you want to start after choosing choice number 1 text = nil, id = "1_choice_example1", -- the answer ID for choice number 1 question = "cutsceneexampleA_next", -- this will be the dialogue ID that will start when you choose this choice onPick = nil, text = _T("cutsceneexampleA_text", "Choice 1 Example."), -- make sure you also put the dialogue ID on the left with _text next to it, and on the right of that will display the text for the option button in the dialogue window. onPick = function(self, dialogueObj, prevQuestionID, answerKey) --this will start the "example_cutscene1" cutscene cutscene.state:start(self.cutsceneID) end }) ------ Option B dialogueHandler.registerAnswer({ cutsceneID = "example_cutscene2", -- enter the name of the cutscene, in this case the "example_cutscene2" cutscene will play text = nil, id = "2_choice_example2", -- the answer ID for choice number 2 question = "cutsceneexampleB_next", -- this will be the dialogue ID that will start when you choose this choice onPick = nil, text = _T("cutsceneexampleB_text", "Choice 2 Example."), -- make sure you also put the dialogue ID on the left with _text next to it, and on the right of that will display the text for the option button in the dialogue window. onPick = function(self, dialogueObj, prevQuestionID, answerKey) --this will start the "example_cutscene2" cutscene cutscene.state:start(self.cutsceneID) end })


Now I know what you are thinking, this looks pretty complicated, and it is, and if you don’t know what does what, it can be pretty stressful, so I will explain how it works.

This first section of code that starts with dialogueHandler.registerQuestion({ does one off two things. For starters this section of code will actually register itself as a line of dialogue independently from the map editor within the map. Yeah believe it or not, you don’t need to write the dialog within the map editor in order to have dialog play in a cutscene. As long as this lua file with this code is loaded from the main.lua, and you have everything set up correctly, you can effectively create your lines of dialog for cutscenes within the lua file (or multiple lua files). Here you can even have the npc’s and player model make gestures within a cutscene as well if you so wish…but I’m getting off topic.

The second thing this section of code will do is register ID’s for the specific options you want the player to be able to choose hence the lines of code 1_choice_example1 and two respectively. Hell if you wanted you can do more choices if you want, but if you add too much it could end up going past the boundaries of the dialog box and the screen as well, so be mindful of that.

The second and third section of code below the first are the dialogueHandler.registerAnswer({ sections of code which will choose from those other cutscenes you set up in the map editor, make sure you assign the choice ID’s for them as well as the corresponding cut-scene ID’s for the cut-scene that will play when choosing either of these options as well as the starting dialog ID for each cut-scene respectively.

If you aren’t sure how to do that, make sure to read the notations next to each line that explains what they are and what to do.

Step 4: Once you setup your lua file from the previous step exactly how you want it. Now we need to register the lua file to the main.lua file.

Open up your main.lua file in your code editor and below the line of code that registers your map to be loaded by the game, type: register(“whateveryourfileisnamedhere”) , and then save the file.

For example, here’s what my code looks like for the example map I made for this:

maps:registerModMap('maps/', 'Choices A and B.gdsmap') require('maps/Choices A and B_localization') require("example_choices_guide_copy")
Once you’ve done this step, save the lua file.

Step 5: Add your map to the mods folder in your steam installation. For this to work properly and for your lua files to be loaded within the game, you need to make sure the lua files and exported map files are put into your mods folder. If you look at your installation folder for IV2 and you don’t see a ‘mods’ folder, just make a new one.

Simply copy and paste your map folder from the intravenous2 folder in the appdata/roaming folder/levels_workshop into your mods folder.

Step 6. Load your map. If all worked accordingly, you setup all your dialog, cutscene, and lua ID’s correctly, you should have a cutscene that has a multiple choice line of dialog.

If it didn’t work, or for some reason the game crashed, you screwed something up and need to double check everything, or start over.

If you still aren’t sure about how any of this work, I took the liberty of making a step by step video on how to do this (minus how to setup your cutscenes, either figure it out, or ask someone how to do it, until someone makes a writeup for this guide.) Which you can find embedded to this guide below:

https://youtu.be/9v5Iw7Q0B30

Also I took the liberty of adding the zip file including the example map including the code: link [drive.google.com]
Player Class



So! In this part I will be covering the topic of "Player Class", basically the skills the player has, like Steve and Sean.



The image above shows Steves player class, just as an example.


And here we have the player class of the "Home Depot Dungeon" protagonist (tweaking steve)


You will most likely need this if you are making a campaign where you play as a different character rather than the main 3, Steve, Sean and Gideon.

So! Lets start off with it!


Step 1
: Create a custom class image.


Open up a 64x64 file and make your own custom icon for the player class with the following palette



Now make your own icon.
I suggest you to make the icon to whatever resembles the class that you are making.
This is the one I made for my wizard class.



Now that we have the class icon image, lets place it in our textures folder, alongside making a text file where we will parse it. After that do not forget the lua spritesheet parse code!

Text file code:


LUA parsing code (main.lua or if you have a .lua file where you do everything textures related do it there):



Step 2
: Write down the code for the player class.


Open up your campaign.lua OR main.lua (campaign if you are making one) and write the following code:


local customclass = playerActor.addPlayerType({
name = "customclass", -- change this here to whatever, match it with the id by the way for easier coding
canUseSloMo = false,
trainedBy = "sean", -- Dont change this, as of right now i dont know what this does
portrait = "portrait_steve", -- default mobile dialogue portrait
id = "customclass",
portraitSkills = "portrait_steve", -- the portrait that is displayed, I have only ever seen it in the campaign hideout levels when you are picking between Sean and Steve for their skill trees
animVar = "player", -- What character sprite will the player type/class use, for example if you wrote "ped4" you would be playing the game as pedestrian model 4 haha
slomoCharges = 0,
icon = "your icon file name here",
showSloMoBar = true,
killSloMo = {
sightingsDropRate = 0.5,
sightingsCeiling = 0,
duration = 0,
sightings = 999,
cooldown = 999,
enabled = false
},
display = _T("PLAYER_TYPE_???", "Your class name here"), -- Replace the ??? with whatever you are gonna name your class but in caps
description = _T("PLAYER_TYPE_???_DESCRIPTION", "Your Description Here"),
getGrabbedBodyAnim = function()
return actor.ANIM_GRABBED_BODY_SEAN
end,
skillLevels = {
loadbear = 1,
sga = 1,
sgr = 1,
mgr = 1,
lga = 1,
lgr = 1,
tough = 1,
covert = 1,
hth = 1
} -- These skill levels can be switched to your liking, from 1 to 4, you can do more but it wont have any effect
})

Heres what mine looks like:



After that we need this next few lines of code and we are done! So write the following code few lines under the one you just wrote:


playerActor.setPlayerTypeLoadoutSlots("customclass", {
{
weapons.TYPES.PRIMARY,
1,
nil,
{
idle = "hud_slot_secondary_idle",
hover = "hud_slot_secondary_hover",
active = "hud_slot_secondary_selected"
}
},
{
weapons.TYPES.SECONDARY,
1,
nil,
{
idle = "hud_slot_main_idle",
hover = "hud_slot_main_hover",
active = "hud_slot_main_selected"
}
},
{
weapons.TYPES.GADGET,
3,
{},
{
idle = "hud_slot_gadget_idle",
hover = "hud_slot_gadget_hover",
active = "hud_slot_gadget_selected"
}
}
})
playerActor.setPlayerTypeMaxWeapons("customclass", {
[weapons.KEY_TYPE.PRIMARY] = 1,
[weapons.KEY_TYPE.SECONDARY] = 1,
[weapons.KEY_TYPE.MISC] = 3,
[weapons.KEY_TYPE.MAGLOAD] = -1
})


So fun fact about this is, you can switch the code of the weapons.TYPES.PRIMARY/SECONDARY/GADGET to your own liking to create the loadout sequence of your own choice, so if you have the primary code under gadget ones that means the primary weapon will appear last in the loadout window! It doesnt work once you are out of the loadout window as the keybinds are hardcoded.

The "nil" part is the default weapons your player class spawns with. You just gotta write the id of the weapon you want the class to spawn in with, and yes, mod guns work, as long as you have the mod installed! As for the gadgets, it goes under the brackets due to multiple slots. so it would be something like this:
{
id1,
id2,
id3
}

Another thing to note is you can raise the number of weapons you can have, as by default it is 1 1 3, 1 for primary.....
But theres only keybinds for 1 prim, 1 sec, and 4 gadgets (IV2 Steve has 4 gadget slots, thats why), so the only way to switch to a second primary or secondary weapon is to drop the current gun and swap it twice. I know, it sucks, but if anything its a way to carry an extra weapon and I already see a lot of ways it could be nicely implemented!

Anyways... Here is how my code for the wizard looks like:




Now that that is done, we are finished with the custom player class!

To see the fruits of your labour, load up the game and go to the map editor, the custom class should now appear under "player type" section





Alongside that, if you tweaked the second part of the code regarding the weapons, your loadout will look differently too! Here is an example.

Custom Animations





So! Right here I will be showing you how to do custom animations. The example will be for weapons but you can do it for anything else if you follow the concept logically. And remember, think about what you are making and what exact animations will be used or which ones you need. You do not wanna lose time or waste resources here... Its time consuming as hell.


First off, there is so many variations of animations, and if you are making a weapon, you need to note down EACH type of animation the weapon has. Custom character? Its work from scratch at this point.

Anyways, for the weapons here is an example of what you may need.

Player Weapon Animations Needed:


(Optional) Goon Weapon Animations Needed:



Now please keep in mind gadgets are all unique and they all use different types of animations, then again same with unarmed ones and base ones like deaths, vaulting, lockpicking and so on...


But to start off, I suggest you to take a player model (if you are making sprites for the existing character, doesnt have to be player) and try to like cut parts and tweak them around to get the animation you are aiming to get. If its a new character? Use the existing models as an example in that case, for it will make the work far greater. Not all characters use the same dimensions by the way!

Anyways heres how I made the katana animations for example.


So as you can see, I made the background a solid colour juuust so I could see what im making, I imported some existing Steve sprites that I thought I could use as an example, then kinda worked around with what I had to make some katana animations.

After making some, I then copy pasted them into another image file where I would have just the sprites, as for different characters (sean and gideon) I imported their models of the head and the body and dragged it on top of copied Steves sprites and did the rework I had to do manually.

And this below was the final result.



Anyways once you are finished with the sprites put them in the textures folder and make a same named text file for parsing.


After you made the animation sprites for all you need, parsing comes up next.


The parsing part is the absolute hell.

Just as an example here is the image of the katana sprite parsing (this is good compared to how much Id have to do for any other fkn character.)



Basically you are following this rule:

"player class id"_"your_item"_"function"_"number" = "lenght pix start" "height pix start" "pix lenght of sprite" "pix height of sprite"

After making and parsing the sprite sheet, we need to register it in the main.lua file like always. Right?
WRONG!

Well, no, we do. But for animation sprites, we need a separate line of code actually registering the sprites as an actor animation. It has its own designation, similar to adding a map editor object, if you've visited that part of the guide:

spritesheetParser:parse("textures/filename")
this one you've seen before

render:registerActorSpritesheet("textures/filename.png")
Don't forget the .png!

Wait, that's not for the katana mod. Mysterious new guide contributor, is that you???

After you have parsed it all, comes the code part. It is easier.

So, you will want to make a new lua for the animations.
In my case i made a lua file called "katana_animations". Keep in mind you need just 1 lua for all animations as long as you do it properly.

Now, open your new lua file for the animations and write down the following stuff:

playerAvatar.ANIM_??? = tdas.countGoon("player_animationID") -- registering the animation id

Heres an image example:


You will only want to do this for animations you are placing in, doing it for non existing animations will crash the game on launch.


Next up regarding animation code is you always need the following lines:

local torsoOff = {
0,
8
}

On the second number write the base player sprite lenght divided by 2, if you are using base game sprites leave this as it is.


Next up, you will want to write down this code:

actor.genericRegisterActorAnimation(playerAvatar.ANIMATION_ID, playerAvatar.SECTIONS.TORSO, "number of how many frames there are", ""id you used in parsing"_", {
["number of how many frames there are"] = avatar.genericGoToAim
},
{
{
"sprite lenght offset",
"sprite height offset"
},
{
"sprite lenght offset",
"sprite height offset"
},
})


One thing to note is the offset.
Offsetting the animation is done so the centre of the animation in the game is always the player, or the torso, same ♥♥♥♥.
Offset lenght of 1 is 2 pixels, so if you wanna offset for each pixel its 0.5
Another thing you should note is the left and right, same with up and down. To offset in a specific direction we use a - symbol for the "negative" value. In the image below you will see what direction needs a "-" prefix.




Also here is the example of the code I did for the katana idle, also displayed on this gideon offset direction image.





Anyways after that, if you were making animations for a weapon you will need to apply it in the code of the weapon like so:




If not a weapon, but a player class, first add this line to your main.lua:

actor.registerGoonVarietion("character_name", "player")

replace character_name with whatever you want to call the character. For simplicity, you can name this the same as the player class id you made before (refer to Custom Player Class section)

Then, write down the player class prefix you wrote in the parsing text file into the "animVar" section of your main.lua or campaign.lua file where you are writing down the player class. Example images below.


Original Soundtrack



So! First off lets clear out the "main.lua" code.
Remember how we had a "custom_ost.lua" file too? Well, the game on its own cant read it (i think), and we need a code in the main lua file for it to work.

Open up your "main.lua" file and add the following code:

require ("custom_ost")


AFTER THAT, WE CAN START WITH THE SOUNDTRACK!

I recommend you to download audacity to work with this. If you have it, then continue with the guide, but if you don't, here is the download link:

https://www.audacityteam.org/download/



SO! Fun fact about the soundtracks, they can have multiple gameplay phases, and while it is the easiest for me to just download one and input the whole song for every phase, that wont help you, so I will tell you the best way to make the OST switch phases like butter.

There are 5 total gameplay phases, but as my grandmaster Avo said, you only really need 3, so we will make 3 gameplay phases tracks with a single song/soundtrack. The 2 optional ones are SUSPICION and ALERT_SUSTAIN. SUSPICION is played when either of these things happen:
  • Unalerted goon became suspicious (heard a noise/noticed turned off lights/noticed you in the dark)
  • You are within close proximity of a goon during stealth
Circumstances under which ALERT_SUSTAIN plays currently aren't well known.
Either way, let's continue.


OPEN UP AUDACITY!!!


After Audacity is open, open the soundtrack/song of your choice in it.

It should look like this:


After that, (if youre not gonna use this as a single track for each phase, or a hideout/peaceful level), you need to find 3 parts in that song, the casual "stealth" phase, suspicion phase, and combat phase, basically parts that play during gameplay, and split the track into 3 pieces.

I suggest you to use instrumental only, or instrumental versions and vocals just on action part, but you do you.

After you found the 3 parts, i suggest you to save them individually and please cut them at points where the start and the end could loop, or copy paste the loop so its longer before the easy start.
Also give the files a simple name with a number at the end (1,2,3) so we can track the phase.

Example of all of my parts, with the track split:


ANOTHER IMPORTANT THING THAT I FORGOT TO SAY LOL. SAVE THE TRACKS IN .OGG FORMAT!!!
Go to "Export Audio" and pick "Ogg Vorbis Files" format:




NOW! I assume you have saved your 3 phases. Please move them to the "ost" folder of your mod folder.

It should look like this:



LUA FOR THE SOUNDTRACK


Open up the "custom_ost.lua" file and add the following code:


musicPlayback:registerPlaylist({
name = "SONGNAME",
startVolume = 1,
snapVolume = false,
volume = 1,
contents = {
-- the CALM (idle) state tracks
[musicPlayback.PLAYLIST_TYPE.CALM] = {
"ost/FILENAME.ogg"
},
[musicPlayback.PLAYLIST_TYPE.SUSPICION] = {
"ost/FILENAME.ogg"
},
-- the ALERT state tracks
[musicPlayback.PLAYLIST_TYPE.ALERT] = {
"ost/FILENAME.ogg"
},
[musicPlayback.PLAYLIST_TYPE.ALERT_SUSTAIN] = {
"ost/FILENAME.ogg"
},
-- the COMBAT state tracks
[musicPlayback.PLAYLIST_TYPE.COMBAT] = {
"ost/FILENAME.ogg"
}
}
})


Now, if you made 3 soundtrack phases, follow the calm, alert, and combat states when you write down the file names to know which track you made goes where.

This is how your .lua file should now look like:


And.. if you followed everything correctly, you should now have a working custom ost in the game!

ALSO should be worth mentioning, if youre adding multiple osts, just copypaste the code above into the same lua file, DONT MAKE SEPARATE LUA FILES IT WONT WORKKKKKKKKK!!!!!!!!!!
Custom Sounds / Ambiance




Custom Sounds!

These can be used for literally anything, most importantly, guns and stuff, but in this part we are only aiming that we cover their usability in the map.

With custom sounds, you can literally implement voice lines in your maps, do ambiance like people chatting around you, cars passing by, hell.. even a war field background noise without the need to implement it as a soundtrack!


Anyways. Let's start by making a "sounds" folder in your "files" folder of the mod, as that is where you will insert all of your sounds, and remember, they MUST be .ogg files! Or the game will go sicko mode and crash.

Now, create a "sounds.lua" file in the "files" folder as that is where we will write down values of each sound!

And before we start doing anything, open up your "main.lua" file and write in the following line:

require ("sounds")

As we have already said, this makes the game read the "sounds.lua" file.


Now! We have prepared everything we need to set up our own custom sounds!


Let's make our first sound!
I'll use the "Think fast, chucklenuts!" sound as an example.


After putting the file in the "sounds" folder go ahead and open up "sounds.lua" and import the following code:

register.newSoundData({
name = "YOURNAMEHERE",
sound = nil,
envSound = true,
looping = false,
soundType = "static",
playAlways = true,
noSpatial = true,
volume = 2,
sound = {
"sounds/FILENAMEHERE.ogg"
},
volumeType = sound.VOLUME_TYPES.AMBIENT,
noStopping = nil,
noEffects = true
}, 1)




This is what it should look like, JUST DON'T FORGET TO CHANGE THE VARIABLES WITH YOUR OWN! (name and sound file name)

Now after turning on the game and going to the editor, this is what we can do.

Also, don't forget that you can use it with the "Play Sound" option for scenes and cutscenes!

And that would be the simple version!
Sound stuff is a bit complicated but easy to understand once you know what is what.
But for the sake of the guide, let's move on to some more advanced stuff regarding sounds!


MULTIPLE SOUNDS UNDER A SINGLE SOUND OPTION



This right here is an image from the games code regarding ambient sounds like winds. Basically, by following the same steps, we can enter multiple sounds under single sound data / sound option.

For example, if in my chucklenuts sound lua file i added more sounds, it would play those other ones after the first is over is over.


SOUND PROPERTIES AND VARIABLES

Anyways.. notice how the code has those true and nil variables and they mention stuff like looping and so? Yeah, that changes the properties of it and the functionality in game and the editor, so I am gonna list down the stuff I know so far about them.


"envSound - true"
Makes the sound available on "enviroment_sound_random" and "enviroment_sound_timed" options in the editor

"envSound - true" + "looping - true"
Makes the sound available on "enviroment_sound_dynamic" and "enviroment_sound"

"volumeType = sound.VOLUME_TYPES.AMBIENT"
"volumeType = sound.VOLUME_TYPES.EFFECTS"

Now regarding this I have no idea what makes the difference but in theory ambient volumes should be, well, ambient and effects used for weapons, footsteps etc.? I don't know, but I want you all to know that theres 2 types of code for "volumeType", and im pretty sure the effect ones can be used in cutscenes and scenes so KEEP THAT IN MIND!

"volume = n"
This one is.. I'd say obvious, but if not, basically it sets the volume of the sound, 1 being the base value. It can be in decimals too such as 0.01, or it could be louder, like 2, 5, 10 etc.

"maxDistance = n" (FOR VOLUME TYPE EFFECTS)
The distance till which the sound can be heard. EACH TILE IS 16, SO 10 TILES AWAY WOULD BE n=160!

"fadeDistance = n" (FOR VOLUME TYPE EFFECTS)
The distance to which it fades, in general just set it same as max distance or don't even write this code down if you won't have the fade.
Map Lua Files




This is a super simple process, especially compared to the first game where i lost my ♥♥♥♥♥♥♥ MIND. But! When your map is ready to be exported OR transferred to your mods map folder, open the map in the level editor, go to the settings of the map (where the dialogues, player related stuff and such is) and on the bottom right corner you have a "Export as Mod" button;



Press that and then you will see this detail sheet;



In here, Fill up the Level ID (I suggest the first initials of your campaign + numbers, like IV01 (Intravenous, level 1) as this will simplify our work in the other lua files.), Description, Level name and timeline.

The localization keys you dont have to worry about unless you plan on translating your mod/map.

After that press "Export as mod" option, and go to the directory it gives you.

Copy the map.gdsmap file and the map.lua file and transfer it to your mods map folder.
Your "maps" folder should look like this:



Please note that you do NOT need LVL.gdsmap.bak and LVL_localization files (localization ones you need if you wanna translate the map)

After doing so write down the following line into your main.lua file

maps:registerModMap("maps/", "LVLID.gdsmap")

It should look like this:



Also should be worth mentioning that if you DID[/b] transfer the localization files that you need to register the localization.lua file of the map, so paste the following line in your main.lua :

require('maps/LVLID_localization')

And it should look like this:



Editing the Map.lua file


So! If you are making just one map, please disregard this and move on. BUT! If you are making a campaign, this is important.

Open up your map.lua file.
It should look like this but without the previous map, next map and campaign code (game does not add it by default.)


If you are working on a campaign, add the following code (as in the screenshot):

previousLevel = "LVLID",
nextmap = "LVLID",
campaign = "NAME_campaign",

Also. If there is no previous level, delete that line.
Same goes for the next level.

If you dont delete these but there is no previous/next level your game will most likely crash or something wont work!
Adding Reinforcement Spawning Events
This section will explain how to add a simple system to your level which would cause enemy reinforcements to spawn in after a certain cutscene, if the alarm was raised, or gunfire was heard. One example of such event being triggered in the game's main campaign can be seen below:

As shown here, this dialogue popped up after I spoke to Dave on the "Police Precinct" level after going guns blazing, which tells me that a new group of bodyguards have been spawned. Although for the purpose of this section the player won't be shown any dialogue.


To begin, we would need to place NPC spawn locations, where the enemies would appear when the spawning event is triggered. Place an "npc_spawn" object onto your level. In the option boxes, choose which visual and experience we want the goon to have and input the ID for the gun we want them to have. The "max trigger number", refers to how many goons will spawn on that spawn area when the spawn event triggers. Here is an example of how it can look like:

Here 1 inexperienced goon with a sawn-off and the bandit4 visual would be spawned when the event gets triggered.

When choosing NPC spawn locations, make sure they are outside of the players FOV when the event gets triggered. This is because the game only spawns the NPCs when the player doesn't have line of sight of the spawn location.


Now that we set up what enemies and where they should spawn, now we'll have to add the trigger to the cutscene, so enemies will spawn after it. To do this, create a new cutscene part with a script trigger, and for the script ID input:

tsa_generic_gunfire

Here is how it should look like:
The event should occur at the end of the cutscene, after any saves to ensure enemy spawning is triggered consistently.

Don't forget to export the map, to make sure the changes are saved and that a map.lua file exists for the map.


Now that we set up when the enemies should spawn, a line of code should be added to the lua file of the map, so it will be able to keep track if the conditions for the event to be triggered are met. Examples of these conditions is an unsuppressed bullet fired or an alarm panel being triggered. Open the map's map.lua file and add this line of code:

maps.genericUnsupFireTriggerOnMapStarted

It should look something like this:


Now you can test to see if it work. Please do keep in mind that this event will only properly trigger if the level has been exported and being is played normally rather than in editor. To do this, drag your map file from the level_workshop folder, into the mods folder in the games directory. From there just open it from the "Load Map" menu. The enemies should only spawn after gunfire is heard or an alarm panel is pressed. Otherwise enemies shouldn't spawn.
Map Objects




SO! This part of the guide will be covering how to add in custom objects, props, decors.. whatever the ♥♥♥♥ you call them!

First, lets start by understanding how pixels influence the size of the asset.



On the image above, I made these 1 tile floor cubes and found out that a tile is 16x16 in size, same goes for walls. You should use this (alongside other props as size examples) as a general guide to what size you want to aim your asset/prop to be.

PLEASE KEEP IN MIND THAT THE LESS PIXELS IT HAS THE LESS DETAIL IT HAS, SO DON'T HOPE FOR SUPER DETAILED, YET SMALL ASSETS!



Image above displays the player as a size reference to the 16x16 tiles.



NOW THAT WE KNOW THAT, LETS MAKE OUR OWN ASSET!


First off, make the sprite or resize an already existing image (like we did with the portrait in the previous step).

Second, name the sprite something simple and put it in your "textures" folder.

Third, create a .txt file with the same name as the image.



Open the text file and insert the details for the prop like so:



REMEMBER THE FORMULA! "id of the prop" = "width start pixel" "height start pixel" "width end pixel" "height end pixel"

And a friendly reminder that you can pack more assets into a single file like this!

MOVING ON!

Fourth, the most important.. DO NOT FORGET THE CODE!



spritesheetParser:parse("textures/asset") - Reads the .txt code to know what to take from the image, how to name it and so on

mapEditor:addObjectQuadsFromTexture("textures/asset.png") - Adds the sprites from the image to the game as map editor objects



And that would, AND SHOULD, be it! If you have done everything properly you should find your assets in the game now!

This is how this exact asset now looks







Before moving to the next step, this is what you can use your props as (using the code mapEditor:addObjectQuadsFromTexture("") in the main.lua) in the editor:

  • weapons_locker
  • objective_pickup
  • money_pickup
  • mod_blueprint
  • map_obstacle_visblocker
  • map_obstacle_aim
  • map_obstacle
  • map_decor_shadowless
  • map_decor_roof
  • map_decor_fade
  • map_decor
  • light_switch
  • interaction_handle
  • interactable_object
  • glass_switch
  • dj_station
  • chef_cook_station
  • balaclava
  • armor
Campaign Lua Files



So! If you wanna register your maps as a campaign this is the step you have to follow now.


Starting off, add a .lua file in the files folder of your mod folder and name it however you want.
Of course.. naming it however you want isn't the smartest unless you know what you're doing, so I suggest you to name it however your campaign is gonna be called.

For the guide I will use images of my "IntraLove" campaign files.


Anyways, if you created the [campaign].lua, it should look like this:


Next up, lets implement the file into "main.lua"
Go to "main.lua" and add the following line:

require("X")

Replace the X with the same name you used for your campaigns .lua file.

It should look like this:



After we have done that. Let's make a header for our campaign!
Campaign header images are 478x180 in size!!!

Same as the portraits step, open up Pixel Studio and just create a 478x180 image that we can use as a header. You can leave it blank for now and work on it later, so you can follow this guide right now.

After this image is created, name it whatever you want and transport it to the /textures folder of your mod (IMAGE MUST BE .PNG), and add a .txt file with the same name as your campaign header!
It should look something like this:


Now open up the text document and write the following line in it:

header_image_id = 0 0 478 180

You can switch the name, just note this is the ID of the image (and numbers indicating what coordinates to crop from the .png file. In theory you can have ALL textures on a single png and use coordinates to crop out each part and give it its unique coordinate, lol.)
It should look something like this:



Now, just like with the other images, lets write it into the main.lua file using the line:

spritesheetParser:parse("textures/art_campaign_header")

It should look something like this:


Anyways. After we have our campaign header image, we can finally move onto the campaigns own .lua file! So please, open your campaign.lua file and paste in the following code lines:



local X_campaign = {}
X_campaign.narration = require("X_narration")

function X_campaign:getNarrationConfig()
if self.narrationConfig then
if type(self.narrationConfig) == "function" then
return self.narrationConfig(self.narration, self)
end

return self.narrationConfig
end
end

function X_campaign:setLevelFinishNarration(id)
self.narrationID = id
self.narrationConfig = self.narration[id]
end


X_campaign.levelScripts = {
LVL1ID = function(self, playthroughData)
self:setLevelFinishNarration("LVL1ID")
end,
LVL2ID = function(self, playthroughData)
self:setLevelFinishNarration("LVL2ID")
end
}

X_campaign.narrationSequence = {
LVL1ID = "LVL1",
LVL2ID = "LVL2"
}

X_campaign.id = "x_campaign"
X_campaign.startingMoney = 0
X_campaign.hideoutFirstVisitMoney = 0
X_campaign.name = _T("CAMPAIGN_X", "Your Campaign Name Here")
X_campaign.listed = true
X_campaign.menuIcon = "my_header"
X_campaign.INTRAVENOUS_BASE_CAMPAIGN = false
X_campaign.limitCompletedLevels = not DEBUG_MODE and not ALL_MAPS_ENABLED
X_campaign.creditsStartAlpha = 1
X_campaign.campaignDescription = _T("CAMPAIGN_X_DESCRIPTION", "Your Campaign Description Here")

X_campaign.levelList = {
"LVL1",
"LVL2"
}

X_campaign.firstLevel = "LVL1"
X_campaign.endMessage = _T("CAMPAIGN_X_THE_END", "Your Campaign End Message Here")
X_campaign.campaignStartText = {
_T("x_start_text_1", "Created By:"),
_T("x_start_text_2", "Me lmfao")

}

function X_campaign:setupNewGameDescbox(dbox, wrapW)
X_campaign.baseClass.setupNewGameDescbox(self, dbox, wrapW)
dbox:addText(_T("X_CAMPAIGN_DESC", "Your Campaign Description Here"), "pixellari18", game.UI_COLORS.GENERIC_TEXT_COLOR, 0, wrapW)
end

game.registerCampaign(X_campaign, "base")



So! As you can see there is a LOT going on here, but I will explain it part by part.
However, copy and paste this, rename each X or x with your campaign name (mine is called intralove so you will see in the next image), rename all campaign descriptions or whatever and the LVL 1 or 2, replace them with your LVL IDs.
Anyways, After you did that, it should look like this:


PLEASE REVIEW THE CODE CAREFULLY OTHERWISE SOMETHING MAY NOT WORK!



Explaining each part of this code


Starting off, top to bottom, we have this line:

As you can read, this is for the narration, or after level cutscenes with those big images. In the code you can see its same as the ones we use when we wanna register other .lua files in our main.lua. For now don't create any new lua files, we will do that in the next step (Cutscenes) anyways

Next, on line 20 we have this:


Basically, if you pay attention when you read, you can see that this sets us a Level narration on level finish. The first ILs are the map IDs, whilst the second ones in quotation marks are the IDs of the narrations.
For simplicity I suggest you to name these 2 same like me.

Next, this line:


It is just narration sequences, so like if you have multiple narrations on a single level (start and end i presume) you will need separate IDs for the same level. But I am ♥♥♥♥ at explaining so I wont write about it yet because end cutscenes/narrations are enough for now.

Next, this line:


This is the list of our levels, please put them in in a proper order.

Next, this line:


So.. First level is self explanatory. Then we have the end message, but also we have the start text. If that is confusing you, it basically displays this bigass text when you start the campaign (not the level):



OH! AND DONT FORGET TO SET THE PROPER ID FOR THE HEADER IMAGE (the one u got in .txt)!



After all of that is done, we are finished with our campaign.lua file!

DO NOT BOOT UP THE GAME UNTIL YOU FINISH THE NEXT STEP!
Level End/Start Cutscenes



SO! Right after the last step, create a [campaignname]_narration.lua file.
Something as same as the campaign.lua but just add the _narration part.

After you have made it, quickly go to your main.lua file and add this code:

require("[campaignname]_narration")

It should look something like this:


After that is done, open the campaign_narration.lua and add in the following code:


local narration = {}
local hideoutColor = color(15, 20, 27, 255)
local endingColor = color(33, 38, 38, 255)

narration.LVLID = {
{
backgroundColor = nil,
text = nil,
background = nil,
text = _T("LEVEL_CUTSCENE", "Text you want displayed during the cutscene"),
backgroundColor = hideoutColor,
background = {
{
align = nil,
scale = 2,
quad = "cutscene_image",
depth = 0,
align = gui.SIDES.BOTTOM
}
}
}
}

return narration


It should look like this:



Please note how there is a cutscene_image in the code, and we didnt set one up?
This is ofcourse, optional. If you don't want any cutscene parts in your level, delete the code from line 5 to 22.
IF YOU DO DELETE IT, this is how it should look:


IF YOU DIDN'T DELETE IT, you can move on!

CUTSCENE ART



So! that "cutscene_image" has to be made.

Cutscene images have the size 640x360 !!

After making the image, move it to the textures folder.
Then add a text document thats named same as the image is and paste the following line in it:

cutscene_image = 0 0 640 360

It should look like this:


And lets not forget it has to be in the main.lua as well! So open up your main.lua file and paste the following line in it:

spritesheetParser:parse("textures/cutscene_image")


After all of that, it should work!

Adding More Than One End Level Cutscene


You can simply copy lines 5 to 22 of the code and replace the LVLID with the level ID of the level you want this new cutscene to play on. Make sure the level is added to the campaign lua file too (check out the previous section of the guide if you don't know how to do that).

If you have more than one cutscene artwork you want to use for your levels, we need to put them all on the same sprite sheet. Simply take your 640 x 360 image and put it on a larger canvas, and make as many more 640 x 360 as you need, and parse them correctly (see A Briefing on Spritesheets and Parsing). Then, in the "quad =" line from before, enter the name of whatever you parsed the sprite as.

My four cutscene artworks I made for the GNN campaign.

How I parsed my four cutscene artworks.

The code for my cutscenes, and the text file for reference.

You're probably sick of hearing it by now, but just in case you forgot, make sure to register the sprite in your main.lua

IF IT CRASHES DOUBLE CHECK EVERYTHING
IF IT DOESN'T WORK, AGAIN, DOUBLE CHECK EVERYTHING!!!
because it should all be functional.
Custom Weapons - Code


SO! In this part of the guide, we will cover the basics of making your own gun!

To start, add in the following code to your main.lua file

local addWeapons = {
{weapons.TYPES.PRIMARY, "YOUR_WEAPON_ID", "VANILLA_WEAPON_ID"}
}

local baseCampaign = game.getCampaignData("base")

for key, data in ipairs(addWeapons) do
baseCampaign:addLoadoutWeapon(data[1], data[2], data[3])
baseCampaign:addLockedWeapon(data[2])
end


Example Image




Now.. copy the code of an already existing gun whose properties are similar to what you are aiming for and paste them in your own weapon.lua file that you are making.



To have everything ready for our weapon mod, I suggest you to have a layout similar to this one in the image below.

And if you don't know what variables use which values, look at the steps labeled "Weapon Value Code"

Write all of your values and variables under:

local id_of_ur_gun = {
}






Also, if you are struggling on what to copy for your weapon, look at the image below. You will want to use the stuff that I have not crossed out.




At the end of your weapon.lua code, add the following line as shown in the image:

weapons:register(your weapon variable, "vanilla weapon variable")





Now if you don't plan on implementing custom textures/sounds, your mod should work! But if you do, look at the next 2 steps of this guide.
Custom Weapons - Texture


MAKING A CUSTOM WEAPON UI SPRITE



So.. this part is the fun artist part (imo atleast).

You can use whatever size you want up to 100x37 (as far as I tested). But if you plan on doing mods too I suggest you to make the sprite in the middle of a bigger sheet!

Here are all the pixel sizes of each weapon if you want examples:

Weapon
HUD Pixel Size (Lenght/Width)
ak107
81 27
ak47
81 27
an94
87 25
ar15
68 23
asval
75 19
auto5
74 15
deagle
40 25
evo3
54 22
famas
71 27
g36c
64 23
glock18
30 24
glock20
31 24
hk417
73 19
hs2000
30 22
m1014
85 19
mk23
31 21
mk4
36 23
mp5
54 22
mp5sd
60 22
mp7
44 25
multilauncher
85 19
muzi
42 22
p320
30 21
p89
30 22
r700
85 13
r870
85 16
saiga12k
77 24
sawnoff
54 17
sf19
29 22
sw457s
30 23
tranq_rifle
85 16
ump40
56 27
mac10
63 30
vp9
46 19



Now, In the image below I explain each step of my personal preference on how to make weapon sprites the easiest and fastest, as I am not a picasso with pixel art.



IF THE IMAGE DOES NOT LOAD, THESE ARE THE STEPS TO SPRITE MAKING

1. Make a square for size reference of your sprite (90x35 for example).
2. Import and downscale the weapon of choice so it fits in the made square.
3. Trace the edges.
4. Open that same image you traced in another window and add the notable details into the sprite until you have a finished sprite.
5. Copy that sprite onto another layer 2 times and recolour them as you need 3 variants.


After you have done all of that, transport the image to your "textures" folder, name it and make a .txt file that we will use to parse the images.

Like so;



Also, In the text file input the following text:

weapon_active =
weapon_idle =
weapon_inactive =


This is how, for an example, mine looks.




Also this is which colour is which status:

Blue - Idle
Red - Inactive/hover
Yellow - Active

DO NOT FORGET TO ADD THE SPRITE SHEET PARSER IN THE MAIN LUA FILE OR IT WONT WORK!

spritesheetParser:parse("textures/")

Once you have finished this regarding textures, go to your weapon lua file and make sure to match the following code with the image names we used in the parser. Like in the image below.



And.. that would be it regarding the HUD textures of the weapon!
Custom Weapons - Audio


AUDIO FOR THE WEAPONS



So, the last step is the audio. Theres quite a lot of audios you could need for a weapon, such as reload, shoot, weapon drop...

But all you need to know here is how to import custom audio into the game and then write it in the weapons lua file where the audio goes!


First, we open up audacity, import any file we want to use and transform it to .ogg file.


Second, we open up our "sounds.lua" file, or whatever your audio related file is called, and write down the following code:


sounds.registerReloadSound("soundID", {
"soundfile.ogg"
})


This is how it looks as an example





Thirdly and lastly, you can now write that same sound ID you wrote in the sound registry in your weapons lua file wherever there is a sound required. For example:

Weapon Value Code - Explanation


This section here will display all weapon values and what each value means and how it acts!

What I suggest you to do is write down the following code:


local yourWeaponHere = {
}



And in the middle, press space 2 times and copy paste ALL of the code below, then delete the -- "description" lines and write down your own values! (Big thanks to spy for making this for IV1 because it saved me so much time)



id = -- ID of the weapon you will need, make it unique
price = -- Price of the weapon
name = -- Name that will be displayed when using the weapon or in the loadout
trivia = -- Extra information that shows up in the loadout while hovering over it
suspicious = -- Will the enemies attack you/NPCs find the weapon suspicious?
concealable= -- Is the weapon concealed while its not being used?
magSize = -- the magazine capacity of this weapon
maxAmmo = -- the increase of ammo limit when this weapon is equipped
meleeDamage = -- The damage the weapon does when you melee with it
damage = -- maximum damage the weapon can deal in it's effective range
damageMin = -- minimum damage the weapon can deal at maximum damage fall-off distance
rangeMin = -- start losing damage past this range
rangeMax = -- reach damageMin at this range
rateOfFire = -- how many rounds can we fire per second?
ammoType = -- use our newly-registered 9x39MM caliber
magType = -- the magazine type to use for the magazine system
ammoOnGive = -- how much ammo to give to the player when we pick this weapon in the loadout menu?
fireSound = -- the sound to use for firing the weapon
slowdown = -- how much movement speed slowdown do we experience by equipping this weapon?
bulletColor = -- what's the color of a bullet that's fired from this weapon?
NPC = -- whether this weapon is usable by NPCs
suppressed = -- mark as suppressed (for UI reasons)
noiseTravelOverride = -- Overrides the default shot noise if a value is given, for example, if supressed override, then it will by default fire supressed, like the asVal
armorPenetration = -- what is the armor penetration value of this weapon?
spread = -- what is the base weapon spread for this weapon?
focusSpread = -- how much extra weapon spread can we get rid of by standing still?
spreadDelay = -- how much time it takes for the spread to begin dropping after firing
spreadPerShot = -- how much spread is added per every shot
spreadPerShotMultiplier = -- multiplier used for certain things when calculating the increase in spread from each shot
spreadVelocityIncrease = -- divide velocity length by this much to get the increase in spread from movement
spreadVelocityMax = -- maximum spread increase from velocity, in degrees
maxSpreadIncrease = -- maximum extra spread from non-stop shooting
spreadDecrease = -- in degrees per second
vibration = -- how much controller vibration to apply when firing this weapon?
emptySound = -- the sound to play when attempting to fire an empty weapon
deploySound = -- the sound sequence to play when we equip this weapon
deployFoley = -- the foley sound to play when we equip this weapon
noiseRadius = -- the radius in game units (where 1 is 1 pixel, 1 tile is 16 pixels!) of noise propagated by a single shot
bulletSpeed = -- the speed at which the bullet flies, in feet per second
worldScale = -- scale of the weapon sprite when dropped to the floor, where 1st number is the X axis, and the second is the Y axis
type = -- what type of weapon is this? available types: PRIMARY, SECONDARY, GADGET
worldSprite = -- what sprite do we use for this weapon when it's dropped on the floor?
casingVelocity = -- at what speed do ejected shell casings come out of the weapon with each shot?
casingConfig = -- which casing sprite config should we use for this weapon? available:
magDropAngOffset = -- the angle at which the magazine is dropped to the floor when reloading
magDropForce = -- the force range at which the mag is dropped to the ground when reloading; greater = faster-moving magazine
magazinePrefabID = --
firemodes = -- start on the full-auto firemode
dropSound = -- Sound the weapon emits on drop
magOutEmptySound = -- Sound the weapon emits when the mag is thrown out (empty)
magOutSound = -- Sound the weapon emits when the mag is thrown out
magInSound = -- Sound the weapon emits when the mag is in
boltPullSound = -- Sound the weapon emits when the bolt is pulled
boltReleaseSound = -- Sound the weapon emits when the bolt is released
meleeImpactSound = -- Sound the weapon emits when you melee with it
pickupSound = -- Sound the weapon emits on pickup
lookSpreadReduceSpeed = -- the value at which the look spread will restore in 1 second
lookSpreadRestoreSpeed = -- the speed at which the look spread restores per each frame
lookSpreadAngleMultiplier = -- multiply angle difference from mouse movement by this much
lookSpreadMax = -- the maximum increase of the look spread in degrees
uiIcon = -- UI icon of the AS VAL when it is active (selected)
uiIconInactive = -- UI icon of the AS VAL when our mouse hovers over an element
uiIconIdle = -- UI icon of the AS VAL when it is inactive (not selected, not hovered)
muzzleTime = -- the amount of time the muzzleflash will be visible for
muzzleFlashShadows = { -- the muzzle flash light emitter setup for the weapon; remove this entire section if you wish to have default behavior
fov = -- FOV of the light caster
range = -- range in game units (1 = 1 pixel)
resolution = -- resolution of the muzzle flash caster, must be double of 'range'
height = -- height of the light caster
duration = -- duration of the muzzle flash light in seconds
visibility = -- firing this weapon will set our visibility to 70%
color = color(255, 156, 50, 255)
}
weapons:register("", "") -- Registers the wepon into the game, first quote marks is the weapon id youre registering, second quote marks are the id of the weapon that will be "foundation code" for your weapon, basically any unwritten value will use the foundations weapons value. Foundation weapon isnt necessary if you have all the right and necesarry values, but use it just in case
Weapon Value Code - Which Variables Use Which Value


All values that don't use one number or true/false written in italic (crooked) is the values you have to change, if the value is short I also underline it so you don't miss it!.

Code that has these longer phrases has ID that can be found in other weapon lua files, so if what you're looking for isn't exampled here, open up the google drive with decompiled weapons back on the weapon section!

id = "ID"

name = _T("WEAPON_NAME_NAME", "Name")

trivia = _T("WEAPON_NAME_NAME_TRIVIA", "YOUR DESCRIPTION HERE")

ammoType = "ammo id" some examples: 7.62x39MM, 5.45x39MM, 9x39MM, 50AE, flare, 10MMAUTO...

magType = weapons.registeredMagTypesMap.id - some examples: ASVAL, AK545, AK...

fireSound = "sound id" For example look at weapon section - audio part

slowdown = weapons.SLOWDOWN_CHANGE.id - some examples: LARGE_RIFLE, SMALL_RIFLE, PISTOL...

noiseTravelOverride = noise.TRAVEL_GUNFIRE_id - Some examples: SUPPRESSED_STRONG, NOPOWDER...

worldScale = {
1,
1
}

type = weapons.TYPES.id - PRIMARY, SECONDARY, GADGET are available IDs

category = weapons.CATEGORIES.id - HANDGUN, SMG, RIFLE, SHOTGUN, LETHAL, NONLETHAL, MISC are available IDs

worldSprite = "sprite id" - If you want an already existing sprite go to map editor and you will see all the objects have their name id!

casingConfig = "casing id" - Example: 762, 45acp, 9x19...

magDropAngOffset = {
number,
number
}

magazinePrefabID = "id" - Example: mag_mp5, mag_ak...

firemodes = {
"id",
"id",
"id"....
} - Available firemodes: fullauto, semiauto, burst2, burst3, boltaction
Custom Ammo


CUSTOM AMMO


So! Now we will cover how to make custom ammo for a weapon or a mod (or both).


First off, we need to make a .lua file for our ammo type! So lets make a file called ammo.lua

And of course, let's register it into our main.lua file with the following line:

require ("ammo")


Now. Open up your ammo.lua file you just made and write in the following line:


ammo.register({
id = "ammo_id",
power = 1,
icon = "icon_id",
iconInactive = "icon_id_inactive",
minimum = 10,
limitID = ammo.makeLimitID(),
display = _T("AMMO_AMMO_ID", "Ammo display text"),
trivia = _T("AMMO_AMMO_ID_TRIVIA", "Ammo description"),
world = {
box = "ammo_box_world_sprite",
pile = "ammo_world_sprite"

}
})

Example image below



This is it! Pretty straight forward and easy.. except the textures XD

The power level indicates (the offset i think??) the position on where the bullet will be once youre picking the weapon. In the napalm mod it is 1 so you will see in the last example image how it looks.

For the textures I suggest you to include the icon ones in the weapon ui sprite image, the box and pile ones in your world objects image OR if its just a weapon mod, the image where you have your world gun object image.

This is the icon sprite I was using. Colours don't matter THAT much if its custom ammo such as napalm or... or maybe arrows..


And this is the world sprite I used. Bad quality image, I know xD



Anyways. In the end this is how it looks.



And that is it! To apply ammo to your weapon just go to the ammoType section in your weapon.lua and change the id to the same one you just used for your own custom ammo!
Attachments



SO! In this part we will cover on how to add your custom attachments to weapons!

First off.. lets make our own weapon_attachments.lua file! In it we can do all attachments for all of our weapons!

Don't forget to register it into the main.lua file via the line:

require ("weapon_attachments")


After we did that, we can start with our attachments.



CUSTOM ATTACHMENT CATEGORY



So, If you wanna make a custom attachment category because the current existing ones don't fit your needs, continue reading.. If not, please scroll down to the next part (big blue letters).


Example of custom attachment categories:



So, to do this, add the following line into your weapon_attachments.lua :

weapons.addModHeader("category_id", _T("WEAPON_MOD_HEADER_CATEGORY_ID", "Category Display Text"), "weapon_mod_magazine", "weapon_mod_category_magazine_active", "weapon_mod_category_magazine_idle", "weapon_mod_category_magazine_hover")



(The image is very thin so please click on it to see the example image, right above this text)

The first "quote mark" where it says weapon mod magazine is what sound to emit on category click, and the other 3 are the UI sprite on active, idle and inactive/hover, as seen on the custom weapon - texture section.

You can also leave the sprites for the category and make a new one that only changes the name.

These are the existing category IDs by the way!




CUSTOM ATTACHMENTS



Now, we will cover how to make your own attachments!

Open up your weapon_attachments.lua file and write the following lines:


local attachment_id = {
id = "attachment_id",
name = _T("WEAPON_MOD_ATTACHMENT_ID_UPGRADE", "Attachment display text"),
universalUnlock = false,
price = 000,
description = {
{
_T("WEAPON_MOD_ATTACHMENT_ID_UPGRADE_DESC1", "Attachment description text"),
true
}
}
}

Example image below



This part adds the attachment into the game, but other than that it is purely "decorational" and without assigning it values the game will crash, so... lets assign it some values next!
We can do that easily by writing the following line:




attachment_id.valueMods = {
{
"weapon variable",
value to add/remove
},
}

Example image below



So! This part is a bit complicated and not at the same time XD. All that matters is that you pay attention now.

Some weapon variables are not displayed when the change is done, such as reloadDuration change or rateOfFire (ROF is but you need to look at the weapons ROF in the loadout after applying the attachment that modifies its value). So don't panic if you don't see a display text for the change.

Some weapon variables will require of you to add a "+" as seen in the image above thats giving the example of the code! And also because a "-" does not work for a reason please just put in a - on to the number you are writing down, because if you know basic maths, a "+ negative number" works just the same as a minus.

MOST weapon variables don't use a solid number variable equation but instead a percentage! You will notice this once you edit a value and instead of the desired change its like an absurdly high/low one! So keep an eye out on that too!



And one more final line of code our weapon_attachments.lua will need is the following one:


weapons.registerWeaponMod(attachment_id, "base_weapon_mod")

Example image below



ADDING THE ATTACHMENTS TO THE DESIRED WEAPON



So! Our attachments are in the game now! But.. they don't show up for out weapons?? That is because we did not specify in our weapon.lua file that we want that gun/item to use our attachment!

We can do that by adding the following line in our weapon lua file:


selectableMods = {
{
header = weapons.getModHeader("attachment_category_id"),
mods = {
"attachment_id",
"attachment_id"
}
}
}

Example image below



You can add as many categories as you want, apply as many mods to a category as you want.

Just do not forget the commas at the end! Look at the example image if you do not know what i am talking about. (comma = ,)



Next up our weapon needs the textures for the attachment! Otherwise the game will crash as soon as you pick the attachment. So please write in the following code, this time outside of your "local weapon_id =" line of code:


weapon_id.modIcons = {
attachment_id = {
hover = "attachment_sprite_hover",
idle = "attachment_sprite_idle",
active = "attachment_sprite_active",
align = gui.SIDES.RIGHT
}
}

Example image below



Now.. with attachment sprites the colour palette works differently, and based on what I saw from other people who used the wrong colours, it can completely ♥♥♥♥ up your attachments textures when you apply them.

Since we did everything regarding the code for applying the attachments, lets now move onto the texture part!
Attachments - Sprite Textures



ATTACHMENT SPRITE TEXTURES


So! Right now we will cover the how the attachment sprites should work.



In this image we have the sprite of my minigun alongside the "tri-barrel" attachment sprite.

Attachment Sprite 1 - Active
Attachment Sprite 2 - Idle
Attachment Sprite 3 - Hover or Inactive

When making your attachment sprites I suggest you to copy the base weapon blue texture onto a separate layer and then use the lighter blue shade to draw your attachment for the weapon, and then copy paste the attachment sprite 2 times, once for the green "idle" sprite and once for the "hover/inactive" sprite. Just like in the example image above!

WARNING

When making attachment sprites, MAKE SURE ATTACHMENTS DO NOT OVERWRITE / DRAW OVER EACH OTHER BECAUSE APPLYING THE 2 THAT DO WILL ♥♥♥♥ IT UP IN THE GAME!!!


Anyways, in the previous section (Attachments) we had that texture code..



Specifically this one in the image above.

What I need you to note is that the image id it uses is, just like for the portraits, and objectives... the parsing ID

This is what I mean when I say parsing ID:


So please, parse your textures correctly and apply the correct IDs! If you notice that on click the gun texture goes left right up or down OR even becomes messed up... The issue is either parsing or the wrong colours.


Thats it! Hope this helped you everyone <3
Uploading the Mod
UPLOADING THE MOD



SO! After you are done and are ready to upload the content you've made, be it just custom music, just portraits, or a bundle of portraits and music, or even a full campaign, you need to transfer your mod file to "mods_staging"

(As of writing this, the game doesn't include that folder, but its the same as the mods folder basically, make it and it will work.)
This folder right here:



After you did that, open up the game, go to "Modding", then "Steam Workshop" and "Publish New".
If you've placed the mod folder into the proper folder, mods staging, then you will see the mod pop up.


Onwards you will have a detail list that you will have to fill out, it looks like this:


After doing that and pressing publish, you now have your own workshop mod!
The mod however will have to be set on public as it is privated by default.

END
So, this is the end of this guide for now, but do not worry, I will update it over time when i find out how other stuff works (if someone doesnt make a guide for those things, like custom weapons and such)
Super secret bonus - bloopers
Here are some screenshots made with the test files we made whilst writing down each part into the guide xD


Gideon VS The Evil Omionus Trollface



The Infinity Pistol completely breaking after applying a smaller mag (Inf pistol has 1 infinite bullet, and the smaller mag modifies the ammo to 4 bullets less lol)


Bishop Bell!
14 Comments
Mambo!  [author] 24 Jun @ 11:26pm 
I bet it's possible just requires far better code knowledge, my suggestion is to check the iv1 grenade launcher mod for some info. What you want to mod in however is the change of nature once the mod is applied and then that it changes back once removed and that's the tricky part, but I believe it's possible
Kyan 24 Jun @ 7:19pm 
I'm guessing this is a no, but I was wondering if there is a way to implement something like an M203 underbarrel grenade-launcher or a Masterkey underbarrel shotgun as an attachment or an entirely unique weapon?

I'm pretty bad at the stealth, and like the intensity of going in guns-blazing anyway, but I'd like a bit more bang in my loadout without resorting to the often-useless Claymore mines, happy to have a shot at making it myself so long as I know it's possible to do.

Thanks!
Mambo!  [author] 26 May @ 5:43am 
Word of advice, dont edit exisiting weapons, its complicated for no reason XD
Osama Bin Lopez 20 May @ 5:41pm 
Uh, I might be missing something but how do you edit existing weapons? I just simply wanna change the max ammo.
Mambo!  [author] 28 Apr @ 11:46am 
No problem man! im glad it managed to help you even in the slightest way, and I hope this "cheat sheet" helps you even more :DD
Zewce 28 Apr @ 10:40am 
Thank you for making this little guide as it's way easier to hop in and start changing stuff around than to try to figure it out from scratch. I just needed to add some names to the map maker, but now I've devolved into needing to make new crawler enemy types, and this will make the process... at least less painful. Good stuff man.
Mambo!  [author] 25 Apr @ 8:13pm 
If youre on the intravenous discord server send me a friend request, im Trickyy cluckk ill send u all files u need.

If not, its fine, ill see to upload said content here on the guide
MrUglyFace 25 Apr @ 4:06pm 
has someone ripped, or is there a way to rip the texture sheets for this game? I want to make custom objects but I'd really like if I had the textures for reference as color palettes and just general artstyle direction
0x41414141 21 Apr @ 12:53am 
yeah I found a few, /marsinator358/luajit-decompiler-v2/ was what worked
Mambo!  [author] 21 Apr @ 12:50am 
Just look up lua decompiler on google