openclonk/planet/System.ocg/Creation.c

252 lines
7.6 KiB
C

/**
Creation.c
Creation of objects, particles or PSX.
@author Ringwaul, Tyron
*/
// Creates amount objects of type id inside the indicated rectangle(optional) in the indicated material.
// Returns the number of iterations needed, or -1 when the placement failed.
// documented in /docs/sdk/script/fn
global func PlaceObjects(id id, int amount, string mat_str, int x, int y, int wdt, int hgt, bool onsf, bool nostuck)
{
var i, j;
var rndx, rndy, obj;
var mat;
var objhgt = id->GetDefCoreVal("Height", "DefCore");
mat = Material(mat_str);
// Some failsavety.
if (mat == -1)
if (mat_str != "GBackSolid" && mat_str != "GBackSemiSolid" && mat_str != "GBackLiquid" && mat_str != "GBackSky")
return -1;
// Optional parameters wdt and hgt.
if (!wdt)
wdt = LandscapeWidth() - x - GetX();
if (!hgt)
hgt = LandscapeHeight() - y - GetY();
// Cycle-saving method.
if (mat != -1)
while (i < amount)
{
// If there's isn't any or not enough of the given material, break before it gets an endless loop.
if (j++ > 20000)
return -1;
// Destinated rectangle.
rndx = x + Random(wdt);
rndy = y + Random(hgt);
// Positioning.
if (GetMaterial(rndx, rndy) == mat)
{
// On-surface option.
if (onsf)
while (GBackSemiSolid(rndx, rndy) && rndy >= y)
rndy--;
if (rndy < y)
continue;
// Create and verify stuckness.
obj = CreateObjectAbove(id, rndx, rndy + objhgt / 2, NO_OWNER);
obj->SetR(Random(360));
if (obj->Stuck() || nostuck)
i++;
else
obj->RemoveObject();
}
}
if (mat == -1)
while (i < amount)
{
// If there's isn't any or not enough of the given material, break before it gets an endless loop.
if (j++ > 20000)
return -1;
// Destinated rectangle.
rndx = x + Random(wdt);
rndy = y + Random(hgt);
// Positioning.
if (eval(Format("%s(%d,%d)", mat_str, rndx, rndy)))
{
// On-surface Option.
if (onsf)
while (GBackSemiSolid(rndx, rndy) && rndy >= y)
rndy--;
if (rndy < y)
continue;
// Create and verify stuckness.
obj = CreateObjectAbove(id, rndx, rndy + objhgt / 2, NO_OWNER);
obj->SetR(Random(360));
if (obj->Stuck() || nostuck)
i++;
else
obj->RemoveObject();
}
}
return j;
}
global func PlaceObjectBatches(array item_ids, int n_per_batch, int batch_radius, int n_batches, string in_material)
{
// place a number (n_batches) of batches of objects of types item_ids. Each batch has n_per_batch objects.
// fewer batches and/or objects may be placed if no space is found
in_material = in_material ?? "Earth";
var n_item_ids=GetLength(item_ids), n_created=0;
for (var i=0; i<n_batches; ++i)
{
var loc = FindLocation(Loc_Material(in_material));
if (loc)
{
for (var j=0; j<n_per_batch; ++j)
{
var loc2 = FindLocation(Loc_InRect(loc.x-batch_radius,loc.y-batch_radius,batch_radius*2,batch_radius*2), Loc_Material(in_material));
if (loc2)
{
var obj = CreateObjectAbove(item_ids[Random(n_item_ids)], loc2.x, loc2.y);
if (obj)
{
obj->SetPosition(loc2.x,loc2.y);
++n_created;
}
}
}
}
}
return n_created;
}
// documented in /docs/sdk/script/fn
global func CastObjects(id def, int am, int lev, int x, int y, int angs, int angw)
{
var objects = [];
var objects_index = 0;
if (!angw)
angw = 360;
for (var i = 0; i < am; i++)
{
var obj = CreateObjectAbove(def, x, y);
// Some objects might directly remove themselves on creation.
if (!obj) continue;
var ang = angs - 90 + RandomX(-angw / 2, angw / 2);
var xdir = Cos(ang, lev) + RandomX(-3, 3);
obj->SetR(Random(360));
obj->SetXDir(xdir);
obj->SetYDir(Sin(ang, lev) + RandomX(-3, 3));
if(xdir != 0)
obj->SetRDir((10 + Random(21)) * (xdir / Abs(xdir)));
else
obj->SetRDir(-10 + Random(21));
objects[objects_index++] = obj;
}
return objects;
}
// documented in /docs/sdk/script/fn
global func CastPXS(string mat, int am, int lev, int x, int y, int angs, int angw)
{
if (!angw)
angw = 360;
for (var i = 0; i < am; i++)
{
var ang = angs - 90 + RandomX(-angw / 2, angw / 2);
InsertMaterial(Material(mat), x, y, Cos(ang, lev) + RandomX(-3, 3), Sin(ang, lev) + RandomX(-3, 3));
}
return;
}
// documented in /docs/sdk/script/fn
global func DrawParticleLine(string particle, int x0, int y0, int x1, int y1, int prtdist, xdir, ydir, lifetime, proplist properties)
{
// Right parameters?
if (!properties)
return 0;
// Calculate required number of particles.
var prtnum = Max(Distance(x0, y0, x1, y1) / prtdist, 2);
var i = prtnum;
// Create particles.
while (i >= 0)
{
var i1, i2;
i2 = i * 256 / prtnum;
i1 = 256 - i2;
CreateParticle(particle, x0 + (x1 - x0) * i / prtnum, y0 + (y1 - y0) * i-- / prtnum, xdir, ydir, lifetime, properties, 1);
}
// Succes, return number of created particles.
return prtnum;
}
/** Place a nice shaped forest. If no area is given, the whole landscape is used (which is not recommended!).
@param plants An array containing all plants that should be in the forest. plants[0] is the main plant, the others will be randomly scattered throughout the forest.
@param x The starting X-coordinate of the forest.
@param y The lowest line at which to start placing plants. Level ground is determined automatically, goind upwards.
@param width The width of the forest
@param foreground Will roughly make every third instance of plants[0] foreground
*/
global func PlaceForest(array plants, int x, int y, int width, bool foreground)
{
// Parameter check
if (GetLength(plants) == 0) return;
if (!x) x = 0;
if (!y) y = LandscapeHeight();
if (!width) width = LandscapeWidth();
if (this) { x = AbsX(x); y = AbsY(y); }
// Roughly 20% of the size (10% per side) are taken for 'forest ending zones'. Plants will be smaller there.
var end_zone = width * 10 / 100;
// The width of the standard plants will roughly be the measure for our plant size
var plant_size = plants[0]->GetDefWidth()/2;
var growth, y_pos, plant, x_variance, variance = 0, count, j, spot;
for (var i = plant_size; i < width; i += plant_size)
{
growth = 95;
y_pos = y;
x_variance = RandomX(-plant_size/2, plant_size/2);
// End zone check
if (i < end_zone)
growth = BoundBy(90 / ((end_zone * 100 / plant_size)/100) * (i/plant_size), 10, 90);
else if (i > width - end_zone)
growth = BoundBy(90 / ((end_zone * 100 / plant_size)/100) * ((width-i)/plant_size), 10, 90);
else if (!Random(10) && GetLength(plants) > 1)
{
variance = Random(GetLength(plants)-1)+1;
// Scatter some other plants
count = RandomX(2, 4);
for (j = 0; j < count; j++)
{
spot = (plant_size*2 / count) * j + RandomX(-5,5) - plant_size;
y_pos = y;
if (!GBackSolid(x + i + spot, y_pos)) continue;
while (!GBackSky(x + i + spot, y_pos) && y_pos > 0) y_pos--;
if (y_pos == 0) continue;
plant = CreateObjectAbove(plants[variance], x + i + spot, y_pos+5, NO_OWNER);
}
continue;
}
// No ground at y_pos?
if (!GBackSolid(x + i + x_variance, y_pos)) continue;
// Get level ground
while (!GBackSky(x + i + x_variance, y_pos) && y_pos > 0) y_pos--;
if (y_pos == 0) continue;
plant = CreateObjectAbove(plants[0], x + i + x_variance, y_pos+5, NO_OWNER);
plant->SetCon(growth);
if (foreground && !Random(3)) plant.Plane = 510;
// Every ~7th plant: double plant!
if (x_variance != 0 && !Random(7))
{
y_pos = y;
if (!GBackSolid(x + i - x_variance, y_pos)) continue;
while (!GBackSky(x + i - x_variance, y_pos) && y_pos > 0) y_pos--;
if (y_pos == 0) continue;
plant = CreateObjectAbove(plants[0], x + i - x_variance, y_pos+5, NO_OWNER);
plant->SetCon(growth);
if (foreground && !Random(3)) plant.Plane = 510;
}
}
}