File: Script.c

package info (click to toggle)
openclonk 8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 169,484 kB
  • sloc: cpp: 180,478; ansic: 108,988; xml: 31,371; python: 1,223; php: 767; makefile: 139; sh: 101
file content (380 lines) | stat: -rw-r--r-- 11,318 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
/**
	Rope bridge
	A bridge consisting of single wooden planks tied together with ropes.

	@author Randrian
*/

#include Library_Rope

local RopeBridge_Precision = 100;

local bridge_post1;
local bridge_post2;


/*-- Bridge Creation --*/

// Create a rope bridge starting at (x1, y1) and ending at (x2, y2).
// Set fragile to make the steps break when pressured.
// Set portable to make a bridge which can be retracted.
public func Create(int x1, int y1, int x2, int y2, bool fragile, bool portable)
{
	if (this != Ropebridge)
		return;
	var post1 = CreateObjectAbove(Ropebridge_Post, x1, y1);
	var post2 = CreateObjectAbove(Ropebridge_Post, x2, y2);
	var bridge = CreateObject(Ropebridge, (x1 + x2) / 2, (y1 + y2) / 2);
	bridge->MakeBridge(post1, post2);
	post1->SetStatic(!portable);
	post2->SetStatic(!portable);
	if (fragile)
		bridge->SetFragile();
	return;
}

// Creates the rope bridge between the two bridge posts.
public func MakeBridge(object post1, object post2)
{
	if (post1->GetID() != Ropebridge_Post || post2->GetID() != Ropebridge_Post)
		return FatalError("Ropebridge can only be connected between bridge posts");
	// Change the properties of the ropebridge object.
	this.Collectible = false;
	SetCategory(C4D_StaticBack);
	// Swap the posts if they have inverted x-coordinates.
	if (post1->GetX() > post2->GetX())
	{
		var swap = post1;
		post1 = post2;
		post2 = swap;	
	}
	// Set the bridge posts.
	bridge_post1 = post1;
	bridge_post2 = post2;
	bridge_post1->SetBridge(this);
	bridge_post2->SetBridge(this);
	bridge_post1->Turn(DIR_Left);
	bridge_post2->Turn(DIR_Right);
	StartRopeConnect(bridge_post1, bridge_post2);
	SetFixed(true, true);
	ConnectPull();
	AddEffect("IntHang", this, 1, 1, this);
	SetAction("Hanging");
	UpdateSegmentOverlays();
}

public func SetFragile()
{
	for (var i = 0; i < lib_rope_particle_count; i++)
		lib_rope_segments[i]->SetFragile(true);
}


/*-- Usage --*/

public func RejectUse(object clonk)
{
	return !clonk->IsWalking();
}

public func ControlUse(object clonk, int x, int y)
{
	// Create the first bridge post at the clonks feet.
	if (!bridge_post1)
	{
		bridge_post1 = CreateObjectAbove(Ropebridge_Post, 0, clonk->GetBottom());
		return true;
	}
	// Check whether the location of the second post is fine.
	var from_x = bridge_post1->GetX();
	var from_y = bridge_post1->GetY() + clonk->GetBottom();
	var to_x = clonk->GetX();
	var to_y = clonk->GetY() + clonk->GetBottom();
	var distance = Distance(from_x, from_y, to_x, to_y);
	var angle = Angle(from_x, from_y, to_x, to_y);
	if (distance > 120)
	{
		clonk->Message("$MsgBridgeLong$");	
		return true;
	}
	if (distance < 40)
	{
		clonk->Message("$MsgBridgeShort$");	
		return true;
	}
	if (!Inside(angle, 240, 300) && !Inside(angle, 60, 120))
	{
		clonk->Message("$MsgBridgeUneven$");	
		return true;
	}
	// Create the second bridge post at the clonks feet.
	bridge_post2 = CreateObjectAbove(Ropebridge_Post, 0, clonk->GetBottom());
	// Exit the ropebridge and connect between the two posts.
	Exit();
	MakeBridge(bridge_post1, bridge_post2);
	return true;
}


/*-- Rope Mechanics --*/

public func FxIntHangTimer()
{
	// Do a time step of the rope library.
	TimeStep();
	// Then apply forces from objects standing on the bridge.
 	for (var i = 1; i < lib_rope_particle_count - 1; i++)
 	{
   		lib_rope_particles[i].accx = 0;
   		lib_rope_particles[i].accy = lib_rope_segments[i]->~GetLoadWeight();
   	}
   	return FX_OK;
}

// To be overloaded for special segment behaviour.
private func CreateSegment(int index, object previous)
{
	var segment = CreateObjectAbove(Ropebridge_Segment);
	segment->SetMaster(this);
	return segment;
}

