December 15, 2018

Rivers VI: Lakes

A few months ago, Alex Schroeder left a comment on one of my river basin head-wrackers. Unfortunately, the comment has now been deleted thanks to Google+'s wonderful management strategies, but the point still stands: when rivers are trapped, they usually can form lakes and flow out anyway, even if the hex is "higher."

Something that I have to remember is that a hex altitude is the average. The hexes are not simple flat planes stacked like the Giant's Causeway.


So, it's totally possible for a river to flow from a "lower" hex to a "higher" hex, simply following the shortest route to the sea. Perhaps it carves a canyon not visible on the main map.

This algorithm needed a little elbow grease to implement, but it works. There are still some totally endorheic lakes. I can either continue to jitter the surface to make everything shake out, or just be ok with it. A shocking amount of the earth's groundwater never reaches the ocean (over land, anyway), so it's not a terrible thing.



Right now, I'm just representing the lakes with circles (and non-navigable rivers with red). I agonized for a bit about making nice, generated lakes, but then I realized that...it was fine not to. This is not a beautiful overland map like Scott makes. This is, more or less, a representation for game purposes. So it's sufficient to say "here is a lake."

The other thing to point out about this map is the size of the rivers. Again, this is purely representational, but I think I will continue to scale the numbers until the map is not quite so busy. I will have to determine the minimum river size necessary for trade, and that will be the low cutoff.

This map is also not adjusted for precipitation. Since I've just now completed the elevation, I still have to run the wind and rain algorithms. But the actual drainage networks should remain unchanged.

December 13, 2018

Elevation XIV: Let It Be Done

I've decided (for now) to be done with terrain generation. I've got something I like even if it's not perfect, and to facilitate the rest of my project I'll let it ride for a while at least. It won't matter too much if I do decide to come back and change it (as will no doubt happen) because everything is generated.


I still might do some tinkering but this will do.

December 7, 2018

Gimme That Old Time Religion

I'm not the first to address the issue of religion in D&D, and I'm certain I won't be the last. But I think it's worthwhile to begin thinking about how the divine works in my world, at least to form a coherent treatment of clerics, monks, and paladins (and sort of warlocks, though they are not connected with outside religion).

For the paladin, his oath is what provides his power. The cleric, however, is granted strength by a Divine Being.

Yet there are more orders of clerics than just one. How can this be in a world where proof of your god's existence is instantly accessible by any mook with a holy symbol? RPG clerics live in a world where they walk by sight, not by faith.

There are some solutions to this, of course. One solution is to make all clerics worship the same deity. There cannot be more than one.

This is a little unsatisfying. But consistent.

If we wish to have more than one clerical order, following different gods, then it follows naturally that these deities cannot be all-powerful, and therefore not gods at all. If they were all-powerful, and also all true, then they could not all coexist logically. Zeus and Odin cannot be both god if both exist (as separate entities).

Therefore, then, clerics could gain their power by belief in a localized (more or less) entity which existed on a plane above their own. This is a different level (higher or lower?) than that which a warlock patron might occupy.

Clerics, of course, are gregarious, while monks are ascetics. I'm thinking that a leveled cleric would probably be a step up in whatever hierarchy exists from the local priest, but of course could certainly take up that mantle. A priest might be able to conjure some small healing magic or other stereotypically clerical things, but for the most part is responsible only for the spiritual journey of the local flock, whatever that might mean. Clerics are holy warriors with additional training from an established temple.

I don't really like monks. So I'll ignore them for now. I think clerics fit the role much better, without the edgy Bruce Lee vibes.

So what do they worship? I was browsing around and found the Book of Urantia, an absolutely insane mystical document of no religious value, but it's got some good inspiration for worldbuilding. Paper 85 identifies 6 progressive sources of worship:
  1. Stones
  2. Plants
  3. Animals
  4. Elements
  5. Heavenly bodies
  6. Man
Of these, elemental forces are the most attractive to me in terms of a cosmology. However, I must recognize the extreme difficulty of putting myself in the shoes of an ancient man who worshiped, say, bears or fire. That said, I suspect the desire to anthropomorphize was still strong in those days. So the lightning that destroys trees and people becomes the Father of Storms and we'll give him a name, Ja-Pater.

Ritual worship involves invoking the power's favor or appeasing it in some way. The question arises as to whether the cleric's powers are dependent on correctly approaching the power. Would schisms arise? If so, wouldn't it immediately be obvious that one side picked the wrong theological formula? Can a rogue cleric be stripped of his magic by his superiors as punishment for some crime against the deity? Food for thought.

