1 /**
2 Rockfall
3 Big pieces of rock that fall down cliffs.
4
5 @author Maikel
6 */
7
8
9 /*-- Disaster Control --*/
10
SetChance(int chance)11 public func SetChance(int chance)
12 {
13 if (this != Rockfall)
14 return;
15 var effect = GetEffect("IntRockfallControl");
16 if (!effect)
17 effect = AddEffect("IntRockfallControl", nil, 100, 20, nil, Rockfall);
18 effect.chance = chance;
19 return;
20 }
21
GetChance()22 public func GetChance()
23 {
24 if (this != Rockfall)
25 return;
26 var effect = GetEffect("IntRockfallControl");
27 if (effect)
28 return effect.chance;
29 return;
30 }
31
SetArea(proplist rect)32 public func SetArea(proplist rect)
33 {
34 if (this != Rockfall)
35 return;
36 var effect = GetEffect("IntRockfallControl");
37 if (effect)
38 effect.area = rect;
39 return;
40 }
41
SetExplosiveness(int explosiveness)42 public func SetExplosiveness(int explosiveness)
43 {
44 if (this != Rockfall)
45 return;
46 var effect = GetEffect("IntRockfallControl");
47 if (!effect)
48 return;
49 effect.explosiveness = explosiveness;
50 return;
51 }
52
53 // Sets the spawn distance from crew members.
SetSpawnDistance(int dist)54 public func SetSpawnDistance(int dist)
55 {
56 if (this != Rockfall)
57 return;
58 var effect = GetEffect("IntRockfallControl");
59 if (!effect)
60 return;
61 effect.spawn_distance = dist;
62 return;
63 }
64
FxIntRockfallControlTimer(object target,proplist effect,int time)65 protected func FxIntRockfallControlTimer(object target, proplist effect, int time)
66 {
67 if (Random(100) < effect.chance && !Random(6))
68 {
69 // Attempt to find a suitable location for the rock to be created.
70 var x, y;
71 var max_tries = 500;
72 for (var i = 0; i < max_tries; i++)
73 {
74 var x = Random(LandscapeWidth());
75 var y = 0;
76 if (effect.area)
77 {
78 x = effect.area.x + Random(effect.area.wdt);
79 y = effect.area.y + Random(effect.area.hgt);
80 }
81 if (effect.spawn_distance)
82 {
83 var dist = (max_tries - i) * effect.spawn_distance / max_tries;
84 if (FindObject(Find_OCF(OCF_CrewMember), Find_Distance(dist, x, y)))
85 continue;
86 }
87 break;
88 }
89 // Explosive rocks demanded?
90 var explosive = effect.explosiveness && Random(100) < effect.explosiveness;
91 // Launch rockfall of varying sizes between 40 and 120.
92 LaunchRockfall(x, y, 80 + Random(41), RandomX(-2, 2), RandomX(4, 8), explosive);
93 }
94 return FX_OK;
95 }
96
97 // Scenario saving through an effect call.
FxIntRockfallControlSaveScen(obj,fx,props)98 public func FxIntRockfallControlSaveScen(obj, fx, props)
99 {
100 props->Add("Rockfall", "Rockfall->SetChance(%d)", fx.chance);
101 props->Add("Rockfall", "Rockfall->SetArea(%v)", fx.area);
102 if (fx.explosiveness)
103 props->Add("Rockfall", "Rockfall->SetExplosiveness(%d)", fx.explosiveness);
104 if (fx.spawn_distance)
105 props->Add("Rockfall", "Rockfall->SetSpawnDistance(%d)", fx.spawn_distance);
106 return true;
107 }
108
109 // Launches an earthquake with epicenter (x,y).
LaunchRockfall(int x,int y,int size,int xdir,int ydir,bool explosive)110 global func LaunchRockfall(int x, int y, int size, int xdir, int ydir, bool explosive)
111 {
112 // The rockfall size is constrained between 40 and 120%.
113 size = BoundBy(size, 40, 120);
114
115 // Rockfall should be launched in the free.
116 if (GBackSemiSolid(x, y))
117 return false;
118
119 // Create rock and adjust its size.
120 var rock = CreateObject(Rockfall, x, y);
121 rock->SetCon(size);
122
123 // Remove rock if stuck.
124 if (rock->Stuck())
125 return rock->RemoveObject();
126
127 // Set speed and rotation.
128 rock->SetXDir(xdir);
129 rock->SetYDir(ydir);
130 rock->SetR(Random(360));
131 rock->SetRDir(RandomX(-6, 6));
132
133 // Make explosive
134 if (explosive)
135 rock->MakeExplosive();
136
137 return true;
138 }
139
140
141 /*-- Rockfall --*/
142
143 local damage, is_explosive;
144
Construction()145 protected func Construction()
146 {
147 damage = 0;
148 this.MeshTransformation = Trans_Scale(2000, 2000, 2000);
149 // Add an effect for rolling then the rock is just lying around.
150 AddEffect("IntRockMovement", this, 100, 4, this);
151 return;
152 }
153
MakeExplosive()154 public func MakeExplosive()
155 {
156 is_explosive = true;
157 SetClrModulation(0xffff0000);
158 return true;
159 }
160
FxIntRockRollStart(object target,proplist effect,int temporary)161 protected func FxIntRockRollStart(object target, proplist effect, int temporary)
162 {
163 if (temporary)
164 return 1;
165 effect.damtime = 0;
166 return 1;
167 }
168
FxIntRockMovementTimer(object target,proplist effect,int time)169 protected func FxIntRockMovementTimer(object target, proplist effect, int time)
170 {
171 // If rock is not moving give it a kick.
172 if (GetXDir() == 0 || GetYDir() == 0)
173 {
174 SetXDir(RandomX(-160, 160), 100);
175 SetYDir(-60 - Random(60), 100);
176 SetRDir(GetRDir() + RandomX(-2, 2));
177 }
178
179 // Damage rock every 120 frames to make sure it breaks at some point.
180 effect.damtime += 4;
181 if (effect.damtime > 120)
182 {
183 effect.damtime = 0;
184 damage++;
185 }
186 return 1;
187 }
188
Hit(int dx,int dy)189 protected func Hit(int dx, int dy)
190 {
191 // Acid kills rockfall
192 if (GetMaterialVal("Corrosive", "Material", GetMaterial()))
193 {
194 Sound("Liquids::Pshshsh");
195 var sz = Max(GetCon()/10, 5);
196 var particles = new Particles_Dust() { Size = sz*3, };
197 if (is_explosive)
198 { particles.R = 200; particles.G =100; particles.B = 0; }
199 else
200 { particles.R = 100; particles.G =200; particles.B = 100; }
201 CreateParticle("Dust", PV_Random(-sz, sz), PV_Random(-sz, sz), PV_Random(-3, 3), PV_Random(-3, -3), PV_Random(36, 2 * 36), particles, sz/2);
202 return RemoveObject();
203 }
204
205 // Determine caused damage to this rock by impact.
206 damage += Distance(dx, dy, 0, 0) / 100;
207 if (damage > 12)
208 return SplitRock();
209
210 // Rebounce or crack on downward hit.
211 if (dy > 0)
212 {
213 SetXDir(dx + RandomX(-160, 160), 100);
214 SetYDir(Min(-60, -7 * dy / 10) - Random(60), 100);
215 SetRDir(GetRDir() + RandomX(-2, 2));
216 }
217
218 // Some particles and smoke.
219 if (GetMaterial(0, 9 * GetCon() / 100))
220 {
221 var clr = GetAverageTextureColor(GetTexture(0, 9 * GetCon() / 100));
222 var particles =
223 {
224 Prototype = Particles_Dust(),
225 R = (clr >> 16) & 0xff,
226 G = (clr >> 8) & 0xff,
227 B = clr & 0xff,
228 Size = 4,
229 };
230 CreateParticle("Dust", PV_Random(-2, 2), 8 * GetCon() / 100, PV_Random(-3, 3), PV_Random(-2, -3), PV_Random(36, 2 * 36), particles, 2);
231 }
232
233 // Fling living beings near impact point for a big hit.
234 if (GetCon() > 80 && dy > 60)
235 {
236 for (var obj in FindObjects(Find_NoContainer(), Find_OCF(OCF_Alive), Find_InRect(-16, -8, 32, 24)))
237 {
238 if (!Random(3) || !obj->GetAction())
239 continue;
240 var act = obj.ActMap[obj->GetAction()];
241 if (act.Attach || (act.Procedure && act.Procedure != DFA_FLIGHT && act.Procedure != DFA_LIFT && act.Procedure != DFA_FLOAT && act.Procedure != DFA_ATTACH && act.Procedure != DFA_CONNECT))
242 obj->Fling(Random(3) - 1);
243 }
244 }
245
246 // Sound.
247 Sound("Environment::Disasters::EarthquakeEnd", nil, 3 * GetCon() / 2);
248 StonyObjectHit(dx, dy);
249 return;
250 }
251
SplitRock()252 private func SplitRock()
253 {
254 var con = GetCon(), erock;
255 // Explosive rocks do some damage
256 if (is_explosive)
257 if (erock = CreateObjectAbove(Rock, 0, 4, GetController()))
258 erock->Explode(Max(15 * con / 100, 3));
259 // Split the rock into smaller ones if it is big enough.
260 if (con > 40)
261 {
262 while (con > 0)
263 {
264 var rock = CreateObjectAbove(Rockfall);
265 var rock_con = Max(30, GetCon() / 2 + RandomX(-20, 20));
266 rock->SetCon(rock_con);
267 con -= 2 * rock_con / 3;
268 rock->SetXDir(RandomX(-100, 100), 100);
269 rock->SetYDir(RandomX(-200, -100), 100);
270 rock->SetRDir(RandomX(-6, 6));
271 if (is_explosive)
272 rock->MakeExplosive();
273 }
274 }
275 // Some particles.
276 var rock_explode =
277 {
278 Size = PV_KeyFrames(0, 180, 25, 1000, 50),
279 DampingY = PV_Random(890, 920, 5),
280 DampingX = PV_Random(900, 930, 5),
281 ForceY=-1,
282 ForceX = PV_Wind(20, PV_Random(-2, 2)),
283 Rotation=PV_Random(0,360,0),
284 R=PV_KeyFrames(0, 0, 255, 260, 64, 1000, 64),
285 G=PV_KeyFrames(0, 0, 128, 260, 64, 1000, 64),
286 B=PV_KeyFrames(0, 0, 0, 260, 108, 1000, 108),
287 Alpha = PV_KeyFrames(0, 0, 0, 100, 20, 500, 20, 1000, 0)
288 };
289 CreateParticle("SmokeDirty", PV_Random(-5, 5), PV_Random(-5, 5), 0, PV_Random(-2, 0), PV_Random(50, 100), rock_explode, 8);
290
291 // Some sound effects.
292 Sound("Environment::Disasters::EarthquakeEnd", nil, 100);
293
294 RemoveObject();
295 return;
296 }
297
298
299 /*-- Proplist --*/
300
301 local Name = "$Name$";
302