1 (*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *)
18
19 {$INCLUDE "options.inc"}
20
21 unit uVisualGearsList;
22 interface
23 uses uTypes;
24
AddVisualGearnull25 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline;
AddVisualGearnull26 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline;
AddVisualGearnull27 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline;
AddVisualGearnull28 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean; Layer: LongInt): PVisualGear;
29 procedure DeleteVisualGear(Gear: PVisualGear);
VisualGearByUIDnull30 function VisualGearByUID(uid : Longword) : PVisualGear;
31
32 const
33 cExplFrameTicks = 110;
34
35 var VGCounter: LongWord;
36 VisualGearLayersStart: array[0..6] of PVisualGear;
37 VisualGearLayersEnd: array[0..6] of PVisualGear;
38
39 implementation
40 uses uCollisions, uFloat, uVariables, uConsts, uTextures, uVisualGearsHandlers, uScript;
41
AddVisualGearnull42 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline;
43 begin
44 // adjust some visual gear types if underwater
45 if CheckCoordInWater(X, Y) and ((Kind = vgtBeeTrace) or (Kind = vgtSmokeTrace) or (Kind = vgtEvilTrace)) then
46 Kind:= vgtBubble;
47
48 AddVisualGear:= AddVisualGear(X, Y, Kind, 0, false, -1);
49 end;
50
AddVisualGearnull51 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline;
52 begin
53 AddVisualGear:= AddVisualGear(X, Y, Kind, State, false, -1);
54 end;
55
AddVisualGearnull56 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; inline;
57 begin
58 AddVisualGear:= AddVisualGear(X, Y, Kind, State, Critical, -1);
59 end;
60
AddVisualGearnull61 function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean; Layer: LongInt): PVisualGear;
62 var gear: PVisualGear;
63 t: Longword;
64 sp: real;
65 begin
66 AddVisualGear:= nil;
67 if (GameType <> gmtRecord) and
68 (((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) or fastScrolling) and // we are scrolling now
69 (not Critical)) then
70 exit;
71
72 if ((cReducedQuality and rqAntiBoom) <> 0) and
73 (not Critical) and
74 (not (Kind in
75 [vgtTeamHealthSorter,
76 vgtSmallDamageTag,
77 vgtSpeechBubble,
78 vgtHealthTag,
79 vgtExplosion,
80 vgtBigExplosion,
81 vgtSmokeTrace,
82 vgtEvilTrace,
83 vgtNote,
84 vgtFeather,
85 vgtSmoothWindBar])) then
86
87 exit;
88
89 inc(VGCounter);
90 New(gear);
91 FillChar(gear^, sizeof(TVisualGear), 0);
92 gear^.X:= real(X);
93 gear^.Y:= real(Y);
94 gear^.Kind := Kind;
95 gear^.doStep:= doStepVGHandlers[Kind];
96 gear^.Tint:= $FFFFFFFF;
97 gear^.uid:= VGCounter;
98
99 with gear^ do
100 case Kind of
101 vgtFlake:
102 begin
103 Timer:= 0;
104 tdX:= 0;
105 tdY:= 0;
106 Scale:= 1.0;
107 if SuddenDeathDmg then
108 begin
109 if vobSDFrameTicks > 0 then
110 FrameTicks:= random(vobSDFrameTicks);
111 Frame:= random(vobSDFramesCount);
112 end
113 else
114 begin
115 if vobFrameTicks > 0 then
116 FrameTicks:= random(vobFrameTicks);
117 Frame:= random(vobFramesCount);
118 end;
119 Angle:= random(360);
120 dx:= 0.0000038654705 * random(10000);
121 dy:= 0.000003506096 * random(7000);
122 if random(2) = 0 then
123 dx := -dx;
124 if SuddenDeathDmg then
125 dAngle:= (random(2) * 2 - 1) * (vobSDVelocity + random(vobSDVelocity)) / 1000
126 else
127 dAngle:= (random(2) * 2 - 1) * (vobVelocity + random(vobVelocity)) / 1000
128 end;
129 vgtCloud:
130 begin
131 Frame:= random(4);
132 dx:= 0.5 + 0.1 * random(5); // how much the cloud will be affected by wind
133 timer:= random(4096);
134 Scale:= 1.0
135 end;
136 vgtExplPart,
137 vgtExplPart2:
138 begin
139 t:= random(1024);
140 sp:= 0.001 * (random(95) + 70);
141 dx:= hwFloat2Float(AngleSin(t)) * sp;
142 dy:= hwFloat2Float(AngleCos(t)) * sp;
143 if random(2) = 0 then
144 dx := -dx;
145 if random(2) = 0 then
146 dy := -dy;
147 Frame:= 7 - random(3);
148 FrameTicks:= cExplFrameTicks
149 end;
150 vgtFire:
151 begin
152 t:= random(1024);
153 sp:= 0.001 * (random(85) + 95);
154 dx:= hwFloat2Float(AngleSin(t)) * sp;
155 dy:= hwFloat2Float(AngleCos(t)) * sp;
156 if random(2) = 0 then
157 dx := -dx;
158 if random(2) = 0 then
159 dy := -dy;
160 FrameTicks:= 650 + random(250);
161 Frame:= random(8)
162 end;
163 vgtEgg:
164 begin
165 t:= random(1024);
166 sp:= 0.001 * (random(85) + 95);
167 dx:= hwFloat2Float(AngleSin(t)) * sp;
168 dy:= hwFloat2Float(AngleCos(t)) * sp;
169 if random(2) = 0 then
170 dx := -dx;
171 if random(2) = 0 then
172 dy := -dy;
173 FrameTicks:= 650 + random(250);
174 Frame:= 1
175 end;
176 vgtShell: FrameTicks:= 500;
177 vgtSmallDamageTag:
178 begin
179 gear^.FrameTicks:= 1100
180 end;
181 vgtBubble:
182 begin
183 dx:= 0.0000038654705 * random(10000);
184 dy:= 0;
185 if random(2) = 0 then
186 dx := -dx;
187 FrameTicks:= 250 + random(1751);
188 Frame:= random(5)
189 end;
190 vgtSteam:
191 begin
192 dx:= 0.0000038654705 * random(10000);
193 dy:= 0.001 * (random(85) + 95);
194 if random(2) = 0 then
195 dx := -dx;
196 Frame:= 7 - random(3);
197 FrameTicks:= cExplFrameTicks * 2;
198 end;
199 vgtAmmo:
200 begin
201 alpha:= 1.0;
202 scale:= 1.0
203 end;
204 vgtSmokeWhite,
205 vgtSmoke:
206 begin
207 Scale:= 1.0;
208 dx:= 0.0002 * (random(45) + 10);
209 dy:= 0.0002 * (random(45) + 10);
210 if random(2) = 0 then
211 dx := -dx;
212 Frame:= 7 - random(2);
213 FrameTicks:= cExplFrameTicks * 2;
214 end;
215 vgtDust:
216 begin
217 dx:= 0.005 * (random(15) + 10);
218 dy:= 0.001 * (random(40) + 20);
219 if random(2) = 0 then dx := -dx;
220 if random(2) = 0 then Tag:= 1
221 else Tag:= -1;
222 Frame:= 7 - random(2);
223 FrameTicks:= random(20) + 15;
224 end;
225 vgtSplash:
226 begin
227 dx:= 0;
228 dy:= 0;
229 FrameTicks:= 740;
230 Frame:= 19;
231 Scale:= 0.75;
232 Timer:= 1;
233 end;
234 vgtDroplet:
235 begin
236 // => min speed ~ 0.098, max speed ~ 0.218, speed range ~ 0.120
237 // => min angle(4096) ~ 129, max angle ~ 1919, angle range ~ 1790
238 dx:= 0.001 * (98 + random(121)); // speed
239 Frame:= 129 + random(1791); // angle
240 dy:= -dx * hwFloat2Float(AngleSin(Frame));
241 // divide by 2 to create an eliptic shape
242 dx:= dx * hwFloat2Float(AngleCos(Frame)) / 2;
243 FrameTicks:= 250 + random(1751);
244 Frame:= random(3)
245 end;
246 vgtBeeTrace:
247 begin
248 FrameTicks:= 1000;
249 Frame:= random(16);
250 end;
251 vgtSmokeRing:
252 begin
253 dx:= 0;
254 dy:= 0;
255 FrameTicks:= 600;
256 Timer:= 0;
257 Frame:= 0;
258 scale:= 0.6;
259 alpha:= 1;
260 angle:= random(360);
261 end;
262 vgtFeather:
263 begin
264 t:= random(1024);
265 sp:= 0.001 * (random(85) + 95);
266 dx:= hwFloat2Float(AngleSin(t)) * sp;
267 dy:= hwFloat2Float(AngleCos(t)) * sp;
268 if random(2) = 0 then
269 dx := -dx;
270 if random(2) = 0 then
271 dy := -dy;
272 FrameTicks:= 650 + random(250);
273 Frame:= 1
274 end;
275 vgtHealthTag:
276 begin
277 Frame:= 0;
278 FrameTicks:= 0;
279 Timer:= 1500;
280 dY:= -0.08;
281 dX:= 0;
282 end;
283 vgtSmokeTrace,
284 vgtEvilTrace:
285 begin
286 gear^.X:= gear^.X - 16;
287 gear^.Y:= gear^.Y - 16;
288 gear^.State:= 8;
289 end;
290 vgtBigExplosion:
291 begin
292 gear^.Angle:= random(360);
293 end;
294 vgtChunk:
295 begin
296 gear^.Frame:= random(4);
297 t:= random(1024);
298 sp:= 0.001 * (random(85) + 47);
299 dx:= hwFloat2Float(AngleSin(t)) * sp;
300 dy:= hwFloat2Float(AngleCos(t)) * sp * -2;
301 if random(2) = 0 then
302 dx := -dx;
303 end;
304 vgtNote:
305 begin
306 dx:= 0.005 * (random(15) + 10);
307 dy:= -0.001 * (random(40) + 20);
308 if random(2) = 0 then
309 dx := -dx;
310 Frame:= random(4);
311 FrameTicks:= random(2000) + 1500;
312 end;
313 vgtBulletHit:
314 begin
315 dx:= 0;
316 dy:= 0;
317 FrameTicks:= 350;
318 Frame:= 7;
319 Angle:= 0;
320 end;
321 vgtSmoothWindBar:
322 begin
323 Angle:= hwFloat2Float(cMaxWindSpeed)*2 / 1440; // seems rate below is supposed to change wind bar at 1px per 10ms. Max time, 1440ms. This tries to match the rate of change
324 Tag:= hwRound(cWindSpeed * 72 / cMaxWindSpeed);
325 end;
326 vgtStraightShot:
327 begin
328 Angle:= 0;
329 Scale:= 1.0;
330 dx:= 0.001 * random(45);
331 dy:= 0.001 * (random(20) + 25);
332 State:= ord(sprHealth);
333 if random(2) = 0 then
334 dx := -dx;
335 Frame:= 0;
336 FrameTicks:= random(750) + 1250;
337 State:= ord(sprSnowDust);
338 end;
339 vgtNoPlaceWarn:
340 begin
341 FrameTicks:= 2000;
342 Tint:= $FF0000FF;
343 Scale:= 1.0;
344 end;
345 end;
346
347 if State <> 0 then
348 gear^.State:= State;
349
350 case Gear^.Kind of
351 vgtFlake:
352 if random(3) = 0 then
353 begin
354 gear^.Scale:= 0.5;
355 gear^.Layer:= 0 // 33% - far back
356 end
357 else if random(3) = 0 then
358 begin
359 gear^.Scale:= 0.8;
360 gear^.Layer:= 4 // 22% - mid-distance
361 end
362 else if random(3) <> 0 then
363 gear^.Layer:= 5 // 30% - just behind land
364 else if (not cFlattenFlakes) and (random(2) = 0) then
365 gear^.Layer:= 6 // 7% - just in front of land
366 else if not cFlattenFlakes then
367 begin
368 gear^.Scale:= 1.5;
369 gear^.Layer:= 2 // 7% - close up
370 end
371 else begin
372 gear^.Layer:= 0;
373 gear^.Scale:= 0.5
374 end;
375
376 vgtCloud: if cFlattenClouds then gear^.Layer:= 5
377 else if random(3) = 0 then
378 begin
379 gear^.Scale:= 0.25;
380 gear^.Layer:= 0
381 end
382 else if random(2) = 0 then
383 gear^.Layer:= 5
384 else
385 begin
386 gear^.Scale:= 0.4;
387 gear^.Layer:= 4
388 end;
389 vgtNoPlaceWarn: gear^.Layer:= 6;
390
391 // 0: this layer is very distant in the background when in stereo
392 vgtTeamHealthSorter,
393 vgtSmoothWindBar: gear^.Layer:= 0;
394
395
396 // 1: this layer is on the land level (which is close but behind the screen plane) when stereo
397 vgtSmokeTrace,
398 vgtEvilTrace,
399 vgtLineTrail,
400 vgtSmoke,
401 vgtSmokeWhite,
402 vgtDust,
403 vgtFire,
404 vgtSplash,
405 vgtDroplet,
406 vgtBubble: gear^.Layer:= 1;
407
408 // 3: this layer is on the screen plane (depth = 0) when stereo
409 vgtSpeechBubble,
410 vgtSmallDamageTag,
411 vgtHealthTag,
412 vgtStraightShot,
413 vgtFeather,
414 vgtChunk: gear^.Layer:= 3;
415
416 // 2: this layer is outside the screen when stereo
417 vgtExplosion,
418 vgtBigExplosion,
419 vgtExplPart,
420 vgtExplPart2,
421 vgtSteam,
422 vgtAmmo,
423 vgtShell,
424 vgtEgg,
425 vgtBeeTrace,
426 vgtSmokeRing,
427 vgtNote,
428 vgtBulletHit,
429 vgtCircle: gear^.Layer:= 2
430 end;
431
432 if Layer <> -1 then gear^.Layer:= Layer;
433
434 if VisualGearLayersStart[gear^.Layer] = nil then
435 VisualGearLayersStart[gear^.Layer]:= gear;
436
437 if VisualGearLayersEnd[gear^.Layer] <> nil then
438 begin
439 VisualGearLayersEnd[gear^.Layer]^.NextGear:= gear;
440 gear^.PrevGear:= VisualGearLayersEnd[gear^.Layer]
441 end;
442 VisualGearLayersEnd[gear^.Layer]:= gear;
443
444 AddVisualGear:= gear;
445 ScriptCall('onVisualGearAdd', gear^.uid);
446 end;
447
448 procedure DeleteVisualGear(Gear: PVisualGear);
449 begin
450 ScriptCall('onVisualGearDelete', Gear^.uid);
451 FreeAndNilTexture(Gear^.Tex);
452
453 if (Gear^.NextGear = nil) and (Gear^.PrevGear = nil) then
454 begin
455 VisualGearLayersStart[Gear^.Layer]:= nil;
456 VisualGearLayersEnd[Gear^.Layer]:= nil;
457 end;
458 if Gear^.PrevGear <> nil then
459 Gear^.PrevGear^.NextGear:= Gear^.NextGear
460 else if Gear^.NextGear <> nil then
461 VisualGearLayersStart[Gear^.Layer]:= Gear^.NextGear;
462 if Gear^.NextGear <> nil then
463 Gear^.NextGear^.PrevGear:= Gear^.PrevGear
464 else if Gear^.PrevGear <> nil then
465 VisualGearLayersEnd[Gear^.Layer]:= Gear^.PrevGear;
466
467 if lastVisualGearByUID = Gear then
468 lastVisualGearByUID:= nil;
469
470 Dispose(Gear);
471 end;
472
VisualGearByUIDnull473 function VisualGearByUID(uid : Longword) : PVisualGear;
474 var vg: PVisualGear;
475 i: LongWord;
476 begin
477 VisualGearByUID:= nil;
478 if uid = 0 then
479 exit;
480 if (lastVisualGearByUID <> nil) and (lastVisualGearByUID^.uid = uid) then
481 begin
482 VisualGearByUID:= lastVisualGearByUID;
483 exit
484 end;
485 // search in an order that is more likely to return layers they actually use. Could perhaps track statistically AddVisualGear in uScript, since that is most likely the ones they want
486 for i:= 2 to 5 do
487 begin
488 vg:= VisualGearLayersStart[i mod 4];
489 while vg <> nil do
490 begin
491 if vg^.uid = uid then
492 begin
493 lastVisualGearByUID:= vg;
494 VisualGearByUID:= vg;
495 exit
496 end;
497 vg:= vg^.NextGear
498 end
499 end
500 end;
501
502
503 end.
504