Ultimately, clerics must operate in some kind of organized (named) religion, whether it's a structured church or a looser collective like shamanism. I'm not sure yet how to determine a local religion procedurally, or if that's something to save for a rainy day. It's not going to matter that much for my planned model of empire growth.

December 6, 2018

Elevation XIII: The Uplifting II: Return of the Uplift

So let's examine some of the intermediate output. The full blended map is a little hard to process, so I'll crack it apart:
C
T
D

So there is very heavy C+T concentration in a few areas, and essentially flat elsewhere. Same with D, but at least its not in the same spots.

While IDW gives a nice smooth interpolation, I want it to be a bit spikier. The top-left continent should have a small C band running across it, yet this is smoothed all the way out (with small whiter patches where T is dominant).

The problem with IDW is that it does not know that the value is supposed to drop off from the actual fault line. Instead, it's a smooth interpolation. Which is good for many other things, but might be a bad design choice here.
What about a multi-modal Gaussian? I could determine the distance to the different peaks, have a controllable drop-off, and then sum it all together to get a nice smooth distribution.
Much better. I could improve a few things, of course. The simulation takes a long time to run still, but that has more to do with the distance calculation - I have to calculate every hex's distance from every other hex.

So the uplift becomes:

This uses $c=1.0, t=0.5, d=-0.3$ for weighting factors, with an additional gamma correction of $\gamma=5$. Gamma correction is common in photomanipulation, but if you want to do it by hand, use this formula:
\[x' = x^{1/\gamma}\]
Where $x$ is your scalar between 0 and 1. This adjusts where the "average" greyscale value is, and can therefore be useful if an image is too bright or dark, while maintaining the 0-1 scale. All in all, there's a nice Fresnel effect going on here.

A niggling thought has entered my mind that I should do like everyone else and build coastlines from the ground up terrain generation. We'll see how things go...it's very tempting. A lot of the highest uplift here is beneath the sea! And many divergent boundaries cross the land (which isn't wrong, but uncommon).

December 5, 2018

Elevation XII: The Uplifting

Recently, I discussed how the sauce was made for the uplift maps. It includes this formula:

uplift = (
    0.5 * c/max_c +
    0.1 * t/max_t -
    0.4 * d/max_d + 0.399
)

I'd originally tinkered around with this to get the right coefficients. Now, though. I want to revisit it.

The greatest uplift should occur when C and D are both at their max. The lowest should be where D is max and the others are min.

But this was a distraction from the real issue: so far, I'm not considering the strength of each fault line. Take a look at these plates:

convergent = red; transform = green; divergent = blue

These boundaries should not be treated the same. The thicker lines indicate stronger interactions, and represent a larger uplift.

To do this, I'll find the closest fault for h in hexes, and capture $s \over d$, where $s$ is the strength of interaction, and $d$ is the distance to the fault line.

.......It was at this point that my project was diverted into fixing the hex distance function. It's not as easy as using the standard distance formula. Why not?

Remember that I'm using a novel hex plate "projection" from the Tao. This means that distances between hemispheres are 1) not displayed according to their true position and 2) are "toroidally" wrapped between the equator. If the two hexes in question are in the same hemisphere, there is no issue: I can use the regular distance formula:
\[d = \sqrt{\left(x_2-x_1\right)^2 + \left(y_2-y_1\right)^2}\]
Which can be found trivially. But how to handle different hemispheres?

Amit's page here (of course) was extremely helpful in working through this. I realized that if the on-screen distance is correct inside a hemisphere, then I needed to transform one of the hemispheres to simulate this, and then take that distance.

I had been using a really inefficient method where I found a point on the equator that minimized the distance between each hex and that point. I feel dirty just describing it. This method is cleaner, quicker, and not brute-force.

Then I realized that it would be even better to use IDW (which is great but can take a while to run). So I get this map (using $n=2$ for IDW), which still doesn't make much sense.



The uplift map is easier to interpret (with only one variable instead of 3 smashed into a single color code).


I still don't think I've found the right formula. But I think I'm on the right track. There are not nearly enough high uplifting ranges, and this causes the simulation to stop "early," when the height reaches the maximum value. The problem is, the rest of the map is still pretty flat at this point. So some work remains to be done.

November 26, 2018

Wind VI: Wind Pun

Getting the wind model to work is the easy part.

Figuring out what parameters to use is the hard part.

One issue that I ran into last time is the fact that if wind starts going in the same direction from a bunch of sources, simple vector addition can add up fast.

That's because I've been treating my wind collisions as elastic collisions. There are two major kinds: elastic and inelastic. In an elastic collision, no energy is lost to heat, etc. So two (or more) winds enter a hex, one wind leaves. The following formula is used:
\[m_0 v_0 + m_1 v_1 + \cdots + m_n v_n = m_f v_f\]
This is essentially what we've been doing, in two dimensions, treating all masses as equal (even the final mass). Because I'm ignoring masses, this isn't really a physically correct description of an elastic collision.

The other option is inelastic. I think this is better, because we can think of the input winds "sticking" together into a new mass of air. This is not how fluids really mix, however, but it will be good enough. This formula is (ignoring masses again):
\[v_f = {v_0 + v_1 + \cdots + v_n \over n}\]
Which is simply the average.

So let's try a few combos of parameters. \[g\] is the ratio of decrease between each successive push, and \[z\] is the slope change (in feet) at which the wind is deflected by 45 degrees.

g=0.7, z=5000, elastic
g=0.7, z=5000, inelastic
g=0.7, z=2500, inelastic
g=0.8, z=2500, inelastic
g=0.9, z=2500, inelastic
g=0.99, z=2500, inelastic
Overall, the inelastic collision seems much more accurate. I also need to take a look at the downhill slope speed increase, which is not set for these simulations. The wind is still modulating into bands of 60 degrees, even with Gaussian blurring. Scott has trouble with straight bands, I have trouble in an additional dimension!

Also, I need to develop some better representation for topography. The heightmap is huge overkill here, but its difficult to see the effect of altitude when height is not shown at all. Something for a rainy day (of which there are many upcoming).

November 21, 2018

Wind V: Clearer Winds

As often happens, I'll publish an initial post on some subject, then add bells and whistles on to my code, then post the "finished" product with a few quips, and expect the audience to have insight into the chaos of my workflow.

So I've decided to put the wind model down on blog-paper, at least to make it clearer to myself and others. I was inspired by Here Dragons Abound, who also revisited his wind model and arrived at the same method that I did (just with better pictures)! So this post is essentially the same, just with some different words and pictures, for my own reference.

Getting the initial wind values at the coast was the easy part, really. But what to do once I have them?

Ideally, I could use some kind of iterative finite element method to solve for the flow across the surface. But that's pretty complicated and if I can find something simpler, I will.

There are a few things to keep in mind as we create this model.

  • Wind slows as it drags across the ground
  • Wind picks up speed when going downhill and slows uphill
  • Wind is turned aside by mountains
So there are (in the most simple representation) 3 parameters that can be tuned. I'll call them $g$ for ground drag, $s$ for slope change (where $\Delta$ can represent the different in height of the hexes), and $\theta(\rho, \Delta)$ for the change in direction by a mountain (or hill, etc), where $\rho$ is the sensitivity of the wind.

Let's begin with a single wind vector.

The basic method of propagation is along the vector projection of the direction between the two hexes. I refer you to this diagram from the post on tectonics. We are interested in the $d_{\perp f}$ vector.


Projecting the vectors:

One downside of this approach is that eventually, every initial vector gets binned into one of the 6 hex directions, so the wind map becomes much more West Marches-esque, with strictly defined angles in increments of 60 degrees. But let's propagate each of these three resultants.

Next, we add the vectors (simply adding the $x$ and $y$ components) for E and B.

And of course, E and B will project onto D, and so on. But I'll ignore that for now.

So what about mountains, or other obstacles? How much is it affected? Let us assume that A represents a high mountain at 10,000 ft, and that the other hexes are 1 ft. We can then define $\theta$ in terms of $\Delta$ as:
\[\theta = {90 \exp\left(\rho\Delta\right) \over 90 + \left(\exp\left(\rho\Delta\right) - 1\right)}\]

The value $\rho={\ln(89) \over 5000}$ indicates that the wind is turned aside 45$^\circ$ at a slope of 5000 ft. So for our Mountain A, it will be 89$^\circ$. I have no idea what number will work best here. The relative direction of change is to the right in the northern hemisphere, and to the left in the southern hemisphere.

And so it continues (I haven't actually propagated A out fully).

As far as picking up speed goes, I have $s = g$ if $\Delta < 0$ else $1$. But I want to do some tweaking of this anyway (I also see a lot of stuff that I want to update/clean up, just from going through this exercise). I've been using $g = 0.946$ after my initial experiments.

So the final model for the effect of wind from Hex 0 to Hex 1 (where $h_{01}$ represents the vector from 0 to 1):
\[w_{01} = g \cdot s \cdot {w_0 \cdot h_{01} \over |h_{01}|^2} h_{01} + \angle \theta(\rho, \Delta)\]
I guess that looks kinda complicated. If you can't wow them with facts, dazzle them with bullshit.

There is also a choice to be made in the algorithm for propagating vectors. Either we can view this as an expanding frontier, where the next set of hexes to be solved is defined by where the vectors in the current frontier are pointing (projection is only ever positive for 3 hexes at most), or we can fully propagate each vector from each source (which are all coastal hexes) and then average them at the end. Right now I'm using source-propagation since its a little faster, but I'd like to take another look at frontier-propagation.

I'll save the good stuff (the actual application on the map) for another post.

November 20, 2018

Elevation XI: Uplift Revisited

One of the most important inputs to the erosion model is the tectonic uplift. I've tried a few things with this in the past, and now I'm back again for another crack at it.

The basic idea behind a hex's uplift value is its distance to convergent, transform, and divergent faults. It's difficult to compress this all into a single number, so I played around with the values until I arrived at this, where i and max_i represent the maximum distance from the C/D/T faults:

uplift = (
    0.5 * c/max_c +
    0.1 * t/max_d -
    0.4 * d/max_t + 0.399
)

So every hex will have a relative amount of uplift on a scale of 0 to 1. I do rescale this later so it goes from 0.1 to 1: no uplift means problems like sub-sea elevation terrain that I don't want to have to deal with (thanks, New Orleans).

Fault map; yellow is the highest uplift, blue is highest divergence, and white is a conjunction of all three
This number can then be fed into the erosion model to determine how much the land is rising while water is trying to eat it away. Usually, the tectonic plate wins.

So this gives us the first version of the uplift model. There are a few surprises, where I expected to see higher values, but it turns out that these are in proximity to divergent boundaries which reduce the overall number.


But this is too simple (of course). Plates don't collide like this: they ripple. There's a good approach here on one solution to this. I'll take a different tack, simply using a screening mask to to modify the values. Yet another optimization step! But this is what I will use initially, after much mucking around with different values, where $u_0$ is the initial uplift, $u_f$ is the final uplift, and $a$ is some constant.
\[u_f = \left(u_0 + {2 \sin(a u_0)\over a} + {\cos(a u_0^2) \over a}\right)^3\]

a = 50
Looks ok. Just too smooth. So we'll salt it a bit with some simplex noise:


There's still a significant problem as it relates to the overall terrain, though. There's really only one major mountain range. Everything else is at least half that value. Either 1) this is ok, and just an acceptable outcome of the inputs or 2) I can use gamma correction to modify the map.

But I'll tackle that later.

November 14, 2018

Resources XVI: Manganese and Nickel Mining

nickel
magnanese

If we want good steel, we need manganese and nickel. Strictly speaking, these are ingredients of wrought iron, but I'm sticking them into steel. The Spartans may have lucked into manganese-rich iron ores, producing superior weapons.

Thankfully, nickel is easy. It's found in a form of limonite, which I've already categorized as an iron ore. So a tiny bit of limonite will yield nickel, and therefore it's not a Stage 0 raw resource.

Manganese is not terribly difficult to research either. It comes from pyrolusite (found, like everything else, with chalcopyrite). In the interest of fantasy-style names, I'll call it umber instead (which is a rich brown pigment made of crushed pyrolusite). In fact, I wonder what might be some more fantastical names for hematite, etc. Good subject for a future post.

Umber has an occurrence per hex of 2%, but this is improved by the presence of goethite, hematite, mountainous terrain, or boggy areas.

November 12, 2018

Resources XV: Revisiting Metals in Demoland

Now that we have data on all the metals we need, I'm going to revisit the resource distribution in Demoland. From here, we can gradually build up society as it might have expanded in this kingdom.

This map shows potential resources in each hex, not necessarily the exploited ones. I've also left off agricultural references, because the map tends to get pretty cluttered, even with just a few symbols. The symbol reference can be found here. I've grayed out the symbols on purpose. Once we being actually placing them (again), they'll be more visible to indicate their exploitation.

Maps like this can be helpful for certain types of campaigns. Knowing what materials are where can inform an ambitious party searching for a source of wealth, or a place to found a town and be king of their own country. At least, until Rich Neighbor gets jealous...

November 9, 2018

Resources XIV: Placing Copper Ore

Chalcopyrite has some relation to both gold and silver, and so it could be considered a lynchpin of this system. But we will also consider the other copper ores here as well.

I've found a really good resource at Mindat.org, which gives the number of mines around the world for different minerals. I can plug this into my formula and get a percentage occurrence for my own world.
  • Chalcopyrite: 27,901 deposits. 16.9%
  • Malachite: 12,759 deposits, 7.8%
  • Azurite: 5,593 deposits, 3.4%
Not bad. Azurite and malachite are linked, and chalcopyrite is linked with volcanic areas and coal seams.

November 7, 2018

Resources XIII: Placing Silver Ore: Redux

I made an error in the previous post. To enshrine my mistake, I'll fix it here.

I forgot to multiply by the hex area factor. If I do so, I get an occurrence of 700%! That implies that there are 7 deposits of silver in each hex. That has to be wrong, otherwise silver would be essentially worthless. So I made a bad assumption.

Instead, I will use the Mindat method (discussed in the future copper post). This yields:
\[{5,484\textrm{ deposits}\over 57,000,000\textrm{ mi}^2}\cdot {347\textrm{ mi}^2 \over 1 \textrm{ hex}} = 3.3\%\]
That's a bit more reasonable. Again, keep in mind that 347 square miles is a massive area, and that's just one hex!

Resources XII: Placing Silver Ore

Silver's crust abundance is 0.0000079%, so about 2000x more common than gold. Let's use the numbers already generated for gold:
\[{2000\cdot580\textrm{ deposits}\over 57,000,000\textrm{ mi}^2}\cdot 347\textrm{ mi}^2 = 2\%\]
For now, I'll ignore galena and pretend we're mining pure silver (galena, remember, is only about 2% silver). Just like with gold, we see an opportunity to increase the occurrence alongside chalcopyrite, which is quickly becoming my favorite mineral. So, to get the placement for gold and silver down, I need to tackle copper ores.

Edit: In which I correct a mistake.

November 6, 2018

Desirability II: Where Do All the People Go?

I've been getting way behind on posting, largely because of my struggles with getting the climates to fall out just right. I know, I know, there's a fair bit of ad hoc going on here. But I've had some trouble in the past with my climates swinging directly from harsh rainforest to harsh deserts. I've got to figure out some methods to smooth that out.

First of all, I can lower the temperature model. It's not just the rain - although that hurts too. Temperature is an odd bird, especially trying to go from this chart on Cartographer's Guild to some actual numbers that I can use for my model. I may need to step back to those isotherms and see if I can build up the coherent model from that. I've been trying several different math functions to try and get smooth interpolation, but I haven't found one that works.

Koppen map
I want to see more green here. The real world has a lot of green (which is the C climate type, really good for crops and such). Some tweaking improved this. However, there's still very little D (boreal), denoted by turquoise. This isn't really a problem, exactly, but I wonder how much of this is due to the terrain/climate system and how much is due to the errors of the model.

However, I can take another look at my "desirability" maps. I still have some tweaking to do to make the right recipe, and then do it again for all the races I want to include! Here is where the climate distribution shows its ugly belly.

The greener, the better
It seems at first glance that there's a lot of empty space. Everyone wants coastline, of course, but no one wants to be in the desert (humans, anyway; I like the idea of desert orcs).

Things to think about:

  • Why did the terrain generator make all my coasts so low? Good coastal access should be a bit rarer - as it is, 98% of coastal hexes are lower than 500 ft.
  • How do different climates affect desirability rating? How about resource availability (which system will take years to fully develop, of course)? How about other cities and their cross-country roads?
  • How should I start placing cities on here once I like the maps? Of course, I can start work on that model right away, since it's all generated, and refine them in tandem.

October 29, 2018

Elevation X: Once More with Filling

I have been working more on my terrain generation, and it's looking better if not entirely Earth-like. At some point, it will have to be "good enough." I made a change to the code which fixed some of the deposition problems, and definitely fixed the problem where the terrain would reach an equilibrium height (which was usually very low, like 3000 ft).

Of course, there are still problems with endorheic basins, and equilibrium. Eventually (around 22,000 ft), I stopped the generator and made some "manual" adjustments, which usually involves adding in some Perlin noise (or other types), running a few erosion cycles, and then scaling all the heights to [1, 25599] ft.

I also made some changes to the SVG code and got rid of the unsightly hex borders, so everything looks a little smoother now. I am considering writing some code to illustrate the height a little better, maybe one of the classic green-brown heightmaps. On a b/w map, anything under 6000 ft is basically not visible.


Next, I want to revisit the wind algorithm. The precipitation model is good, but the winds blow in odd directions onto the coast, and I think there is some weirdness going on there. There is too much moisture in some places, and not enough in others, so the Koppen map ends up evenly divided between utter desert and hostile rainforest. There's not nearly enough of Koppen class C, which is the most desirable (imho) for human habitation.

(what sorts of races prefer other climates? food for thought)

October 26, 2018

Resources XI: Silver Mining

silver
lead/galena

The primary ore of silver (at least in ancient times and for my purposes) is galena. Galena is also a lead ore, and produces about 1-2% of silver. To my geologically untrained eye, its occurrence is more or less random, except for a relationship with chalcopyrite.

Because I'm itching to start playing, I may skip over silver (and gold) production in the trade system. I'll still work on them, of course, but I might use "silver" directly (as a raw resource) rather than bother to work out the details of production.

That brings to mind an interesting question about coinage. This would be restricted to an administrative center or licensed foundry, and while raw nuggets of metal could certainly be used in frontier towns, a gold mine does not necessarily equate to a flourishing coinage economy. The bullion has to make its way down to the foundry (if its located elsewhere) to be converted to spendable coinage appropriate to the realm. So that might be interesting to study later on, if I include foreign currencies rather than a flat "gp/sp/cp" across the globe. Unrealistic, I know, but gameable. I'm not a monster.

October 19, 2018

Detail I: One Mile Hexes

Since I'm not even close to being done with my world map, I decided to tackle the 1 mile hexes instead. (In my defense, the simulation I'm running now is taking a really long time to finish).

