File: Script.c

package info (click to toggle)
openclonk 8.1-4
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 169,520 kB
  • sloc: cpp: 180,479; ansic: 108,988; xml: 31,371; python: 1,223; php: 767; makefile: 145; sh: 101; javascript: 34
file content (318 lines) | stat: -rw-r--r-- 7,954 bytes parent folder | download | duplicates (5)
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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/**
	Seed

	Logic for plant seed that will eventually sprout a plant if left alone long enough.
	Seeds may also be manually planted by using leftclick.

	@author Clonkonaut
*/

local Description = "$Description$";

// The last known position of the seed
local lib_seed_pos;
// The timer cycles the seed has been lying idly (until it has reached 10)
local lib_seed_idletime = 0;
// The time it takes until a new plant might be created, feel free to reassign
local lib_seed_planttime = 10500;
// Maximum lifetime until this seeds becomes un-collectible and destroys itself (not running if contained)
local lib_seed_lifetime = 0; // If zero, seed never decays
// Creation frame
local lib_seed_creationtime;

// ****** Must be the id of the plant to create. Do not leave empty.
local lib_seed_plant;

// Can be planted manually?
local lib_seed_can_plant_manually = true;

/* Reproduction control */

/** Chance of seeded plant to reproduce again (does not affect the seed itself)
	@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 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 maximum amount of plants.
*/

local plant_seed_offset = 20;

public func SeedOffset()
{
	return plant_seed_offset;
}

public func SetSeedOffset(int v)
{
	plant_seed_offset = v;
	return true;
}


/* Confinement: Restrict area in which the plant can grow and re-seed */

local Confinement;

public func SetConfinement(proplist v)
{
	Confinement = v;
	return true;
}


/* Reproduction */

private func Initialize()
{
	AddTimer("CheckSprout", lib_seed_planttime / 10);
	lib_seed_pos =  { x = GetX(), y = GetY() };
	lib_seed_creationtime = FrameCounter();
}

// Check whether a plant should be created
private func CheckSprout()
{
	if (Contained()) return;
	if (OnFire()) return;
	if (lib_seed_lifetime && FrameCounter() - lib_seed_creationtime >= lib_seed_lifetime)
	{
		// Took too long. Decay.
		this.Collectible = 0;
		this->DoCon(-5);
		return;
	}
	if (!CheckPosition()) return;

	lib_seed_idletime++;
	if (lib_seed_idletime >= 10)
	{
		// Is this place nice to make a new plant?
		if (!CheckPlantConditions())
			lib_seed_idletime = 0;
		else
			Sprout();
	}
}

// Check whether the seed was moved
private func CheckPosition()
{
	if (GetX() != lib_seed_pos.x || GetY() != lib_seed_pos.y)
	{
		lib_seed_pos.x = GetX();
		lib_seed_pos.y = GetY();
		lib_seed_idletime = 0;
		return false;
	}
	return true;
}


/* Sprouting */

// Returns the relative y to the ground from center to max 20 pixels.
// If no ground is found returns -1, also if the center is underground (stuck in material)
private func GetGroundPos()
{
	if (GBackSolid()) return -1;

	var y = 0;
	while (!GBackSolid(0,y) && y < 21)
		y++;
	if (y >= 21) return -1;
	return y;
}

/** Overload as necessary to check for further conditions. Return true if a new plant should be created.
*/
private func CheckPlantConditions()
{
	var ground = GetGroundPos();
	// No solid ground
	if (ground == -1) return false;
	// No fertile ground
	if (GetMaterialVal("Soil", "Material", GetMaterial(0,ground)) != 1) return false;
	// Do not grow underground
	if (!GBackSky(0, ground-1) && !GBackSky(0,0) && !GBackSky(0,-30)) return false;
	// or underwater
	if (GBackLiquid(0,-15)) return false;
	// Too many plants around
	var size = SeedArea();
	if (ObjectCount(Find_ID(lib_seed_plant), Find_InRect(-size / 2, -size / 2, size, size)) > SeedAmount()) return false;
	// Another plant too close
	var max_offset = SeedOffset();
	if (max_offset)
	{
		var neighbour = FindObject(Find_ID(lib_seed_plant), Find_Distance(max_offset, 0, -lib_seed_plant->GetDefHeight()/2));
		if (neighbour)
			return false;
	}
	// Check confinement
	if (this.Confinement && !this.Confinement->IsPointContained(GetX(), GetY()))
		return false;
	// Alright, can plant here!
	return true;
}


