You had a logic problem in the script, changing nicks would infact defeat the script, as the timer was using the contents of $2 the passed nick, and not the current contents of %warned.1 at the time the timer went off.

This code should work.

Code:
on *:TEXT:!warn *:*:{
  if ($nick == %logged.in.nick) {
    set %warned.1 $2
    set %reason $4-
    TIMERwarner 1 $3 .ban -ku $+ 30 $chan [color:blue]$(%warned.1,)[/color] $4- [color:blue]$(| unset %warned.1,)[/color]
    .msg $chan 4 $2 2is being warned because they're3 %reason 2,they need to say 7sorry 2or they will be kbed for 30 secs in10 $3 seconds...
    .notice %warned.1 2You better say 7sorry 2, or you will be kicked...
  }
}
on *:NICK:{
  if ($nick == %warned.1) {
    set %warned.1 $newnick
    .notice %warned.1 2If that was to get out of this, it didn't work...
  }
}
on *:TEXT:sorry:#:{
  if ($nick == %warned.1) {
    TIMERwarner off
    .msg $chan 4 %warned.1 2says they're sorry for3 %reason
    .notice %warned.1 2 You were warned because you were3 %reason 2, dont do it again...
    .notice %warned.1 2Next time there will be no warning... Just a KB!
     [color:blue]unset %warned.1[/color]
  }
}


The first blue section replaces $2 with the $(%warned.1,) this is so the timer has the variable %warned.1 in there not the contents of $2 since changing nick would bet $2, being that it is the old nick. The $(<text>,) is needed to tell mirc, DONT evaluate <text> (in this case dont get the contents of %warned.1) but just leave it as "%warned.1".

The second blue section adds a second command to the timer "| unset %warned.1" command sepertor and unset command, both are incased in another $(<text>,) to tell mirc this is still part of the timer command, and to not cause %warned.1 to be evaluated, as it might have been

The 3rd blue bit is just unsetting the %warned.1 in the sorry section.