Nuclear Throne

Nuclear Throne

72 ratings
Basic Modding Guide For Update 19
By 9joao6
Let's say you had this crazy idea for a weapon, character, mutation, or even area, but you know for sure that the devs will never add it. One thing is adding a Super Bazooka, another thing is adding a Quad-Gatling Ultra Toxic Grenade Launcher. Well, that doesn't mean you can't experience it! Why not add it yourself?
   
Award
Favorite
Favorited
Unfavorite
Foreword
Ever since the release of Nuclear Throne Together's second major update, which lets you create script-based GML mods, this guide has become mostly irrelevant. Most u19 mods will be ignored by everyone nowadays, as NTT is just that much better.

Get Nuclear Throne Together and make mods for it instead. Trust me on this one.
Requirements

This guide was built under the assumption that you have basic GameMaker and GML (GameMaker Language) knowledge. Examples provided are for educational purposes and only provide what you need to understand to make your own assets. I will not be teaching you how to code.

This guide is aimed for Update 19 of Nuclear Throne. Therefore, you will need:
  • GameMaker 8.1 Standard
  • The 'mods' branch of Nuclear Throne on Steam, installed and ready to go
  • A copy of the decompiled build of Update 19 (download here (updated 9/24/2016)[my.mixtape.moe])
    You should place that file inside the Nuclear Throne folder. It shouldn't be deleted if you switch from or to the 'mods' branch, but it's recommended that you back it up occasionally.

If you want the font that Vlambeer uses, download Press Start[www.dafont.com] and replace the one that comes with the decompiled build with Press Start.
Weapons
  • Sprites: add a new sprite for your weapon.

    You don't have to, but having 7 frames (for the glow effect) helps for finding the weapon in the ground.

  • Scripts > scrWeapons: copy the code from LINE 12 to LINE 20...
    wep_name[1] = "REVOLVER" wep_type[1] = 1 wep_auto[1] = 0 wep_load[1] = 6 wep_cost[1] = 1 wep_sprt[1] = sprRevolver wep_area[1] = -1 wep_text[1] = "trusty old revolver" wep_swap[1] = sndSwapPistol
    ...and paste it under LINE 605.
    wep_swap[59] = sndSwapEnergy
    Change everything in that block, including the array ID, to your weapon's stats.
    // EXAMPLE WEAPON wep_name[60] = "ULTRA PLASMA CANNON" // name wep_type[60] = 5// 0 = melee, 1 = bullets, 2 = shells, 3 = bolts, 4 = explosives, 5 = energy wep_auto[60] = 1// automatic, true/false wep_load[60] = 1 // reload time wep_cost[60] = 1 // ammo per shot wep_sprt[60] = sprUltraPlasmaCannon // sprite wep_area[60] = 4 // area it can start dropping in, -1 = never drops (revolver) wep_text[60] = "'i couldn't help it' -rami ismail" // tip wep_swap[60] = sndSwapEnergy // swap sound
    Add 1 to the maxwep variable near the bottom of the script. It should match your new weapon's ID.
    maxwep = 60
  • Scripts > scrFire: copy the code from LINE 13 to LINE 30...
    //REVOLVER if wep = 1 { snd_play(sndPistol) with instance_create(x,y,Shell) motion_add(point_direction(x,y,mouse_x,mouse_y)+other.right*100+random(50)-25,2+random(2)) with instance_create(x,y,Bullet1) {motion_add(point_direction(x,y,mouse_x,mouse_y)+(random(8)-4)*other.accuracy,16) image_angle = direction team = other.team} BackCont.viewx2 += lengthdir_x(6,point_direction(x,y,mouse_x,mouse_y)+180)*UberCont.opt_shake BackCont.viewy2 += lengthdir_y(6,point_direction(x,y,mouse_x,mouse_y)+180)*UberCont.opt_shake BackCont.shake += 4 wkick = 2 }
    ...and paste it under LINE 1260. Change the code to fit what your weapon fires, but keep the following in mind:
    Projectiles' "team" variable is what tells the game to shoot something like an enemy bullet fired from the player's weapon, while hurting enemies instead. If the team is set to 0, it hurts everything. If it's 1 it only hurts players, and if it's 2 it only hurts enemies.
