-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSandBar.py
6375 lines (5860 loc) · 257 KB
/
SandBar.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
##############################################################################
## File: SandBar.py version 20120612 (June 12, 2012)
## Original file: PerfectWorld.py version 2.06
## Author: Rich Marinaccio
## Modified by Sam Trenholme; I am assigning all copyright to Rich
## Copyright 2007 Rich Marinaccio
##
## This map script for Civ4 generates a random, earth-like map, usually with a
## 'New World' with no starting locations that can only be reached with
## ocean going technology. Though great pains are taken to accurately simulate
## landforms and climate, the goal must be to make unpredictible, beautiful
## looking maps that are fun to play on.
##
## -- Summary of creation process: --
## First, a random heightfield is created using midpoint displacement. The
## resulting altitudes are then modified by a plate tectonics scheme that
## grows random plates and raises the altitudes near the plate borders to
## create mountain ranges and island chains.
##
## In generating the plot types from a heightmap, I had found that using
## peaks for high altitude and land for less altitude created large clusters
## of peaks, surrounded by a donut of hills, surrounded again by a donut of
## land. This looked absolutely terrible for Civ, so I made it such that
## peaks and hills are determined by altitude *differences* rather than by
## absolute altitude. This approach looks much better and more natural.
##
## The terrain generator gives the other needed visual cues to communicate
## altitude. Since air temperature gets colder with altitude, the peaks will
## be plots of ice and tundra, even near the equator, if the altitude
## is high enough. Prevailing winds, temperature and rainfall are all simulated
## in the terrain generator. You will notice that the deserts and rainforests
## are where they should be, as well as rain shadows behind mountain ranges.
##
## Rivers and lakes are also generated from the heightmap and follow accurate
## drainage paths, although with such a small heightmap some randomness needs
## to be thrown in to prevent rivers from being merely straight lines.
##
## Map bonuses are placed following the XML Rules but slightly differently than
## the default implimentation to better accomodate this map script.
##
## I've always felt that the most satisfying civ games are the ones that
## provide a use for those explorers and caravels. Though the map generator
## does not explicitly create a 'New World', it will take advantage of any
## continents that can serve that purpose. No starting locations will be placed
## on these continents. Therefore, the likelyhood of a significant new world
## is very high, but not guaranteed. It might also come in the form of multiple
## smaller 'New Worlds' rather than a large continent.
##
##############################################################################
## Version History
## SandBar - A version of Totestra/PerfectWorld to make maps for
## Arabian mods (Legends of Ancients Arabia, Rhye's and Fall:
## The Sword of Islam, Crossroads of the World, etc.)
##
## 20120612:
## 1) Made map more Arabia-like: Much bigger tropics, more dry, no snowy peaks
## fewer map options, flat square map with roundish island on it which will
## probably connect on some parts at the edge of the map. Lots of sand.
##
## Totestra - Sam Trenholme's update of PerfectWorld2.py
##
## 20120603:
## 1) The chance of a large continent splitting at the edge of the map
## has been greatly reduced
## 2) There is now an option to remove coastal mountains and reduce inland
## mountains
## 3) New map wrap option: PerfectWorld-compatible toric wrap
## 4) All bits in the service tag are now used up. I'm declaring Totestra
## finished.
##
## 20120530:
## 1) Added 2-bit "parity" to service tag
##
## 20120527:
## 1) Adding test case for bug reported by En Dotter
##
## 20120526:
## 1) En Dotter feature request: It's now possible to change how the map
## distributes resources
##
## 20120524:
## 1) New civ placement option: Have all civs placed on the same continent
##
## 20120523:
## 1) New option: Handicap level. This can give the human player extra
## starting resources.
## 2) Options to split continents and to have players on the "new world"
## verified to work.
##
## 20120522:
## 1) "Service tag" added as a sign in maps, so people who forgot to log
## Python debug information can still get full support.
##
## 20120521:
## 1) Overhaul of selection code; symbolic names are now used instead of
## numbers
## 2) Map ratios now can be selected (within reason)
##
## 20120519:
## 1) Extra-slow maps disabled; they just put PerfectWorld in to an
## infinite loop
## 2) "Fast and cheap" maps tested; they work with all three preset
## seeds so I'm declaring these maps stable.
## 3) Iceberg code overhaul: Polar icebergs are now more common in cold
## and/or large maps, and less common in smaller and/or warmer maps.
##
## 20120512:
## 1) Faster lower-quality maps now tweaked and fixed. "fast and dirty" caps
## the size: if you ask for huge you will get a medium sized map.
## 2) Climate can now be selected and it affects the map
## 3) Begin work on making the map ratio user-configurable
##
## 20120505:
## 1) Water level can now be adjusted in Civ4's GUI
## 2) It is now possible to use a fixed or a random map seed
## 3) There is an untested ability to more quickly make lower-quality maps,
## or, likewise, more slowly make better maps.
##
## Perfect World 2, Cephalo's original map generator changelog history:
##
## 2.06 - Fixed a few bugs from my minimum hill/maximum bad feature function.
##
## 2.05 - Made maps of standard size and below a bit smaller. Changed the way I
## remove jungle to prevent excessive health problems. Tiles in FC on different
## continents have zero value. Tiles on different continents will not be boosted
## with resources or hills. Water tiles have zero value for non-coastal cities.
## Water tiles will not be boosted with resources for non-coastal cities, land
## tiles will be boosted instead. (lookout Sid's Sushi!)
##
## 2.04 - Changed many percent values to be a percent of land tiles rather than
## total map tiles for easier, more predictable adjustment. Ensured a minimum
## number of hills in a starting fat cross. Disabled the normalizeRemovePeaks
## function a replaced it with a maximum peaks in FC function. Added bonus
## resources to FC depending on player handicap. Added a value bonus for cities
## placed on river sides.
##
## 2.03 - Fixed an initialization problem related to Blue Marble. Added some
## enhanced error handling to help me track down some of the intermittant bugs
## that still remain.
##
## 2.02 - Fixed some problems with monsoons that were creating strange artifacts
## near the tropics. Added an exponential curve to heat loss due to altitude, so
## that jungles can appear more readily without crawling to inappropriate
## latitudes.
##
## 2.01 - Changed the way I handled a vanilla version difference. Added toroidal
## and flat map options. Made tree amount more easily adjustable. Added a variable to
## tune the level of resource bonuses. Changed the rules for fixing tundra/ice next
## to desert. Added altitude noise to the plate map to improve island chains. Added
## a variable to control heat loss due to high altitude. Implimented a new interleaved
## bonus placement scheme so that bonuses are placed individually in random order,
## rather than all of each bonus type at once. Brought back the meteor code from
## PerfectWorld 1 and eliminated the east/west continent divide.
##
## 2.0 - Rebuilt the landmass and climate model using the FaireWeather.py for
## Colonization map script engine. Improved the river system. Fixed some
## old bugs.
##
## 1.13 - Fixed a bug where starting on a goody hut would crash the game.
## Prevented start plots from being on mountain peaks. Changed an internal
## distance calculation from a straight line to a path distance, improving
## start locations somewhat. Created a new tuning variable called
## DesertLowTemp. Since deserts in civ are intended to be hot deserts, this
## variable will prevent deserts from appearing near the poles where the
## desert texture clashes horribly with the tundra texture.
##
## 1.12 - Found a small bug in the bonus placer that gave bonuses a minimum
## of zero, this is why duel size maps were having so much trouble.
##
## 1.11 - limited the features mixing with bonuses to forests only. This
## eliminates certain undesireable effects like floodplains being erased by
## or coinciding with oil or incense, or corn appearing in jungle.
##
## 1.10 - Wrapped all map constants into a class to avoid all those
## variables being loaded up when PW is not used. Also this makes it a
## little easier to change them programatically. Added two in-game options,
## New World Rules and Pangaea Rules. Added a tuning variable that allows
## bonuses with a tech requirement to co-exist with features, so that the
## absence of those features does not give away their location.
##
## 1.09 - Fixed a starting placement bug introduced in 1.07. Added a tuning
## variable to turn off 'New world' placement.
##
## 1.08 - Removed the hemispheres logic and replaced it with a simulated meteor
## shower to break up pangeas. Added a tuning variable to allow pangeas.
##
## 1.07 - Placing lakes and harbors after river placement was not updating river
## crossings. Resetting rivers after lake placement should solve this. Fixed a
## small discrepancy between Python randint and mapRand to make them behave the
## same way. Bonuses of the same bonus class, when forced to appear on the
## same continent, were sometimes crowding each other off the map. This was
## especially problematic on the smaller maps. I added some additional, less
## restrictive, passes to ensure that every resource has at least one placement
## unless the random factors decide that none should be placed. Starting plot
## normalization now will place food if a different bonus can not be used due
## to lack of food. Changed heightmap generation to more likely create a
## new world.
##
## 1.06 - Overhauled starting positions and resource placement to better
## suit the peculiarities of PerfectWorld
##
## 1.05 - Fixed the Mac bug and the multi-player bug.
##
## 1.04a - I had unfairly slandered getMapRand in my comments. I had stated
## that the period was shortened unnecessarily, which is not the case.
##
## 1.04 - Added and option to use the superior Python random number generator
## or the getMapRand that civ uses. Made the number of rivers generated tunable.
## Fixed a bug that prevented floodplains on river corners. Made floodplains
## in desert tunable.
##
## 1.03a - very minor change in hope of finding the source of a multi-player
## glitch.
##
## 1.03 - Improved lake generation. Added tuning variables to control some
## new features. Fixed some minor bugs involving the Areamap filler
## and fixed the issue with oasis appearing on lakes. Maps will now report
## the random seed value that was used to create them, so they can be easily
## re-created for debugging purposes.
##
## 1.02 - Fixed a bug that miscalculated the random placing of deserts. This
## also necessitated a readjustment of the default settings.
##
## 1.01 - Added global tuning variables for easier customization. Fixed a few
## bugs that caused deserts to get out of control.
##
from CvPythonExtensions import *
import CvUtil
import CvMapGeneratorUtil
from array import array
from random import random,randint,seed
import math
import sys
import time
import os
# Options
OPTION_Patience = 11 # Hidden
OPTION_Huts = 0
OPTION_MapResources = 1
OPTION_Handicap = 2
OPTION_Pangaea = 3 # HIDDEN
OPTION_MapSeed = 4 # HIDDEN
OPTION_Wrap = 5 # HIDDEN
OPTION_NoRotate = 6 # HIDDEN
OPTION_SmoothPeaks = 7 # HIDDEN
OPTION_NewWorld = 8 # HIDDEN: Everyone always starts on same land
OPTION_IslandFactor = 9 # HIDDEN
OPTION_MapRatio = 10 # HIDDEN
OPTION_MAX = OPTION_Handicap + 1 # Add 1 because it's 1-indexed
# Setting this to 1 will allow the buggy 1:2 ratio; this ratio has
# problems because of limitations in Civ 4's engine. You have been warned.
ALLOW_EXTREME_RATIOS = 0
# Setting this to 0 will make it so the map does not have a "Service Tag"
# sign placed on it. If the sign (which should be placed in an unusable
# ice square, usually at the top of the map) annoys you, disabled this, but
# heed this warning first:
# I CAN NOT PROVIDE TECHNICAL ASSISTANCE WITHOUT A SERVICE TAG FOR YOUR
# MAP. DO NOT FILE A BUG REPORT OR ASK FOR TECHNICAL ASSISTANCE UNLESS YOU
# HAVE A SERVICE TAG.
ADD_SERVICE_TAG = 1
# Quick and dirty 2-bit "party" of hex number
def a91a15d7(x):
# a and b are 2-bit inputs for the s-box
# s is a 32-bit representation of this s-box
def sbox(a,b,s):
a &= 3
b &= 3
index = (a | (b << 2))
out = s
out >>= (index * 2)
out &= 3
return out
if x < 0:
return -1 # ERROR
out = 0
index = 0
while(x > 0):
q = (x & 3)
s = sbox(q,index,0xa91a15d7) # From RadioGatun[32] of "parity"
out += s
out ^= q
out &= 3
index += 1
index &= 3
x >>= 2
return out & 3
class MapConstants :
def __init__(self):
self.totestra = 0
self.hmWidth = 0
self.hmHeight = 0
self.noRotate = 0
self.smoothPeaks = 1
self.serviceFlags = 0 # Used for concise description of flags
self.xtraFlags = 0 # We're running out of bits :(
self.AllowPangeas = False
self.serviceString = "MP No Tag" # No cheating in multiplayer!
return
def initialize(self):
print "Initializing map constants"
##############################################################################
## GLOBAL TUNING VARIABLES: Change these to customize the map results
#---The following variables are not based on percentages. Because temperature
#---is so strongly tied to latitude, using percentages for things like ice and
#---tundra leads to very strange results if most of the worlds land lies near
#---the equator
#Sets the threshold for jungle rainfall by modifying the plains threshold by this factor.
self.TreeFactor = 1.5
#This is the maximum chance for a tree to be placed when rainfall is above jungle level.
#use a value between 0.0 and 1.0
self.MaxTreeChance = 0.3
#How many squares are added to a lake for each unit of drainage flowing
#into it.
self.LakeSizePerDrainage = 14.0
#This value modifies LakeSizePerRiverLength when a lake begins in desert
self.DesertLakeModifier = .60
#This value controls the amount of siltification in lakes
self.maxSiltPanSize = 200
#This value controls the number of mid-altitude lake depressions per
#map square. It will become a lake if enough water flows into the
#depression.
self.numberOfLakesPerPlot = 0.003
#This value sets the minimum altitude of lake depressions. They
#generally look better higher up.
self.minLakeAltitude = 0.45
#This value is used to decide if enough water has accumulated to form a river.
#A lower value creates more rivers over the entire map.
self.RiverThreshold = 5 # Lowered for Arab
#The percent chance that an oasis may appear in desert. A tile must be desert and
#surrounded on all sides by desert.
self.OasisChance = .16 # Doubled for Arab map
#This sets the amount of heat lost at the highest altitude. 1.0 loses all heat
#0.0 loses no heat.
self.heatLostAtOne = 0.01 # Arab
#This value is an exponent that controls the curve associated with
#temperature loss. Higher values create a steeper curve.
self.temperatureLossCurve = 1.3
#Degrees latitude for the top and bottom of the map. This allows
#for more specific climate zones
self.topLatitude = 30
self.bottomLatitude = -30
#Horse latitudes and polar fronts plus and minus in case you
#want some zones to be compressed or emphasized.
self.horseLatitude = 60
self.polarFrontLatitude = 80
#Tropics of Cancer and Capricorn plus and minus respectively
#self.tropicsLatitude = 23
#Oceans are slow to gain and lose heat, so the max and min temps
#are reduced and raised by this much.
self.oceanTempClamp = .10
#Minimum amount of rain dropped by default before other factors
#add to the amount of rain dropped
self.minimumRainCost = 0.01
#Strength of geostrophic rainfall versus monsoon rainfall
self.geostrophicFactor = 6.0
#Monsoon uplift factor. This value is an ajustment so that monsoon uplift
#matches geostrophic uplift.
self.monsoonUplift = 500.0
#Option to divide map into two continents as far as the midpoint
#displacement is concerned. For guaranteed continent separation, further
#steps will be needed but this option will cause more ocean in the
#middle of the map. The possible choices are 0 = NO_SEPARATION,
#1 = NORTH_SOUTH_SEPARATION and 2 = EAST_WEST_SEPARATION.
self.hmSeparation = 0
#Creates a water margin around the map edges.
self.northMargin = False
self.southMargin = False
self.eastMargin = False
self.westMargin = False
#If you sink the margins all the way to 0.0, they become too obvious.
#This variable sets the maximum amount of sinking
self.hmMarginDepth = 0.60
#Margin of ocean around map edge when not wrapping and also through
#middle when using separation.
self.hmGrainMargin = 2
#These are not mountain peaks, but points on the height map initialized
#to 1.0 before the midpoint displacement process begins. This sets the
#percentage of 'peaks' for points that are not on the grain margin.
self.hmInitialPeakPercent = 0.30
#Scales the heuristic for random midpoint displacement. A higher number
#will create more noise(bumpy), a smaller number will make less
#noise(smooth).
self.hmNoiseLevel = 2.0
#Influence of the plate map, or how much of it is added to the height map.
self.plateMapScale = 1.1
#Minimun distance from one plate seed to another
self.minSeedRange = 15
#Minimum distance from a plate seed to edge of map
self.minEdgeRange = 5
#Chance for plates to grow. Higher chance tends to make more regular
#shapes. Lower chance makes more irregular shapes and takes longer.
self.plateGrowthChanceX = 0.3
self.plateGrowthChanceY = 0.3
#This sets the amount that tectonic plates differ in altitude.
self.plateStagger = 0.1
#This sets the max amount a plate can be staggered up to on the heightmap
self.plateStaggerRange = 1.0
#This is the chance for a plate to sink into the water when it is on map edge
self.chanceForWaterEdgePlate = 0.45
#This is the frequency of the cosine ripple near plate boundaries.
self.rippleFrequency = 0.5
#This is the amplitude of the ripples near plate boundaries.
self.rippleAmplitude = 0.75
#This is the amount of noise added to the plate map.
self.plateNoiseFactor = 1.2
#Filter size for temperature smoothing. Must be odd number
self.filterSize = 15
#Filter size for altitude smoothing and distance finding. Must be
#odd number
self.distanceFilterSize = 5
#It is necessary to eliminate small inland lakes during the initial
#heightmap generation. Keep in mind this number is in relation to
#the initial large heightmap (mc.hmWidth, mc.hmHeight) before the
#shrinking process
self.minInlandSeaSize = 100
#Too many meteors will simply destroy the Earth, and just
#in case the meteor shower can't break the pangaea, this will also
#prevent and endless loop.
self.maximumMeteorCount = 7
#Minimum size for a meteor strike that attemps to break pangaeas.
#Don't bother to change this it will be overwritten depending on
#map size.
self.minimumMeteorSize = 6
#---These values are for evaluating starting locations
#Minimum number of hills in fat cross
self.MinHillsInFC = 2
#Max number of peaks in fat cross
self.MaxPeaksInFC = 3
#Max number of bad features(jungle) in fat cross
self.MaxBadFeaturesInFC = 4
#The following values are used for assigning starting locations. For now,
#they have the same ratio that is found in CvPlot::getFoundValue
self.CommerceValue = 20
self.ProductionValue = 40
self.FoodValue = 10
#Coastal cities are important, how important is determined by this
#value.
self.CoastalCityValueBonus = 1.3
#River side cities are also important, how important is determined by this
#value.
self.RiverCityValueBonus = 1.2
#Decides whether to use the Python random generator or the one that is
#intended for use with civ maps. The Python random has much higher precision
#than the civ one. 53 bits for Python result versus 16 for getMapRand. The
#rand they use is actually 32 bits, but they shorten the result to 16 bits.
#However, the problem with using the Python random is that it may create
#syncing issues for multi-player now or in the future, therefore it must
#be optional.
self.UsePythonRandom = True
#Below here are static defines. If you change these, the map won't work.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
self.L = 0
self.N = 1
self.S = 2
self.E = 3
self.W = 4
self.NE = 5
self.NW = 6
self.SE = 7
self.SW = 8
self.NO_SEPARATION = 0
self.NORTH_SOUTH_SEPARATION = 1
self.EAST_WEST_SEPARATION = 2
# THESE DO NOT CHANGE ANYTHING. They are overwritten by
# mmap.getGridWidth() and mmap.getGridHeight()
self.width = 104
self.height = 64
# These should probably not be changed...
self.OCEAN = 0
self.LAND = 1
self.HILLS = 2
self.PEAK = 3
self.OCEAN = 0
self.COAST = 1
self.MARSH = 2
self.GRASS = 3
self.PLAINS = 4
self.DESERT = 5
self.TUNDRA = 6
self.SNOW = 7
self.minimumLandInChoke = 0.5
return
def initInGameOptions(self):
gc = CyGlobalContext()
mmap = gc.getMap()
# Sea level comes from preferences
# Made lower for Arabian mod ("high" sealevel is "normal" in Totestra)
try:
seaLevel = int(mmap.getSeaLevel())
except:
seaLevel = 1
seaLevel = 0 # Arab: Force high sea level
self.landPercent = 0.43
if seaLevel == 0:
self.landPercent = 0.65 # ARAB SEA LEVEL
elif seaLevel == 2:
self.landPercent = 0.29
self.serviceFlags = 0 # Used for concise description of flags
if(seaLevel != 0):
self.serviceFlags |= (seaLevel & 3) # 2 bits; total 2
else:
self.serviceFlags |= 3 # Make sure Service Tag is always 21 digits
# Have climate affect the maps
# This is increased for a "tropical" climate
self.tropicsLatitude = 61 # Arab land is very tropical
# These are increased for "rocky" climates
#How many land squares will be above peak threshold and thus 'peaks'.
self.PeakPercent = 0.12
#How many land squares will be above hill threshold and thus 'hills'
#unless hey are also above peak threshold in which case they will
#be 'peaks'.
self.HillPercent = 0.23
#In addition to the relative peak and hill generation, there is also a
#process that changes flats to hills or peaks based on altitude. This tends
#to randomize the high altitude areas somewhat and improve their appearance.
#These variables control the frequency of hills and peaks at the highest altitude.
self.HillChanceAtOne = .43
self.PeakChanceAtOne = .27
#How many land squares will be below desert rainfall threshold. In this case,
#rain levels close to zero are very likely to be desert, while rain levels close
#to the desert threshold will more likely be plains.
self.DesertPercent = 0.20
#How many land squares will be below plains rainfall threshold. Rain levels close
#to the desert threshold are likely to be plains, while those close to the plains
#threshold are likely to be grassland.
self.PlainsPercent = 0.42
#What temperature will be considered cold enough to be ice. Temperatures range
#from coldest 0.0 to hottest 1.0.
self.SnowTemp = .30
#What temperature will be considered cold enough to be tundra. Temperatures range
#from coldest 0.0 to hottest 1.0.
self.TundraTemp = .35
#Hotter than this temperature will be considered deciduous forest, colder will
#be evergreen forest.Temperatures range from coldest 0.0 to hottest 1.0.
self.ForestTemp = .50
#What temperature will be considered hot enough to be jungle. Temperatures range
#from coldest 0.0 to hottest 1.0.
self.JungleTemp = .7
# Temperate: 0 Tropical: 1 Arid: 2 Rocky: 3 Cold: 4
self.iceChance = 1.0 # Chance of having iceberg at top/bottom of map
self.iceRange = 4 # Number of squares we add icebergs to
self.iceSlope = 0 # Very little ice on an Arabian map
#clim = mmap.getClimate()
clim = 2 # Always Arid for Arabia
if clim == 1: # Tropical
self.tropicsLatitude = 61
self.iceSlope = 0.33 # Less ice
elif clim == 2: # Arid
self.DesertPercent = 0.61
self.PlainsPercent = 0.83
self.iceSlope = 0 # Almost no ice
elif clim == 3: # Rocky
self.PeakPercent = 0.24
self.HillPercent = 0.70
self.HillChanceAtOne = 0.70
self.PeakChanceAtOne = 0.43
self.iceSlope = 0.75 # Some more ice
self.iceRange = 6
elif clim == 4: # Cold
self.tropicsLatitude = 0
self.SnowTemp = .50
self.TundraTemp = .75
self.ForestTemp = .85
self.JungleTemp = .99
self.iceRange = 12
self.iceChance = 1.2
self.iceSlope = 0.87 # Lots of ice
self.serviceFlags <<= 3
self.serviceFlags |= (clim & 7) # 3 bits; total 5
#New World Rules
selectionID = mmap.getCustomMapOption(OPTION_NewWorld)
selectionID = 2 # We always share the continent for LoAA
self.AllowNewWorld = True
self.ShareContinent = False
if selectionID == 1:
self.AllowNewWorld = False
elif selectionID == 2:
self.ShareContinent = True
self.xtraFlags = 0
self.xtraFlags |= ((self.ShareContinent & 1) << 4)
self.serviceFlags <<= 1
self.serviceFlags |= (self.AllowNewWorld & 1) # 1 bit; total 6
#Pangaea Rules
selectionID = mmap.getCustomMapOption(OPTION_Pangaea)
selectionID = 0 # Have meteors on Arab maps
self.AllowPangeas = False
if selectionID == 1:
self.AllowPangeas = True
self.serviceFlags <<= 1
self.serviceFlags |= (self.AllowPangeas & 1) # 1 bit; total 7
self.nohuts = mmap.getCustomMapOption(OPTION_Huts)
# How long are they willing to wait for the map to be made
#patience = mmap.getCustomMapOption(OPTION_Patience)
patience = 1 # Sand Bar fixed value
patience += 1 # Patience at 0 is broken
# This allows me to have one final usable bit in the service tag
self.serviceFlags <<= 3
self.serviceFlags |= (patience & 3) # 2 bits; total 10
# The preset worlds have hard-coded values
selectionID = mmap.getCustomMapOption(OPTION_MapSeed)
# DISABLED; no more Caulixtla!
#if selectionID != 0:
# patience = 2
# self.landPercent = 0.29 # We will force this here too
self.patience = patience
#Size of largest map increment to begin midpoint displacement. Must
#be a power of 2.
self.hmMaxGrain = 2 ** (2 + patience)
#Height and Width of main climate and height maps. This does not
#reflect the resulting map size. Both dimensions( + 1 if wrapping in
#that dimension = False) must be evenly divisble by self.hmMaxGrain
# SAM:
# Make it easy to change the size while keeping the aspect ratio
# The bigger the size factor, the smaller the continents and the
# slower the map generation process
# X and Y values for the map's aspect ratio
# We use OPTION_Wrap here because the map type affects the ratio
# Wrapping maps are *always* 3:2; Flat maps are always square
ratioValue = mmap.getCustomMapOption(OPTION_Wrap)
ratioValue = 0 # Hard-coded in SandBar
if ratioValue == 0: # 1:1
self.ratioX = 2
self.ratioY = 2
elif ratioValue == 1: # 3:2
self.ratioX = 3
self.ratioY = 2
self.serviceFlags <<= 3
self.serviceFlags |= (ratioValue & 7) # 3 bits; total 13
selectionID = mmap.getCustomMapOption(OPTION_IslandFactor)
selectionID = 0 # We hide this option for Arabian maps
# Arabia really wants as few islands as possible
self.serviceFlags <<= 2
self.serviceFlags |= (selectionID & 3) # Island factor
# If they want a fast map, don't allow them to select more islands
if(patience < 2):
selectionID = 0
heightmap_size_factor = 3 + selectionID
self.hmWidth = (self.hmMaxGrain * self.ratioX *
heightmap_size_factor)
self.hmHeight = (self.hmMaxGrain * self.ratioY *
heightmap_size_factor) + 1
# These are expressed in 4x4 "Grid" units
self.maxMapWidth = int(self.hmWidth / 4)
self.maxMapHeight = int(self.hmHeight / 4)
#Wrap options
selectionID = mmap.getCustomMapOption(OPTION_Wrap)
selectionID = 0 # No wrap (forced in SandBar)
wrapString = "Cylindrical"
self.WrapX = True
self.WrapY = False
self.serviceFlags <<= 2
self.serviceFlags |= (selectionID & 3) # Map wrap; 2 bits total 15
self.serviceFlags <<= 6 # 6 bits so we know the map size total 21
# handicap of 0 means player is equal to AI and may get a
# starting position that can not be won at higher difficulty
# settings. Values of 1, 2, or 3 make it easier for the player
handicap = mmap.getCustomMapOption(OPTION_Handicap)
self.xtraFlags |= ((handicap & 3) << 5)
#Bonus resources to add depending on difficulty settings
self.SettlerBonus = handicap
self.ChieftainBonus = handicap
self.WarlordBonus = handicap
self.NobleBonus = handicap
self.PrinceBonus = handicap
self.MonarchBonus = handicap
self.EmperorBonus = handicap
self.ImmortalBonus = handicap
self.DeityBonus = handicap
# Now that we have calculated the player's bonus resources, how many
# resources should the map as a whole have?
#This variable adjusts the amount of bonuses on the map. Values above 1.0 will add bonus
#bonuses. People often want lots of bonuses, and for those people, this variable is definately
#a bonus.
self.BonusBonus = 1.0
self.spreadResources = False
bonus_add = mmap.getCustomMapOption(OPTION_MapResources)
if bonus_add == 0: # More evenly spread out
self.BonusBonus = 0.7 # Compensate for spread's increase
self.spreadResources = True # Increases resources
if bonus_add == 1: # Full of resources
self.BonusBonus = 1.5 # Increases resources
self.spreadResources = True # Increases resources more
self.xtraFlags |= ((bonus_add & 3) << 2)
#self.noRotate = mmap.getCustomMapOption(OPTION_NoRotate)
self.noRotate = 0
#self.smoothPeaks = mmap.getCustomMapOption(OPTION_SmoothPeaks)
self.smoothPeaks = 1 # Forced for Arabian maps
#After generating the heightmap, bands of ocean can be added to the map
#to allow a more consistent climate generation. These bands are useful
#if you are generating part of a world where the weather might be coming
#in from off the map. These bands can be kept if needed or cropped off
#later in the process.
self.northWaterBand = 10
self.southWaterBand = 10
self.eastWaterBand = 0
self.westWaterBand = 0
#These variables are intended for use with the above water band variables
#but you can crop the map edge after climate generation for any reason.
self.northCrop = 10
self.southCrop = 10
self.eastCrop = 0
self.westCrop = 0
if selectionID == 0: #Flat
self.hmWidth += 1
self.WrapX = False
wrapString = "Flat"
# Random seed options (fixed or random)
selectionID = mmap.getCustomMapOption(OPTION_MapSeed)
selectionID = 0 # Always random for now
mapRString = "Random"
self.totestra = 0
if selectionID == 1: # Caulixtla
self.totestra = 8939185639133313 # Fixed map seed
mapRstring = "Caulixtla"
# Ignore most map parameters
self.wrapX = True
self.wrapY = False
wrapString = "Cylindrical"
self.hmWidth = 144
self.hmHeight = 97
heightmap_size_factor = 3
self.AllowPangeas = False
#Number of tectonic plates
self.hmNumberOfPlates = int(float(self.hmWidth * self.hmHeight) * 0.0016)
if patience == 0:
self.hmNumberOfPlates = int(
float(self.hmWidth * self.hmHeight) * 0.0032)
elif patience == 1:
self.hmNumberOfPlates = int(
float(self.hmWidth * self.hmHeight) * 0.0024)
elif patience == 3:
self.hmNumberOfPlates = int(
float(self.hmWidth * self.hmHeight) * 0.0008)
elif patience == 4:
self.hmNumberOfPlates = int(
float(self.hmWidth * self.hmHeight) * 0.0004)
elif patience >= 5:
self.hmNumberOfPlates = int(
float(self.hmWidth * self.hmHeight) * 0.0002)
self.optionsString = "Map Options: \n"
if self.AllowNewWorld:
self.optionsString += "AllowNewWorld = true\n"
else:
self.optionsString += "AllowNewWorld = false\n"
if self.AllowPangeas:
self.optionsString += "AllowPangeas = true\n"
else:
self.optionsString += "AllowPangeas = false\n"
self.optionsString += "Wrap Option = " + wrapString + "\n"
self.optionsString += "Map world = " + mapRString + "\n"
self.optionsString += "Land percent = " + str(self.landPercent) + "\n"
self.optionsString += "RatioX = " + str(self.ratioX) + "\n"
self.optionsString += "RatioY = " + str(self.ratioY) + "\n"
self.optionsString += "Climate = " + str(clim) + "\n"
self.optionsString += "Patience = " + str(patience) + "\n"
self.optionsString += "Island factor = " + str(heightmap_size_factor) +"\n"
print str(self.optionsString) + "\n"
return
mc = MapConstants()
class PythonRandom :
def __init__(self):
return
def seed(self):
#Python randoms are not usable in network games.
if mc.UsePythonRandom:
self.usePR = True
else:
self.usePR = False
if self.usePR and CyGame().isNetworkMultiPlayer():
print "Detecting network game. Setting UsePythonRandom to False."
self.usePR = False
if self.usePR:
# Python 'long' has unlimited precision, while the random generator
# has 53 bits of precision, so I'm using a 53 bit integer to seed the map!
seed() #Start with system time
if(mc.totestra == 0):
seedValue = randint(0,9007199254740991)
self.seedString = "Random seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
else:
seedValue = mc.totestra
self.seedString = "Fixed seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
mc.serviceTag = (seedValue & 0xffffffffffffff)
mc.serviceTag |= (mc.serviceFlags << 60)
mc.serviceTag |= (mc.xtraFlags << 53)
if(mc.noRotate == 0):
mc.serviceTag |= (1 << 83)
if(mc.smoothPeaks == 1):
mc.serviceTag |= (1 << 75)
mc.serviceTag |= (a91a15d7(mc.serviceTag) << 53)
mc.serviceString = ("%x" % mc.serviceTag)
print "SERVICE TAG: " + mc.serviceString
seed(seedValue)
else:
gc = CyGlobalContext()
self.mapRand = gc.getGame().getMapRand()
seedValue = self.mapRand.get(65535,"Seeding mapRand - FairWeather.py")
self.mapRand.init(seedValue)
self.seedString = "Random seed (Using getMapRand) for this map is %(s)20d" % {"s":seedValue}
## seedValue = 56870
## self.mapRand.init(seedValue)
## self.seedString = "Pre-set seed (Using getMapRand) for this map is %(s)20d" % {"s":seedValue}
print str(self.seedString)
return
def random(self):
if self.usePR:
return random()
else:
#This formula is identical to the getFloat function in CvRandom. It
#is not exposed to Python so I have to recreate it.
fResult = float(self.mapRand.get(65535,"Getting float -FairWeather.py"))/float(65535)
# print fResult
return fResult
def randint(self,rMin,rMax):
#if rMin and rMax are the same, then return the only option
if rMin == rMax:
return rMin
#returns a number between rMin and rMax inclusive
if self.usePR:
return randint(rMin,rMax)
else:
#mapRand.get() is not inclusive, so we must make it so
return rMin + self.mapRand.get(rMax + 1 - rMin,"Getting a randint - FairWeather.py")
#Set up random number system for global access
PRand = PythonRandom()
################################################################################
## Global functions
################################################################################
def errorPopUp(message):
gc = CyGlobalContext()
iPlayerNum = 0
for iPlayer in range(gc.getMAX_PLAYERS()):
player = gc.getPlayer(iPlayer)
if player.isAlive():
iPlayerNum = iPlayerNum + 1
if player.isHuman():
text = message + "\n\n" + mc.optionsString + "\n" + PRand.seedString
popupInfo = CyPopupInfo()
popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_PYTHON)
popupInfo.setText(text)
popupInfo.setOnClickedPythonCallback("")
popupInfo.addPythonButton("Ok","")
popupInfo.addPopup(iPlayer)
#This function converts x and y to a one-dimensional index.
def GetIndex(x,y):
#Check X for wrap
if mc.WrapX == True:
xx = x % mc.width
elif x < 0 or x >= mc.width:
return -1
else:
xx = x
#Check y for wrap
if mc.WrapY == True:
yy = y % mc.height
elif y < 0 or y >= mc.height:
return -1
else:
yy = y
i = yy * mc.width + xx
return i
# This does the same thing for the height map (as opposed to the plot map)
def GetHmIndex(x,y):
#Check X for wrap
if mc.WrapX == True:
xx = x % mc.hmWidth
elif x < 0 or x >= mc.hmWidth:
return -1
else:
xx = x
#Check y for wrap
if mc.WrapY == True:
yy = y % mc.hmHeight
elif y < 0 or y >= mc.hmHeight:
return -1
else:
yy = y
i = yy * mc.hmWidth + xx