File: Script.c

package info (click to toggle)
openclonk 8.1-4
  • links: PTS, VCS
  • area: main
  • in suites:
  • 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 (276 lines) | stat: -rw-r--r-- 6,974 bytes parent folder | download | duplicates (6)
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
/*
	Cotton Fruit
	Author: Clonkonaut, Win
*/

// Animation length is 875
// At about 500 the fruit starts filling up with gas
local first_animation_stage = 500;
// Growing time for first stage in frames
local grow_time = 3100;
local grow_anim;
local attach_branch;
// Filling time & also animation duration for second stage
local fill_time = 3100;
// When the balloon is full of gas, it's ripe!
local ripened = false;
// Time the filled balloon will stay on the branch (time the player has to prepare for harvesting)
local ripe_time = 3500;
// Time the player has for harvesting before the balloon starts rising
local float_time = 350;
// Half of the time the balloon will fly up and half will just drift with the wind
local fly_time = 700;

private func Construction()
{
	SetProperty("MeshTransformation", Trans_Rotate(180, 0,0,1));
}

/* Growing & filling with gas */

public func Grow(int branch, bool fullgrown)
{
	attach_branch = branch;
	if (!fullgrown)
	{
		grow_anim = PlayAnimation("grow", 1, Anim_Linear(0,0, first_animation_stage, grow_time, ANIM_Hold), Anim_Const(1000));
		AddTimer("Growing", 35);
		ScheduleCall(this, "Fill", grow_time);
	}
	else
	{
		if (!grow_anim) grow_anim = PlayAnimation("grow", 1, Anim_Linear(GetAnimationLength("grow"),0, GetAnimationLength("grow"), 1, ANIM_Hold), Anim_Const(1000));
		else SetAnimationPosition(grow_anim, Anim_Const(GetAnimationLength("grow")));
		if (Contained())
		{
			Contained()->UpdateFruitAttachTransform(attach_branch, 2000);
			Contained()->FruitFills(attach_branch, nil, true);
		}
		Ripen();
	}
}

public func IsGrowing()
{
	return !ripened;
}

// Every ~1 second update scaling on cotton plant
private func Growing()
{
	// Shouldn't happen
	if (!Contained()) return RemoveTimer("Growing");
	Contained()->UpdateFruitAttachTransform(attach_branch, GetAnimationPosition(grow_anim) * 1000 / first_animation_stage);
	if (GetAnimationPosition(grow_anim) >= first_animation_stage) RemoveTimer("Growing");
}

// The fruit is now grown and starts filling with gas, this will also move the branch
private func Fill()
{
	// No plant, no fill
	if (!Contained()) return;
	SetAnimationPosition(grow_anim, Anim_Linear(GetAnimationPosition(grow_anim), 0, GetAnimationLength("grow"), fill_time, ANIM_Hold));
	Contained()->FruitFills(attach_branch, fill_time, false); // Will start the branch's animation
	ScheduleCall(this, "Ripen", fill_time);
}

/* Ripening & Flying */

private func Ripen()
{
	SetMeshMaterial("Cotton_Fruit_Ripe");
	ripened = true;
	AddEffect("IntPrepareFlight", this, 1, 1, this);
}

private func FxIntPrepareFlightTimer(object target, proplist effect, int time)
{
	if (!Contained()) return FX_Execute_Kill;
	if (time > 10 && !effect.shakes)
	{
		effect.shakes = 1;
		Contained()->ShakeFruit(attach_branch);
	}
	if (time > ripe_time / 2 && effect.shakes == 1)
	{
		effect.shakes = 2;
		Contained()->ShakeFruit(attach_branch);
	}
	if (time >= ripe_time)
	{
		Fly();
		return FX_Execute_Kill;
	}
	return FX_OK;
}

// May be called from elsewhere e.g. the cotton plant when harvesting with a sickle
public func Fly() // you fools
{
	var plant = Contained();
	if (plant)
	{
		var pos = plant->GetFruitExitPosition(attach_branch);
		Exit(pos.x, pos.y);
		plant->DetachFruit(attach_branch); // bye-bye
	}
	SetAction("Fly");
	SetYDir(-1);
	var effect = AddEffect("IntFlight", this, 1, 5, this);
	effect.random = Random(360);
}

