openclonk/planet/Objects.ocd/Libraries.ocd/Map.ocd/Script.c

257 lines
9.1 KiB
C

/*--
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;
}