/** Overload as necessary. Should remove the seed.
*/
private func Sprout()
{
	var ground = GetGroundPos();
	var plant = CreateObjectAbove(lib_seed_plant, 0, ground, GetOwner());
	if (plant)
	{
		plant->SetCon(1);
		// Copy confinement, seed area, etc. from seed
		plant->InitChild(this);
	}
	// Always remove, even if planting failed. Otherwise we may accumulate crazy numbers of seeds.
	return RemoveObject();
}


/* Planting */


// When the clonk is able to use the item
public func RejectUse(object clonk)
{
	return _inherited(clonk) || (lib_seed_can_plant_manually && !clonk->IsWalking());
}

public func ControlUse(object clonk, int x, int y, bool box)
{
	var used = _inherited(clonk, x, y, box, ...);
	if (!used && lib_seed_can_plant_manually)
	{
		return PlantManually(clonk);
	}
	return used;
}


private func PlantManually(object clonk)
{
	var ground = GetGroundPos();
	if (ground == -1)
	{
		return CustomMessage(Format("$TxtBadGround$", GetName()), clonk, clonk->GetController(), 0, 0, 0xff0000);
	}

	// Soil is needed
	if (GetMaterialVal("Soil", "Material", GetMaterial(0,ground)) != 1)
	{
		return CustomMessage(Format("$TxtBadGround$", GetName()), clonk, clonk->GetController(), 0, 0, 0xff0000);
	}

	// Plant!
	clonk->DoKneel();
	Sprout();
	return true;
}


/* Editor */

private func GetDefaultConfinement(plant, def_val)
{
	// Default confinement is seed size
	var x, y, size;
	if (plant)
	{
		x = plant->GetX();
		y = plant->GetY();
		size = plant->SeedArea();
	}
	else
	{
		// Doesn't matter.
		// This point is only reached if the plant got deleted between value change in editor and execution in network
		x=LandscapeWidth()/2;
		y=LandscapeHeight()/2;
		size=200;
	}
	return Shape->Rectangle(x - size / 2, y - size / 2, size, size);
}

private func SetConfinementRect(to_rect)
{
	// Editor sets properties only; convert to rectangle.
	if (to_rect) to_rect = Shape->Rectangle(to_rect.x, to_rect.y, to_rect.wdt, to_rect.hgt);
	this.Confinement = to_rect;
	return true;
}

public func AddSeedEditorProps(def)
{
	// Seed props used by seed and plant
	if (!def.EditorProps) def.EditorProps = {};
	def.EditorProps.plant_seed_chance = { Name="$SeedChance$", EditorHelp="$SeedChanceHelp$", Type="int", Min=0, Max=10000, Asyncget="SeedChance", Set="SetSeedChance", Save="Seed" };
	def.EditorProps.plant_seed_area = { Name="$SeedArea$", EditorHelp="$SeedAreaHelp$", Type="int", Min=0, Asyncget="SeedArea", Set="SetSeedArea", Save="Seed" };
	def.EditorProps.plant_seed_amount = { Name="$SeedAmount$", EditorHelp="$SeedAmountHelp$", Type="int", Min=0, Asyncget="SeedAmount", Set="SetSeedAmount", Save="Seed" };
	def.EditorProps.plant_seed_offset = { Name="$SeedOffset$", EditorHelp="$SeedOffsetHelp$", Type="int", Min=0, Asyncget="SeedOffset", Set="SetSeedOffset", Save="Seed" };
	def.EditorProps.Confinement = { Name="$Confinement$", EditorHelp="$ConfinementHelp$", Type="enum", Set="SetConfinementRect", Save="Seed", Options = [
		{ Name="$NoConfinmenet$" },
		{ Name="$Rect$", OptionKey="Type", DefaultValueFunction=Library_Seed.GetDefaultConfinement, Value={ Type="rect" }, Delegate={ Type="rect", Relative=false, Storage="proplist", Color=0x30ff30, Set="SetConfinementRect" } }
		// other shapes not supported for now
		] };
	def.SetConfinementRect = Library_Seed.SetConfinementRect;
	return true;
}

public func Definition(def, ...)
{
	AddSeedEditorProps(def);
	return _inherited(def, ...);
}