Jump to content

Photo

Utopia Revealed!

utopia reverse engineering intellivision

13 replies to this topic

#1 intvnut OFFLINE  

intvnut

    River Patroller

  • 2,816 posts
  • Location:@R6 (top of stack)

Posted Sun Apr 29, 2012 10:19 PM

Introduction

Ok, so I spent a couple days reverse engineering Utopia. I now know pretty much how it all works, and now you can find out too.

Here's some highlights, in case you don't want to wade through the code:
  • Forts:
    • Do protect a 1 unit radius against rebels
    • Will protect parked ships that have the same owner as the fort. Radius of 1 card.
    • Will protect both players ships when they're under active control. Radius of 1 card.
    • Do not otherwise contribute to your score
  • Bugs:
    • Scoring will overflow, if you have more than 65 crops + fishing boats combined.
    • It will also overflow if you earn more than 255 gold bars in a single turn. (Is that possible?)
    • The "float off bottom of screen" bug is likely due to island/status collision detection interacting poorly with EXEC's boundary detection. The game can "nudge" a boat across the boundary the EXEC checks for. There is a check for this, but it only has been applied properly to the left/right edges.
    • Delay loop in scoring display appear to want to update random number generator, but they don't actually.
  • Code quality:
    • Let's just say it's likely the priority was to have a compelling game, and have it out quickly, as opposed to writing the tightest, cleanest possible code.
  • Miscellaneous:
    • Pirates will never sail toward a parked PT boat on purpose. The "nudge" code, though, can nudge them through a tangle of PT boats.
    • Hurricanes are 5 times more destructive as tropical storms. All three forms of weather water crops at the same rate, though.
    • There's two copies of the "sinking fishing boat" animation in the ROM, due to how the EXEC distinguishes between background cards and MOB animations
    • Each island has precisely 29 squares.
    • When weather takes out something on your island, it will incur 0 to 101 casualties.
If you want more details, you'll have to dig through the code unless it's covered in the slightly more detailed review below. :-) Code attached.

Code Structure

The bulk of the main game code can be put into one of three categories -- The Timer Tic Task, Dispatches, Scoring Logic. The rest of the code is either initialization code, or support code for those three categories.

Timer Tic Task

The bulk of the game logic hangs off the the Timer Tic Task. This task runs 20 times a second, and it handles everything from spawning weather to collision detection to animating certain sinking ships. It's quite a lot of code and is fairly linear. The rough order of execution:
  • Update the weather, possibly creating new weather
  • Update the fish, possibly creating new schools of fish
  • Update the pirates, possibly spawning new pirates
  • Island vs. boat collision detection
  • Update the game clock
  • If not end of round / end of game:
    • Update the status line at bottom of screen
    • Update parked sinking ships
  • At end-of-round, fire off the scoring code and then show the scores
  • At end-of-game, show final score and halt
Dispatches

The next big set of code is the set of Dispatches. The EXEC mostly works by calling various functions in response to various events. Dispatches fall into a few categories:
  • Controller input dispatches. The action-button and disc handlers are pretty simple. The keypad dispatch is amazingly convoluted, but that makes sense when you consider everything that it has to account for.
  • Object vs. object dispatches. These handle pirates vs. PT boats and similar such things.
  • Object vs. land dispatches. This is what handles rain on crops, pirates sinking parked ships, or parked ships going fishing.
Scoring Logic

The final big piece of code is the scoring logic. Scoring happens in four phases: Income Computation, Population Update, Round Score Calculation, Rebellion.
  • Income Computation
  • During the round, every gold bar you earn (say due to fishing, rain on crops, etc.) gets tallied in this round's Gross Domestic Product (Round GDP), separate from your actual gold bar balance. That is, spending does not subtract from GDP even though it lowers your treasury balance.
  • At the end of the round, you get awarded additional gold as follows. Each of these contributes to the Round GDP except for the "baseline 10 bars."
  • 4 gold bars per factory
  • 1 gold bar per fishing boat
  • Productivity bonus: ((Schools + Hospitals) * Factories) + Hospitals, clamped to a maximum of 30 gold bars.
  • 10 gold bars of baseline income (does not contribute to the Round GDP).
[*]Population computation -- expressed as a growth rate, resulting in exponential growth.
  • Fertility computation
    • Start with a baseline fertility rate of 5.0%
    • Increase fertility by 0.3% for every crop
    • Increase fertility by 0.3% for every hospital
    • Increase fertility by 0.1% for every house
    • Decrease fertility by 0.3% for every school
    • Clamp fertility to a minimum of 4.0%. You can't have fertility below 4% even if you filled the island with schools.
  • Mortality computation
    • Start with a baseline mortality rate of 1.1%
    • Decrease mortality by 0.3% for every hospital, but not below 0.2%. (This limit is applied before the next step.)
    • Increase mortality by 0.1% for every factory. If you fill the island with factories, your mortality rate will be 4.0%, matching the minimum allowed fertility.
  • New population: Population + Population * Fertility - Population * Mortality. Maximum allowed population is limited to 9999.
[*]Round Score Calculation -- roughly, "approval rating", 0-100%
  • First compute the following four subscores
    • Housing score: ((Houses * 500) / (Population / 100)) / 3. If larger than 30, clamp it to 30.
    • Per-capita GDP score: ((Round GDP * 100) / (Population / 100)) / 12. If larger than 30, clamp it at 30.
    • Food supply score: (((Fishing boats + Crops) * 500) / (Population / 100)) / 3. If this value is larger than 30, clamp it to 30.
      • Note: 65 * 500 = 32500. So, if you have more than 65 fishing boats + crops, this score can go negative. This is fixable by changing the BLE at $5B1E to a BNC, I think, so it treats the overflow case as a case that needs to clamp to 30.
    • General welfare score: 1 point for every school or hospital
  • Add up the four subscores, limiting the total to 100 or less. That's the per-round score.
[*]Rebellion
  • Compare this round's score to the previous round, and consider it in absolute terms as well
  • If it dropped by more than 10 points or is below 30 points, add a rebel
  • If it increased by more than 10 points or is above 70 points, remove a rebel
[/list]If you want to see the exact details of how these pieces work, look at the code. For example, in the scoring section, some divides are rounding divides, and some are truncating divides. The population computation is actually carried out with scaled arithmetic (ie. fertility/mortality rates are multiplied by 10, and population divided by 10 when computing numbers of births and deaths.)

For anything else... see the code!

Attached Files



#2 thegoldenband OFFLINE  

thegoldenband

    Quadrunner

  • 5,249 posts
  • Location:The Sapphire Galaxy

Posted Sun Apr 29, 2012 11:42 PM

Joe, thank you so much for this! There are already some fascinating revelations, like the fact that schools decrease fertility. I'm very much looking forward to taking a browse through your disassembly; a lot of it will go over my head, but here's hoping some doesn't.

I assume this also puts to bed the notion that there's any hidden in-game content or "governor's award". I didn't think so, but a part of me held out hope. :)

#3 intvnut OFFLINE  

intvnut

    River Patroller

  • Topic Starter
  • 2,816 posts
  • Location:@R6 (top of stack)

Posted Sun Apr 29, 2012 11:46 PM

Joe, thank you so much for this! There are already some fascinating revelations, like the fact that schools decrease fertility. I'm very much looking forward to taking a browse through your disassembly; a lot of it will go over my head, but here's hoping some doesn't.

I assume this also puts to bed the notion that there's any hidden in-game content or "governor's award". I didn't think so, but a part of me held out hope. :)


Yep, the "Governor's Award" is just a fiction of the documentation, I suppose. If you make it to the end of the game with a reasonably successful island, you can award it to yourself. You know if you've succeeded or not, after all. ;-)