This is really complex project. The main issue is getting each chunk of 1hexes to match with the 20hex from the world map, in terms of the connective data like river flow, terrain at the edges, etc.

Each 20hex is made up of 400 1hexes. Getting raw heighmaps is easy enough. I just use an inverse distance weighting between all of the 20hex neighbors, then add noise until the total average is reasonably close to the 20hex height average.

Hex 13 from the Demoland Master Map
Then I can figure out drainage and streams (not really rivers at this point) using similar code for the world map - water flows downhill.


Thats a lot of basins! I would like most of them to pull together to a single exit hex. Each hex always has 0 or 1 river exits. Hex 13 happens to not have any rivers flowing into it; I picked it because I only want to worry about one new issue at a time. With a little playing around with the heights, I can get the following:


However, it takes a few minutes to run this particular code: I'll adjust the height of a basin, check the drainage pattern, and repeat until I've worked that number down to a point I like. The result looks good, but ends up messing with the average value too much. Sometimes, it's almost impossible to find a good network.

When rivers flow in and out, it gets even more complicated. I want to be able to put these together like puzzle pieces, so that means saving the entrance and exit hexes so I can use them again later if I want. But to ensure a consistent flow, I have to cheat again.

Hex 14, river sizes exaggerated
Right now I think I have a problem with my A* algorithm, which is supposed to find the best downhill path from the blue (entrance) to the red (exit). It works, but I think I can do better.

