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
|
/*--
Library_Map
Authors: Sven2
Utility functions for map drawing in InitializeMap().
--*/
// Returns p if p is an int. If p is an array of two ints, returns a
// random value between both (assuming p[1]>p[0])
func EvalIntPar(p) { if (GetType(p) == C4V_Array) return p[0]+Random(p[1]-p[0]); return p; }
// Returns p if p is an int. If p is an array of two ints, returns the
// medium value of both
func EvalIntParM(p) { if (GetType(p) == C4V_Array) return (p[0]+p[1])/2; return p; }
func DrawVaried(string mat, proplist algo, array rect, vary_mats)
{
// Draw material and put rndchecker variations of other materials on it
if (GetType(vary_mats) != C4V_Array || (GetLength(vary_mats)==3 && GetType(vary_mats[1])==C4V_Int)) vary_mats = [vary_mats];
var ratio = 100/(GetLength(vary_mats)+1);
var main_shape = this->CreateLayer();
main_shape->Draw(mat, algo, rect);
this->Blit(main_shape, rect);
for (var vary_def in vary_mats)
{
var sx=3,sy=3;
if (GetType(vary_def) == C4V_Array)
{
sx=vary_def[1]; sy = vary_def[2];
vary_def = vary_def[0];
}
var rand_algo = {Algo=MAPALGO_RndChecker, Ratio=ratio, Wdt=sx, Hgt=sy};
var turb_algo = {Algo=MAPALGO_Turbulence, Amplitude=12, Scale=8, Op=rand_algo};
this->Draw(vary_def, {Algo=MAPALGO_And, Op=[main_shape, turb_algo]});
}
return main_shape;
}
func DrawSpots(string mat, int num, sizex, sizey, array rect, inmat, vary_mats)
{
// Default parameters
if (!inmat) inmat="Earth";
if (!sizex) sizex = [5, 20];
if (!sizey) sizey = [5, 7];
if (!num) num=Max(this->GetPixelCount(inmat, rect) / (EvalIntParM(sizex)*EvalIntParM(sizey)*10), 1);
// Draw num spots
var spot = {Algo=MAPALGO_Ellipsis};
while (num--)
{
if (!this->FindPosition(spot, inmat, rect)) break;
var mask = this->Duplicate(inmat);
spot.Wdt = EvalIntPar(sizex)/2;
spot.Hgt = EvalIntPar(sizey)/2;
var algo = {Algo=MAPALGO_And, Op=[mask, {Algo=MAPALGO_Turbulence, Amplitude=Max(spot.Wdt, spot.Hgt), Scale=Max(spot.Wdt, spot.Hgt), Op=spot}]};
if (vary_mats)
DrawVaried(mat, algo, rect, vary_mats);
else
this->Draw(mat, algo, rect);
}
return true;
}
func DrawCoal(int num, array rect) { return DrawSpots("Coal", num, [20,60], [4,8], rect, nil); }
func DrawFirestone(int num, array rect) { return DrawSpots("Firestone", num, [20,60], [8,12], rect, nil); }
func DrawOre(int num, array rect) { return DrawSpots("Ore", num, [8,12], [14,20], rect, nil); }
func DrawGold(int num, array rect) { return DrawSpots("Gold", num, [10,14], [10,14], rect, nil); }
func DrawRock(int num, array rect) { return DrawSpots("Rock-rock", num, [20,80], [6,8], rect, nil, [["Rock-rock", 3,10], ["Granite", 6,2]]); }
// Draws the given material onto an existing mask with given size and ratio.
public func DrawMaterial(string mat, proplist onto_mask, speck_size, int ratio)
{
// Defaults and check whether speck size is an array.
if (!speck_size)
speck_size = 4;
if (!ratio)
ratio = 15;
if (GetType(speck_size) != C4V_Array)
speck_size = [speck_size, speck_size];
// Use random checker algorithm to draw patches of the material.
var rnd_checker = {Algo = MAPALGO_RndChecker, Ratio = ratio, Wdt = speck_size[0], Hgt = speck_size[1]};
rnd_checker = {Algo = MAPALGO_Turbulence, Iterations = 4, Op = rnd_checker};
var material_mask = {Algo = MAPALGO_And, Op = [onto_mask, rnd_checker]};
// Draw the material onto the calling map object.
this->Draw(mat, material_mask);
return;
}
func DrawWaterVeins(int num, array rect)
{
while (num--) DrawLiquidVein("Water", 3, 5, rect);
return true;
}
func DrawLiquidVein(string mat, int wdt, int spread, array rect, inmat)
{
if (!rect) rect = [0,0,this.Wdt, this.Hgt];
if (!inmat) inmat="Earth";
var mask = this->Duplicate(inmat);
var x1 = rect[0]-rect[2]+Random(rect[2]*3);
var y1 = rect[1]-rect[3]+Random(rect[3]*3);
var x2 = rect[0]+Random(rect[2]);
var y2 = rect[1]+Random(rect[3]);
var water = {Algo=MAPALGO_Polygon, X=[x1,x2], Y=[y1,y2], Wdt=wdt};
var water_rand = {Algo=MAPALGO_Turbulence, Amplitude=30, Scale=30, Op=water};
var water_rand2 = {Algo=MAPALGO_Turbulence, Amplitude=spread*2, Scale=2, Op=water_rand};
this->Draw(mat, {Algo=MAPALGO_And, Op=[mask, water_rand2]});
return true;
}
func DrawRegularGround(array rect, yoff, turbulence)
{
// Draw regular (boring) ground level
if (!rect) rect = [0,0,this.Wdt, this.Hgt];
if (GetType(yoff) == C4V_Nil) yoff = rect[3]/4;
if (GetType(turbulence) == C4V_Nil) turbulence = 30;
var ground_rect = {Algo=MAPALGO_Rect, X=-100, Y=rect[1]+yoff, Wdt=rect[0]+rect[2]+200, Hgt=rect[3]+100};
var ground_algo = {Algo=MAPALGO_Turbulence, Amplitude=turbulence, Scale=30, Op=ground_rect};
var earth_shape = DrawVaried("Earth-earth", ground_algo, rect, [["Earth-earth_root", 3,10], ["Earth-earth_spongy", 6,2]]);
var top1 = {Algo=MAPALGO_Border, Top=[-10, 3], Op=earth_shape};
var top2 = {Algo=MAPALGO_Border, Top=[-10, 1], Op=earth_shape};
this->Draw("Earth-earth", {Algo=MAPALGO_And, Op=[earth_shape, {Algo=MAPALGO_Turbulence, Amplitude=4, Scale=10, Op=top1}]});
this->Draw("Earth-earth", {Algo=MAPALGO_And, Op=[earth_shape, {Algo=MAPALGO_Turbulence, Amplitude=4, Scale=10, Op=top2}]});
return true;
}
func FixLiquidBorders(border_material, lava_border_material)
{
// Makes sure liquids bordering other liquids as well as liquids
// bordering background materials are surrounded by fix_material
// Default border materials
if (!border_material) border_material = "Earth-earth";
if (!lava_border_material) lava_border_material = "Rock";
// Find liquid-to-background borders
var liquids = this->Duplicate("Liquid");
var liquid_borders = {Algo=MAPALGO_Border, Op=liquids, Wdt=-1, Top=0 };
var background = this->Duplicate("Background");
background->Draw("Sky", {Algo=MAPALGO_Not, Op=this});
this->Draw(border_material, {Algo=MAPALGO_And, Op=[liquid_borders, background]});
// Put lava on top of other liquids
var lava_borders = {Algo=MAPALGO_Border, Op=this->Duplicate(["Lava", "DuroLava"])};
this->Draw(lava_border_material, {Algo=MAPALGO_And, Op=[lava_borders, liquids]});
// Put acid on top of water
var acid_borders = {Algo=MAPALGO_Border, Op=this->Duplicate("Acid")};
this->Draw(border_material, {Algo=MAPALGO_And, Op=[acid_borders, liquids]});
return true;
}
func DrawPlatform(string material, int x0, int y0, int wdt, int hgt_up, int hgt_down)
{
var ground_mask = this->Duplicate("~Background");
for (var x=x0; x<x0+wdt; ++x)
{
// Adjust materials bottom
var y, px;
for (y=y0+1; y<=y0+hgt_down; ++y)
{
px = ground_mask->GetPixel(x,y);
if (px) break;
}
if (px) for (--y; y>y0; --y) this->SetPixel(x,y,px);
}
// Free top
if (hgt_up) this->Draw("Sky", nil, [x0,y0-hgt_up,wdt,hgt_up]);
// Draw platform itself
if (material) this->Draw(material, nil, [x0,y0,wdt,1]);
}
func FindBottomPeaks(array rect, int min_dx)
{
// Find downwards peaks in bottom-most nonzero pixels in rect
// Peaks are minimum min_dx apart horizontally
var x1,y1,x2,y2;
if (rect)
{ x1=rect[0]; y1=rect[1]; x2=x1+rect[2]; y2=y1+rect[3]; }
else
{ x2=this.Wdt; y2=this.Hgt; }
var last_y = y2+1, last_dy = 0, num_dy_0 = 0, last_peak_x = -min_dx-1;
var peaks = [];
for (var x=x1; x<x2; ++x)
{
var y = y2;
while (!this->GetPixel(x,y)) if (y1>=--y) break;
var dy = y - last_y;
if (!dy)
{
++num_dy_0;
}
else
{
if (dy < 0 && last_dy > 0)
{
var peak_x = x-1-num_dy_0/2;
if (num_dy_0%2 && !Random(2)) --peak_x;
if (peak_x-last_peak_x < min_dx)
{
if (!Random(2)) peaks[GetLength(peaks)-1] = {X=peak_x, Y=last_y};
}
else
{
peaks[GetLength(peaks)] = {X=peak_x, Y=last_y};
}
last_peak_x = peaks[GetLength(peaks)-1].X;
}
num_dy_0 = 0;
last_dy = dy;
}
last_y = y;
}
return peaks;
}
func FillLiquid(string mat, int x, int y, max_wdt, int max_hgt)
{
// Fill area downwards and sideways of given x and y with mat
// Stay within rectangle x-max_wdt and x+max_wdt horizontally and within y and y+max_hgt vertically
var background_mask = this->CreateMatTexMask("Background");
var open_ranges = [[x,x]], n_open = 1;
max_hgt = BoundBy(max_hgt, 0, this.Hgt-y);
if (GetType(max_wdt) != C4V_Array) max_wdt = [max_wdt,max_wdt];
var min_x1 = Max(x-max_wdt[0]), min_x2 = Max(x-max_wdt[1]);
var max_x1 = Min(x+max_wdt[0], this.Wdt-1), max_x2 = Min(x+max_wdt[1], this.Wdt-1);
var min_x = (min_x1+min_x2)/2, max_x = (max_x1+max_x2)/2;
var n_pix_set = 0;
while (n_open && max_hgt)
{
var next_ranges = [], n_next = 0;
x = -1;
for (var range in open_ranges)
{
var x1=range[0], x2=range[1];
if (x>x1) continue; // two or more paths merged
while (x1>min_x && background_mask[this->GetPixel(x1-1,y)]) --x1;
while (x2<max_x && background_mask[this->GetPixel(x2+1,y)]) ++x2;
var below_was_background = false;
for (x=x1; x<=x2; ++x)
{
this->SetPixel(x,y,mat); ++n_pix_set;
var below_is_background = background_mask[this->GetPixel(x,y+1)];
if (below_is_background)
{
if (!below_was_background)
next_ranges[n_next++] = [x,x];
else
++next_ranges[n_next-1][1];
}
below_was_background = below_is_background;
}
}
open_ranges = next_ranges;
n_open = n_next;
++y; --max_hgt;
min_x = BoundBy(min_x+Random(3)-1, min_x1,min_x2);
max_x = BoundBy(max_x+Random(3)-1, max_x1,max_x2);
}
return n_pix_set;
}
|