Mutations
  • Sprites > sprSkillIcon: add a new subimage for your mutation's portrait.

  • Scripts > scrSkills: copy the code from LINE 6 to LINE 9...
    skill_name[1] = "RHINO SKIN" skill_text[1] = "+4 MAX HP" skill_msnd[1] = sndMutRhinoSkin skill_tips[1] = "thick skin"
    ...and paste it under LINE 116.
    skill_tips[22] = "shaking"
    Change the stats and array ID to match your mutation.
    // EXAMPLE skill_name[23] = "BRRAAAAAAAAP" // name skill_text[23] = "75% more bullet per bullet" // description skill_msnd[23] = sndMutRhinoSkin // sound when picking it skill_tips[23] = "fish johnson here" // tip

The way your mutation works can vary wildly, from being a speed increase that only needs to be set once to something that happens every step, so I won't go further than this. However, if you want to make it a one-time change, editing the Step event of the SkillIcon object is the best way to go.
Characters
I will not go into adding passive or active abilities, those are yours to choose how to code.

  • Objects > UberCont > Create > Execute a piece of code: increase LINE 4's variable by one everytime you add a character.
    version = 380
    This will avoid a one-time crash at the start of the game.
  • Scripts > scrRaces: Copy the existing code from LINE 8 to LINE 14...
    race_name[1] = "[FISH]" race_pass[1] = "GETS MORE AMMO" race_acti[1] = "DODGE ROLL" race_butt[1] = "WATER BOOST" race_lock[1] = "UNLOCKED FROM THE START" race_have[1] = 1 race_swep[1] = 1
    ...and paste it under LINE 93.
    race_have[13] = 0
    Increase the array ID to be one more than before. Modify the rest to match your new character.
    // EXAMPLE race_name[11] = "[BLONIC]" // name race_pass[11] = "IS MORE ORIGINAL" // passive description race_acti[11] = "SPINBOL" // active description race_butt[11] = "WATER BOOST" // throne butt description race_lock[11] = "UNLOCKED FROM THE START" // unlock method race_have[11] = 1 // whether you have it when you start a new save or not, true/false race_swep[11] = 1 // starting weapon ID
    If race_have[your character's ID] is later set to 0 on the same script, delete it.
    race_have[11] = 0
    Change racemax at the end of the script to one number above, which should match your character's ID.
    racemax = 11
  • Create sprites for all these states:
    (the following sprite names DO matter // the ID should be your character's ID)
    sprMutantIDIdle
    sprMutantIDWalk
    sprMutantIDHurt
    sprMutantIDDead
    (the following sprite names do NOT matter)
    sprMutantIDMenu - Menu idle
    sprMutantIDSelect - Menu transition from idle to selected
    sprMutantIDSelected - Menu selected
    sprMutantIDDeselect - Menu transition from selected to idle

  • Objects > Menu > Create > Execute a piece of code: Copy the existing code from LINE 116 to LINE 121...
    if UberCont.cgot[10] = 1{ char[10].sprite_index = sprRebelMenu char[10].spr_menu = sprRebelMenuSelected char[10].spr_to = sprRebelMenuSelect char[10].spr_from = sprRebelMenuDeselect char[10].spr_slct = sprRebelMenu}
    ...and paste it right under that block.
    Change the ID to your character's. Change each sprite name to your character's.
    // EXAMPLE if UberCont.cgot[11] = 1{ // check if this character is unlocked char[11].sprite_index = sprRebelMenu // campfire default sprite char[11].spr_menu = sprRebelMenuSelected // campfire selected sprite char[11].spr_to = sprRebelMenuSelect // campfire idle-to-selected char[11].spr_from = sprRebelMenuDeselect // campfire selected-to-idle char[11].spr_slct = sprRebelMenu} // campfire idle sprite
  • Create sounds for all of the following:
    (names DO matter // the ID should be your character's ID)
    sndMutantIDCnfm - Confirm character selection on the menu
    sndMutantIDSlct - Selected character on the menu
    sndMutantIDDead - Death
    sndMutantIDHurt - Damage taken
    sndMutantIDLowA - Low ammo
    sndMutantIDLowH - Low health
    sndMutantIDCrwn - Picked up the crown on the vault
    sndMutantIDChst - Big weapon chest
    sndMutantIDWrld - Exiting a portal into a new area
    sndMutantIDThrn - ???
    sndMutantIDValt - Exiting a portal into the crown vault
    sndMutantIDSpch - ???
  • Scripts > scrTips: Copy the existing code from LINE 122 to LINE 123...
    if Player.race = 10 tip = choose("forget the old days","change is coming","a new generation","it will get better","spawning new allies heals old ones","allies take damage over time")
    ...and paste it right under those 2 lines.
    Modify the race = ID to your character's. Modify each tip to what you want.
    // EXAMPLE if Player.race = 11 tip = choose("i'm my original character","where's blails","do not steal")
  • Sprites > sprCharSelect: add your character's portrait before the lock icon.
    Not doing this seems to crash the game once you select a locked character, so you SHOULD do it.
  • Sprites > sprExplain: add an example of one of your character's abilities.

The passive and active abilities can vary wildly, but if you want to add code to your passive ability, edit the scrPowers script.
Areas
What I'll be creating:


Here's a reference you should keep in mind if you're using portals to test your new areas:
  • Area:
    1: Desert and Sewers
    3: Scrapyard and Crystal Caves
    5: Frozen City and Labs

  • Subarea:
    0 to 2: Normal levels
    3: Transition levels

Objects > Player > Room End: Add 1 to LINE 20.
if area < 7

Create the following sprites:
(names DO matter // ID is the area)
sprFloorID - floor
sprWallIDBot - bottom wall
sprWallIDTop - top wall
sprFloorIDB - special floor (mud on Desert, ice on Frozen City, cobwebs on Crystal Caves...)
sprFloorIDExplo - remains of a blown-up wall
sprWallIDOut - bits that stick out of the walls, makes them look less flat
sprWallIDTrans - transition from wall to background
sprDebrisID - what flies off of walls when they're blown up


Scripts > scrMakeFloor: Copy the code from LINE 9 to LINE 14...
if area = 1 { if random(2) < 1 {instance_create(x,y,Floor) instance_create(x+32,y,Floor) instance_create(x+32,y+32,Floor) instance_create(x,y+32,Floor) }else instance_create(x,y,Floor)}
...and paste it at the bottom of the script. Change the values to how you want your area to generate.
// EXAMPLE if area = 7 { if random(20) < 1 // random chance of generating an area that looks like an upside-down cross (I was thinking of Spelunky's Hell level when creating this, DON'T JUDGE ME) {instance_create(x,y,Floor) instance_create(x,y-32,Floor) instance_create(x,y-64,Floor) instance_create(x,y+32,Floor) instance_create(x-32,y,Floor) instance_create(x+32,y,Floor) }else instance_create(x,y,Floor)} // if the random chance fails, then just create a single floor tile
Still in the scrMakeFloor script, copy LINE 113 and LINE 114...
if area = 3 trn = choose(0,0,0,0,90,-90)
...and paste it under LINE 124.
trn = choose(0,0,0,90,-90,180)
Change it to how often you want the level generator to turn. 0 is no change, 90 or -90 are left and right respectively, and 180 is a half-360. :)
// EXAMPLE if area = 7 trn = choose(0,0,0,0,0,0,0,0,90) // this will never turn right
Scripts > scrTips: copy the code from LINE 63 to LINE 64...
if Player.area = 6 tip = choose("beep boop","nerds","don't push any buttons")
...and paste it under LINE 64.
Change its contents, along with the area number, to tips that the players can see when entering the level.
if Player.area = 7 tip = choose("hell","getting hot","the floor is lava")
Objects > GenCont > Create Event: copy the code from LINE 22 to LINE 23...
if Player.area = 6 background_color = make_color_rgb(9,28,32)
...and paste it under LINE 23.
Change the RGB value to what you want the background to be. Usually this color matches the color of the transition sprites.
if Player.area = 6 background_color = make_color_rgb(127,39,39)
Scripts > scrPopulate: copy LINE 11...
if Player.area = 6 spawnarea = 6
and paste it under that line.
Change both numbers to your area.
if Player.area = 7 spawnarea = 7
Scripts > scrPopEnemies: copy the code from LINE 1 to LINE 21...
//DESERT if spawnarea = 1 or spawnarea = 0// or spawnarea = 100 { if styleb = 1 { instance_create(x+16,y+16,choose(MaggotSpawn,BigMaggot,BigMaggot,Maggot)) } else { if random(7) < 1 instance_create(x+16,y+16,choose(MaggotSpawn,Scorpion)) else {if random(30) < 1 {instance_create(x+16,y+16,Barrel) instance_create(x+16+random(4)-2,y+16+random(4)-2,Bandit) instance_create(x+16+random(4)-2,y+16+random(4)-2,Bandit) instance_create(x+16+random(4)-2,y+16+random(4)-2,Bandit)} else instance_create(x+16,y+16,choose(Bandit,Bandit,Bandit,Bandit,Bandit,Bandit,Maggot,Scorpion))} } }
and paste it at the bottom of the script.
Change its contents to spawn whatever enemies you want, either pre-existing or custom-made.
//HELL if spawnarea = 7 { if styleb = 1 // if the ground has the sprFloorIDB sprite { instance_create(x+16,y+16,choose(Sniper,Sniper,GatorSmoke,GatorSmoke,Torch)) } else { if random(5) < 1 instance_create(x+16,y+16,choose(Ratking,SuperFrog,Car,SnowBot,Molesarge)) else {if random(30) < 1 {instance_create(x+16,y+16,ToxicBarrel) instance_create(x+16+random(4)-2,y+16+random(4)-2,Molefish) instance_create(x+16+random(4)-2,y+16+random(4)-2,Molefish) instance_create(x+16+random(4)-2,y+16+random(4)-2,SuperFireBaller)} else instance_create(x+16,y+16,choose(Molefish,Molefish,Molefish,Molefish,Molefish,SnowBot,Car))} } }
Scripts > scrMakeFloor: copy the code from LINE 162 to LINE 173...
if area = 1 { if random(19+instance_number(FloorMaker)) > 20 { instance_destroy() if point_distance(x,y,10016,10016) > 48{ instance_create(x+16,y+16,AmmoChest) instance_create(x,y,Floor)} } if random(8) < 1 instance_create(x,y,FloorMaker) }
and paste it at the bottom of the script.
Change its values to how often you want the level generator to spawn a duplicate of itself to speed up the level generation and make things less linear.
// EXAMPLE if area = 7 { if random(19+instance_number(FloorMaker)) > 20 { instance_destroy() if point_distance(x,y,10016,10016) > 48{ instance_create(x+16,y+16,AmmoChest) instance_create(x,y,Floor)} } if random(8) < 1 instance_create(x,y,FloorMaker) }
Objects > MusCont > Room Start: copy the code from LINE 26 to LINE 30...
if area = 0 { song = sound_add("themeA.wav",0,0) amb = sound_add("ambient0.wav",0,0) }
...and paste it under that line.
Change the area number to your area's, and change themeA and ambient0 to your area's music. It can be any .WAV file inside the Nuclear Throne folder, or anywhere inside its hierarchy.
// EXAMPLE if area = 7 { song = sound_add("boss2.wav",0,0) amb = sound_add("ambient3.wav",0,0) }
Credits
BioOnPC - figuring out character and area code
Nommin, Krognol - Proofreading, testing
yyyfa, A_BEAUTIFUL_PONY, BioOnPC - sprites
24gz - decompiled build
129 Comments
kabrok Jul 6, 2017 @ 1:21pm 
Skelton, can u pass me the link, pls?
Thanks!
sajuuk Jun 25, 2017 @ 8:05pm 
i have the latest GM 8.1 on my Google Drive account HMU if anyone wants it :D
THEOmega215 Jun 17, 2017 @ 7:31am 
no unavaliable file
Rat Facing Charges in Court Jan 7, 2017 @ 11:19am 
nvm i'm pretty sure you can't after reading more comments
Rat Facing Charges in Court Jan 7, 2017 @ 11:11am 
am i still able to do this with GM 14.1?
9joao6  [author] Dec 16, 2016 @ 3:31am 
Oh, you mean ripped straight out of the game files. Fair enough, thought you had them named properly.
Radon Dec 15, 2016 @ 8:59pm 
Nope.
Already found it, it was called like something "manynumbers".wav
9joao6  [author] Dec 15, 2016 @ 8:03am 
Why would this guide contain raw sound files...? :frogzone:
You're looking for sndGruntFire.
Radon Dec 15, 2016 @ 7:43am 
Guys...
I've got a little butt-hurted fhile looking for the I.D.P.D. Grunt gun shot sound file (that sound that plays when I.D.P.D. Grunt shoots it's gun). But I didn't find it. I thought that THIS guide will contain just raw files, but... "nt_decompiled.gm81"? Seriously?
I NEED THIS LITTLE SOUNDFILE. Because I am making a mod with this sound... And...
Guys, help. Help, please.
Physics Nov 28, 2016 @ 1:15pm 
other means being?