the inner workings of UNDERTALE

recently, i've been working on a few undertale fangames, which means i've had to give really far into the code of undertale to understand how it works. i thought that since i've spent so much time figuring it out (and my that i mean just looking at code), i might as well share what i found?!

most of what i found was in the battle system, so unless i say otherwise, assume it's from there.

also, i will be using the demo version of undertale for this. the calculations are the same, but the decompilation is more accurate.

(finally, just to keep this in mind, global.mytarget is usually the monster that you're trying to attack.)



attack/damage

when attacking an enemy, and you hit the line (known internally as obj_targetchoice), the game runs this calculation:

defarg = global.monsterdef[global.mytarget]

global.pwr = global.wstrength + global.at

damage = global.pwr - defarg

this code is kept in the script scr_attackcalc. once that is done, the damage variable is returned to the targetchoice object, and it then runs this code to finalize the damage dealt:


global.damage = damage

global.damage += random(2)

myx = (x + (sprite_width/2))

myperfectx = (obj_target.x + (obj_target.sprite_width/2))

bonusfactor = abs(myx - myperfectx)

if bonusfactor = 0 then bonusfactor = 1

global.stretch = ((obj_target.sprite_width - bonusfactor)/obj_target.sprite_width)

if bonusfactor <= 12 then

{

global.damage = round(global.damage*2.2)

}

if bonusfactor > 12 then

{

global.damage = round(global.damage*global.stretch*2)

}


this is a lot, so i'll break it down a bit.

global.damage = damage

global.damage += random(2)

this takes the damage calculation from scr_attackcalc, and randomizes it a little bit.

myx = (x + (sprite_width/2))

myx is the middle of the targetchoice. in gamemaker (the engine used for undertale) the x and y positions of an object are at the top left by default. so, this line makes sure that all calculations are done from the center of the object.

myperfectx = (obj_target.x + (obj_target.sprite_width/2))

this line basically does the same as the last line, but for the target itself, not the moving slice of it.

bonusfactor = abs(myx - myperfectx)

if bonusfactor = 0 then bonusfactor = 1

this is the distance between the center of the slice and the target. abs is a function in gamemaker to make any number positive, so this number will never be negative. the second line just makes sure it isn't 0. if it was 0, then the damage you deal would always be 0. we'll see why later.

global.stretch = ((obj_target.sprite_width - bonusfactor)/obj_target.sprite_width)

this line is pretty interesting. it's basically an accuracy percentage. think about it like this:
you hit the slice straight in the middle of the target, which means bonusfactor will be 1. now, the game will calculate:
((obj_target.sprite_width - 1)/obj_target.sprite_width)
which calculates to about (1/1), so it would be 1. if you didnt hit it perfectly, then maybe it would be something like 0.7.

pretty interesting!

if bonusfactor <= 12 then

{

global.damage = round(global.damage*2.2)

}

this is the reason behind the bonusfactor name!! this is literally giving you a bonus for hitting the target.

you have about 12px of wiggle room, but seeing as the slice moves at about 11-13 pixels per second, this makes sense.

so, if you do get the bonus factor, it takes your damage and multiplies it by 2.2.

if bonusfactor > 12 then

{

global.damage = round(global.damage*global.stretch*2)

}

this is if you don't get the bonus. if that doesn't happen, it takes your damage, multiplies it by the percentage you were off (told you it would be a percentage later!), and then multiples that by 2.


when i started working on replicating undertale's battle system, this was all so compilcated. but, the more you look at it, it makes you really appreciate the work and determination that went into this game.

it also makes you realize that math can be really important when it comes to programming!

(doesn't change the fact that i flunked out of math, but whatever!!)