Idea: Naval Unit Ranges

Joined
Jul 28, 2006
Messages
4,016
When I was messing around with my WWII Pacific scenario, a thought occurred to me about naval units and how they travel. In the real world, when the Japanese attacked Pearl Harbor on December 7, 1941, they were not able to stay around for very long (or to advance beyond Pearl Harbor) due to supply constraints and fuel consumption.

In Civ 4, this is not really represented. A battleship may travel around the world without regard to its supply capacity and does not need ports. While this makes sense in the standard game for balance reasons, scenarios are much more flexible there. I therefore started toying with the idea of creating maximum naval ranges, like air unit ranges.

Right now, I'm thinking along the lines of one max range for all naval types (though that could be changed later on), and a unit would not be able to move beyond its effective range. Range is defined as the distance between one of that civ's (or one of its vassals') coastal cities and the unit itself.

Looking through some of the code I've used already, I can foresee three needed mechanisms in Python: 1, a test to see where the nearest coastal city is, 2, a comparison of how far away the unit is from it, and 3, the actual "cannotmove" command itself.

Off the top of my head, I think it would look something like this:

Spoiler :
Code:
tMarrakech = (1, 6) # example of a city plot
tCityList = [  tMarrakech,
    tRabat,
    tTangiers,
    tBechar,
    tGibraltar,
    tOran,
    tAlgiers,
    tTunis,
    tGabes  ]
bForceCoastal = True
MaxNavalRange = [some # of plots, say 20]
 
city = self.findCity(tCityList, [], iPlayerTeam, bForceCoastal)
cityPlot = city.plot()
distance = self.plotDistance(cityPlot, unit.plot())
if distance > MaxNavalRange:
 [Cannot move into plot]
 
 
# Functions called
 def findCity( self, tRegionList, tExceptionList, iTeam, bForceCoastal ):
  """If possible, returns a (coastal) city from tRegionList that belongs to team iTeam 
  and is not in tExceptionList."""
  if iTeam == iAxis:
   for tCoords in tRegionList:
    city = getCity(tCoords)
    if self.isCityAxis(city):
     if not tCoords in tExceptionList:
      if bForceCoastal:
       if city.isCoastal():
        return city
      else:
       return city
 
 def plotDistance( self, tFirst, tSecond ):
  """Returns the number distance between tFirst and tSecond in the number of plots."""
  xDistance = tFirst[iX] - tSecond[iX]
  yDistance = tFirst[iY] - tSecond[iY]
  if xDistance < 0:
   xDistance = -xDistance
  if yDistance < 0:
   yDistance = -yDistance
  if xDistance >= yDistance:
   return xDistance
  else:
   return yDistance
 
 def unitCannotMoveInto(self,argsList):
  ePlayer = argsList[0]  
  iUnitId = argsList[1]
  iPlotX = argsList[2]
  iPlotY = argsList[3]
  return False

This is from Desert War, so there are a lot of scenario-specific examples in it. The coastal finder is also not perfect, since it only locates a city and not the nearest city. Yet I think you can understand where I'm going here, and I was curious if anyone had already done anything in this area or has any suggestions for me.

Potential problems: what happens if your unit is far away, and your nearest city gets captured, thus putting your ships beyond their maximum range?
 
I thought of a "not quite what you want" option. My apologies if this is considered off-topic. ^_^

Set all ships to move 1 per turn. Make a building ("Refueling Facilities") that provides a one-turn movement bonus (temporary promotion "Fueled") to ships that end/begin their turn in port. You could even make several grades of "Fueled" to reflect different endurances of different sized ships. For example, Fueled Destroyer (+4 move), Fueled Cruiser (+6 move), Fueled Battleship (+8 move).

A ship that ends its turn in port could move 4, 10, or however many spaces you want on the following turn. If it ends its turn at sea, it's out of gas, and moves at a snail's pace.

For more options, add a "Logistical Flotilla" naval unit that works kind-of like a Blood Pet in FFH. If you use its special ability, it can give one unit the "Fueled" temp Promotion. For balance, have LFs disappear after use, and make them cheap.
 
A quick suggestion...

Don't use 'cannotMove' if you'll be adding any complicated code since that will be called by every unit everytime it moves and will result in a substantial slowdown.

Instead, add a quick check at the beginning of the turn for each naval unit, it the ship is within range do nothing, if not set it's movement to something lower.

Or, if you prefer, every time a ship is in a city (or perhaps only those with harbors for example) give it a promotion like FUEL1, then at the end of the turn if the ship has moved change the promotion, if it has the first change it to FUEL2, it it already has FUEL2 change it to FUEL3 or remove it depending on how far you want it to go. You'll have to work out the specifics but the general idea is to use promotions to represent fule storage with each of those giving a movement bonus, without them the ship moves at a slow crawl. You don't want to stop them dead unless you're going to get into the SDK and teach the AI how to use the change.
 
Right now, I'm thinking along the lines of one max range for all naval types (though that could be changed later on), and a unit would not be able to move beyond its effective range. Range is defined as the distance between one of that civ's (or one of its vassals') coastal cities and the unit itself.

I think you'll definitely want to vary the ranges. One of the things that makes larger ships distinct from smaller ships is their distance to go much further from friendly ports and operate on their own as opposed to part of a fleet.

Also, depending on the time period, technological breakthroughs could increase that range.

This is from Desert War, so there are a lot of scenario-specific examples in it. The coastal finder is also not perfect, since it only locates a city and not the nearest city. Yet I think you can understand where I'm going here, and I was curious if anyone had already done anything in this area or has any suggestions for me.

You do know that there's a function for finding the nearest city, right? And I'm fairly certain that there's a parameter for setting coastal as a condition. Look at my Extra Pillage and Food From Animals modcomps. These use that function to send pillaged and captured yields back to the nearest city.

Potential problems: what happens if your unit is far away, and your nearest city gets captured, thus putting your ships beyond their maximum range?

I'd say destroying the unit is a bad idea... instead half the unit's HP and it can limp its way back into port. At least then it gives the enemy to destroy them while they're weakened.

Now, what I don't know is if there's a way to force the unit to only move in a particular range of directions back towards the nearest port as opposed to being able to use breaking out of that range to travel anywhere.
 
Well, I have been thinking about this issue, and I appreciate all the comments. My take on it thus far:

The promotion system that gives extra movement sounds like the easiest way to do this, but I am reluctant to pursue that option because it sounds like it would add to micromanagement. Creating new resupplier unit just for the sake of giving a ship movement range... well, you multiply that times 40 ships in a typical world-sized fleet and you've got too much to handle. I'm also not too crazy about the idea that fleets must end their turn in a port.

You do know that there's a function for finding the nearest city, right? And I'm fairly certain that there's a parameter for setting coastal as a condition. Look at my Extra Pillage and Food From Animals modcomps. These use that function to send pillaged and captured yields back to the nearest city.

Actually, I had not seen this function, although I figured one must be out there. :) Does it look for only your civ's nearest cities, or would vassal states count? The problem with what I'm trying to do is to incorporate the complexities of port rights that come with client nations and open borders, and that may be difficult at first.

I'd say destroying the unit is a bad idea... instead half the unit's HP and it can limp its way back into port. At least then it gives the enemy to destroy them while they're weakened.

Now, what I don't know is if there's a way to force the unit to only move in a particular range of directions back towards the nearest port as opposed to being able to use breaking out of that range to travel anywhere.

I would not want to destroy the unit, and unless it effectively was left stranded in the square it happened to be in at the unfortunate moment its supply chain got severed, I do not see a clear answer on how to proceed. The one thing I could think of would be to add a check at the beginning of a turn to see if a unit is outside of its maximum range, and if it is, move it to the nearest friendly city. That's actually very easy to do, but it's hardly an ideal solution - and there's the potential for undesirable moves, exploits, you name it...

The only other solution would be to create a new characteristic for units (supply) which functions like :strength:. Each turn it would decrease by 1.0 until it hit 0, whereupon the ship could only move something like 1 :move: per turn. But that will require quite a bit more work, and there is much less abstraction, which is what I was aiming for.
 
Actually, I had not seen this function, although I figured one must be out there. :) Does it look for only your civ's nearest cities, or would vassal states count? The problem with what I'm trying to do is to incorporate the complexities of port rights that come with client nations and open borders, and that may be difficult at first.

Well, it can check for all civs in the same team, but it doesn't look for civs that are vassals... of course, it would be fairly simple to extend this option to vassals too with a little SDK modding. Failing that, you can also create this in python by finding the nearest city of each player and compare them to find the closest one.

I would not want to destroy the unit, and unless it effectively was left stranded in the square it happened to be in at the unfortunate moment its supply chain got severed, I do not see a clear answer on how to proceed. The one thing I could think of would be to add a check at the beginning of a turn to see if a unit is outside of its maximum range, and if it is, move it to the nearest friendly city. That's actually very easy to do, but it's hardly an ideal solution - and there's the potential for undesirable moves, exploits, you name it...

The only other solution would be to create a new characteristic for units (supply) which functions like :strength:. Each turn it would decrease by 1.0 until it hit 0, whereupon the ship could only move something like 1 :move: per turn. But that will require quite a bit more work, and there is much less abstraction, which is what I was aiming for.

I would not move it to the nearest friendly city. Nor would I restrict the movement. Instead I would just make it so that every turn spent out of range of a friendly port would cause the unit to start taking damage. Maybe it's not ideal if you want it to be representing a lack of fuel to move, but it will represent the weakness the ships will have and make them sitting ducks for attack.
 
Well, it can check for all civs in the same team, but it doesn't look for civs that are vassals... of course, it would be fairly simple to extend this option to vassals too with a little SDK modding. Failing that, you can also create this in python by finding the nearest city of each player and compare them to find the closest one.
Or just check to see if you can enter the city, if so you have open borders even if the current unit is something like a Caravel than can enter rival territory. I believe the function you'd want is 'canMoveThrough'

The biggest problem you're going to have isn't so much how to impliment it as it is how to get the AI to understand it. If you damage the unit when it's out of range you'll kill off the AI ships, if you stop them dead you'll end up with a lot of AI ships sitting in the water unable to move.

What may be the best solution is to leave their movement alone and give them an 'out of supply' promotion that reduces their attack strength. For carriers/transports you could immobolize units in their cargo when they're out of supply to prevent them from blockading, launching missiles/fighters or landing troops. If they can't do anything but move they're not going to be very useful. It'll still cripple the AI but not as much as the other suggestions.
 
Or just check to see if you can enter the city, if so you have open borders even if the current unit is something like a Caravel than can enter rival territory. I believe the function you'd want is 'canMoveThrough'

True, but you still have to find the nearest city first. You can set being able to move through it as a criteria for being able to stop there. This can also be done with a modified findCity(...) function since that's the function that finds the nearest city. Changing the current function is exceptionally easy. Doing it through python is not so much.

The biggest problem you're going to have isn't so much how to impliment it as it is how to get the AI to understand it. If you damage the unit when it's out of range you'll kill off the AI ships, if you stop them dead you'll end up with a lot of AI ships sitting in the water unable to move.

What may be the best solution is to leave their movement alone and give them an 'out of supply' promotion that reduces their attack strength. For carriers/transports you could immobolize units in their cargo when they're out of supply to prevent them from blockading, launching missiles/fighters or landing troops. If they can't do anything but move they're not going to be very useful. It'll still cripple the AI but not as much as the other suggestions.

Well, I originally thought we were forcing a ship to stay within the range. If that's the case, it would actually be fairly easy to keep the AI within the proper bounds. If you simply make them unable to move beyond the range of the city, the problem is solved. That's not too tricky either.. just check whether the plot it's trying to move into is inside or out of range as it tries to move. It'll increase processing time slightly, but only for naval units which the game has relatively few of anyway.

The real problem is how you're going to be able to conduct trans-oceanic trips. I mean there should theoretically be areas of the ocean too far between a valid port on one side and a valid port on the other.
 
This only makes sense for modern naval units.

Prior to steamships, vessels could travel a coastline for an indefinite period, by occasionally making landfall and foraging ... the only time they were constrained by supplies was venturing into open water for any distance.

Steamships without sail could not travel on the open sea for hardly any distance at all. The coal weighed too much to allow them to move much distance. The most powerful of the early ironclads, such as the Warrior, featured sails so they could be deployed a long distance (potentially overseas), and then had steam engines for use in battle.
 
This only makes sense for modern naval units.

Prior to steamships, vessels could travel a coastline for an indefinite period, by occasionally making landfall and foraging ... the only time they were constrained by supplies was venturing into open water for any distance.

Steamships without sail could not travel on the open sea for hardly any distance at all. The coal weighed too much to allow them to move much distance. The most powerful of the early ironclads, such as the Warrior, featured sails so they could be deployed a long distance (potentially overseas), and then had steam engines for use in battle.

Well, since Gaius intends this for his WWII Pacific scenario, I think he's expecting it to apply for modern naval units.

Of course, as you said, prior to this, sailing ships could stay near coasts. So perhaps not ending your turn on a coastal tile could cause damage to a sailing ship. That way, if they make the trip to find the second continent (in most civ games), they'd arrive on the other side in dire need of supplies with very little health left which is fairly accurate. It would make intercontinental wars harder over long distances since the weakened ships arriving on the other side would be easy prey for enemy warships that prowl their local waters.
 
Back
Top Bottom