The only thing even remotely hidden in the ROM is a copyright notice, and a couple pieces of dead code.

#4 DZ-Jay ONLINE  

DZ-Jay

    Quadrunner

  • 9,932 posts
  • Triple-Stripe Mo' Bro
  • Location:NC, USA

Posted Mon Apr 30, 2012 2:41 AM

Fantastic, Joe!

Now, for the question on everybody's mind...

Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz?

-dZ.

#5 intvnut OFFLINE  

intvnut

    River Patroller

  • Topic Starter
  • 2,816 posts
  • Location:@R6 (top of stack)

Posted Mon Apr 30, 2012 3:28 AM

Fantastic, Joe!

Now, for the question on everybody's mind...

Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz?

-dZ.


Well, I can't answer for anyone else.

I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2.

That said, if somebody does, I'd like to get a copy!

#6 DZ-Jay ONLINE  

DZ-Jay

    Quadrunner

  • 9,932 posts
  • Triple-Stripe Mo' Bro
  • Location:NC, USA

Posted Mon Apr 30, 2012 3:32 AM

Fantastic, Joe!

Now, for the question on everybody's mind...

Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz?

-dZ.


Well, I can't answer for anyone else.

I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2.

That said, if somebody does, I'd like to get a copy!


Well, I was hoping. Perhaps that can be a test case when the new version of P-Machinery is ready? It certainly does not look as demanding (graphically), except for the back-end logic of the simulation--but you have de-mystified that. :)

I can imagine the various stages of the game and the event-driven nature of game-play lend themselves well to P-Machinery's abilities.

-dZ.

#7 Pixelboy ONLINE  

Pixelboy

    Quadrunner

  • 7,702 posts
  • Location:Montreal, Canada

Posted Mon Apr 30, 2012 4:36 AM

My friend, you just earned yourself a spot in the credits section of the manual of the ColecoVision version. :D

Thank you SOOOOO much for doing this!

Utopia is a game that I want to code myself for the CV eventually, but it will take a couple of years, because I want to develop my BasicVision language and IDE first (in 2013). If it all works out, I'll code Utopia with BasicVision in 2014. Until then, I'll keep the data in the first post (as well as your disassembly file) in a safe spot in my records. :)

Thanks again!

#8 Ransom OFFLINE  

Ransom

    River Patroller

  • 4,941 posts
  • Pre-Crash Gaming and Computing Enthusiast
  • Location:Just south of the Wisconsin border.

Posted Mon Apr 30, 2012 9:05 AM

Awesome! Thank you very, very much!

#9 cmart604 OFFLINE  

cmart604

    Intellivision Hoarder

  • 11,415 posts
  • But Atari Rules!
  • Location:Vancouver

Posted Mon Apr 30, 2012 9:18 AM

My friend, you just earned yourself a spot in the credits section of the manual of the ColecoVision version. :D

Thank you SOOOOO much for doing this!

Utopia is a game that I want to code myself for the CV eventually, but it will take a couple of years, because I want to develop my BasicVision language and IDE first (in 2013). If it all works out, I'll code Utopia with BasicVision in 2014. Until then, I'll keep the data in the first post (as well as your disassembly file) in a safe spot in my records. :)

Thanks again!


Awesome! I love seeing my two favourite consoles come together! :)

#10 Carl Mueller Jr OFFLINE  

Carl Mueller Jr

    Moonsweeper

  • 466 posts
  • Location:Kagoshima, Japan

Posted Tue May 1, 2012 4:47 AM

Fantastic, Joe!

Now, for the question on everybody's mind...

Is this just a prelude to working on Utopia 2, or a souped up version that runs at 60 Hz?

-dZ.


Well, I can't answer for anyone else.

I can say this: I certainly am not working on either a 60Hz Utopia or a Utopia 2.

That said, if somebody does, I'd like to get a copy!



I would be willing to do it if it can be sanctioned by Intellivision Productions, which I strongly doubt. I also would not recommend anyone else attempting a port without their permission.

#11 BillyHW OFFLINE  

BillyHW

    River Patroller

  • 3,394 posts

Posted Tue May 1, 2012 5:15 AM

Someone needs to code a 1-player version with an AI player 2.

#12 intvnut OFFLINE  

intvnut

    River Patroller

  • Topic Starter
  • 2,816 posts
  • Location:@R6 (top of stack)

Posted Tue Dec 27, 2016 2:04 PM

In this question in another thread, dZ recently asked the following question, which was inspired by the Utopia graphics chart that had been displayed at ICHEG some time back.

 

 

Since you reversed-engineered the code, can you confirm that the outer edge is not in the map itself made invisible with the background colour? I know you disagreed before, but it seems plausible to me that they could mark some "special zone" that could be detected with hardware collision detection for the position of fish schools or pirate ships. It could also mark the range of the fort protection on your fishing ship.

 

The 1 card zone around the border of the islands does not contain special cards for hardware collision detection purposes, such as indicating the zone potentially protected by a fort.

 

Utopia uses a single method for determining whether a MOB or a card is in a 1 unit radius of a fort:  Given a card position on the map, it looks through a 1 card radius of that position to see if the card contains a fort graphic.  For MOBs, it first converts the MOB coordinate to a card position.  The code which performs both checks looks like this:

.

        ;; ---------------------------------------------------------------- ;;
        ;;  Look for a fort within 1 square of a MOB.                       ;;
        ;; ---------------------------------------------------------------- ;;
FORT_NEAR_MOB:
L_564A: PSHR    R5                  ; 564A  
        PSHR    R1                  ; 564B  

        JSR     R5, MOB_TO_CARD     ; 564C  Convert MOB position to card posn

L_564F: CLRR    R0                  ; 564F  First search positive offsets

L_5650: SDBD                        ; 5650  \__ Point to neighbor offset table
        MVII    #NBR_TBL, R4        ; 5651  /

L_5654: MVI@    R4,     R1          ; 5654  Get neighbor offset

        TSTR    R0                  ; 5655  \__ Add or subtract?
        BNEQ    L_565B              ; 5656  /

        ADDR    R2,     R1          ; 5658  \__ R0 = 0:  Add neighbor offset
        B       L_565F              ; 5659  /

L_565B: PSHR    R2                  ; 565B  \
        SUBR    R1,     R2          ; 565C   |_ R0 = 1:  Sub neighbor offset
        MOVR    R2,     R1          ; 565D   |
        PULR    R2                  ; 565E  /

L_565F: MVI@    R1,     R3          ; 565F  Get card 
        SLR     R3,     2           ; 5660  \
        SLR     R3,     1           ; 5661   |_ Is it a fort?
        ANDI    #$00FF, R3          ; 5662   |
        CMPI    #$0001, R3          ; 5664  /
        BEQ     L_567A              ; 5666  

        SDBD                        ; 5668  \__ Are we at end of table?
        CMPI    #NBR_TBL+4, R4      ; 5669  /
        BEQ     L_5670              ; 566C  Yes:  Go around outer loop
        B       L_5654              ; 566E  No:   Get next offset.

L_5670: TSTR    R0                  ; 5670  \_ We did both pos and neg?
        BNEQ    L_5677              ; 5671  /  Yes:  Leave, no fort found.

        MVII    #$0001, R0          ; 5673  \_ Do negative offsets
        B       L_5650              ; 5675  /

L_5677: CLRR    R1                  ; 5677  \
        PULR    R2                  ; 5678   |- No fort found.  Leave.
        PULR    R7                  ; 5679  /

L_567A: MVI@    R1,     R3          ; 567A  \
        SLLC    R3,     2           ; 567B   |- Whose fort is it?  CURPLR
        BOV     L_5686              ; 567C  /   or other player?

        MVII    #$0001, R3          ; 567E  Player 1's fort

