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 (426 lines) | stat: -rw-r--r-- 11,547 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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/**
 Library for shapes
 See docs/sdk/script/Shape.xml for documentation.
 
 @author Sven2
 
 */

/* Base shape */

local BaseShape;

// Returns the area in squared pixels covered by the shape. Works for all specific shapes by using their functionality.
public func GetArea()
{
	var check_rect = this->GetBoundingRectangle();
	var area = 0;
	for (var x = check_rect.x; x < check_rect.x + check_rect.wdt; x++)
		for (var y = check_rect.y; y < check_rect.y + check_rect.hgt; y++)
			if (this->IsPointContained(x, y))
				area++;
	return area;
}

/* Rectangle shape */

local BaseRectangle; // properties x,y,w,h

// Point contained in rectangle?
private func BaseRectangle_IsPointContained(int x, int y)
{
	//return ((x-this.x+this.wdt)/this.wdt) * ((y-this.y+this.hgt)/this.hgt) == 1 && x>=this.x;
	return x>=this.x && y>=this.y && x<this.x+this.wdt && y<this.y+this.hgt;
}

// bounding rectangle is just self
private func BaseRectangle_GetBoundingRectangle() { return this; }

private func BaseRectangle_Find_In(context)
{
	if (!context) context = Global;
	return context->Find_InRect(this.x, this.y, this.wdt, this.hgt);
}

private func BaseRectangle_Find_At(context)
{
	if (!context) context = Global;
	return context->Find_AtRect(this.x, this.y, this.wdt, this.hgt);
}

private func BaseRectangle_GetRandomPoint(proplist result)
{
	if (this.wdt<=0 || this.hgt <=0) return false;
	result.x = this.x + Random(this.wdt);
	result.y = this.y + Random(this.hgt);
	return true;
}

private func BaseRectangle_GetArea()
{
	return this.wdt * this.hgt;
}

private func BaseRectangle_ToString()
{
	return Format("Shape->Rectangle(%d, %d, %d, %d)", this.x, this.y, this.wdt, this.hgt);
}

private func BaseRectangle_IsFullMap()
{
	return !this.x && !this.y && this.wdt == LandscapeWidth() && this.hgt == LandscapeHeight();
}

/** Constructor of rectangle area. (x,y) is included; (x+w,y+h) is excluded.
 @par x Global left side of rectangle
 @par y Global top side of rectangle
 @par w Rectangle width
 @par h Rectangle height
 @return return a shape proplist representing a rectangle
*/
public func Rectangle(int x, int y, int w, int h)
{
	return new BaseRectangle { x=x, y=y, wdt=w, hgt=h };
}


/* Circle shape */

local BaseCircle; // properties cx,cy,r

// point contained in circle?
private func BaseCircle_IsPointContained(int x, int y)
{
	x-=this.cx; y-=this.cy;
	var r=this.r;
	return x*x + y*y <= r*r;
}

// bounding rectangle
private func BaseCircle_GetBoundingRectangle()
{
	var r=this.r;
	return new Shape.BaseRectangle { x=this.cx-r, y=this.cy-r, wdt=r*2+1, hgt=r*2+1 };
}

private func BaseCircle_GetRandomPoint(proplist result)
{
	// Make sure radius circles are weighed equally
	var r2 = this.r * this.r + 1;
	if (r2>0x7fff) // for large numbers, the random function doesn't work
		r2 = Random(0x8000) + Random(r2/0x8000+1) * 0x8000;
	else
		r2 = Random(r2);
	var r = Sqrt(r2), a = Random(360);
	result.x = this.cx + Sin(a, r);
	result.y = this.cy + Cos(a, r);
	return true;
}

public func BaseCircle_GetArea()
{
	// Is aleady covered by general method, but this direct calculation for a simple circle is faster.
	var area = 0;
	for (var x = 0; x <= this.r; x++)
		for (var y = 1; y <= this.r; y++)
			if (x*x + y*y <= this.r*this.r)
				area++;
	return 4 * area + 1;
}

/** Constructor of circular area.
 @par cx Global center x of circle
 @par cy Global center y of circle
 @par r Circle radius
 @return return a shape proplist representing a filled circle around a point
*/
public func Circle(int cx, int cy, int r)
{
	return new BaseCircle { cx=cx, cy=cy, r=r };
}


/* Intersection */

local BaseIntersection;

private func BaseIntersection_IsPointContained(int x, int y)
{
	// Intersection: If any of the sub-areas exclude the point, then it's excluded
	for (var area in this.areas)
		if (!area->IsPointContained(x, y))
			return false;
	return true;
}

