-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathT2.py
executable file
·5949 lines (5475 loc) · 248 KB
/
T2.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
##############################################################################
## T2 version 2021-08-07
## T2 changes from PerfectWorld 2.06 (*not* 2.06f)
## 1) Ability to select climate
## 2) Ability to use fixed random seed
## 3) Remove mountains next to coast (fix coastside mountain ring bug)
## 4) Allow everyone to start on same landmass (biggest or 2nd biggest)
## 5) Have options for changing resource distribution
## 6) Note random seed on sign placed in the arctic
## 7) Ability to have bigger (192x128) or smaller (96x64) maps
## File: PerfectWorld.py version 2.06
## Author: Rich Marinaccio
## 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
## 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
class MapConstants :
def __init__(self):
return
def initialize(self):
print "Initializing map constants"
##############################################################################
## GLOBAL TUNING VARIABLES: Change these to customize the map results
gc = CyGlobalContext()
mmap = gc.getMap()
# Default seed
self.randomSeed = 0
#Percent of land vs. water
self.landPercent = 0.29
#If this variable is set to False, a shower of colossal meteors will attempt to
#break up any pangea-like continents. Setting this variable to True will allow
#pangeas to sometimes occur and should be greatly favored by any dinosaurs
#that might be living on this planet at the time.
self.AllowPangeas = False
#This variable can be used to turn off 'New world' logic and place starting
#positions anywhere in the world. For some mods, a new world doesn't make
#sense.
self.AllowNewWorld = True
self.ShareContinent = False
self.ShareContinentIndex = 1
#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
#they are also above peak threshold in which case they will be 'peaks'.
self.HillPercent = 0.35
#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 = .50
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
#---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
#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
#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 = 1.0
#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
# This determines if we use Perfect World's way of spreading
# resources (False), or use a more normal resource distribution
self.spreadResources = False
#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 = 4
#The percent chance that an oasis may appear in desert. A tile must be desert and
#surrounded on all sides by desert.
self.OasisChance = .08
#This sets the amount of heat lost at the highest altitude. 1.0 loses all heat
#0.0 loses no heat.
self.heatLostAtOne = 1.0
#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 = 90
self.bottomLatitude = -90
#Horse latitudes and polar fronts plus and minus in case you
#want some zones to be compressed or emphasized.
self.horseLatitude = 30
self.polarFrontLatitude = 60
#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
#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
# Note: The bigger map option may rewrite these values (see below)
self.hmWidth = 144
self.hmHeight = 97
#Controls wrapping (not sure if this makes sense yet)
self.WrapX = True
self.WrapY = False
#Size of largest map increment to begin midpoint displacement. Must
#be a power of 2.
self.hmMaxGrain = 16
#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
#Number of tectonic plates
self.hmNumberOfPlates = int(float(self.hmWidth * self.hmHeight) * 0.0016)
#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
#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
#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 = 15
#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
#Bonus resources to add depending on difficulty settings
self.SettlerBonus = 0
self.ChieftainBonus = 0
self.WarlordBonus = 0
self.NobleBonus = 0
self.PrinceBonus = 1
self.MonarchBonus = 1
self.EmperorBonus = 2
self.ImmortalBonus = 2
self.DeityBonus = 3
#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
# T2 change: Add the ability to select climate
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.66 # How quickly we reduce icebergs
clim = mmap.getClimate()
if clim == 1: # Tropical
self.tropicsLatitude = 46
# q3max changes
self.DesertPercent = .10 # added
self.PlainsPercent = .30 # added
self.SnowTemp = .20 # added
self.TundraTemp = .30 # added
self.ForestTemp = .35 # added
self.JungleTemp = .50 # added
# End q3max changes
self.iceSlope = 0.33 # Less ice
elif clim == 2: # Arid
self.DesertPercent = 0.40
self.PlainsPercent = 0.82
self.iceSlope = 0.33 # Less 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
#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
self.width = 104
self.height = 64
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()
#New World Rules
selectionID = mmap.getCustomMapOption(0)
if selectionID == 1:
self.AllowNewWorld = not self.AllowNewWorld
if selectionID == 2: # Everyone on same landmass (largest)
self.AllowNewWorld = True
self.ShareContinent = True
if selectionID == 3: # Everyone on same landmass (second largest)
self.AllowNewWorld = True
self.ShareContinent = True
self.ShareContinentIndex = 2
#Pangaea Rules
selectionID = mmap.getCustomMapOption(1)
if selectionID == 1:
self.AllowPangeas = not self.AllowPangeas
# Bigger maps option
selectionID = mmap.getCustomMapOption(4)
# Default size is 144 wide, 97 tall
if selectionID == 1: # Big maps
self.hmHeight = 129
self.hmWidth = 192
elif selectionID == 2: # Small maps
self.hmHeight = 65
self.hmWidth = 96
self.hmNumberOfPlates = (int(
float(self.hmWidth * self.hmHeight) * 0.0016))
#Wrap options
selectionID = mmap.getCustomMapOption(2)
wrapString = "Cylindrical"
if selectionID == 1: #Toroidal
self.hmHeight -= 1
self.WrapY = True
self.northWaterBand = 0
self.northCrop = 0
self.southWaterBand = 0
self.southCrop = 0
wrapString = "Toroidal"
elif selectionID == 2: #Flat
self.hmWidth += 1
self.WrapX = False
wrapString = "Flat"
# How we distribute resources
selectionID = mmap.getCustomMapOption(3)
if selectionID == 0:
self.BonusBonus = 1.5 # Increases resources
self.spreadResources = True
elif selectionID == 1: # Evenly spaced resources
self.BonusBonus = 0.7
self.spreadResources = True
elif selectionID == 2: # Like Perfect World
self.BonusBonus = 1.0
self.spreadResources = False
else:
self.BonusBonus = 1.5 # Increases resources
self.spreadResources = True
# Do we use a fixed or random map seed?
selectionID = mmap.getCustomMapOption(5)
if selectionID == 1: # Fixed random seed
self.randomSeed = 8939185639133313
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"
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
seedValue = randint(0,9007199254740991)
if mc.randomSeed == 0:
seed(seedValue)
mc.randomSeed = seedValue
else:
seed(mc.randomSeed)
seedValue = mc.randomSeed
self.seedString = "Random seed (Using Python rands) for this map is %(s)20d" % {"s":seedValue}
## seedValue = 4316490043753041
## seed(seedValue)
## self.seedString = "Pre-set seed (Using Pyhon rands) for this map is %(s)20d" % {"s":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 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 an 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
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
return i
#Handles arbitrary size
def GetIndexGeneral(x,y,width,height):
#Check X for wrap
if mc.WrapX == True:
xx = x % width
elif x < 0 or x >= width:
return -1
else:
xx = x
#Check y for wrap
if mc.WrapY == True:
yy = y % height
elif y < 0 or y >= height:
return -1
else:
yy = y
i = yy * width + xx
return i
#This function scales a float map so that all values are between
#0.0 and 1.0.
def NormalizeMap(fMap,width,height):
#find highest and lowest points
maxAlt = 0.0
minAlt = 0.0
for y in range(height):
for x in range(width):
plot = fMap[GetIndexGeneral(x,y,width,height)]
if plot > maxAlt:
maxAlt = plot
if plot < minAlt:
minAlt = plot
#normalize map so that all altitudes are between 1 and 0
#first add minAlt to all values if necessary
if minAlt < 0.0:
for y in range(height):
for x in range(width):
fMap[GetIndexGeneral(x,y,width,height)] -= minAlt
#add minAlt to maxAlt also before scaling entire map
maxAlt -= minAlt
scaler = 1.0/maxAlt
for y in range(height):
for x in range(width):
fMap[GetIndexGeneral(x,y,width,height)] = fMap[GetIndexGeneral(x,y,width,height)] * scaler
return
def ShrinkMap(largeMap,lWidth,lHeight,sWidth,sHeight):
smallMap = array('d')
yScale = float(lHeight)/float(sHeight)
xScale = float(lWidth)/float(sWidth)
for y in range(sHeight):
for x in range(sWidth):
## print "x = %d, y = %d" % (x,y)
weights = 0.0
contributors = 0.0
yyStart = int(y * yScale)
yyStop = int((y + 1) * yScale)
if yyStop < ((y + 1) * yScale):
yyStop += 1
for yy in range(yyStart,yyStop):
xxStart = int(x * xScale)
xxStop = int((x + 1) * xScale)
if xxStop < ((x + 1) * xScale):
xxStop += 1
for xx in range(xxStart,xxStop):
## print " xx = %d, yy = %d" % (xx,yy)
weight = GetWeight(x,y,xx,yy,xScale,yScale)
## print " weight = %f" % weight
i = yy * lWidth + xx
## print " i = %d" % i
contributor = largeMap[i]
## print " contributer = %f" % contributor
weights += weight
contributors += weight * contributor
## print " final height = %f" % (contributors/weights)
smallMap.append(contributors/weights)
return smallMap
def GetWeight(x,y,xx,yy,xScale,yScale):
xWeight = 1.0
## print " xScale = %f" % xScale
## print " x * xScale = %f, xx = %f" % ((x * xScale),xx)
if float(xx) < x * xScale:
## print " first"
xWeight = 1.0 - ((x * xScale) - float(xx))
elif float(xx + 1) > (x + 1) * xScale:
## print " second"
xWeight = ((x + 1) * xScale) - float(xx)
## print " xWeight = %f" % xWeight
yWeight = 1.0
## print " yScale = %f" % yScale
## print " y * yScale = %f, yy = %f" % ((y * yScale),yy)
if float(yy) < y * yScale:
## print " first"
yWeight = 1.0 - ((y * yScale) - float(yy))
elif float(yy + 1) > (y + 1) * yScale:
## print " second"
yWeight = ((y + 1) * yScale) - float(yy)
## print " yWeight = %f" % yWeight
return xWeight * yWeight
def CropMap(theMap):
newMap = array('d')
for y in range(mc.hmHeight):
if y < mc.southCrop or y >= mc.hmHeight - mc.northCrop:
continue
for x in range(mc.hmWidth):
if x < mc.westCrop or x >= mc.hmWidth - mc.eastCrop:
continue
i = GetHmIndex(x,y)
newMap.append(theMap[i])
return newMap
def AngleDifference(a1,a2):
diff = a1 - a2
while(diff < -180.0):
diff += 360.0
while(diff > 180.0):
diff -= 360.0
return diff
def AppendUnique(theList,newItem):
if IsInList(theList,newItem) == False:
theList.append(newItem)
return
def IsInList(theList,newItem):
itemFound = False
for item in theList:
if item == newItem:
itemFound = True
break
return itemFound
def DeleteFromList(theList,oldItem):
for n in range(len(theList)):
if theList[n] == oldItem:
del theList[n]
break
return
def ShuffleList(theList):
preshuffle = list()
shuffled = list()
numElements = len(theList)
for i in range(numElements):
preshuffle.append(theList[i])
for i in range(numElements):
n = PRand.randint(0,len(preshuffle)-1)
shuffled.append(preshuffle[n])
del preshuffle[n]
return shuffled
def GetInfoType(string):
cgc = CyGlobalContext()
return cgc.getInfoTypeForString(string)
def GetDistance(x,y,dx,dy):
distance = math.sqrt(abs((float(x - dx) * float(x - dx)) + (float(y - dy) * float(y - dy))))
return distance
def GetOppositeDirection(direction):
opposite = mc.L
if direction == mc.N:
opposite = mc.S
elif direction == mc.S:
opposite = mc.N
elif direction == mc.E:
opposite = mc.W
elif direction == mc.W:
opposite = mc.E
elif direction == mc.NW:
opposite = mc.SE
elif direction == mc.SE:
opposite = mc.NW
elif direction == mc.SW:
opposite = mc.NE
elif direction == mc.NE:
opposite = mc.SW
return opposite
def GetXYFromDirection(x,y,direction):
xx = x
yy = y
if direction == mc.N:
yy += 1
elif direction == mc.S:
yy -= 1
elif direction == mc.E:
xx += 1
elif direction == mc.W:
xx -= 1
elif direction == mc.NW:
yy += 1
xx -= 1
elif direction == mc.NE:
yy += 1
xx += 1
elif direction == mc.SW:
yy -= 1
xx -= 1
elif direction == mc.SE:
yy -= 1
xx += 1
return xx,yy
##This function is a general purpose value tuner. It finds a value that will be greater
##than or less than the desired percent of a whole map within a given tolerance. Map values
##should be between 0 and 1. To exclude parts of the map, set them to value 0.0
def FindValueFromPercent(mmap,width,height,percent,tolerance,greaterThan):
inTolerance = False
#to speed things up a little, lets take some time to find the middle value
#in the dataset and use that to begin our search
minV = 100.0
maxV = 0.0
totalCount = 0
for i in range(height*width):
if mmap[i] != 0.0:
totalCount += 1
if minV > mmap[i]:
minV = mmap[i]
if maxV < mmap[i]:
maxV = mmap[i]
mid = (maxV - minV)/2.0 + minV