forked from Mirrors/openclonk
252 lines
7.6 KiB
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;
|
|
}
|
|
}
|
|
}
|