private func BaseIntersection_GetBoundingRectangle()
{
	// Bounding rectangle of intersection
	var result, rt;
	for (var area in this.areas)
		if (rt = area->GetBoundingRectangle())
		{
			// first bounds determine area
			if (!result)
			{
				result = new Shape.BaseRectangle {x = rt.x, y = rt.y, wdt = rt.wdt, hgt = rt.hgt};
			}
			else
			{
				// following bounds reduce area
				if (rt.x + rt.wdt < result.x + result.wdt) result.wdt = Max(rt.x + rt.wdt - result.x);
				if (rt.y + rt.hgt < result.y + result.hgt) result.hgt = Max(rt.y + rt.hgt - result.y);
				if (rt.x > result.x)
				{
					result.wdt = Max(result.wdt - rt.x + result.x);
					result.x = rt.x;
				}
				if (rt.y > result.y)
				{
					result.hgt = Max(result.hgt - rt.y + result.y);
					result.y = rt.y;
				}
			}
		}
	return result;
}

private func BaseIntersection_GetRandomPoint(proplist result, int num_tries)
{
	// The precise shape of the intersection is unknown. So try 100 times to get a location from any of the subsections
	if (!GetLength(this.areas)) return false;
	if (!num_tries) num_tries = 200;
	while (num_tries>0)
	{
		for (var area in this.areas)
		{
			// get point from subsection
			if (!area->GetRandomPoint(result, num_tries)) return false; // sub-area empty? (note num-tries goes down)
			var pt_ok = true;
			// ensure it's contained in all other subsections
			for (var area2 in this.areas) if (area != area2)
				if (!area2->IsPointContained(result.x, result.y))
				{
					pt_ok = false;
					break;
				}
			if (pt_ok) return true;
			--num_tries;
		}
	}
	return false;
}

/** Constructor of intersection area.
 @par c1, c2, ... Up to ten parameters for intersected areas.
 @return return a shape proplist representing a shape that contains only the points included in all the passed sub-shapes.
*/
public func Intersect(proplist c1, proplist c2, ...)
{
	// Intersection of one area?
	if (!c2) return c1;
	// Otherwise, built array
	var areas = [c1, c2], i=1, area;
	while (area = Par(++i)) areas[i] = area;
	return new BaseIntersection { areas = areas };
}


/* Combination */

local BaseCombination;

private func BaseCombination_IsPointContained(int x, int y)
{
	// Combination: If any of the sub-areas include the point, then it's included
	for (var area in this.areas)
		if (area->IsPointContained(x, y))
			return true;
	return false;
}

private func BaseCombination_GetBoundingRectangle()
{
	// Bounding rectangle of combination
	var result, rt;
	for (var area in this.areas)
		if (rt = area->GetBoundingRectangle())
		{
			// first bounds determine area
			if (!result)
			{
				result = new Shape.BaseRectangle {x = rt.x, y = rt.y, wdt = rt.wdt, hgt = rt.hgt};
			}
			else
			{
				// following bounds enlarge area
				if (rt.x + rt.wdt > result.x + result.wdt) result.wdt = rt.x + rt.wdt - result.x;
				if (rt.y + rt.hgt > result.y + result.hgt) result.hgt = rt.y + rt.hgt - result.y;
				if (rt.x < result.x)
				{
					result.wdt += result.x - rt.x;
					result.x = rt.x;
				}
				if (rt.y < result.y)
				{
					result.hgt += result.y - rt.y;
					result.y = rt.y;
				}
			}
		}
	return result;
}

private func BaseCombination_GetRandomPoint(proplist result, int num_tries)
{
	// Just get a random point from a random subsection
	// Note that this doesn't weigh areas equally.
	var len = GetLength(this.areas);
	if (!len) return false;
	return this.areas[Random(len)]->GetRandomPoint(result, num_tries);
}

/** Constructor of combined area.
 @par c1, c2, ... Up to ten parameters for combined areas.
 @return return a shape proplist representing a shape that contains all the points included in any of the passed sub-shapes.
*/
public func Combine(proplist c1, proplist c2, ...)
{
	// Combination of one area?
	if (!c2) return c1;
	// Otherwise, built array
	var areas = [c1, c2], i=1, area;
	while (area = Par(++i)) areas[i] = area;
	return new BaseCombination { areas = areas };
}


/* Subtraction */

local BaseSubtraction;