private func FxIntFlightTimer(object target, proplist effect, int time)
{
	var xdir = GetXDir();
	var ydir = GetYDir();
	if (time < float_time)
	{
		ydir += Sin(time + effect.random, 2);
	}
	// Fly upwards
	if (time > float_time)
	{
		if (time < float_time + fly_time / 2)
			ydir -= Max(Tan((900 - time + float_time)%900, 10, 10), 0);
		// Fly with the wind
		if (GetWind())
			xdir += Tan((time - float_time)%900, GetWind(), 10);
		else if (xdir != 0)
			xdir -= xdir / Abs(xdir);
	}
	if (time > float_time + fly_time / 2)
	{
		if (ydir < 0) ydir += 1;
		// ~1 second until pop!
	}
	if (time > float_time + fly_time + 35)
	{
		if (TryPop())
		{
			// Pop deletes this object
			return FX_Execute_Kill;
		}
		else
		{
			// Try again in a second or so.
			fly_time += 20 + Random(20);
			if (!Random(5) || GetContact())
			{
				Pop(true);
				return FX_Execute_Kill;
			}
		}
	}

	SetSpeed(xdir, ydir);
	SetProperty("MeshTransformation", Trans_Rotate(180 + BoundBy(xdir, -50, 50), 0,0,1));
	return FX_OK;
}

/* Shooting, harvesting, popping */

public func IsProjectileTarget() { return true; }
public func IsHarvestable() { return true; }
public func SickleHarvesting() { return true; }

public func OnProjectileHit() { Pop(); }
public func Damage() { Pop(); }
public func Harvest() { Pop(); }

// Checks whether the ground below would be suitable for planting and then pops the fruit.
public func TryPop()
{
	// Find the ground below.
	var max_h = 300;
	var stepsize = 20;
	var y = 0;
	for (; y < max_h; y += stepsize)
		if (GBackSolid(0, y))
			break;
	// No ground hit?
	if (y >= max_h) return false;
	
	// Ground found! Then check again in smaller steps to actually find the surface.
	y -= stepsize;
	for (var i = 0; i < stepsize; i += 2)
	{
		y += 2;
		if (!GBackSolid(0, y)) continue;
		// Solid! But is it any good?
		if (GetMaterialVal("Soil", "Material", GetMaterial(0, y)) != 1)
			return false;
		break;
	}
	
	// Soil found! Overpopulated already?
	if (ObjectCount(Find_AtPoint(0, y), Find_ID(Cotton)) > 1) return false;
	
	// Spot found! Place a plant and cast some "seed" like particles.
	var plant = CreateObjectAbove(Cotton, 0, y, GetOwner());
	plant->SetCon(1);
	
	var particles = 
	{
		Prototype = Particles_Glimmer(),
		ForceX = PV_Wind(10),
		Stretch = PV_Speed(500, 500),
		R = 255, G = 255, B = 255,
		BlitMode = nil,
		Size = PV_Linear(1, 0)
	};
	CreateParticle("SphereSpark", 0, 0, PV_Random(-5, 5), PV_Random(-5, 20), PV_Random(100, 300), particles, 30);
	Pop(true);
	return true;
}

public func Pop(bool no_seed)
{
	CreateParticle("CottonBalloon", 0,0, PV_Random(-30,30), PV_Random(-30,30), PV_Random(40, 60), Particles_CottonBalloon(), 50);
	if (!no_seed)
	{
		var seed = CreateObject(CottonSeed);
		if (OnFire()) seed->Incinerate();
	}
	RemoveObject();
}

/*-- Saving --*/

public func SaveScenarioObject(proplist props)
{
	// Do not save fruit inside a cotton plant
	if (Contained())
		if (Contained()->GetID() == Cotton)
			return false;
	var effect;
	if (effect = GetEffect("IntFlight", this))
	{
		props->AddCall("Flight", this, "ResumeFlight", effect.time, effect.random);
	}
	return true;
}

// Loading
private func ResumeFlight(int effect_time, int effect_random)
{
	var effect = AddEffect("IntFlight", this, 1, 5, this);
	effect.time = effect_time;
	effect.random = effect_random;
}

local ActMap = {
	Fly = {
		Prototype = Action,
		Name = "Fly",
		Procedure = DFA_FLOAT,
		Speed = 50,
		Accel = 5,
		NextAction = "Fly",
	},
};

local Plane = 460;
local BlastIncinerate = 1;
local ContactIncinerate = 3;
local BorderBound = C4D_Border_Sides;