L_5680: MVO     R3,     CURPLR      ; 5680  Remember which player's fort
        MVII    #$0001, R1          ; 5682  We found a fort!
        PULR    R2                  ; 5684  \_ return
        PULR    R7                  ; 5685  /

L_5686: CLRR    R3                  ; 5686  Player 0's fort
        B       L_5680              ; 5687  

        ; Offsets for looking around a square for a fort.
        ;   +1, +19, +20, +21 => E, SW, S, SE
        ;   -1, -19, -20, -21 => W, NE, N, NW
NBR_TBL:
        DECLE   $0001, $0013, $0014, $0015      ; 5689   0001 0013 0014 0015

        ;; ---------------------------------------------------------------- ;;
        ;;  Look for a fort within 1 square of a card.                      ;;
        ;;  Reuses code above for fort within 1 square of MOB.              ;;
        ;; ---------------------------------------------------------------- ;;
FORT_NEAR_CARD:
L_568D: PSHR    R5                  ; 568D  
        PSHR    R1                  ; 568E  
        B       L_564F              ; 568F  

.

As for the island graphics themselves, nothing special is stored in the cards around the border of the island.  The islands are both represented as a list of card offsets:

.

        ;;  List of display offsets for the left and right islands,
        ;;  followed by GRAM card indices associated with these positions.
        ;;  (Ref: code @ 50D0 - 50F1, and subrtn at 511D - 5124)
        ;;
        ;;  $512F - $514B           $514C - $5168
        ;;  ....................    ....................
        ;;  ....................    ....................
        ;;  ..#.................    ..........##.#..#...
        ;;  .###................    ..........#######...
        ;;  .###................    ..........##..####..
        ;;  .#####..............    ...............####.
        ;;  ...####.............    ................###.
        ;;  ....#####...........    ...............####.
        ;;  ...###.#####........    .................#..
        ;;  ....................    ....................
        ;;  ....................    ....................
        ;;  ....................    ....................
LFT_ISLE_OFS_TBL:
        DECLE   $002A,  $003D,  $003E,  $003F   ; 512F   002A 003D 003E 003F
        DECLE   $0051,  $0052,  $0053,  $0065   ; 5133   0051 0052 0053 0065
        DECLE   $0066,  $0067,  $0068,  $0069   ; 5137   0066 0067 0068 0069
        DECLE   $007B,  $007C,  $007D,  $007E   ; 513B   007B 007C 007D 007E
        DECLE   $0090,  $0091,  $0092,  $0093   ; 513F   0090 0091 0092 0093
        DECLE   $0094,  $00A3,  $00A4,  $00A5   ; 5143   0094 00A3 00A4 00A5
        DECLE   $00A7,  $00A8,  $00A9,  $00AA   ; 5147   00A7 00A8 00A9 00AA
        DECLE   $00AB                           ; 514B   00AB

RGT_ISLE_OFS_TBL:
        DECLE   $0032,  $0033,  $0035,  $0038   ; 514C   0032 0033 0035 0038
        DECLE   $0046,  $0047,  $0048,  $0049   ; 5150   0046 0047 0048 0049
        DECLE   $004A,  $004B,  $004C,  $005A   ; 5154   004A 004B 004C 005A
        DECLE   $005B,  $005E,  $005F,  $0060   ; 5158   005B 005E 005F 0060
        DECLE   $0061,  $0073,  $0074,  $0075   ; 515C   0061 0073 0074 0075
        DECLE   $0076,  $0088,  $0089,  $008A   ; 5160   0076 0088 0089 008A
        DECLE   $009B,  $009C,  $009D,  $009E   ; 5164   009B 009C 009D 009E
        DECLE   $00B1                           ; 5168   00B1 

