‹ BackHN Continuity

Thread

You can defeat the Dream Devourer from Chrono Trigger using an int overflow

177 points · 106 comments · ronreiter

  1. PlunderBunny · · focus · HN ↗
    I figured out that, in the early versions of Realmz for the Mac, there was an enchanted item (a helm?) that added a few points to the wearer’s stats when equipped, but when removed it took away more than it added (i.e. left you worse off). It only took a few seconds for my programmer brain to start me furiously equipping and then un-equipping it, and sure enough, when that stat went to -127, one more equip-and-unequip was sufficient to give me a +128 bonus for that stat.

    I never understood how that particular bit of logic could have been coded that way - it seemed so unnatural.

    1. LoganDark · · focus · HN ↗
      I would figure the reason was because calculating it based on your inventory every time it's needed would've been expensive, so the developers decided to cache it, but instead of recalculating it from scratch when there's a stat change, they decided to try updating it incrementally, but I guess also imperatively. A string of mistakes has to happen to lead to such a situation but it doesn't seem super unnatural to me.
      1. Nition · · focus · HN ↗
        I wonder if it was percentage based. That'd be one of the easiest mistakes to make.

        - Player has a stat of 100

        - Wears "+10%" helm

        - Player has a stat of 110

        - Removes helm, game subtracts 10%

        - Player has a stat of 99

        Actually, now that I think about it more, that wouldn't work as described above because once your stat got close to zero you'd enter a kind of Zeno's Paradox situation.

        1. chii · · focus · HN ↗
          it's more likely that the stat for equipping is calculated differently to when unequipping (ala, "same" code in two different places, and one got modified at some point and forgot about the other).
        2. thaumasiotes · · focus · HN ↗
          > I wonder if it was percentage based. That'd be one of the easiest mistakes to make.

          How would that be one of the easiest mistakes to make? There are no operations for working with percentages, nor is it a thing you'd want to do.

          To make that helm work as you describe, you'd need to multiply the stat by 1.1 when equipping the helm and then by 0.9 when deequipping it. But at that point you've lost any justification for making the mistake; those are just two random numbers that don't cancel each other out. It's not like you're adding a number in one place and then erroneously subtracting that same number somewhere else.

          (Also, these are very strange multipliers to use this way, because they can never produce exact results. You're bound to see rounding errors if you insist on update-when-equipping and update-when-deequipping instead of recalculate-stats-when-equipment-changes.)

          1. Nition · · focus · HN ↗
            That exact sort of magic numbering is rife in games from what I've seen over the years, especially in stuff like an old RPG from 1994. Maybe you've seen better game code on average than me. But you don't even really need the "random numbers" - I'd expect a mistake like:

                GetItemChange(curVal, percentBoost) { return curVal * PERCENT_BOOST; }
                
                eqipVal = val + GetItemChange(val, percentBoost);
                // ... later...
                unequipVal = val - GetItemChange(val, percentBoost);
            
            
            Edit: I see there's Realmz source code on GitHub.[1] Although I can't see anything that'd cause the specific bug PlunderBunny mentions (they did say 'early versions' so maybe it was fixed), this is the kind of thing I mean re old games and "random numbers". This is part of the Wear method for equipping items:

                if ((item.sp1 > 59) && (item.sp1 < 100))
                  c[character].condition[item.sp2] = 0; /**** neutralize condition ***/
                if (item.sp1 == 122) /******** item adds attacks **********/
                {
                  c[character].attackbonus += item.sp2;
                }
            
                if ((item.sp1 > 19) && (item.sp1 < 60)) /******* adds condition *****/
                {
                  if (c[character].condition[item.sp1 - 20] > -1)
                    c[character].condition[item.sp1 - 20] = 0;
                  c[character].condition[item.sp1 - 20] += item.sp2; /**** make condition[sp1-20] = sp2 ***/
                }
            
                if (item.sp3) /******* adds special ability *****/
                {
                  if (item.sp3 < 0)
                    c[character].special[abs(item.sp3) - 1] += item.sp5;
                  else if ((item.sp3 < 16) && (item.sp3 > 0))
                    c[character].spec[item.sp3 - 1] += item.sp5;
                  else
                    partycondition[item.sp3 - 30] -= abs(item.sp5);
                }
            
                if (item.sp4) {
                  if (item.sp4 < 0)
                    c[character].special[abs(item.sp4) - 1] += item.sp5;
                  else if ((item.sp4 < 16) && (item.sp4 > 0))
                    c[character].spec[item.sp4 - 1] += item.sp5;
                  else
                    partycondition[item.sp4 - 30] -= abs(item.sp5);
                }
              }
            
            [1] <a href="https:&#x2F;&#x2F;github.com&#x2F;Realmz-Castle&#x2F;realmz" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;Realmz-Castle&#x2F;realmz
        3. Garlef · · focus · HN ↗
          Never mutate the source of truth
          1. Garlef · · focus · HN ↗
            (caveats of course): memory and processing is restricted on the old systems; so these kind of simplifications might be the only way to deliver a big game
Open on Hacker News to reply ↗

Unofficial Hacker News client; not affiliated with Y Combinator.