1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

HEX Mod (discontinued)

Discussion in 'Civ4 - Creation & Customization' started by PieceOfMind, Jul 25, 2010.

  1. PieceOfMind

    PieceOfMind Drill IV Defender Retired Moderator

    Joined:
    Jan 15, 2006
    Messages:
    9,312
    Location:
    Australia


    (discontinued)

    Prologue
    Spoiler :

    With the news that civ5 will be requiring us to make a transition from squares to hexagons (aka hexes) as our preferred unit of gameboard, there has been much discussion on how these new shapes will behave. Some have even wondered so much the pesky hexes have invaded their dreams, tormenting their civ4-playing souls. "How", they wonder, "could civ4 be imagined in this new geometry?"

    With this in mind, I decided to embark on a journey of epic proportions. I took the first step towards creating a love-child of hexes and squares, in the hope that civ4 could borrow some of the genius of the tactically superior hex formation. This was a messy process and not pleasant for the victim square who went under the knife. Two of its opposite corners were cut clean off, proving the point to all the other squares on the grid that it was possible for them too to become hexes. Packed like sardines, these artificial hexes were only partly relieved to find they now only touched 6 other units and noted that it was at least better than being crammed to the point where they would be touching 8 other units.

    Meanwhile the omniscient began to play civ on their mangled bodies. :D


    Now that I've proven I've no writing talent, why don't we get straight to the good bit: The HEX Mod.

    In this mod we mess with one of the most fundamental rules of the game - adjacency. Currently all tiles on the board, apart from those at the poles, neighbour 8 other tiles because they are all packed as squares. By changing the adjacency rules slightly, by banning one of the diagonal directions for movement (among other things) we can functionally duplicate a true hex board.

    Now, my progress so far has been sketchy. My first attempt allowed a successful compile but this is how it looked in game - clearly I broke it! :eek:


    After fixing a few obvious and careless mistakes/bugs, I obtained:


    Also getting infinite loops, almost certainly because I've mucked up something with a loop through NUM_DIRECTION_TYPES.



    Here's how you use a square gameboard to play on hexes:
    (each number is the distance from the red 0.
    Code:
                // 3 | 3 | 3 | 3 | 4 | 5 | 6
               // -------------------------
              // 3 | [b]2[/b] | [b]2[/b] | [b]2[/b] | 3 | 4 | 5
             // -------------------------
    	// 3 | [b]2[/b] | [color="blue"]1[/color] | [color="blue"]1[/color] | [b]2[/b] | 3 | 4
           // -------------------------
          // 3 | [b]2[/b] | [color="blue"]1[/color] | [color="red"]0[/color] | [color="blue"]1[/color] | [b]2[/b] | 3
         // -------------------------
        // 4 | 3 | [b]2[/b] | [color="blue"]1[/color] | [color="blue"]1[/color] | [b]2[/b] | 3
       // -------------------------
      // 5 | 4 | 3 | [b]2[/b] | [b]2[/b] | [b]2[/b] | 3
     // -------------------------
    // 6 | 5 | 4 | 3 | 3 | 3 | 3
    
    
    // 3 | 3 | 3 | 3 | 4 | 5 | 6
    // -------------------------
    // 3 | [b]2[/b] | [b]2[/b] | [b]2[/b] | 3 | 4 | 5
    // -------------------------
    // 3 | [b]2[/b] | [color="blue"]1[/color] | [color="blue"]1[/color] | [b]2[/b] | 3 | 4
    // -------------------------
    // 3 | [b]2[/b] | [color="blue"]1[/color] | [color="red"]0[/color] | [color="blue"]1[/color] | [b]2[/b] | 3
    // -------------------------
    // 4 | 3 | [b]2[/b] | [color="blue"]1[/color] | [color="blue"]1[/color] | [b]2[/b] | 3
    // -------------------------
    // 5 | 4 | 3 | [b]2[/b] | [b]2[/b] | [b]2[/b] | 3
    // -------------------------
    // 6 | 5 | 4 | 3 | 3 | 3 | 3
    
    Here is the picture from earlier mangled to try and show the hex nature:



    This distance function does the job, and is enough to get the cultural expansions of cities looking correct. It is in CvGameCoreUtils.cpp
    Code:
    inline int plotDistance(int iX1, int iY1, int iX2, int iY2)													// Exposed to Python
    {
    	int iDX;
    	int iDY;
    	
    	iDX = iX2 - iX1;
    	iDY = iY2 - iY1;
    	int sizeX = (iDX<0?-iDX:iDX);
    	int sizeY = (iDY<0?-iDY:iDY);
    
    	int iA = std::max(iDX - iDY,iDY - iDX); // distance b/n the two.
    	int iM = std::min(sizeX,sizeY);
    	
    	return std::min(iA + iM,sizeX+sizeY);
    
    }		
    
    If anyone can think of a more efficient way to do that calculation I'd love to hear it. A challenge to all you mathematicians/computer scientists out there! :)
    It's already pretty speedy, but since this code is called extremely frequently, perfection is preferred.



    This is what the above replaces. Note that both plotDistance and stepDistance I plan to replace for the HEX Mod.
    Code:
    [COLOR="SeaGreen"]// 4 | 4 | 3 | 3 | 3 | 4 | 4
    // -------------------------
    // 4 | 3 | 2 | 2 | 2 | 3 | 4
    // -------------------------
    // 3 | 2 | 1 | 1 | 1 | 2 | 3
    // -------------------------
    // 3 | 2 | 1 | 0 | 1 | 2 | 3
    // -------------------------
    // 3 | 2 | 1 | 1 | 1 | 2 | 3
    // -------------------------
    // 4 | 3 | 2 | 2 | 2 | 3 | 4
    // -------------------------
    // 4 | 4 | 3 | 3 | 3 | 4 | 4
    //
    // Returns the distance between plots according to the pattern above...[/COLOR]
    inline int plotDistance(int iX1, int iY1, int iX2, int iY2)													// Exposed to Python
    {
    	int iDX;
    	int iDY;
    
    	iDX = xDistance(iX1, iX2);
    	iDY = yDistance(iY1, iY2);
    
    	return (std::max(iDX, iDY) + (std::min(iDX, iDY) / 2));
    }
    
    [COLOR="SeaGreen"]// 3 | 3 | 3 | 3 | 3 | 3 | 3
    // -------------------------
    // 3 | 2 | 2 | 2 | 2 | 2 | 3
    // -------------------------
    // 3 | 2 | 1 | 1 | 1 | 2 | 3
    // -------------------------
    // 3 | 2 | 1 | 0 | 1 | 2 | 3
    // -------------------------
    // 3 | 2 | 1 | 1 | 1 | 2 | 3
    // -------------------------
    // 3 | 2 | 2 | 2 | 2 | 2 | 3
    // -------------------------
    // 3 | 3 | 3 | 3 | 3 | 3 | 3
    //
    // Returns the distance between plots according to the pattern above...[/COLOR]
    inline int stepDistance(int iX1, int iY1, int iX2, int iY2)													// Exposed to Python
    {
    	return std::max(xDistance(iX1, iX2), yDistance(iY1, iY2));
    }

    Notes:
    Spoiler :
    The things the mod should affect:
    -Unit movement (not done yet)
    -City border expansion (done)
    -plus more



    Files, locations to examine:
    CvGameCoreUtils.h: plotDistance() and stepDistance()
    plotDistance is nearly the Euclidean distance. stepDistance is the sort that units would use - diagonalss count as distance 1.
    (Change1)


    CvMap.cpp: maxStepDistance(), maxPlotDistance()
    Uses stepDistance() and plotDistance() but alos makes calls to things like getGridWidthINLINE. Probably needs fixing.
    How this will change will depend on how they get used. It's use in CvPlayer.cpp looks the most worrying. It also appears near the end of CvPlayerAI::AI_foundValue.
    Whatever change is made, both functions will presumably look identical. It also looks like there's no need to tie it to the (0,0) coordinate. In fact doing so would be wrong.

    How will this work if Y wrap is permitted?

    (Change2)

    ...
    +Several other points in the code most likely. Some bits could get messy, like looking at how rivers work, and what corners of tiles are.

    Notes:
    stepDistance() is used in CvGameCoreUtils.cpp in two places, for both of pathHeuristic and stepHeuristic. To my knowledge, both these things are to do with the A* pathfinding algorithm.

    (0,0) coordinate is in the bottom left corner of the map.
    Hence we make the banned direction be NW and SE (for easier distance calculations I think).


    This mod is intended as a bit of fun and should be treated as mostly experimental at this point. I don't have plans to make it very pretty and if it does reach a final stage it will probably have some visual quirks associated with it.

    I will likely need some help/advice from some SDK and perhaps Python modders out there. Anyone interested in helping out? It probably won't take a lot of work, luckily.

    The first thing I am trying to figure out how to do is to restrict unit movement so that NW and SE directions are banned. If anyone can confirm it's not something locked in the EXE, that would give me peace of mind. :)
     
  2. Kid R

    Kid R Chieftain

    Joined:
    Jan 26, 2009
    Messages:
    1,482
    I think this might be faster for the plot distance function...

    Edit: corrected bug - :D

    Spoiler :
    Code:
    [SPOILER]No diagonal travel: distance is sum of the 
    distances in the X and Y directions:
    
    //--------------------------
    //   |   |   |   | 4 | 5 | 6
    // -------------------------
    //   |   |   |   | 3 | 4 | 5
    // -------------------------
    //   |   |   |   | 2 | 3 | 4
    // -------------------------
    //   |   |   | 0 |   |   |  
    // -------------------------
    // 4 | 3 | 2 |   |   |   |  
    // -------------------------
    // 5 | 4 | 3 |   |   |   |  
    // -------------------------
    // 6 | 5 | 4 |   |   |   |  
    //--------------------------
    
    Yes diagonal travel: Distance is the larger of the 
    two X and Y distances:
    
    //--------------------------
    // 3 | 3 | 3 | 3 |   |   |  
    // -------------------------
    // 3 | 2 | 2 | 2 |   |   |  
    // -------------------------
    // 3 | 2 | 1 | 1 |   |   |  
    // -------------------------
    // 3 | 2 | 1 | 0 | 1 | 2 | 3
    // -------------------------
    //   |   |   | 1 | 1 | 2 | 3
    // -------------------------
    //   |   |   | 2 | 2 | 2 | 3
    // -------------------------
    //   |   |   | 3 | 3 | 3 | 3
    //--------------------------
    [/SPOILER]
    
    inline int plotDistance(int iX1, int iY1, int iX2, int iY2)
    {
        int iDX = iX2 - iX1;
        int iDY = iY2 - iY1;
        int sizeX = (iDX<0?-iDX:iDX);
        int sizeY = (iDY<0?-iDY:iDY);
    
        if (iDX > 0 && iDY > 0 || iDX < 0 && iDY < 0)
    
            return sizeX + sizeY;                   [COLOR="SeaGreen"]//No diagonal travel[/COLOR]
    
        else
    
            return std::max(sizeX, sizeY);          [COLOR="SeaGreen"]//Yes diagonal travel[/COLOR]
    }
     
  3. Souron

    Souron The Dark Lord

    Joined:
    Mar 9, 2003
    Messages:
    5,947
    Location:
    (GMT-5)
    Don't try to shift the map over, just use isometric view. Isometric view is the same as hex view if you disallow horizontal movement.
     
  4. PieceOfMind

    PieceOfMind Drill IV Defender Retired Moderator

    Joined:
    Jan 15, 2006
    Messages:
    9,312
    Location:
    Australia
    That's a nice improvement there. Thanks, I will use it.

    @Souron. Hmm, that could work too.

    Code:
    // 3 | 3 | 3 | 3 | 3 | 3 | 3
    // -------------------------
    // 4 | [B]2[/B] | [B]2[/B] | [B]2[/B] | [B]2[/B] | [B]2[/B] | 4
    // -------------------------
    // 5 | 3 | [COLOR="blue"]1[/COLOR] | [COLOR="blue"]1[/COLOR] | [COLOR="blue"]1[/COLOR] | 3 | 5
    // -------------------------
    // 6 | 4 | [B]2[/B] | [COLOR="Red"]0[/COLOR] | [B]2[/B] | 4 | 6
    // -------------------------
    // 5 | 3 | [COLOR="Blue"]1[/COLOR] | [COLOR="blue"]1[/COLOR] | [COLOR="blue"]1[/COLOR] | 3 | 5
    // -------------------------
    // 4 | [B]2[/B] | [B]2[/B] | [B]2[/B] | [B]2[/B] | [B]2[/B] | 4
    // -------------------------
    // 3 | 3 | 3 | 3 | 3 | 3 | 3
    Like that? A circle of radius 2 (i.e. a hexagon) doesn't even look right.
     
  5. The_J

    The_J Say No 2 Net Validations Retired Moderator Supporter

    Joined:
    Oct 22, 2008
    Messages:
    30,811
    Location:
    Germany / Netherlands
    So, together with the field capacity mod and Ekmeks new LH attempts, there'll now really be a civ5 mod for civ4 :D.
    -> Interesting :goodjob:.
     
  6. PieceOfMind

    PieceOfMind Drill IV Defender Retired Moderator

    Joined:
    Jan 15, 2006
    Messages:
    9,312
    Location:
    Australia
    Hi J,
    Yeah I was thinking too about the limited capacity modcomp and how it might be combined with this.

    First step for me though is to get this one working.

    My question to modders is this:

    At the moment when you want to move a unit you select it and then hold the right mouse button over the destination and the path along with the movement time will be shown to you. What parts of the code would I be looking at in order to ensure that NW and SE moves are disallowed? A unit that wants to go 1NW would have to go one square north and one square west, costing 2 movement points all up.
     
  7. PieceOfMind

    PieceOfMind Drill IV Defender Retired Moderator

    Joined:
    Jan 15, 2006
    Messages:
    9,312
    Location:
    Australia
    So I've tracked down the pathfinding code to this in CvDLLFAStarIFaceBase.h:
    Code:
    virtual bool GeneratePath(FAStar*, int iXstart, int iYstart, int iXdest, int iYdest, bool [B]bCardinalOnly [/B]= false, int iInfo = 0, bool bReuse = false) = 0;
    Anyone know where to see where that function is worked out? I'm very curious to see what the bCardinalOnly does as it would likely help me with this mod.

    I'm fearing it's locked away from us to tamper with it.
     
  8. ParadigmShifter

    ParadigmShifter Random Nonsense Generator

    Joined:
    Apr 4, 2007
    Messages:
    21,810
    Location:
    Liverpool, home of Everton FC
    It will be an implementation of the A Star algorithm, which is pretty straightforward.

    bCardinalOnly will only consider NESW moves as valid.

    http://en.wikipedia.org/wiki/A*_search_algorithm

    Just use your distance metric to the target as the heuristic function (ok since it always underestimates the cost due to terrain costs).

    EDIT: It's a pure virtual function so you can just make your own implementation by deriving a class from the interface.

    EDIT2: I don't mod myself but I know a great deal about C++ and pathfinding. PM me if you need any help on implemetantion details.
     
  9. Souron

    Souron The Dark Lord

    Joined:
    Mar 9, 2003
    Messages:
    5,947
    Location:
    (GMT-5)
    No, horizontal from the isometric perspective. It's the same distance function as you have, effectively. So if you keep the same world shape, then diagonal movement in one direction is disallowed. Ideally you'd also want to go back to to map shapes of civ 2 and 3.

    Here's a iso grid with hex distances drawn on it.



    Simple!
    Notice that it actually looks like a big hexagon.
     

    Attached Files:

  10. Chiyochan

    Chiyochan Chieftain

    Joined:
    Jul 28, 2007
    Messages:
    710
    Location:
    Chiyochan's Country
    well WHATEVER mode you guys go for I would suggest two things
    1, a map script that might work for this with hexes (pipedream)

    2. a layer that shows places a unit may move into, something akin to the Settler or archer bombard graphical layer in BTS
     
  11. Lenowill

    Lenowill Chieftain

    Joined:
    Sep 10, 2009
    Messages:
    174
    I heartily endorse this product and/or service.

    I would love to see this implemented in Civ4 somehow, along with proper tile capacity and ranged warfare elements, and zone of control if possible. Those are the best aspects of Civ5 and are the ones I'm really eager to see back-ported to Civ4.
     
  12. Thomas SG

    Thomas SG CCV-designer

    Joined:
    Aug 10, 2007
    Messages:
    1,164
    Location:
    Germany
    Great idea.

    But two things.

    1) Can you move the shown plots on the map?
    2) You will destroy very many functions!!! It's not just the distance calculation and the move rule. All area calculations are a peace of toast. Keep that in mind.
     

Share This Page