LFT_ISLE_PIC_TBL:
        DECLE           $0013,  $0014,  $0000   ; 5169        0013 0014 0000
        DECLE   $0015,  $0017,  $0000,  $0018   ; 516C   0015 0017 0000 0018
        DECLE   $001D,  $001E,  $0000,  $0020   ; 5170   001D 001E 0000 0020 
        DECLE   $0015,  $001D,  $0000,  $0000   ; 5174   0015 001D 0000 0000
        DECLE   $0015,  $0017,  $0000,  $001E   ; 5178   0015 0017 0000 001E
        DECLE   $0020,  $0015,  $0019,  $001E   ; 517C   0020 0015 0019 001E
        DECLE   $001F,  $001D,  $001E,  $001C   ; 5180   001F 001D 001E 001C
        DECLE   $001B,  $001A                   ; 5184   001B 001A          

RGT_ISLE_PIC_TBL:
        DECLE                   $0014,  $0015   ; 5186             0014 0015
        DECLE   $0013,  $0013,  $0017,  $0000   ; 5188   0013 0013 0017 0000
        DECLE   $001B,  $001E,  $001C,  $0020   ; 518C   001B 001E 001C 0020
        DECLE   $0018,  $001D,  $001F,  $001D   ; 5190   0018 001D 001F 001D
        DECLE   $0000,  $0000,  $0015,  $001D   ; 5194   0000 0000 0015 001D
        DECLE   $0000,  $0000,  $0015,  $0017   ; 5198   0000 0000 0015 0017
        DECLE   $0000,  $0018,  $0019,  $001E   ; 519C   0000 0018 0019 001E
        DECLE   $0000,  $001F,  $0016           ; 51A0   0000 001F 0016

.

No corresponding table exists for the boundary of either island.

 

And finally, if you examine BACKTAB during the game, you can see that there is nothing special about the water cards outside the islands.  If there were, then there would be non-zero BACKTAB entries in the leftmost and rightmost columns in particular.  Rather, they're all zeros.  If you look more closely, the only non-zero BACKTAB cards in the main map exactly correspond to the two sets of tables above.

.

0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 489B 0000 0000 0000 0000 0000 0000 0000 08A3 08AB 0000 089B 0000 0000 089B 0000 0000 0000 
0000 48A3 4803 48AB 0000 0000 0000 0000 0000 0000 08BB 0803 08DB 08F3 08E3 0903 08C3 0000 0000 0000
0000 48BB 4803 48C3 0000 0000 0000 0000 0000 0000 08EB 08FB 0000 0000 08EB 0803 0803 08AB 0000 0000 
0000 48EB 48F3 4803 4903 48AB 0000 0000 0000 0000 0000 0000 0000 0000 0000 08EB 0803 0803 08AB 0000
0000 0000 0000 48EB 4803 4803 48AB 0000 0000 0000 0000 0000 0000 0000 0000 0000 08BB 0803 08C3 0000 
0000 0000 0000 0000 48BB 4803 48F3 4903 48AB 0000 0000 0000 0000 0000 0000 08CB 08F3 0803 08FB 0000
0000 0000 0000 48CB 48F3 48FB 0000 48EB 48F3 48E3 48DB 48D3 0000 0000 0000 0000 0000 08B3 0000 0000 
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 
0000 0004 008C 0084 0084 0000 0000 00AE 0086 0006 008E 008E 00BE 0000 0000 0002 008A 0082 0082 0000

.



#13 DZ-Jay ONLINE  

DZ-Jay

    Quadrunner

  • 9,932 posts
  • Triple-Stripe Mo' Bro
  • Location:NC, USA

Posted Tue Dec 27, 2016 2:07 PM

Cool! Thanks. :cool:

#14 BBWW OFFLINE  

BBWW

    River Patroller

  • 2,908 posts
  • If it is not fun move away slowly.
  • Location:On the Northern Coast of San Mateo County, CA

Posted Tue Dec 27, 2016 7:41 PM

I'm waiting for Dystopia. 







Also tagged with one or more of these keywords: utopia, reverse engineering, intellivision

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users