Explicitly connecting the drainage hexes together like this might actually end up being better in the long run; better than the height adjustment from above. I'd like to add some of the erosion code (or something similar) to give more realistic topography.

Why is there  a little break there??

So I need to fix my A* and work on erosion. I wonder if a simple flood fill path-finder wouldn't work better for connecting source and drain. It'd be less efficient, but then again, a winding path would look much better.



I'll probably put this back on the shelf and tackle the remaining issues with the world map generator. However, that's nearing a stage where it'll mostly be research for the trade system, at least to a point where I can get a game up and running.

October 8, 2018

Koppen with Smoothing

I kinda like this. I've also tweaked the temperature distribution a bit.


October 5, 2018

More Koppen

Testing out the new wind algorithm. With a perfectly flat world, we get the following Koppen distribution.



It's interesting that Af (tropical forest) butts right up against Cfa (temperate). This has to do with the heavy rain sweeping across the Af areas - on the border with Cfa, the temperature drops (and changes the category) but the precipitation does not change in kind. This leads to an odd boundary, but it's not unheard of in the wild. I'd just like to see a few more A categories, so I need to tweak the amount of precipitation.

I also am considering the mode smoothing discussed here. I think that might help the artistic effect at least.

October 3, 2018

Citizens of Demoland

Let's generate a few people to see how well the system is working. I'll use the Markov generator for names, and the probability calculators to generate properties. No classes yet, and the age doesn't account for the racial differences I plan.
  • Cadein, a 46 year old female human of level 0
  • Siâm, a 22 year old male human of level 0
  • Trynn, a 33 year old female human of level 1
  • Estan, a 66 year old female human of level 0
  • Refa, a 21 year old female human of level 0
  • Browrt, a 72 year old female human of level 0
  • Suli, a 71 year old female human of level 0
  • Siân, a 63 year old female human of level 2
  • Lynn, a 71 year old female human of level 0
  • Ifre, a 44 year old male human of level 0
  • Elyn, a 23 year old female human of level 0
  • Beth, a 36 year old female human of level 0
  • Wilym, a 47 year old male human of level 1
  • Arai, a 40 year old male elf of level 4
  • Treddun, a 31 year old male human of level 0
  • Gwalled, a 21 year old male human of level 1
  • Olina, a 37 year old female human of level 0
  • Jestaffith, a 27 year old male human of level 1
Arai is an elf of some distinction. Assuming the players are lower than level 4, they'd probably have to talk to Siân, and she could either make an introduction to Arai or find someone who could. In a martial culture (as this is D&D), combative prowess fits in rather nicely with social order - even in a "civilized" area. Perhaps he is even an artisan of some kind.

Gwalled is an up-and-comer. Certainly someone to keep an eye on, or who might be looking to sharpen his natural talent as a man-at-arms for the party. Trynn and Jestaffith are also not far behind.

Browrt, Suli, and Lynn have given their lives in labor with little to show for it.

The name generator also gave me these gems, which I assume sounds like someone on Pobol y Cwm having a stroke:

  • Llteigraidrynwenoladei
  • Gawalltegwen
  • Emlynervieulurice
  • Efoliesylynwenn
  • Neiloyddaffithan
  • Terchtonwynwynna
  • Gaendandawaladeweniforwen
  • Maromnellynnallyn
Obviously I need to fix the maximum length specification.

October 1, 2018

Demographics V: Battle of the Bulge

I really should have added this to the last post, but I'm kinda locked into my 3-times-a-week posting schedule, and I'm running dry on content while I work out some of the terrain issues. So the 2 real people and the 40 bots that read my page will just have to suffer through this journalistic malpractice.

Anyway, I presented a logistic model to describe the age distribution of a city. In reality, not every place/race will have a bottom heavy pyramid, which is indicative of rapid growth. Low growth pyramids (like many countries today) have square shaped "pyramids," and negative growth shows a bulge.

