Forgive me if this gets a little rambling, but this is, in part, a matter of my reasoning through the problem (and also my showing my work), so it will be long and discursive. I will try to make an "executive summary" post at the end of all this to make a more concise case for a fluid system.
Allright, for my next trick, I have to reconcile the fact that we are dealing not with a single cross-sectional view of how much force will be pushing water across a plane, but to deal with the fact that water in this game is actually going to have to look much more like this:
http://en.wikipedia.org/wiki/Shallow-water_equationsThat is, if you are piping water off a cliff into a large pit at a constant rate of flow, then as water is added at the point where the water lands, then the sudden "pile" of water that is generated in one tile has to then be distributed outward into all the surrounding tiles.
One way of handling this is to simply divide the pressure into 8 seperate horizontal flows, but this starts to reveal one of the problems with doing this this way - we would then start to be dealing with fluids in the same interative, per-tile manner that I am trying to avoid making us use all over again.
Instead, I want to go back a step, and talk about how fluid actually MOVES once we have calculated the force behind it.
For the purpose of talking about flows, I decided that I would simply have to create a mock-up constant to illustrate how the system will work. Again, the problem is that it is difficult to find data on just how fast a puddle should spread, which the Internet is simply not helping with. (It keeps wanting to talk to me about refilling my water bottles. Stupid Internet.)
I looked at the current model we have to see what it would be replacing. Amusingly, the first 30 or so steps after I dug into the murky pool, it did nothing but stood there as an immobile wall of water, and the miner had a chance to walk several steps away. On the step that it finally did move the water (lets call this 31), it immediately pushed 4 of the 7 units of water on the adjacent tile into the opened hole. The next step (32) moved the tile behind it, so that it went from 4/7 north of 3/7 north of 7/7 followed by many 7/7s (thanks to the shape of the pool), into being 4/7, 5/7, 5/7, and then 7/7s. The next step (33) did nothing. The step after that (34) pushed one of the 7/7 to be 4/7, 5/7, 6/7, then a 6/7 surrounded on four sides by 7/7. This is what's in the first image below. Note the water not flowing into the open field. Step 35 did nothing. Step 36 finally pushed the water forward, leaving only 1/7 water in the previously 4/7 tile, and 1/7 in one diagonal, 0/7 in another diagonal, and 2/7 in the orthogonal. 37 did nothing. 38 caused a minor shift in the 6/7s. 39 did nothing. 40 gave my miner another step away, and shifted some 6/7s in the back of the pond. 41 simply passed the 2/7 water at the edge to the 1/7 without spreading the water outwards any (third image)... Then 42 just has the 2/7 flow back into the lake, instead of spreading outwards from it. frankly, this only impresses upon me the need to remake the entire system, so I'm not going to use this for any inspiration. In fact, by the time that the water spread to that other tile diagonal from the breach, another 22 steps had passed.
So then, I'm going to just have to make something that "looks right". By which, I mean I have to basically just figure out how fast water should flow based upon estimating how tall a wall of water a dwarf should be able to outrun. The answer to that is "probably not very tall" or let's just say .5 meters of depth just to give it an arbitrary number.
Now, we can start reverse-engineering what sort of constant we are using to get this number back from the predetermined answer that was pulled out of thin air.
Rate of Flow = Velocity * Area through which the fluid is travelling. We are making velocity up, and declaring it .3 meters per frame, since a tile is 3 meters, and 1 turn tends to take around 10 frames. This means time is a measure of frames, now, not seconds, which would screw up most of the other calculations if I wanted to go out actually using calculus and newtons of force. The area for this one particular case is the three-meter-wide tile, but even if it is a 3-meter square opening, only the bottom .5 meters are actually filled, so we are dealing with a Rate of Flow of .3 m/frame * 1.5 m2. This winds up giving me .45 m3/frame, or 450 liters to be pushed from one tile to the other in this one frame. For reference, .5 meters depth of fluid in a 3-meter-cube tile is 4,500 liters of fluid, total, so 1/10th of this fluid would be pushed in the first frame/check into the empty tile. (Note that this would cause the original water tile to have a depth of .45 m in the next check, and the second tile to have a depth of .05 m of water when the next flow check takes place, for a relative difference of .4 m of water, cutting subsequent flow.)
Now, we need to make the link between this velocity, and the pressure of the relative water depth difference. If the pressure generated from .5 m of water results in .3 m/frame velocity (or, to make it as a force, .3 m/frame2), then we are making this constant .6 units/frame.
Now, we can actually start doing many more equations - a full 3m pit of water when a wall is mined out adjacent to it will then generate pressure that creates an immediate velocity of 1.8 m/frame, and have an opening area of a 3 meter square, or 9 m2. This results in a rate of flow at the first check of 16.2 m3/frame, or 16,200 liters of the total 27,000 liters. Amusingly, this is actually more than half (which probably indicates my arbitrary constant is too large, but whatever), which would result in water sloshing back and forth until it would level off. Because mechanics like this would simply keep sloshing back and forth smaller and smaller amounts of water approaching infinity, there will have to be a point where the volumes that are flowing are considered so small that the CPU just stops tracking it, and teleports water to the point where it is at rest, but I'll go into that in more detail when I have the rest of this better fleshed out.
Now then, I can go back to where I started this post, and start talking about how this model needs expansion based upon two major problems - one is that bodies of water can have multiple "outflows" from it at once, like a single "swell" of water that must be divided in multiple directions, but the other is that I am talking about a velocity of water (and technically an acceleration of water) based upon pressure. If we had a situation somewhat like having a large water tower with a hole punched in the side of the tower near the bottom, the water would not simply drop, it would be projected into a stream of water. This latter problem is the problem that I want to tackle next - bodies that have a flow/velocity.
Therefore, bodies of water have to be divided into two types - lakes and rivers (or pipes), or bodies that either have no flow or do have a flow.
Lakes are those standing bodies of water that have no motion other than sloshing around the water in response to more water being added, or perhaps water being drained. When water is drained off of a lake, thanks to opening a floodgate, or maybe a dwarf digging a channel, water then starts flowing out of the lake at a given velocity. If the tile that is being flowed into is just a dead end, it simply becomes part of the lake whose water it took, expanding the body of water. If, however, once water flows into an empty space, and the computer checks, and finds more space for that newly flowing water to flow into (like a long channel), then a new body of water is created, a river, and the river has a flow. Even though it is contguous to the body of water that is a lake that created it, the river now has a special flow and velocity mechanic associated with the body of water, with the interface between the two bodies of water being the opened floodgate. The flow of water into the new river translates into its velocity travelling onward through the channel. As water is pushed into a river, it tries to push the water it is recieving forward at a similar rate, or picking up the pace if it happens to go down a drop.
This is viewed from the side. The "lake" that this channel is draining is arbitrarily wide, so it does not appear to drain, for the purposes of just demonstrating a continuous flow through an empty channel.
This might seem semantic at first, but it is important in the building of rivers and brooks and dwarven perpetual motion devices that the notion that water has a velocity, and is maintaining its momentum until it has a reason to stop.
Consider again the water tower with a hole in the side - if that water starts spouting out from the hole under pressure, it will have forward momentum. Falling water itself would actually need special rules for applying gravity to the momentum (downward) so that it accellerates to a terminal velocity before it finally can come to rest wherever it lands. In this case, the "river" would be a continuous flow from the hole down to wherever it lands, although perhaps this might be translated as individual globs of water with individual trajectories being pathed individually, depending on how much "spray" we want to have. (In this case, it might be necessary to create a third type of body, a "spill" body, whose purpose is just to handle projectiles where there is no inflow or outflow of water, simply a traveling body of water that is trying to path to the lowest point it can find to be at rest, at which point it can turn into a "lake" body, unless it joins another body.)
In a brook or even a rapids, we can consider the "river" type body of water to simply be having an inflow and and outflow, which we can easily measure, and as long as the two are equal, we can save on processor power by just assuming the river continues flowing without changing in its shape in any fundementally important way. The same can be said of "Dwarven Perpetual Motion Devices" - when pumps remove water from a current (and place it back into the same current), we can just balance the inflow and outflow to find a stable state where a constant rate of flow occurs, without having to waste CPU cycles finding the exact same answer over and over.
In the case that the inflow and outflow are not equal, such as that diagram I made above, where water is inflowing, then the water has to expand outwards to find a place to store the extra water that is entering this body. If, again, this turns into a dead-end channel, then the whole body of the "river" will eventually turn into a "lake" as it fills its container, and the two contiguous "lake" bodies can be joined (until the floodgate is once again closed).
The important part is that the expanding water has a starting velocity. Real rivers will then have dips and rapids and narrows that can alter velocity accordingly (the narrower the river, the faster it will run, proportional to its starting point). This means that we cannot track a river as having a velocity, but rather that different slices of rivers will have different velocities. (This may actually be better handled by simply making seperate river bodies to track, however.) This would get especially important when dealing with pipes, or aquaducts or other ceilinged water flows, as dropping water down in elevation before forcing it to come back up a bend will actually dramatically increase the water pressure and potentially its velocity as it drops.
This means we can handle the problem I was talking about in the start of this post in a different manner - when water is added into a pool from above, this creates a "swell", a raised portion of water in a lake, which then has to be distributed. How this ultimately gets distributed in a lake is obvious: it should be perfectly level, with all tiles at the highest z-level having the same water depth. The problem is finding the right rate to balance this swell out against the nearby tiles (which is a matter of that constant, above), and having a final smoothing function that simply makes a lake that has sloshing finally just die down instead of having "ripples" that slosh water around at the top of a lake. More important, however, is that this allows us to still treat this as a single body with a single set of calculations, rather than as a bunch of individual tiles. This means, rather than having one tile with more water in it than others, we are tracking a "lake" which happens to have a swell centered in one tile, whose water can be tracked as partially distributed. (Inversely, there should be a "depression" function for water draining out of a lake at a certain point, so that water outflowing a body creates a small whirlpool as water is removed first from the tiles near the outflow, with water flowing towards the depression to even out the lake again.)
I've been keeping this up on my screen for a couple days, and I don't want to lose it again, so I'm going to post this here.