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
|
/**
Plant
Basic functionality for all plants
@author Clonkonaut
*/
// This is a plant
public func IsPlant()
{
return true;
}
/** Automated positioning via RootSurface, make sure to call this if needed (in case Construction is overloaded)
*/
protected func Construction(...)
{
Schedule(this, "RootSurface()", 1);
UpdateSeedTimer();
AddTimer("Seed", 72 + Random(10));
_inherited(...);
}
public func InitChild(object parent)
{
// Copy settings from parent plant
KeepArea(parent.Confinement);
SetSeedChance(parent->SeedChance());
SetSeedArea(parent->SeedArea());
SetSeedAmount(parent->SeedAmount());
SetSeedOffset(parent->SeedOffset());
return true;
}
/* Placement */
/** Places the given amount of plants inside the area. If no area is given, the whole landscape is used.
@param amount The amount of plants to be created (not necessarily every plant is created).
@param rectangle The area where to put the plants.
@param settings A proplist defining further setttings: { growth = 100000, keep_area = false }. Growth will get passed over to PlaceVegetation, keep_area will confine the plants and their offspring to rectangle.
@return Returns an array of all objects created.
*/
public func Place(int amount, proplist area, proplist settings)
{
// No calls to objects, only definitions
if (GetType(this) == C4V_C4Object) return;
// Default parameters
if (!settings) settings = { growth = 100000, keep_area = false };
if (!settings.growth) settings.growth = 100000;
var rectangle;
if (area) rectangle = area->GetBoundingRectangle(); else rectangle = Shape->LandscapeRectangle();
var plants = CreateArray(), plant;
for (var i = 0 ; i < amount ; i++)
{
plant = PlaceVegetation(this, rectangle.x, rectangle.y, rectangle.wdt, rectangle.hgt, settings.growth, area);
if (plant)
{
plants[GetLength(plants)] = plant;
if (settings.keep_area && area)
plant->KeepArea(area);
}
plant = nil;
}
return plants;
}
/* Reproduction */
/** Will confine the the plant and its offspring to a certain area.
@params area The confinement area.
*/
func KeepArea(proplist area)
{
this.Confinement = area;
}
/** Chance to reproduce plant. Chances are one out of return value. From 0 to 10000. Default is 20.
@return the chance, higher = more chance. 0 = does not reproduce.
*/
local plant_seed_chance = 20;
public func SeedChance()
{
return plant_seed_chance;
}
public func SetSeedChance(int v)
{
plant_seed_chance = v;
return UpdateSeedTimer();
}
private func UpdateSeedTimer()
{
RemoveTimer("Seed");
if (plant_seed_chance) AddTimer("Seed", 72 + Random(10));
return true;
}
/** Distance the seeds may travel. Default is 250.
@return the maximum distance.
*/
local plant_seed_area = 250;
public func SeedArea()
{
return plant_seed_area;
}
public func SetSeedArea(int v)
{
plant_seed_area = v;
return true;
}
/** The amount of plants allowed within SeedAreaSize. Default is 10.
@return the maximum amount of plants.
*/
local plant_seed_amount = 10;
private func SeedAmount()
{
return plant_seed_amount;
}
public func SetSeedAmount(int v)
{
plant_seed_amount = v;
return true;
}
/** The closest distance a new plant may seed to its nearest neighbour. Default is 20.
@return the closest distance to another plant.
*/
local plant_seed_offset = 20;
public func SeedOffset()
{
return plant_seed_offset;
}
public func SetSeedOffset(int v)
{
plant_seed_offset = v;
return true;
}
/** Evaluates parameters for this definition to determine if seeding should occur.
@par offx X offset added to context position for check.
@par offy Y offset added to context position for check.
@plant_id plant to check for whether it's already too crowded. Default to GetID().
@return true iff seeding should occur
*/
public func CheckSeedChance(int offx, int offy, id plant_id)
{
// Find number of plants in seed area.
// Ignored confinement - that's only used for actual placement
var size = this->SeedArea();
var amount = this->SeedAmount();
var plant_cnt = ObjectCount(Find_ID(plant_id ?? GetID()), Find_InRect(offx - size / 2, offy - size / 2, size, size));
// Increase seed chance by number of missing plants to reach maximum amount
// Note the chance will become negative if the maximum has been reached, in which case the random check will never succeed.
// That's intended
var chance = this->SeedChance() * (amount - plant_cnt);
if (!chance) return;
// Place a plant if we are lucky
return (Random(10000) < chance);
}
/** Reproduction of plants: Called every 2 seconds by a timer.
*/
private func Seed()
{
if (OnFire()) return;
// Place a plant if we are lucky, but no more than seed amount.
var plant;
if (CheckSeedChance())
{
plant = DoSeed(true);
// Check if it is not close to another one.
if (plant)
{
var neighbours = FindObjects(Find_Func("IsPlant"), Find_Exclude(plant),
Sort_Multiple(Sort_Distance(plant->GetX() - GetX(), plant->GetY() - GetY()), Sort_Reverse(Sort_Func("SeedOffset"))));
// Only check the nearest 3 plants
var too_close = false;
for (var i = 0; i < GetLength(neighbours) && i < 3; i++)
{
var neighbour = neighbours[i];
var x_distance = plant->SeedOffset() + 1;
var y_distance = 151;
if (neighbour)
{
x_distance = Abs(neighbour->GetX() - plant->GetX());
y_distance = Abs(neighbour->GetY() - plant->GetY());
}
if ((x_distance < plant->SeedOffset() || x_distance < neighbour->~SeedOffset()) && y_distance < 151)
{
too_close = true;
break;
}
}
// Closeness check
if (too_close)
plant->RemoveObject();
else
plant->InitChild(this);
}
}
return plant;
}
/** Forcefully places a seed of the plant, without random chance
or other sanity checks. This is useful for testing.
*/
public func DoSeed(bool no_init)
{
// Apply confinement for plant placement
var size = SeedArea();
var area = Shape->Rectangle(GetX() - size / 2, GetY() - size / 2, size, size);
var confined_area = nil;
if (this.Confinement)
{
confined_area = Shape->Intersect(this.Confinement, area);
// Quick-check if intersection to confinement yields an empty area
// to avoid unnecessery search by PlaceVegetation
area = confined_area->GetBoundingRectangle();
if (area.wdt <= 0 || area.hgt <= 0) return;
}
else
{
// Place the new plant in the original area
confined_area = area;
}
// Place the plant
var plant = PlaceVegetation(GetID(), 0, 0, 0, 0, 3, confined_area);
if (!no_init && plant)
plant->InitChild(this);
return plant;
}
private func RemoveInTunnel()
{
if (GetMaterial() == Material("Tunnel") || GetMaterial(0, -10) == Material("Tunnel"))
{
RemoveObject();
}
}
/* Editor */
public func Definition(def, ...)
{
Library_Seed->AddSeedEditorProps(def);
return _inherited(def, ...);
}
|