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 (267 lines) | stat: -rw-r--r-- 6,605 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
/**
	Meteor
	A burning rock falling from the sky, explodes on impact.
	
	@author Maikel, Zapper
*/

// A meteor can spawn objects on impact.
local spawn_id, spawn_amount;

/*-- Disaster Control --*/

/**
	Enables meteorites.
	The meteorites can be set to spawn objects on impact. The amount to spawn will be spawn_amount randomized by 50%.
*/
public func SetChance(int chance, id spawn_id, int spawn_amount)
{
	spawn_amount = spawn_amount ?? 1;	
	if (GetType(this) != C4V_Def) return FatalError("Must be called as a definition call.");
	
	var effect = FindMeteoriteEffectFor(spawn_id);
	if (!effect)
	 	effect = AddEffect("IntMeteorControl", nil, 100, 20, nil, this, spawn_id, spawn_amount);
	effect.chance = chance;
	
	return true;
}

/**
	Returns the chance of meteorites that is currently set. spawn_id can be nil for the normal meteorite.
*/
public func GetChance(id spawn_id)
{
	if (GetType(this) != C4V_Def) return FatalError("Must be called as a definition call.");
	var effect = FindMeteoriteEffectFor(spawn_id);
	if (effect)
		return effect.chance;
	return 0;
}

// Finds the meteorite effect that matches a specific spawn id.
private func FindMeteoriteEffectFor(id spawn_id)
{
	var i = 0, fx;
	while (fx = GetEffect("IntMeteorControl", nil, i++))
	{
		if (fx.spawn_id == spawn_id) return fx;
	}
	return nil;
}

private func FxIntMeteorControlStart(object target, effect fx, temp, id spawn_id, int spawn_amount)
{
	if (temp) return;
	fx.spawn_id = spawn_id;
	fx.spawn_amount = spawn_amount;
}

private func FxIntMeteorControlTimer(object target, effect fx, int time)
{
	if (Random(100) < fx.chance && !Random(10))
	{
		// Launch a meteor.
		var x = Random(LandscapeWidth());
		var y = 0;
		var size = RandomX(60, 90);
		var xdir = RandomX(-22, 22);
		var ydir = RandomX(28, 36);
		var real_spawn_amount = Max(1, RandomX(fx.spawn_amount / 2, 3 * fx.spawn_amount / 2));
		LaunchMeteor(x, y, size, xdir, ydir, fx.spawn_id, real_spawn_amount);
	}
	return FX_OK;
}

// Scenario saving
public func FxIntMeteorControlSaveScen(obj, fx, props)
{
	props->Add("Meteor", "Meteor->SetChance(%d, %v, %d)", fx.chance, fx.spawn_id, fx.spawn_amount);
	return true;
}

/*-- Meteor --*/

public func Launch(int x, int y, int size, int xdir, int ydir, id spawn_id, int spawn_amount)
{
	// Launch from indicated position.
	SetPosition(x, y);
	// Set the meteor's size.
	SetCon(BoundBy(size, 20, 100));
	// Set the initial velocity.
	SetXDir(xdir);
	SetYDir(ydir);
	// Remember spawning information.
	this.spawn_id = spawn_id;
	this.spawn_amount = spawn_amount ?? 1;
	// Safety check.
	if (!IsLaunchable())
		return false;
	// Allow for some more effects (overloadable).
	this->OnAfterLaunch();
	return this;
}

public func OnAfterLaunch()
{
	// Set random rotation.
	SetR(Random(360));
	SetRDir(RandomX(-10, 10));
	// Emit light
	SetLightRange(300, 30);
	SetLightColor(RGB(255, 160, 120));
	// Set right action.
	AddEffect("IntMeteor", this, 100, 1, this);
}

private func IsLaunchable()
{
	if (GBackSemiSolid() || Stuck())
	{
		RemoveObject();
		return false;
	}
	return true;
}

