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