I wanted to replicate this better. At first, I thought I could just multiply by a shape function to force the distribution to be like I wanted, but there were too many challenges. Eventually, I settled on using polynomials.

But not just any polynomials. To ensure smoothness, I want to use Bezier curves.

Only, Bezier curves are parametric, and I want a function such that $B(x) = y$. Hm. After some searching, I found a way to interpolate between given points as long as they're evenly spaced, using Bernstein polynomials:
\[B^n_k (x) = \sum_{k=0}^n {n \choose k} t^k (1 - t)^{n-k}\]
This polynomial is used to approximate the curve like so, given sets of points $\lbrace x_k, y_k\rbrace$:
\[y(x) = \sum_{k=0}^n y_k B^n_k \left({x - \textrm{min}(X) \over \textrm{max}(X) - \textrm{min}(X)}\right)\]
Easy!

For now, I'm using $n=5$, because I want 6 total points. This will allow 3 control nodes to restrict the shape of the curve.

High growth
Low growth

Decline


It's still not perfect. Nothing ever is. But it should give more realistic results. Given a growth rate regime, I can add some noise to the control points to generate gently varying curves. This takes a little more time to run through in Python than the level generation does, but that's ok. Most of this type of thing can be run on request when the party arrives in a town.

The players will never see it. But I'll know.

Also, I guess I shouldn't be surprised that no one else (that I've found) has worked on stuff like this for their games. Where are the designers? Or are they just worried about the art in their next Point A to Point B splat book adventure?