private func BaseSubtraction_IsPointContained(int x, int y)
{
	// Subtraction contains everything in "in" area that is not contained in "ex" area
	return this.in->IsPointContained(x, y) && !this.ex->IsPointContained(x, y);
}

private func BaseSubtraction_GetBoundingRectangle()
{
	// Simply use "in" area since it bounds everything
	return this.in->GetBoundingRectangle();
}

private func BaseSubtraction_GetRandomPoint(proplist result, int num_tries)
{
	// Get random point in in-area until it lies outside ex-area
	if (!num_tries) num_tries = 200;
	while (num_tries>0)
	{
		if (!this.in->GetRandomPoint(result, num_tries)) return false; // sub-area empty? (note num-tries goes down)
		if (!this.ex->IsPointContained(result.x, result.y)) return true;
		--num_tries;
	}
	// Failed to find a point
	return false;
}

/** Constructor of sutraction area.
 @par in Shape that is included.
 @par ex Shape that is excluded.
 @return return a shape proplist representing a shape that includes the "in" shape and excludes the "ex" shape.
*/
public func Subtract(proplist in, proplist ex)
{
	// Create a new subtraction area
	return new BaseSubtraction { in = in, ex = ex };
}



/* Library initialization */

public func Definition(def)
{
	// Initialize function pointers in shape classes
	BaseShape =
	{
		GetArea = Shape.GetArea
	};
	BaseRectangle = new BaseShape
	{
		Type = "rect",
		IsPointContained = Shape.BaseRectangle_IsPointContained,
		GetBoundingRectangle = Shape.BaseRectangle_GetBoundingRectangle,
		GetRandomPoint = Shape.BaseRectangle_GetRandomPoint,
		GetArea = Shape.BaseRectangle_GetArea,
		Find_In = Shape.BaseRectangle_Find_In,
		Find_At = Shape.BaseRectangle_Find_At,
		ToString = Shape.BaseRectangle_ToString,
		IsFullMap = Shape.BaseRectangle_IsFullMap
	};
	BaseCircle = new BaseShape
	{
		IsPointContained = Shape.BaseCircle_IsPointContained,
		GetBoundingRectangle = Shape.BaseCircle_GetBoundingRectangle,
		GetRandomPoint = Shape.BaseCircle_GetRandomPoint,
		GetArea = Shape.BaseCircle_GetArea
	};
	BaseIntersection = new BaseShape
	{
		IsPointContained = Shape.BaseIntersection_IsPointContained,
		GetBoundingRectangle = Shape.BaseIntersection_GetBoundingRectangle,
		GetRandomPoint = Shape.BaseIntersection_GetRandomPoint,
	};
	BaseCombination = new BaseShape
	{
		IsPointContained = Shape.BaseCombination_IsPointContained,
		GetBoundingRectangle = Shape.BaseCombination_GetBoundingRectangle,
		GetRandomPoint = Shape.BaseCombination_GetRandomPoint,
	};
	BaseSubtraction = new BaseShape
	{
		IsPointContained = Shape.BaseSubtraction_IsPointContained,
		GetBoundingRectangle = Shape.BaseSubtraction_GetBoundingRectangle,
		GetRandomPoint = Shape.BaseSubtraction_GetRandomPoint,
	};
	// shape class identity
	BaseRectangle.shape = BaseRectangle;
	BaseCircle.shape = BaseCircle;
	BaseIntersection.shape = BaseIntersection;
	BaseCombination.shape = BaseCombination;
	BaseSubtraction.shape = BaseSubtraction;
	return true;
}

/** Full landscape rectangle
 @return Return a shape proplist representing the rectangle covering the whole current landscape.
*/
public func LandscapeRectangle()
{
	return Rectangle(0, 0, LandscapeWidth(), LandscapeHeight());
}


/** Constructor of rectangle area. (x,y) is included; (x+w,y+h) is excluded. Automatically flips rectangles of negative size in any dimension.
 @par x Global left side of rectangle
 @par y Global top side of rectangle
 @par w Rectangle width
 @par h Rectangle height
 @return return a shape proplist representing a rectangle
*/
global func Rectangle(int x2, int y2, int w2, int h2)
{
	// normalize
	if(w2 < 0)
	{
		x2 += w2;
		w2 = -w2;
	}
	if(h2 < 0)
	{
		y2 += h2;
		h2 = - h2;
	}
	return new Shape.BaseRectangle {x = x2, y = y2, wdt = w2, hgt = h2};
}