private func DeleteSegment(object segment, object previous)
{
	if (segment)
		segment->RemoveObject();
}

// Called if either of the posts got destroyed: consider the bridge lost and remove it.
public func OnBridgePostDestruction()
{
	RemoveObject();
	return;
}

public func Destruction()
{
	// Make sure both bridge posts are removed as well.
	if (bridge_post1)
	{
		bridge_post1->SetBridge(nil);
		bridge_post1->RemoveObject();
	}
	if (bridge_post2)
	{
		bridge_post2->SetBridge(nil);
		bridge_post2->RemoveObject();
	}
	return _inherited(...);
}


/*-- Segment Graphics --*/

public func DrawRopeLine(array start, array end, int i, int index)
{
	var angle = -Angle(end[0], end[1], start[0], start[1]);
	var point_x = 5 * (start[0] + end[0]);
	var point_y = 5 * (start[1] + end[1]);
	var length = Vec_Length(Vec_Sub(end, start)) * 125 / RopeBridge_Precision + 100;

	if (index != 4 && index != 7)
		SetLineTransform(lib_rope_segments[i], angle, point_x - lib_rope_particles[i].x * 10, point_y - lib_rope_particles[i].y * 10 + 2000, length, index);
	else if (lib_rope_segments[i]->GetDouble())
    	SetLineTransform(lib_rope_segments[i]->GetDouble(), angle, point_x - lib_rope_particles[i].x * 10, point_y - lib_rope_particles[i].y * 10 + 2000, length, index);
    return;
}

public func DrawPlank(array start, array end, int i)
{
	var angle = -Angle(end[0], end[1], start[0], start[1]);
	var point_x = 5 * (start[0] + end[0]);
	var point_y = 5 * (start[1] + end[1]);
	SetLineTransform(lib_rope_segments[i], angle, point_x - lib_rope_particles[i].x * 10, point_y - lib_rope_particles[i].y * 10 + 2000, 1000, 6);
	return;
}

public func UpdateLines()
{
	var oldangle = Angle(lib_rope_particles[1].x, lib_rope_particles[1].y, lib_rope_particles[0].x, lib_rope_particles[0].y);
	for (var i = 1; i < lib_rope_particle_count; i++)
	{
		var segment = lib_rope_segments[i];
		var particle = lib_rope_particles[i];
		// Update the position of the segment.
		if (segment)
		{
			segment->SetPosition(particle.x, particle.y, 0, LIB_ROPE_Precision);
			if (segment->GetDouble())
				segment->GetDouble()->SetPosition(particle.x, particle.y, 0, LIB_ROPE_Precision);
		}
		
		// Calculate the angle to the previous segment.
		var prev_particle = lib_rope_particles[i - 1];
		var angle = Angle(particle.x, particle.y, prev_particle.x, prev_particle.y);

		// Every segment has not its graphics, but the graphics of the previous segment (or achor for the first).
		// Otherwise the drawing order would be wrong an we would get lines over segments.
		
		// Draw the segment as an overlay for the following segment (only the last segment has two graphics: its and the previous).
		segment->SetR(90 + angle);
		// Draw the left line.
		var start = GetRopeConnectPosition(i, 0, 0, angle, oldangle);
		var end = GetRopeConnectPosition(i, 0, 1, angle, oldangle);
    	var end1 = start;
    	DrawRopeLine(start, end, i, 2);
    	if (segment->HasPlank())
      		DrawPlank(start, end, i);
    
		// Draw the right line.
		var start = GetRopeConnectPosition(i, 1, 0, angle, oldangle);
		var end = GetRopeConnectPosition(i, 1, 1, angle, oldangle);
		var end2 = start;
    	DrawRopeLine(start, end, i, 3);
    	
		// Draw the upper left line.
		var start = GetRopeConnectPosition(i, 0, 0, angle, oldangle);
		var end = GetRopeConnectPosition(i, 0, 1, angle, oldangle);
		var end3 = start[:];
		start[1] -= 800;
		end[1] -= 800;
		DrawRopeLine(start, end, i, 4);
	
		// Draw the upper right line.
		var start = GetRopeConnectPosition(i, 1, 0, angle, oldangle);
		var end = GetRopeConnectPosition(i, 1, 1, angle, oldangle);
		var end4 = start[:];
		start[1] -= 800;
		end[1] -= 800;
		DrawRopeLine(start, end, i, 5);
	    
	    if (i > 1)
	    {
			// Draw the upper left line.
			var start = end1;
			var end = end3;
			end[1] -= 800;
			DrawRopeLine(start, end, i, 7);
	      
			// Draw the upper right line.
			var start = end2;
			var end = end4;
			end[1] -= 800;
			DrawRopeLine(start, end, i, 8);
	    }
		// Remember the angle.
		oldangle = angle;
	}
	return;
}

public func UpdateSegmentOverlays()
{
	for (var i = 1; i < GetLength(lib_rope_segments); i++)
	{
    	var segment = lib_rope_segments[i];
    	segment->CreateDouble();
		segment->SetGraphics("Line", GetID(), 2, GFXOV_MODE_Base);
		segment->SetGraphics("Line", GetID(), 3, GFXOV_MODE_Base);
    	segment->GetDouble()->SetGraphics("Line", GetID(), 4, GFXOV_MODE_Base);
    	segment->SetGraphics("Line", GetID(), 5, GFXOV_MODE_Base);
	    if (i > 1)
	    {
			segment->GetDouble()->SetGraphics("Line", GetID(), 7, GFXOV_MODE_Base);
			segment->SetGraphics("Line", GetID(), 8, GFXOV_MODE_Base);
	    }
		segment->SetSolidMask(6, 0, 7, 3, -5, 9);
		if (i > 1 && i < GetLength(lib_rope_segments) - 1)
   		{
			segment->SetPlank(true);
			segment->SetGraphics("Segment", GetID(), 6, GFXOV_MODE_Base);
      		segment->SetClrModulation(HSL(255, 0, 128 + Random(128)), 6);
    	}
		if (i % 2 == 0)
		{
			var color = RGB(200,200,200);
			segment->SetClrModulation(color, 2);
			segment->SetClrModulation(color, 3);
			segment->GetDouble()->SetClrModulation(color, 4);
			segment->SetClrModulation(color, 5);
			segment->GetDouble()->SetClrModulation(color, 7);
			segment->SetClrModulation(color, 8);
    	}
	}
	lib_rope_segments[0]->CreateDouble();
	lib_rope_segments[-1]->CreateDouble();
	lib_rope_segments[1]->SetSolidMask();
	lib_rope_segments[-1]->SetSolidMask();
	lib_rope_segments[1]->SetGraphics(nil, nil, 6);
	return;
}

static const Ropebridge_Segment_Offset = [[200, 0], [-100, -100]];
static const Ropebridge_Anchor_Offset = [[-150, -100], [200, 0]];

public func GetRopeConnectPosition(int index, int right, int end, int angle, int oldangle)
{
	if ((!end && index == 1) || (end && index == lib_rope_particle_count - 1))
	{
		var obj = lib_rope_objects[end][0];
		var point_x = obj->GetX(RopeBridge_Precision) - Cos(obj->GetR(), Ropebridge_Anchor_Offset[right][0] * (-1 + 2 * end)) + Sin(obj->GetR(), Ropebridge_Anchor_Offset[right][1]);
		var point_y = obj->GetY(RopeBridge_Precision) - Sin(obj->GetR(), Ropebridge_Anchor_Offset[right][0] * (-1 + 2 * end)) - Cos(obj->GetR(), Ropebridge_Anchor_Offset[right][1]);
		return [point_x, point_y];
	}
	if (!end) 
		index -= 1;
	var point_x = lib_rope_particles[index].x - Cos(oldangle, Ropebridge_Segment_Offset[right][0]) - Sin(oldangle, Ropebridge_Segment_Offset[right][1]); 
	var point_y = lib_rope_particles[index].y - Sin(oldangle, Ropebridge_Segment_Offset[right][0]) + Cos(oldangle, Ropebridge_Segment_Offset[right][1]);
	return [point_x, point_y];
}

public func SetLineTransform(object obj, int r, int xoff, int yoff, int length, int layer, int mirror)
{
	if (!mirror) 
		mirror = 1;
	var fsin = Sin(r, 1000), fcos = Cos(r, 1000);
	// Set matrix values.
	obj->SetObjDrawTransform (
		+fcos * mirror, +fsin * length / 1000, xoff,
		-fsin * mirror, +fcos * length / 1000, yoff, layer
	);
}

public func Hit()
{
	Sound("Hits::Materials::Wood::WoodHit?");
}

public func IsInventorProduct() { return true; }


/*-- Scenario Saving --*/

public func SaveScenarioObject(props)
{
	if (!inherited(props, ...)) return false;
	props->Remove("Category");
	// Save bridge creation on scenario save.
	if (lib_rope_objects && lib_rope_objects[0] && lib_rope_objects[1])
	{
		var o1 = lib_rope_objects[0][0], o2 = lib_rope_objects[1][0];
		if (o1 && o2) props->AddCall("Bridge", this, "MakeBridge", o1, o2);
	}
	return true;
}


/*-- Properties --*/

local ActMap = {
	Hanging = {
		Prototype = Action,
		Name = "Hanging",
		Width = 0,
		Height = 0
	},
};
local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local Components = {Wood = 2/*, Rope = 2*/};