private func FxIntMeteorStart(object target, effect fx, bool temp)
{
	if (temp) return;
	fx.smoketrail = 
	{
		R = 255,
		B = PV_KeyFrames(0,  0,100,    30,0,  100,255, 1000,255),
		G = PV_KeyFrames(0,  0,150,  30,0, 100,255, 1000,255),
		
		Alpha = PV_KeyFrames(1000, 0, 0, 30, 255, 1000, 0),
		Size = PV_Linear(20,60),
		Stretch = 1000,
		Phase = PV_Random(0,4),
		Rotation = PV_Random(-GetR() - 15, -GetR() + 15),
		DampingX = 1000,
		DampingY = 1000,
		BlitMode = 0,
		CollisionVertex = 0,
		OnCollision = PC_Stop(),
		Attach = nil
	};
	fx.brighttrail = 
	{
		Prototype = fx.smoketrail,
		Alpha = PV_Linear(180,0),
		Size = PV_Linear(20,30),
		BlitMode = GFX_BLIT_Additive,
	};
	fx.frontburn = 
	{
		R = 255,
		B = 50,
		G = 190,
		
		Alpha = PV_KeyFrames(0, 0, 0, 500, 25, 1000, 0),
		Size = PV_Linear(4,5),
		Stretch = 1000,
		Phase = PV_Random(0,4),
		Rotation = PV_Random(-GetR() - 15, -GetR() + 15),
		DampingX = 1000,
		DampingY = 1000,
		BlitMode = GFX_BLIT_Additive,
		CollisionVertex = 0,
		OnCollision = PC_Stop(),
		Attach = ATTACH_Front | ATTACH_MoveRelative
	};
}

protected func FxIntMeteorTimer(object target, effect fx, bool temp)
{
	var size = GetCon();
	// Air drag.
	var ydir = GetYDir(100);
	ydir -= size * ydir ** 2 / 11552000; // Magic number.
	SetYDir(ydir, 100);
	// Smoke trail.
	CreateParticle("SmokeThick", 0, 0, PV_Random(-3, 3), PV_Random(-3, 3), 200, fx.smoketrail, 5);
	// Flash
	CreateParticle("SmokeThick", 0, -4, PV_Random(-3, 3), PV_Random(-3, 3), 3, fx.brighttrail, 2);
	// left and right burn
	CreateParticle("FireDense", PV_Random(-5, 5), 15, PV_Random(-5, -20), PV_Random(-15, -30), 20, fx.frontburn, 30);
	CreateParticle("FireDense", PV_Random(-5, 5), 15, PV_Random(5, 20), PV_Random(-15, -30), 20, fx.frontburn, 30);
	// Sound.

	// Burning and friction decrease size.
	if (size > 10 && !Random(5))
		DoCon(-1);

	return FX_OK;
}

// Scenario saving
func FxIntMeteorSaveScen(obj, fx, props)
{
	props->AddCall("Meteor", obj, "AddEffect", "\"IntMeteor\"", obj, 100, 1, obj);
	return true;
}

protected func Hit(int xdir, int ydir)
{
	var size = 10 + GetCon();
	var speed2 = 20 + (xdir ** 2 + ydir ** 2) / 10000;
	// Some fire sparks.
	var particles =
	{
		Prototype = Particles_Fire(),
		Attach = nil
	};
	CreateParticle("Fire", PV_Random(-size / 4, size / 4), PV_Random(-size / 4, size / 4), PV_Random(-size/4, size/4), PV_Random(-size/4, size/4), 30, particles, 20 + size);
	// Explode meteor, explode size scales with the energy of the meteor.	
	var dam = size * speed2 / 500;
	dam = BoundBy(size/2, 5, 30);
	// Blow up a dummy object so that the explosion can happen before we spawn items.
	var dummy = CreateObject(Dummy, 0, 0, GetController());
	dummy->Explode(dam);
	
	// Fling around some objects?
	if (spawn_id)
	{
		for (var i = 0; i < spawn_amount; ++i)
		{
			var angle = Angle(0, 0, -xdir, -ydir) + RandomX(-45, 45);
			var force = BoundBy(GetCon() / 2, 10, 50) + RandomX(-10, 10);
			var item = CreateObject(spawn_id, 0, 0, GetOwner());
			item->SetSpeed(Sin(angle, force), -Cos(angle, force));
		}
	}
	
	RemoveObject();
	return;
}


/*-- Target --*/

public func OnLightningStrike(object lightning, int damage)
{
	SetController(lightning->GetController());
	if (GetDamage() + damage >= 6)
		Hit();
	return;
}

public func IsProjectileTarget(object projectile)
{
	return true;
}

public func OnProjectileHit(object projectile)
{
	SetController(projectile->GetController());
	Hit();
	return;
}

public func IsMeteor() { return true; }


/*-- Proplist --*/

local Name = "$Name$";