Are you, in fact, a pregnant lady who lives in the apartment next door to Superdeath's parents? - Commodore

Create an account  

 
how does MoO calculate distances?

hi

Does anyone know how MoO1 calculates distances between stars?

My own reverse engineering efforts (starmap-offs 0x227cd, adr 0x1eed:0xfd for those who must know :-) brought the following (essentially an average between maximum norm and the L1 Manhattan Distance Metric):

Code:
int pdist_tenths(int i,int j) {
    int x=abs(planet[i].xpos-planet[j].xpos);
    int y=abs(planet[i].ypos-planet[j].ypos);
    return max(x,y)+(min(x,y)>>1);
  }

It is however unclear to me, how these values are converted (rounded) to parsec-distances. In the current RBO39 e.g. we have

Kakata (163, 47), 40 Desert, 16 pop, 1 fact (Silicoid)
Reticuli (154, 70), 90 Terran, 0 pop, 0 fact
Vulcan (196, 96), 90 Jungle, 0 pop, 0 fact

and thus pdist_tenths(Kakata,Vulcan) = 65 and pdist_tenths(Reticuli,Vulcan) = 55, however in-game, the parsec distances are 7 pc and 5 pc. Does it do round-to-odd? Or does it use a completely different method? Any ideas?

ignatius
Reply

It might be using the reverse of the non-biased rounding rule: round to the nearest whole number, if at .5, round to the even.
Merovech's Mapmaking Guidelines:
0. Player Requests: The player's requests take precedence, even if they contradict the following guidelines.

1. Balance: The map must be balanced, both in regards to land quality and availability and in regards to special civilization features. A map may be wonderfully unique and surprising, but, if it is unbalanced, the game will suffer and the player's enjoyment will not be as high as it could be.

2. Identity and Enjoyment: The map should be interesting to play at all levels, from city placement and management to the border-created interactions between civilizations, and should include varied terrain. Flavor should enhance the inherent pleasure resulting from the underlying tile arrangements. The map should not be exceedingly lush, but it is better to err on the lush side than on the poor side when placing terrain.

3. Feel (Avoiding Gimmicks): The map should not be overwhelmed or dominated by the mapmaker's flavor. Embellishment of the map through the use of special improvements, barbarian units, and abnormal terrain can enhance the identity and enjoyment of the map, but should take a backseat to the more normal aspects of the map. The game should usually not revolve around the flavor, but merely be accented by it.

4. Realism: Where possible, the terrain of the map should be realistic. Jungles on desert tiles, or even next to desert tiles, should therefore have a very specific reason for existing. Rivers should run downhill or across level ground into bodies of water. Irrigated terrain should have a higher grassland to plains ratio than dry terrain. Mountain chains should cast rain shadows. Islands, mountains, and peninsulas should follow logical plate tectonics.
Reply

There's a thread specifically on this here:

http://realmsbeyond.net/forums/showthread.php?tid=2768
Reply

@Merovech

It might be using the reverse of the non-biased rounding rule

Well, this would be round-to-odd. But not, the truth is much much more horrible ...

@Vanshilar

Thanks for the link. This was most helpful, esp. kyrub's hint. I've already found the routine he spoke of (starmap.exe:0xd4d6) but for reasons that will become clear very soon, I refused to believe that this was part of the core game mechanics, but considered it an (extremely poorly written) part of the fleet-movement animation code.

What they do: they call a version atan2 (yes, that's arcus tangent - a trancendental function, about the most expensive stuff you can come up with, even if you don't implement it with a braindead sequential reverse lookup in a tangent table) and then compute sine and cosine (again with sign handling and table lookup) - and all this, just to normalize a fscking vector! And they don't do it once - they do it for every warp factor - twice! Once with length 5 and once with lenght 6, the components of which get rounded down for all values except 0°, 90°, 180° and 270°, which doubles the error and explains the 11 vs. 9 starlane effect.

But somehow they managed to still get it wrong. I logged all actual fleet movements which happed during my RBO39 game and noted some really strange things:

Code:
dx dy     x   y  x/y    y/x
+6 -7     8  -9  0.8889 1.1250
-7 -7   -17 -19  0.8947 1.1176
-6 -7    -9 -10  0.9000 1.1111
-7 -7   -22 -24  0.9167 1.0909

+9 +5    28  16  1.7500 0.5714
-9 -4   -58 -33  1.7576 0.5690
+9 -4    46 -26  1.7692 0.5652
-9 +4   -46  26  1.7692 0.5652
+9 +5    16   9  1.7778 0.5625
-9 -5   -16  -9  1.7778 0.5625
+9 +4    67  37  1.8108 0.5522

the x,y are the coord. of the target planet and dx,dy is the resulting warp1 movement. As you can see the steepness dy/dx of the movement is not even monotonic.

kyrub, if you are reading this, maybe you could have a look at the following piece of code in starmap.exe:

Code:
286c3  24ec3  80FF00            cmp bh,0x0    # deg >= 45?
286c6  24ec6  753D              jnz 0x4f05   # short 0x24f05
286c8  24ec8  80FB67            cmp bl,0x67    # 103, deg < 21.9?
286cb  24ecb  721C              jc 0x4ee9   # short 0x24ee9
286cd  24ecd  BE2400            mov si,0x24
286d0  24ed0  B91700            mov cx,0x17
286d3  24ed3  BF1600            mov di,0x16    # 22
286d6  24ed6  AC                lodsb        # lookup in tan table
286d7  24ed7  3AD8              cmp bl,al    # bl<al?
286d9  24ed9  7206              jc 0x4ee1   # short 0x24ee1
286db  24edb  47                inc di
286dc  24edc  E2F8              loop 0x4ed6
286de  24ede  BF2C00            mov di,0x2c    # 44, returns 43 (bug?)
286e1 @24ee1  4F                dec di
286e2  24ee2  8BC7              mov ax,di

From my limited understanding, the arctan funtion would never return a value of 44° and mov di,0x2c should in fact read mov di,0x2d.

ignatius
Reply

Hmm I don't have a background in coding but...maybe they were pretty enamored with math regardless of the computation lol after all, sin(arctan x) = x/sqrt(1+x^2) etc. -- there's a variety of trigonometric relationships that you can use there (from wikipedia). I wouldn't be surprised if they had some weird math going on though; when I was looking into combat damage here, I got some weird results too (although Zygot might be right that they calculate in tenths of a HP even if they only display full HP values).
Reply

Well, the best way for a game of this era and comp. speed would have been to stick with the non-Euklidian (L_1+L_inf)/2 metric and use it consistently for all distance computations. Anyway, I solved the problem in the meantime using the game internal tanget-table to come up with a numerically exact algorithm. Here is the code, in case anyone is interested:

Code:
bool move_parsec(int& x,int &y) {
  int rx=abs(x),ry=abs(y);
  if(rx>255||ry>255) { rx>>=1; ry>>=1; }  
  int dx=0,dy=0;
  
  if(rx==0) {                
    dy=5;                    
  } else {                    
    int r=(ry<<8)/rx;        
    if(r<192) {              
      if(r<54) {              
        if(r==0) {            
          dx=5;              
        } else {              
          dx=4;              
        }                    
      } else { // r>=54      
        if(r<113) {          
          dx=4; dy=1;          
        } else {              
          dx=4; dy=2;        
        }                    
      }                      
    } else { // r>=192        
      if(r<603) {            
        if(r<352) {          
          dx=3; dy=3;        
        } else {              
          dx=2; dy=4;        
        }                    
      } else { // r>=603      
        if(r<1317) {          
          dx=1; dy=4;          
        } else if(r<15669) {  
          dy=4;              
        } else {              
          dy=5;              
        }                    
      }                      
    }                        
  }

  if(dx>rx) dx=rx;
  if(dy>ry) dy=ry;
  if(x<0) x+=dx; else x-=dx;
  if(y<0) y+=dy; else y-=dy;
                              
  rx=abs(x),ry=abs(y);                            
  if(x>255||y>255) { rx>>=1; ry>>=1; }  
  dx=dy=0;

  if(rx==0) {                          
    dy=6;                              
  } else {                              
    int r=(ry<<8)/rx;                  
    if(r<230) {                        
      if(r<93) {                        
        if(r==0) {                      
          dx=6;                        
        } else if(r<45) {              
          dx=5;                        
        } else {                        
          dx=5; dy=1;                    
        }                              
      } else { // r>=93                
        if(r<147) {                    
          dx=5; dy=2;                  
        } else if(r<172) {              
          dx=5; dy=3;                  
        } else {                        
          dx=4; dy=3;                  
        }                              
      }                                
    } else { // r>=230                  
      if(r<443) {                      
        if(r<294) {                    
          dx=4; dy=4;                  
        } else if(r<394) {              
          dx=3; dy=4;                  
        } else {                        
          dx=3; dy=5;                  
        }                              
      } else { // r>=443                
        if(r<743) {                    
          dx=2; dy=5;                  
        } else if(r<1616) {            
          dx=1; dy=5;                    
        } else if(r<14669) {            
          dy=5;                        
        } else {                        
          dy=6;                        
        }                              
      }                                
    }                                  
  }                                    

  if(dx>rx) dx=rx;
  if(dy>ry) dy=ry;
  if(x<0) x+=dx; else x-=dx;
  if(y<0) y+=dy; else y-=dy;
  return x||y;
}

I've tested the code with some 8000+ fleet movements form 6 different games and it seems to work. I'm not sure, however, if this also gives the same results for all player relevant game-internal distance and reachability computations.

cu

ignatius
Reply



Forum Jump: