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 uWorld;
22 interface
23 uses SDLh, uGears, uConsts, uFloat, uRandom, uTypes, uRenderUtils;
24 
25 procedure initModule;
26 procedure freeModule;
27 
28 procedure InitWorld;
29 procedure ResetWorldTex;
30 
31 procedure DrawWorld(Lag: LongInt);
32 procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
33 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt);
34 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt; forceDisplay : boolean);
35 procedure HideMission;
36 procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring; autoLabels: boolean);
37 procedure ShakeCamera(amount: LongInt);
38 procedure InitCameraBorders;
39 procedure InitTouchInterface;
40 procedure SetUtilityWidgetState(ammoType: TAmmoType);
41 procedure animateWidget(widget: POnScreenWidget; fade, showWidget: boolean);
42 procedure MoveCamera;
43 procedure onFocusStateChanged;
44 procedure updateCursorVisibility;
45 procedure updateTouchWidgets(ammoType: TAmmoType);
46 
47 implementation
48 uses
49     uStore
50     , uMisc
51     , uIO
52     , uLocale
53     , uSound
54     , uAmmos
55     , uVisualGears
56     , uChat
57     , uLandTexture
58     , uVariables
59     , uUtils
60     , uTextures
61     , uRender
62     , uCaptions
63     , uCursor
64     , uCommands
65     , uTeams
66     , uDebug
67     , uInputHandler
68 {$IFDEF USE_VIDEO_RECORDING}
69     , uVideoRec
70 {$ENDIF}
71     ;
72 
73 var AMShiftTargetX, AMShiftTargetY, AMShiftX, AMShiftY, SlotsNum: LongInt;
74     AMAnimStartTime, AMState : LongInt;
75     AMAnimState: Single;
76     tmpSurface: PSDL_Surface;
77     fpsTexture: PTexture;
78     timeTexture: PTexture;
79     FPS: Longword;
80     CountTicks: Longword;
81     prevPoint: TPoint;
82     amSel: TAmmoType = amNothing;
83     missionTex: PTexture;
84     missionTimer: LongInt;
85     isFirstFrame: boolean;
86     AMAnimType: LongInt;
87     recTexture: PTexture;
88     AmmoMenuTex     : PTexture;
89     HorizontOffset: LongInt;
90     cOffsetY: LongInt;
91     WorldEnd, WorldFade : array[0..3] of HwColor4f;
92 
93 const cStereo_Sky           = 0.0500;
94       cStereo_Horizon       = 0.0250;
95       cStereo_MidDistance   = 0.0175;
96       cStereo_Water_distant = 0.0125;
97       cStereo_Land          = 0.0075;
98       cStereo_Water_near    = 0.0025;
99       cStereo_Outside       = -0.0400;
100 
101       AMAnimDuration = 200;
102       AMHidden    = 0;//AMState values
103       AMShowingUp = 1;
104       AMShowing   = 2;
105       AMHiding    = 3;
106 
107       AMTypeMaskX     = $00000001;
108       AMTypeMaskY     = $00000002;
109       AMTypeMaskAlpha = $00000004;
110 
111 {$IFDEF MOBILE}
112       AMSlotSize = 48;
113 {$ELSE}
114       AMSlotSize = 32;
115 {$ENDIF}
116       AMSlotPadding = (AMSlotSize - 32) shr 1;
117 
118 {$IFDEF USE_LANDSCAPE_AMMOMENU}
119       amNumOffsetX = 0;
120       {$IFDEF USE_AM_NUMCOLUMN}
121       amNumOffsetY = AMSlotSize;
122       {$ELSE}
123       amNumOffsetY = 0;
124       {$ENDIF}
125 {$ELSE}
126       amNumOffsetY = 0;
127       {$IFDEF USE_AM_NUMCOLUMN}
128       amNumOffsetX = AMSlotSize;
129       {$ELSE}
130       amNumOffsetX = 0;
131       {$ENDIF}
132 {$ENDIF}
133 
134       cSendCursorPosTime = 50;
135       cCursorEdgesDist   = 100;
136 
137 // helper functions to create the goal/game mode string
AddGoalnull138 function AddGoal(s: ansistring; gf: longword; si: TGoalStrId; i: LongInt): ansistring;
139 var t: ansistring;
140 begin
141     if (GameFlags and gf) <> 0 then
142         begin
143         t:= inttostr(i);
144         s:= s + FormatA(trgoal[si], t) + '|'
145         end;
146     AddGoal:= s;
147 end;
148 
AddGoalnull149 function AddGoal(s: ansistring; gf: longword; si: TGoalStrId): ansistring;
150 begin
151     if (GameFlags and gf) <> 0 then
152         s:= s + trgoal[si] + '|';
153     AddGoal:= s;
154 end;
155 
156 procedure InitWorld;
157 var i, t: LongInt;
158     cp: PClan;
159     g: ansistring;
160 begin
161 missionTimer:= 0;
162 
163 if (GameFlags and gfRandomOrder) <> 0 then  // shuffle them up a bit
164     begin
165     for i:= 0 to ClansCount * 4 do
166         begin
167         t:= GetRandom(ClansCount);
168         if t <> 0 then
169             begin
170             cp:= ClansArray[0];
171             ClansArray[0]:= ClansArray[t];
172             ClansArray[t]:= cp;
173             ClansArray[t]^.ClanIndex:= t;
174             ClansArray[0]^.ClanIndex:= 0;
175             end;
176         end;
177     CurrentTeam:= ClansArray[0]^.Teams[0];
178     end;
179 
180 if (GameFlags and gfInvulnerable) <> 0 then
181     cTagsMask:= cTagsMask and (not htHealth);
182 
183 // if special game flags/settings are changed, add them to the game mode notice window and then show it
184 g:= ''; // no text/things to note yet
185 
186 // add custom goals from lua script if there are any
187 if LuaGoals <> ansistring('') then
188     g:= LuaGoals + '|';
189 
190 // check different game flags
191 g:= AddGoal(g, gfKing, gidKing); // king?
192 if ((GameFlags and gfKing) <> 0) and ((GameFlags and gfPlaceHog) = 0) then
193     g:= AddGoal(g, gfAny, gidPlaceKing);
194 g:= AddGoal(g, gfPlaceHog, gidPlaceHog); // placement?
195 g:= AddGoal(g, gfTagTeam, gidTagTeam); // tag team mode?
196 g:= AddGoal(g, gfSharedAmmo, gidSharedAmmo); // shared ammo?
197 g:= AddGoal(g, gfPerHogAmmo, gidPerHogAmmo);
198 g:= AddGoal(g, gfMoreWind, gidMoreWind);
199 g:= AddGoal(g, gfLowGravity, gidLowGravity); // low gravity?
200 g:= AddGoal(g, gfSolidLand, gidSolidLand); // solid land?
201 g:= AddGoal(g, gfArtillery, gidArtillery); // artillery?
202 g:= AddGoal(g, gfInfAttack, gidInfAttack);
203 g:= AddGoal(g, gfResetWeps, gidResetWeps);
204 g:= AddGoal(g, gfResetHealth, gidResetHealth);
205 g:= AddGoal(g, gfKarma, gidKarma); // karma?
206 g:= AddGoal(g, gfVampiric, gidVampiric); // vampirism?
207 g:= AddGoal(g, gfInvulnerable, gidInvulnerable); // invulnerability?
208 g:= AddGoal(g, gfAISurvival, gidAISurvival);
209 
210 // modified damage modificator?
211 if cDamagePercent <> 100 then
212     g:= AddGoal(g, gfAny, gidDamageModifier, cDamagePercent);
213 
214 // fade in
215 ScreenFade:= sfFromBlack;
216 ScreenFadeValue:= sfMax;
217 ScreenFadeSpeed:= 1;
218 
219 // modified mine timers?
220 if cMinesTime <> 3000 then
221     begin
222     if cMinesTime = 0 then
223         g:= AddGoal(g, gfAny, gidNoMineTimer)
224     else if cMinesTime < 0 then
225         g:= AddGoal(g, gfAny, gidRandomMineTimer)
226     else
227         g:= AddGoal(g, gfAny, gidMineTimer, cMinesTime div 1000);
228     end;
229 
230 // if the string has been set, show it for (default timeframe) seconds
231 if length(g) > 0 then
232     // choose icon
233     if ((GameFlags and gfKing) <> 0) then
234         // crown icon for King Mode
235         ShowMission(trgoal[gidCaption], trgoal[gidSubCaption], g, 0, 0)
236     else
237         // target icon for anything else
238         ShowMission(trgoal[gidCaption], trgoal[gidSubCaption], g, 1, 0);
239 
240 cWaveHeight:= 32;
241 
242 InitCameraBorders();
243 uCursor.init();
244 prevPoint.X:= 0;
245 prevPoint.Y:= cScreenHeight div 2;
246 WorldDx:=  -(LongInt(leftX + (playWidth div 2)));
247 WorldDy:=  -(LAND_HEIGHT - (playHeight div 2)) + (cScreenHeight div 2);
248 
249 //aligns it to the bottom of the screen, minus the border
250 SkyOffset:= 0;
251 HorizontOffset:= 0;
252 
253 InitTouchInterface();
254 AMAnimType:= AMTypeMaskX or AMTypeMaskAlpha;
255 end;
256 
257 procedure InitCameraBorders;
258 begin
259 cGearScrEdgesDist:= min(2 * cScreenHeight div 5, 2 * cScreenWidth div 5);
260 end;
261 
262 procedure InitTouchInterface;
263 begin
264 {$IFDEF USE_TOUCH_INTERFACE}
265 
266 //positioning of the buttons
267 buttonScale:= 1 / cDefaultZoomLevel;
268 
269 
270 with JumpWidget do
271     begin
272     show:= true;
273     sprite:= sprJumpWidget;
274     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
275     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
276     frame.x:= (cScreenWidth shr 1) - Round(frame.w * 1.2);
277     frame.y:= cScreenHeight - frame.h * 2;
278     active.x:= frame.x;
279     active.y:= frame.y;
280     active.w:= frame.w;
281     active.h:= frame.h;
282     end;
283 
284 with AMWidget do
285     begin
286     show:= true;
287     sprite:= sprAMWidget;
288     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
289     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
290     frame.x:= (cScreenWidth shr 1) - frame.w * 2;
291     frame.y:= cScreenHeight - Round(frame.h * 1.2);
292     active.x:= frame.x;
293     active.y:= frame.y;
294     active.w:= frame.w;
295     active.h:= frame.h;
296     end;
297 
298 with arrowLeft do
299     begin
300     show:= true;
301     sprite:= sprArrowLeft;
302     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
303     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
304     frame.x:= -(cScreenWidth shr 1) + Round(frame.w * 0.25);
305     frame.y:= cScreenHeight - Round(frame.h * 1.5);
306     active.x:= frame.x;
307     active.y:= frame.y;
308     active.w:= frame.w;
309     active.h:= frame.h;
310     end;
311 
312 with arrowRight do
313     begin
314     show:= true;
315     sprite:= sprArrowRight;
316     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
317     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
318     frame.x:= -(cScreenWidth shr 1) + Round(frame.w * 1.5);
319     frame.y:= cScreenHeight - Round(frame.h * 1.5);
320     active.x:= frame.x;
321     active.y:= frame.y;
322     active.w:= frame.w;
323     active.h:= frame.h;
324     end;
325 
326 with firebutton do
327     begin
328     show:= true;
329     sprite:= sprFireButton;
330     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
331     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
332     frame.x:= arrowRight.frame.x + arrowRight.frame.w;
333     frame.y:= arrowRight.frame.y + (arrowRight.frame.w shr 1) - (frame.w shr 1);
334     active.x:= frame.x;
335     active.y:= frame.y;
336     active.w:= frame.w;
337     active.h:= frame.h;
338     end;
339 
340 with arrowUp do
341     begin
342     show:= false;
343     sprite:= sprArrowUp;
344     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
345     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
346     frame.x:= (cScreenWidth shr 1) - frame.w * 2;
347     frame.y:= jumpWidget.frame.y - Round(frame.h * 1.25);
348     active.x:= frame.x;
349     active.y:= frame.y;
350     active.w:= frame.w;
351     active.h:= frame.h;
352     with moveAnim do
353          begin
354          target.x:= frame.x;
355          target.y:= frame.y;
356          source.x:= frame.x - Round(frame.w * 0.75);
357          source.y:= frame.y;
358          end;
359     end;
360 
361 with arrowDown do
362     begin
363     show:= false;
364     sprite:= sprArrowDown;
365     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
366     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
367     frame.x:= (cScreenWidth shr 1) - frame.w * 2;
368     frame.y:= jumpWidget.frame.y - Round(frame.h * 1.25);
369     active.x:= frame.x;
370     active.y:= frame.y;
371     active.w:= frame.w;
372     active.h:= frame.h;
373     with moveAnim do
374         begin
375         target.x:= frame.x;
376         target.y:= frame.y;
377         source.x:= frame.x + Round(frame.w * 0.75);
378         source.y:= frame.y;
379         end;
380     end;
381 
382 with pauseButton do
383     begin
384     show:= true;
385     sprite:= sprPauseButton;
386     frame.w:= Round(spritesData[sprPauseButton].Texture^.w * buttonScale);
387     frame.h:= Round(spritesData[sprPauseButton].Texture^.h * buttonScale);
388     frame.x:= cScreenWidth div 2 - frame.w;
389     frame.y:= 0;
390     active.x:= frame.x;
391     active.y:= frame.y;
392     active.w:= frame.w;
393     active.h:= frame.h;
394     end;
395 
396 with utilityWidget do
397     begin
398     show:= false;
399     sprite:= sprTimerButton;
400     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
401     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
402     frame.x:= arrowLeft.frame.x;
403     frame.y:= arrowLeft.frame.y - Round(frame.h * 1.25);
404     active.x:= frame.x;
405     active.y:= frame.y;
406     active.w:= frame.w;
407     active.h:= frame.h;
408     with moveAnim do
409         begin
410         target.x:= frame.x;
411         target.y:= frame.y;
412         source.x:= frame.x;
413         source.y:= frame.y;
414         end;
415     end;
416 
417 with utilityWidget2 do
418     begin
419     show:= false;
420     sprite:= sprBounceButton;
421     frame.w:= Round(spritesData[sprite].Texture^.w * buttonScale);
422     frame.h:= Round(spritesData[sprite].Texture^.h * buttonScale);
423     frame.x:= utilityWidget.frame.x + Round(frame.w * 1.25);
424     frame.y:= arrowLeft.frame.y - Round(frame.h * 1.25);
425     active.x:= frame.x;
426     active.y:= frame.y;
427     active.w:= frame.w;
428     active.h:= frame.h;
429     with moveAnim do
430         begin
431         target.x:= frame.x;
432         target.y:= frame.y;
433         source.x:= frame.x;
434         source.y:= frame.y;
435         end;
436     end;
437 
438 {$ENDIF}
439 end;
440 
441 // for uStore texture resetting
442 procedure ResetWorldTex;
443 begin
444     FreeAndNilTexture(fpsTexture);
445     FreeAndNilTexture(timeTexture);
446     FreeAndNilTexture(missionTex);
447     FreeAndNilTexture(recTexture);
448     FreeAndNilTexture(AmmoMenuTex);
449     AmmoMenuInvalidated:= true;
450 end;
451 
GetAmmoMenuTexturenull452 function GetAmmoMenuTexture(Ammo: PHHAmmo): PTexture;
453 const BORDERSIZE = 2;
454 var x, y, i, t, SlotsNumY, SlotsNumX, AMFrame: LongInt;
455     STurns: LongInt;
456     amSurface: PSDL_Surface;
457     AMRect: TSDL_Rect;
458 {$IFDEF USE_AM_NUMCOLUMN}
459     tmpsurf: PSDL_Surface;
460     usesDefaultSlotKeys: boolean;
461 {$ENDIF}
462 begin
463     if cOnlyStats then exit(nil);
464 
465     SlotsNum:= 0;
466     for i:= 0 to cMaxSlotIndex do
467         if (i <> cHiddenSlotIndex) and (Ammo^[i, 0].Count > 0) then
468             inc(SlotsNum);
469 {$IFDEF USE_LANDSCAPE_AMMOMENU}
470     SlotsNumX:= SlotsNum;
471     SlotsNumY:= cMaxSlotAmmoIndex + 2;
472     {$IFDEF USE_AM_NUMCOLUMN}
473     inc(SlotsNumY);
474     {$ENDIF}
475 {$ELSE}
476     SlotsNumX:= cMaxSlotAmmoIndex + 1;
477     SlotsNumY:= SlotsNum + 1;
478     {$IFDEF USE_AM_NUMCOLUMN}
479     inc(SlotsNumX);
480     {$ENDIF}
481 {$ENDIF}
482 
483 
484     AmmoRect.w:= (BORDERSIZE*2) + (SlotsNumX * AMSlotSize) + (SlotsNumX-1);
485     AmmoRect.h:= (BORDERSIZE*2) + (SlotsNumY * AMSlotSize) + (SlotsNumY-1);
486     amSurface := SDL_CreateRGBSurface(SDL_SWSURFACE, AmmoRect.w, AmmoRect.h, 32, RMask, GMask, BMask, AMask);
487 
488     AMRect.x:= BORDERSIZE;
489     AMRect.y:= BORDERSIZE;
490     AMRect.w:= AmmoRect.w - (BORDERSIZE*2);
491     AMRect.h:= AmmoRect.h - (BORDERSIZE*2);
492 
493     SDL_FillRect(amSurface, @AMRect, SDL_MapRGB(amSurface^.format, 0,0,0));
494 
495     x:= AMRect.x;
496     y:= AMRect.y;
497 {$IFDEF USE_AM_NUMCOLUMN}
498     usesDefaultSlotKeys:= CheckDefaultSlotKeys;
499 {$ENDIF USE_AM_NUMCOLUMN}
500     for i:= 0 to cMaxSlotIndex do
501         if (i <> cHiddenSlotIndex) and (Ammo^[i, 0].Count > 0) then
502             begin
503 {$IFDEF USE_LANDSCAPE_AMMOMENU}
504             y:= AMRect.y;
505 {$ELSE}
506             x:= AMRect.x;
507 {$ENDIF}
508 {$IFDEF USE_AM_NUMCOLUMN}
509             // Ammo slot number column
510             if usesDefaultSlotKeys then
511                 // F1, F2, F3, F4, ...
512                 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar('F'+IntToStr(i+1)), cWhiteColorChannels)
513             else
514                 // 1, 2, 3, 4, ...
515                 tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(IntToStr(i+1)), cWhiteColorChannels);
516             copyToXY(tmpsurf, amSurface,
517                      x + AMSlotPadding + (AMSlotSize shr 1) - (tmpsurf^.w shr 1),
518                      y + AMSlotPadding + (AMSlotSize shr 1) - (tmpsurf^.h shr 1));
519 
520             SDL_FreeSurface(tmpsurf);
521     {$IFDEF USE_LANDSCAPE_AMMOMENU}
522             y:= AMRect.y + AMSlotSize + 1;
523     {$ELSE}
524             x:= AMRect.x + AMSlotSize + 1;
525     {$ENDIF}
526 {$ENDIF}
527 
528 
529             for t:=0 to cMaxSlotAmmoIndex do
530                 begin
531                 if (Ammo^[i, t].Count > 0)  and (Ammo^[i, t].AmmoType <> amNothing) then
532                     begin
533                     STurns:= Ammoz[Ammo^[i, t].AmmoType].SkipTurns - CurrentTeam^.Clan^.TurnNumber;
534                     AMFrame:= LongInt(Ammo^[i,t].AmmoType) - 1;
535                     if STurns >= 0 then //weapon not usable yet, draw grayed out with turns remaining
536                         begin
537                         DrawSpriteFrame2Surf(sprAMAmmosBW, amSurface, x + AMSlotPadding,
538                                                                  y + AMSlotPadding, AMFrame);
539                         if STurns < 100 then
540                             DrawSpriteFrame2Surf(sprTurnsLeft, amSurface,
541                                 x + AMSlotSize-16,
542                                 y + AMSlotSize + 1 - 16, STurns);
543                         end
544                     else //draw colored version
545                         begin
546                         DrawSpriteFrame2Surf(sprAMAmmos, amSurface, x + AMSlotPadding,
547                                                                y + AMSlotPadding, AMFrame);
548                         end;
549 {$IFDEF USE_LANDSCAPE_AMMOMENU}
550         inc(y, AMSlotSize + 1); //the plus one is for the border
551 {$ELSE}
552         inc(x, AMSlotSize + 1);
553 {$ENDIF}
554         end;
555     end;
556 {$IFDEF USE_LANDSCAPE_AMMOMENU}
557     inc(x, AMSlotSize + 1);
558 {$ELSE}
559     inc(y, AMSlotSize + 1);
560 {$ENDIF}
561     end;
562 
563 for i:= 1 to SlotsNumX -1 do
564 DrawLine2Surf(amSurface, i * (AMSlotSize+1)+1, BORDERSIZE, i * (AMSlotSize+1)+1, AMRect.h + BORDERSIZE - AMSlotSize - 2,160,160,160);
565 for i:= 1 to SlotsNumY -1 do
566 DrawLine2Surf(amSurface, BORDERSIZE, i * (AMSlotSize+1)+1, AMRect.w + BORDERSIZE, i * (AMSlotSize+1)+1,160,160,160);
567 
568 //draw outer border
569 DrawSpriteFrame2Surf(sprAMCorners, amSurface, 0                    , 0                    , 0);
570 DrawSpriteFrame2Surf(sprAMCorners, amSurface, AMRect.w + BORDERSIZE, AMRect.y             , 1);
571 DrawSpriteFrame2Surf(sprAMCorners, amSurface, AMRect.x             , AMRect.h + BORDERSIZE, 2);
572 DrawSpriteFrame2Surf(sprAMCorners, amSurface, AMRect.w + BORDERSIZE, AMRect.h + BORDERSIZE, 3);
573 
574 for i:=0 to BORDERSIZE-1 do
575 begin
576 DrawLine2Surf(amSurface, BORDERSIZE, i, AMRect.w + BORDERSIZE, i,160,160,160);//top
577 DrawLine2Surf(amSurface, BORDERSIZE, AMRect.h+BORDERSIZE+i, AMRect.w + BORDERSIZE, AMRect.h+BORDERSIZE+i,160,160,160);//bottom
578 DrawLine2Surf(amSurface, i, BORDERSIZE, i, AMRect.h + BORDERSIZE,160,160,160);//left
579 DrawLine2Surf(amSurface, AMRect.w+BORDERSIZE+i, BORDERSIZE, AMRect.w + BORDERSIZE+i, AMRect.h + BORDERSIZE, 160,160,160);//right
580 end;
581 
582 GetAmmoMenuTexture:= Surface2Tex(amSurface, false);
583 if amSurface <> nil then SDL_FreeSurface(amSurface);
584 end;
585 
586 procedure ShowAmmoMenu;
587 const BORDERSIZE = 2;
588 var Slot, Pos: LongInt;
589     Ammo: PHHAmmo;
590     c,i,g,t,STurns: LongInt;
591 begin
592 if TurnTimeLeft = 0 then bShowAmmoMenu:= false;
593 
594 // give the assigned ammo to hedgehog
595 Ammo:= nil;
596 if (CurrentTeam <> nil) and (CurrentHedgehog <> nil)
597 and (not CurrentTeam^.ExtDriven) and (CurrentHedgehog^.BotLevel = 0) then
598     Ammo:= CurrentHedgehog^.Ammo
599 else if (LocalAmmo <> -1) then
600     Ammo:= GetAmmoByNum(LocalAmmo);
601 Pos:= -1;
602 if Ammo = nil then
603     begin
604     bShowAmmoMenu:= false;
605     AMState:= AMHidden;
606     exit
607     end;
608 
609 //Init the menu
610 if(AmmoMenuInvalidated) then
611     begin
612     AmmoMenuInvalidated:= false;
613     FreeAndNilTexture(AmmoMenuTex);
614     AmmoMenuTex:= GetAmmoMenuTexture(Ammo);
615 
616 {$IFDEF USE_LANDSCAPE_AMMOMENU}
617     if isPhone() then
618         begin
619         AmmoRect.x:= -(AmmoRect.w shr 1);
620         AmmoRect.y:= (cScreenHeight shr 1) - (AmmoRect.h shr 1);
621         end
622     else
623         begin
624         AmmoRect.x:= -(AmmoRect.w shr 1);
625         AmmoRect.y:= cScreenHeight - (AmmoRect.h + AMSlotSize);
626         end;
627 {$ELSE}
628         AmmoRect.x:= (cScreenWidth shr 1) - AmmoRect.w - AMSlotSize;
629         AmmoRect.y:= cScreenHeight - (AmmoRect.h + AMSlotSize);
630 {$ENDIF}
631     if AMState <> AMShowing then
632         begin
633         AMShiftTargetX:= (cScreenWidth shr 1) - AmmoRect.x;
634         AMShiftTargetY:= cScreenHeight        - AmmoRect.y;
635 
636         if (AMAnimType and AMTypeMaskX) <> 0 then AMShiftTargetX:= (cScreenWidth shr 1) - AmmoRect.x
637         else AMShiftTargetX:= 0;
638         if (AMAnimType and AMTypeMaskY) <> 0 then AMShiftTargetY:= cScreenHeight        - AmmoRect.y
639         else AMShiftTargetY:= 0;
640 
641         AMShiftX:= AMShiftTargetX;
642         AMShiftY:= AMShiftTargetY
643         end
644 end;
645 
646 AMAnimState:= (RealTicks - AMAnimStartTime) / AMAnimDuration;
647 
648 if AMState = AMShowing then
649     begin
650     FollowGear:=nil;
651     end;
652 
653 if AMState = AMShowingUp then // show ammo menu
654     begin
655     // No "appear" animation in low quality or playing with very short turn time.
656     if ((cReducedQuality and rqSlowMenu) <> 0) or (cHedgehogTurnTime <= 10000) then
657         begin
658         AMShiftX:= 0;
659         AMShiftY:= 0;
660         CursorPoint.X:= AmmoRect.x + AmmoRect.w - 3;
661         CursorPoint.Y:= cScreenHeight - AmmoRect.y - amNumOffsetY - 1;
662         AMState:= AMShowing;
663         end
664     // "Appear" animation
665     else
666         if AMAnimState < 1 then
667             begin
668             AMShiftX:= Round(AMShiftTargetX * (1 - AMAnimState));
669             AMShiftY:= Round(AMShiftTargetY * (1 - AMAnimState));
670             if (AMAnimType and AMTypeMaskAlpha) <> 0 then
671                 Tint($FF, $ff, $ff, Round($ff * AMAnimState));
672             end
673         else
674             begin
675             AMShiftX:= 0;
676             AMShiftY:= 0;
677             CursorPoint.X:= AmmoRect.x + AmmoRect.w - 3;
678             CursorPoint.Y:= cScreenHeight - AmmoRect.y - amNumOffsetY - 1;
679             AMState:= AMShowing;
680             end;
681     end;
682 if AMState = AMHiding then // hide ammo menu
683     begin
684     // No "disappear" animation (see above)
685     if ((cReducedQuality and rqSlowMenu) <> 0) or (cHedgehogTurnTime <= 10000) then
686         begin
687         AMShiftX:= AMShiftTargetX;
688         AMShiftY:= AMShiftTargetY;
689         prevPoint:= CursorPoint;
690         AMState:= AMHidden;
691         end
692     // "Disappear" animation
693     else
694         if AMAnimState < 1 then
695             begin
696             AMShiftX:= Round(AMShiftTargetX * AMAnimState);
697             AMShiftY:= Round(AMShiftTargetY * AMAnimState);
698             if (AMAnimType and AMTypeMaskAlpha) <> 0 then
699                 Tint($FF, $ff, $ff, Round($ff * (1-AMAnimState)));
700             end
701          else
702             begin
703             AMShiftX:= AMShiftTargetX;
704             AMShiftY:= AMShiftTargetY;
705             prevPoint:= CursorPoint;
706             AMState:= AMHidden;
707             end;
708     end;
709 
710 DrawTexture(AmmoRect.x + AMShiftX, AmmoRect.y + AMShiftY, AmmoMenuTex);
711 
712 if ((AMState = AMHiding) or (AMState = AMShowingUp)) and ((AMAnimType and AMTypeMaskAlpha) <> 0 )then
713     untint;
714 
715 Pos:= -1;
716 Slot:= -1;
717 {$IFDEF USE_LANDSCAPE_AMMOMENU}
718 c:= -1;
719     for i:= 0 to cMaxSlotIndex do
720         if (i <> cHiddenSlotIndex) and (Ammo^[i, 0].Count > 0) then
721             begin
722             inc(c);
723     {$IFDEF USE_AM_NUMCOLUMN}
724             g:= 1;
725     {$ELSE}
726             g:= 0;
727     {$ENDIF}
728             for t:=0 to cMaxSlotAmmoIndex do
729                 if (Ammo^[i, t].Count > 0) and (Ammo^[i, t].AmmoType <> amNothing) then
730                     begin
731                     if (CursorPoint.Y <= (cScreenHeight - AmmoRect.y) - ( g    * (AMSlotSize+1))) and
732                        (CursorPoint.Y >  (cScreenHeight - AmmoRect.y) - ((g+1) * (AMSlotSize+1))) and
733                        (CursorPoint.X >  AmmoRect.x                   + ( c    * (AMSlotSize+1))) and
734                        (CursorPoint.X <= AmmoRect.x                   + ((c+1) * (AMSlotSize+1))) then
735                         begin
736                         Slot:= i;
737                         Pos:= t;
738                         STurns:= Ammoz[Ammo^[i, t].AmmoType].SkipTurns - CurrentTeam^.Clan^.TurnNumber;
739                         if (STurns < 0) and (AMShiftX = 0) and (AMShiftY = 0) then
740                             DrawSprite(sprAMSlot,
741                                        AmmoRect.x + BORDERSIZE + (c * (AMSlotSize+1)) + AMSlotPadding,
742                                        AmmoRect.y + BORDERSIZE + (g  * (AMSlotSize+1)) + AMSlotPadding -1, 0);
743                         end;
744                         inc(g);
745                    end;
746             end;
747 {$ELSE}
748 c:= -1;
749     for i:= 0 to cMaxSlotIndex do
750         if (i <> cHiddenSlotIndex) and (Ammo^[i, 0].Count > 0) then
751             begin
752             inc(c);
753     {$IFDEF USE_AM_NUMCOLUMN}
754             g:= 1;
755     {$ELSE}
756             g:= 0;
757     {$ENDIF}
758             for t:=0 to cMaxSlotAmmoIndex do
759                 if (Ammo^[i, t].Count > 0) and (Ammo^[i, t].AmmoType <> amNothing) then
760                     begin
761                     if (CursorPoint.Y <= (cScreenHeight - AmmoRect.y) - ( c    * (AMSlotSize+1))) and
762                        (CursorPoint.Y >  (cScreenHeight - AmmoRect.y) - ((c+1) * (AMSlotSize+1))) and
763                        (CursorPoint.X >  AmmoRect.x                   + ( g    * (AMSlotSize+1))) and
764                        (CursorPoint.X <= AmmoRect.x                   + ((g+1) * (AMSlotSize+1))) then
765                         begin
766                         Slot:= i;
767                         Pos:= t;
768                         STurns:= Ammoz[Ammo^[i, t].AmmoType].SkipTurns - CurrentTeam^.Clan^.TurnNumber;
769                         if (STurns < 0) and (AMShiftX = 0) and (AMShiftY = 0) then
770                             DrawSprite(sprAMSlot,
771                                        AmmoRect.x + BORDERSIZE + (g * (AMSlotSize+1)) + AMSlotPadding,
772                                        AmmoRect.y + BORDERSIZE + (c  * (AMSlotSize+1)) + AMSlotPadding -1, 0);
773                         end;
774                         inc(g);
775                    end;
776             end;
777 {$ENDIF}
778     if (Pos >= 0) and (Pos <= cMaxSlotAmmoIndex) and (Slot >= 0) and (Slot <= cMaxSlotIndex) and (Slot <> cHiddenSlotIndex) then
779         begin
780         if (AMShiftX = 0) and (AMShiftY = 0) then
781         if (Ammo^[Slot, Pos].Count > 0) and (Ammo^[Slot, Pos].AmmoType <> amNothing) then
782             begin
783             if (amSel <> Ammo^[Slot, Pos].AmmoType) or (WeaponTooltipTex = nil) then
784                 begin
785                 amSel:= Ammo^[Slot, Pos].AmmoType;
786                 RenderWeaponTooltip(amSel)
787                 end;
788 
789             DrawTexture(AmmoRect.x + (AMSlotSize shr 1),
790                         AmmoRect.y + AmmoRect.h - BORDERSIZE - (AMSlotSize shr 1) - (Ammoz[Ammo^[Slot, Pos].AmmoType].NameTex^.h shr 1),
791                         Ammoz[Ammo^[Slot, Pos].AmmoType].NameTex);
792             if Ammo^[Slot, Pos].Count < AMMO_INFINITE then
793                 DrawTexture(AmmoRect.x + AmmoRect.w - 20 - (CountTexz[Ammo^[Slot, Pos].Count]^.w),
794                             AmmoRect.y + AmmoRect.h - BORDERSIZE - (AMslotSize shr 1) - (CountTexz[Ammo^[Slot, Pos].Count]^.h shr 1),
795                             CountTexz[Ammo^[Slot, Pos].Count]);
796 
797             if bSelected and (Ammoz[Ammo^[Slot, Pos].AmmoType].SkipTurns - CurrentTeam^.Clan^.TurnNumber < 0) then
798                 begin
799                 bShowAmmoMenu:= false;
800                 SetWeapon(Ammo^[Slot, Pos].AmmoType);
801                 bSelected:= false;
802                 FreeAndNilTexture(WeaponTooltipTex);
803                 updateTouchWidgets(Ammo^[Slot, Pos].AmmoType);
804                 exit
805                 end;
806             end
807         end
808     else
809         FreeAndNilTexture(WeaponTooltipTex);
810 
811     if (WeaponTooltipTex <> nil) and (AMShiftX = 0) and (AMShiftY = 0) then
812 {$IFDEF USE_LANDSCAPE_AMMOMENU}
813         if (not isPhone()) then
814             ShowWeaponTooltip(-WeaponTooltipTex^.w div 2, AmmoRect.y - WeaponTooltipTex^.h - AMSlotSize);
815 {$ELSE}
816         ShowWeaponTooltip(AmmoRect.x - WeaponTooltipTex^.w - 3, Min(AmmoRect.y + 1, cScreenHeight - WeaponTooltipTex^.h - 40));
817 {$ENDIF}
818 
819     bSelected:= false;
820 {$IFNDEF USE_TOUCH_INTERFACE}
821    if (AMShiftX = 0) and (AMShiftY = 0) then
822         DrawSprite(sprArrow, CursorPoint.X, cScreenHeight - CursorPoint.Y, (RealTicks shr 6) mod 8);
823 {$ENDIF}
824 end;
825 
826 procedure DrawRepeated(spr, sprL, sprR: TSprite; Shift, OffsetY: LongInt);
827 var i, w, h, lw, lh, rw, rh, sw: LongInt;
828 begin
829 sw:= round(cScreenWidth / cScaleFactor);
830 if (SpritesData[sprL].Texture = nil) and (SpritesData[spr].Texture <> nil) then
831     begin
832     w:= SpritesData[spr].Width * SpritesData[spr].Texture^.Scale;
833     h:= SpritesData[spr].Height * SpritesData[spr].Texture^.Scale;
834     i:= Shift mod w;
835     if i > 0 then
836         dec(i, w);
837     dec(i, w * (sw div w + 1));
838     repeat
839     DrawTexture(i, WorldDy + LAND_HEIGHT + OffsetY - h, SpritesData[spr].Texture, SpritesData[spr].Texture^.Scale);
840     inc(i, w)
841     until i > sw
842     end
843 else if SpritesData[spr].Texture <> nil then
844     begin
845     w:= SpritesData[spr].Width * SpritesData[spr].Texture^.Scale;
846     h:= SpritesData[spr].Height * SpritesData[spr].Texture^.Scale;
847     lw:= SpritesData[sprL].Width * SpritesData[spr].Texture^.Scale;
848     lh:= SpritesData[sprL].Height * SpritesData[spr].Texture^.Scale;
849     if SpritesData[sprR].Texture <> nil then
850         begin
851         rw:= SpritesData[sprR].Width * SpritesData[spr].Texture^.Scale;
852         rh:= SpritesData[sprR].Height * SpritesData[spr].Texture^.Scale
853         end;
854     dec(Shift, w div 2);
855     DrawTexture(Shift, WorldDy + LAND_HEIGHT + OffsetY - h, SpritesData[spr].Texture, SpritesData[spr].Texture^.Scale);
856 
857     i:= Shift - lw;
858     while i >= -sw - lw do
859         begin
860         DrawTexture(i, WorldDy + LAND_HEIGHT + OffsetY - lh, SpritesData[sprL].Texture, SpritesData[sprL].Texture^.Scale);
861         dec(i, lw);
862         end;
863 
864     i:= Shift + w;
865     if SpritesData[sprR].Texture <> nil then
866         while i <= sw do
867             begin
868             DrawTexture(i, WorldDy + LAND_HEIGHT + OffsetY - rh, SpritesData[sprR].Texture, SpritesData[sprR].Texture^.Scale);
869             inc(i, rw)
870             end
871     else
872         while i <= sw do
873             begin
874             DrawTexture(i, WorldDy + LAND_HEIGHT + OffsetY - lh, SpritesData[sprL].Texture, SpritesData[sprL].Texture^.Scale);
875             inc(i, lw)
876             end
877     end
878 end;
879 
880 
881 procedure DrawWorld(Lag: LongInt);
882 begin
883     if ZoomValue < zoom then
884     begin
885         zoom:= zoom - 0.002 * Lag;
886         if ZoomValue > zoom then
887             zoom:= ZoomValue
888     end
889     else
890         if ZoomValue > zoom then
891         begin
892         zoom:= zoom + 0.002 * Lag;
893         if ZoomValue < zoom then
894             zoom:= ZoomValue
895         end
896     else
897         ZoomValue:= zoom;
898 
899     if (not isPaused) and (not isAFK) and (GameType <> gmtRecord) then
900         MoveCamera;
901 
902     if cStereoMode = smNone then
903         begin
904         RenderClear();
905         DrawWorldStereo(Lag, rmDefault)
906 {$IFDEF USE_S3D_RENDERING}
907         end
908     else
909         begin
910         // draw frame for left eye
911         RenderClear(rmLeftEye);
912         DrawWorldStereo(Lag, rmLeftEye);
913 
914         // draw frame for right eye
915         RenderClear(rmRightEye);
916         DrawWorldStereo(0, rmRightEye);
917 {$ENDIF}
918         end;
919 
920 FinishRender();
921 end;
922 
923 procedure RenderWorldEdge;
924 var
925     tmp, w: LongInt;
926     rect: TSDL_Rect;
927 begin
928 if (WorldEdge <> weNone) and (WorldEdge <> weSea) then
929     begin
930 (* I think for a bounded world, will fill the left and right areas with black or something. Also will probably want various border effects/animations based on border type.  Prob also, say, trigger a border animation timer on an impact. *)
931 
932     rect.y:= ViewTopY;
933     rect.h:= ViewHeight;
934     tmp:= leftX + WorldDx;
935     w:= tmp - ViewLeftX;
936 
937     if w > 0 then
938         begin
939         rect.w:= w;
940         rect.x:= ViewLeftX;
941         DrawRect(rect, $10, $10, $10, $80, true);
942         if WorldEdge = weBounce then
943             DrawLineOnScreen(tmp - 1, ViewTopY, tmp - 1, ViewBottomY, 2, $54, $54, $FF, $FF);
944         end;
945 
946     tmp:= rightX + WorldDx;
947     w:= ViewRightX - tmp;
948 
949     if w > 0 then
950         begin
951         rect.w:= w;
952         rect.x:= tmp;
953         DrawRect(rect, $10, $10, $10, $80, true);
954         if WorldEdge = weBounce then
955             DrawLineOnScreen(tmp - 1, ViewTopY, tmp - 1, ViewBottomY, 2, $54, $54, $FF, $FF);
956         end;
957 
958     end;
959 end;
960 
961 
962 procedure RenderTeamsHealth;
963 var t, i, h, v, smallScreenOffset, TeamHealthBarWidth : LongInt;
964     r: TSDL_Rect;
965     highlight: boolean;
966     hasVisibleHog: boolean;
967     htex: PTexture;
968 begin
969 if VisibleTeamsCount * 20 > Longword(cScreenHeight) div 7 then  // take up less screen on small displays
970     begin
971     SetScale(1.5);
972     smallScreenOffset:= cScreenHeight div 6;
973     if VisibleTeamsCount * 100 > Longword(cScreenHeight) then
974         Tint($FF,$FF,$FF,$80);
975     end
976 else smallScreenOffset:= 0;
977 v:= 0; // for updating VisibleTeamsCount
978 for t:= 0 to Pred(TeamsCount) do
979     with TeamsArray[t]^ do
980       begin
981       hasVisibleHog:= false;
982       for i:= 0 to cMaxHHIndex do
983           if (Hedgehogs[i].Gear <> nil) then
984               hasVisibleHog:= true;
985       if (TeamHealth > 0) and hasVisibleHog then
986         begin
987         // count visible teams
988         inc(v);
989         highlight:= bShowFinger and (CurrentTeam = TeamsArray[t]) and ((RealTicks mod 1000) < 500);
990 
991         if highlight then
992             begin
993             Tint(Clan^.Color shl 8 or $FF);
994             htex:= GenericHealthTexture
995             end
996         else
997             htex:= Clan^.HealthTex;
998 
999         // draw owner
1000         if OwnerTex <> nil then
1001             DrawTexture(-OwnerTex^.w - NameTagTex^.w - 18, cScreenHeight + DrawHealthY + smallScreenOffset, OwnerTex);
1002 
1003         // draw name
1004         DrawTexture(-NameTagTex^.w - 16, cScreenHeight + DrawHealthY + smallScreenOffset, NameTagTex);
1005 
1006         // draw flag
1007         DrawTexture(-14, cScreenHeight + DrawHealthY + smallScreenOffset, FlagTex);
1008 
1009         TeamHealthBarWidth:= cTeamHealthWidth * TeamHealthBarHealth div MaxTeamHealth;
1010 
1011         // draw team health bar
1012         r.x:= 0;
1013         r.y:= 0;
1014         r.w:= 2 + TeamHealthBarWidth;
1015         r.h:= htex^.h;
1016         DrawTextureFromRect(14, cScreenHeight + DrawHealthY + smallScreenOffset, @r, htex);
1017 
1018         // draw health bar's right border
1019         inc(r.x, cTeamHealthWidth + 2);
1020         r.w:= 3;
1021         DrawTextureFromRect(TeamHealthBarWidth + 15, cScreenHeight + DrawHealthY + smallScreenOffset, @r, htex);
1022 
1023         // draw hedgehog health separators in team health bar
1024         h:= 0;
1025         if not hasGone then
1026             for i:= 0 to cMaxHHIndex do
1027                 begin
1028                 inc(h, Hedgehogs[i].HealthBarHealth);
1029                 if (h < TeamHealthBarHealth) and (Hedgehogs[i].HealthBarHealth > 0) then
1030                     if (IsTooDarkToRead(Clan^.Color)) then
1031                         DrawTexture(15 + h * TeamHealthBarWidth div TeamHealthBarHealth, cScreenHeight + DrawHealthY + smallScreenOffset + 1, SpritesData[sprSlider].Texture)
1032                     else
1033                         DrawTexture(15 + h * TeamHealthBarWidth div TeamHealthBarHealth, cScreenHeight + DrawHealthY + smallScreenOffset + 1, SpritesData[sprSliderInverted].Texture);
1034                 end;
1035 
1036         // draw Lua value, if set
1037         if (hasLuaTeamValue) then
1038             DrawTexture(TeamHealthBarWidth + 22, cScreenHeight + DrawHealthY + smallScreenOffset, LuaTeamValueTex)
1039         // otherwise, draw AI kill counter for gfAISurvival
1040         else if (GameFlags and gfAISurvival) <> 0 then
1041             DrawTexture(TeamHealthBarWidth + 22, cScreenHeight + DrawHealthY + smallScreenOffset, AIKillsTex);
1042 
1043         // if highlighted, draw flag and other contents again to keep their colors
1044         // this approach should be faster than drawing all borders one by one tinted or not
1045         if highlight then
1046             begin
1047             if VisibleTeamsCount * 100 > Longword(cScreenHeight) then
1048                 Tint($FF,$FF,$FF,$80)
1049             else untint;
1050 
1051             // draw name
1052             r.x:= 2;
1053             r.y:= 2;
1054             r.w:= NameTagTex^.w - 4;
1055             r.h:= NameTagTex^.h - 4;
1056             DrawTextureFromRect(-NameTagTex^.w - 14, cScreenHeight + DrawHealthY + smallScreenOffset + 2, @r, NameTagTex);
1057 
1058             if OwnerTex <> nil then
1059                 begin
1060                 r.w:= OwnerTex^.w - 4;
1061                 r.h:= OwnerTex^.h - 4;
1062                 DrawTextureFromRect(-OwnerTex^.w - NameTagTex^.w - 16, cScreenHeight + DrawHealthY + smallScreenOffset + 2, @r, OwnerTex)
1063                 end;
1064 
1065             if (hasLuaTeamValue) then
1066                 begin
1067                 r.w:= LuaTeamValueTex^.w - 4;
1068                 r.h:= LuaTeamValueTex^.h - 4;
1069                 DrawTextureFromRect(TeamHealthBarWidth + 24, cScreenHeight + DrawHealthY + smallScreenOffset + 2, @r, LuaTeamValueTex);
1070                 end
1071             else if (GameFlags and gfAISurvival) <> 0 then
1072                 begin
1073                 r.w:= AIKillsTex^.w - 4;
1074                 r.h:= AIKillsTex^.h - 4;
1075                 DrawTextureFromRect(TeamHealthBarWidth + 24, cScreenHeight + DrawHealthY + smallScreenOffset + 2, @r, AIKillsTex);
1076                 end;
1077 
1078             // draw flag
1079             r.w:= 22;
1080             r.h:= 15;
1081             DrawTextureFromRect(-12, cScreenHeight + DrawHealthY + smallScreenOffset + 2, @r, FlagTex);
1082             end
1083         // draw an arrow next to active team
1084         else if (CurrentTeam = TeamsArray[t]) and (TurnTimeLeft > 0) then
1085             begin
1086             h:= -NameTagTex^.w - 24;
1087             if OwnerTex <> nil then
1088                 h:= h - OwnerTex^.w - 4;
1089             if (IsTooDarkToRead(TeamsArray[t]^.Clan^.Color)) then
1090                 DrawSpriteRotatedF(sprFingerBackInv, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFingerBackInv].Width div 4, 0, 1, -90)
1091             else
1092                 DrawSpriteRotatedF(sprFingerBack, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFingerBack].Width div 4, 0, 1, -90);
1093             Tint(TeamsArray[t]^.Clan^.Color shl 8 or $FF);
1094             DrawSpriteRotatedF(sprFinger, h, cScreenHeight + DrawHealthY + smallScreenOffset + 2 + SpritesData[sprFinger].Width div 4, 0, 1, -90);
1095             untint;
1096             end;
1097         end;
1098       end;
1099 if smallScreenOffset <> 0 then
1100     begin
1101     SetScale(cDefaultZoomLevel);
1102     if VisibleTeamsCount * 20 > Longword(cScreenHeight) div 5 then
1103         untint;
1104     end;
1105 VisibleTeamsCount:= v;
1106 end;
1107 
1108 procedure RenderAttackBar();
1109 var i: LongInt;
1110     tdx, tdy: Double;
1111 begin
1112     if CurrentTeam <> nil then
1113         case AttackBar of
1114         2: with CurrentHedgehog^ do
1115                 begin
1116                 tdx:= hwSign(Gear^.dX) * Sin(Gear^.Angle * Pi / cMaxAngle);
1117                 tdy:= - Cos(Gear^.Angle * Pi / cMaxAngle);
1118                 for i:= (Gear^.Power * 24) div cPowerDivisor downto 0 do
1119                     DrawSprite(sprPower,
1120                             hwRound(Gear^.X) + GetLaunchX(CurAmmoType, hwSign(Gear^.dX), Gear^.Angle) + LongInt(round(WorldDx + tdx * (24 + i * 2))) - 16,
1121                             hwRound(Gear^.Y) + GetLaunchY(CurAmmoType, Gear^.Angle) + LongInt(round(WorldDy + tdy * (24 + i * 2))) - 16,
1122                             i)
1123                 end;
1124         end;
1125 end;
1126 
1127 var preShiftWorldDx: LongInt;
1128 
1129 procedure ShiftWorld(Dir: LongInt); inline;
1130 begin
1131     preShiftWorldDx:= WorldDx;
1132     WorldDx:= WorldDx + LongInt(Dir * LongInt(playWidth));
1133 
1134 end;
1135 
1136 procedure UnshiftWorld(); inline;
1137 begin
1138     WorldDx:= preShiftWorldDx;
1139 end;
1140 
1141 procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
1142 var i, t: LongInt;
1143     spr: TSprite;
1144     r: TSDL_Rect;
1145     s: shortstring;
1146     offsetX, offsetY, screenBottom: LongInt;
1147     replicateToLeft, replicateToRight, isNotHiddenByCinematic: boolean;
1148 {$IFDEF USE_VIDEO_RECORDING}
1149     a: Byte;
1150 {$ENDIF}
1151 begin
1152 if WorldEdge <> weWrap then
1153     begin
1154     replicateToLeft := false;
1155     replicateToRight:= false;
1156     end
1157 else
1158     begin
1159     replicateToLeft := (leftX  + WorldDx > ViewLeftX);
1160     replicateToRight:= (rightX + WorldDx < ViewRightX);
1161     end;
1162 
1163 ScreenBottom:= (WorldDy - trunc(cScreenHeight/cScaleFactor) - (cScreenHeight div 2) + cWaterLine);
1164 
1165 // note: offsetY is negative!
1166 offsetY:= 10 *  Min(0, -145 - ScreenBottom); // TODO limit this in the other direction too
1167 
1168 // Sky and horizont
1169 if (cReducedQuality and rqNoBackground) = 0 then
1170     begin
1171         // Offsets relative to camera - spare them to wimpier cpus, no bg or flakes for them anyway
1172         SkyOffset:= offsetY div 35 + cWaveHeight;
1173         HorizontOffset:= SkyOffset;
1174         if ScreenBottom > SkyOffset then
1175             HorizontOffset:= HorizontOffset + ((ScreenBottom-SkyOffset) div 20);
1176 
1177         // background
1178         ChangeDepth(RM, cStereo_Sky);
1179         if SuddenDeathDmg then
1180             Tint(SDTint.r, SDTint.g, SDTint.b, SDTint.a);
1181         DrawRepeated(sprSky, sprSkyL, sprSkyR, (WorldDx + LAND_WIDTH div 2) * 3 div 8, SkyOffset);
1182         ChangeDepth(RM, -cStereo_Horizon);
1183         DrawRepeated(sprHorizont, sprHorizontL, sprHorizontR, (WorldDx + LAND_WIDTH div 2) * 3 div 5, HorizontOffset);
1184         if SuddenDeathDmg then
1185             untint;
1186     end;
1187 
1188 DrawVisualGears(0, false);
1189 ChangeDepth(RM, -cStereo_MidDistance);
1190 DrawVisualGears(4, false);
1191 
1192 if (cReducedQuality and rq2DWater) = 0 then
1193     begin
1194         // Waves
1195         DrawWater(255, SkyOffset, 0);
1196         ChangeDepth(RM, -cStereo_Water_distant);
1197         DrawWaves( 1,  0 - WorldDx div 32, offsetY div 35, -49, 64);
1198         ChangeDepth(RM, -cStereo_Water_distant);
1199         DrawWaves( -1,  25 + WorldDx div 25, offsetY div 38, -37, 48);
1200         ChangeDepth(RM, -cStereo_Water_distant);
1201         DrawWaves( 1,  75 - WorldDx div 19, offsetY div 45, -23, 32);
1202         ChangeDepth(RM, -cStereo_Water_distant);
1203         DrawWaves(-1, 100 + WorldDx div 14, offsetY div 70, -7, 24);
1204     end
1205 else
1206     DrawWaves(-1, 100, - cWaveHeight div 2, - cWaveHeight div 2, 0);
1207 
1208 ChangeDepth(RM, cStereo_Land);
1209 DrawVisualGears(5, false);
1210 DrawLand(WorldDx, WorldDy);
1211 
1212 if replicateToLeft then
1213     begin
1214     ShiftWorld(-1);
1215     DrawLand(WorldDx, WorldDy);
1216     UnshiftWorld();
1217     end;
1218 
1219 if replicateToRight then
1220     begin
1221     ShiftWorld(1);
1222     DrawLand(WorldDx, WorldDy);
1223     UnshiftWorld();
1224     end;
1225 
1226 DrawWater(255, 0, 0);
1227 
1228 if replicateToLeft then
1229     begin
1230     ShiftWorld(-1);
1231     DrawVisualGears(1, true);
1232     DrawGears();
1233     DrawVisualGears(6, true);
1234     UnshiftWorld();
1235     end;
1236 
1237 if replicateToRight then
1238     begin
1239     ShiftWorld(1);
1240     DrawVisualGears(1, true);
1241     DrawGears();
1242     DrawVisualGears(6, true);
1243     UnshiftWorld();
1244     end;
1245 
1246 DrawVisualGears(1, false);
1247 DrawGears;
1248 DrawVisualGears(6, false);
1249 
1250 
1251 if SuddenDeathDmg then
1252     DrawWater(SDWaterOpacity, 0, 0)
1253 else
1254     DrawWater(WaterOpacity, 0, 0);
1255 
1256 // Waves
1257 ChangeDepth(RM, cStereo_Water_near);
1258 DrawWaves( 1, 25 - WorldDx div 9, 0, 0, 12);
1259 
1260 if (cReducedQuality and rq2DWater) = 0 then
1261     begin
1262     ChangeDepth(RM, cStereo_Water_near);
1263     DrawWaves(-1, 50 + WorldDx div 6, - offsetY div 40, 23, 8);
1264     if SuddenDeathDmg then
1265         DrawWater(SDWaterOpacity, - offsetY div 20, 23)
1266     else
1267         DrawWater(WaterOpacity, - offsetY div 20, 23);
1268     ChangeDepth(RM, cStereo_Water_near);
1269     DrawWaves( 1, 75 - WorldDx div 4, - offsetY div 20, 37, 2);
1270         if SuddenDeathDmg then
1271             DrawWater(SDWaterOpacity, - offsetY div 10, 47)
1272         else
1273             DrawWater(WaterOpacity, - offsetY div 10, 47);
1274         ChangeDepth(RM, cStereo_Water_near);
1275         DrawWaves( -1, 25 + WorldDx div 3, - offsetY div 10, 59, 0);
1276         end
1277     else
1278         DrawWaves(-1, 50, cWaveHeight div 2, cWaveHeight div 2, 0);
1279 
1280 // line at airplane height for certain airstrike types (when spawning height is important)
1281 with CurrentHedgehog^ do
1282     if (isCursorVisible) and ((CurAmmoType = amNapalm) or (CurAmmoType = amMineStrike) or (((GameFlags and gfMoreWind) <> 0) and ((CurAmmoType = amDrillStrike) or (CurAmmoType = amAirAttack)))) then
1283         DrawLine(-3000, topY-300, 7000, topY-300, 3.0, (Team^.Clan^.Color shr 16), (Team^.Clan^.Color shr 8) and $FF, Team^.Clan^.Color and $FF, $FF);
1284 
1285 // gear HUD extras (fuel indicator, secondary ammo, etc.)
1286 if replicateToLeft then
1287     begin
1288     ShiftWorld(-1);
1289     DrawGearsGui();
1290     UnshiftWorld();
1291     end;
1292 
1293 if replicateToRight then
1294     begin
1295     ShiftWorld(1);
1296     DrawGearsGui();
1297     UnshiftWorld();
1298     end;
1299 
1300 DrawGearsGui();
1301 
1302 // Finger (arrow pointing to hedgehog).
1303 // NOT wrapped like the other stuff because it might be confusing.
1304 DrawFinger();
1305 
1306 // everything after this ChangeDepth will be drawn outside the screen
1307 // note: negative parallax gears should last very little for a smooth stereo effect
1308     ChangeDepth(RM, cStereo_Outside);
1309 
1310     if replicateToLeft then
1311         begin
1312         ShiftWorld(-1);
1313         DrawVisualGears(2, true);
1314         UnshiftWorld();
1315         end;
1316 
1317     if replicateToRight then
1318         begin
1319         ShiftWorld(1);
1320         DrawVisualGears(2, true);
1321         UnshiftWorld();
1322         end;
1323 
1324     DrawVisualGears(2, false);
1325 
1326 // everything after this ResetDepth will be drawn at screen level (depth = 0)
1327 // note: everything that needs to be readable should be on this level
1328     ResetDepth(RM);
1329 
1330     if replicateToLeft then
1331         begin
1332         ShiftWorld(-1);
1333         DrawVisualGears(3, true);
1334         UnshiftWorld();
1335         end;
1336 
1337     if replicateToRight then
1338         begin
1339         ShiftWorld(1);
1340         DrawVisualGears(3, true);
1341         UnshiftWorld();
1342         end;
1343 
1344     DrawVisualGears(3, false);
1345 
1346 // Target (e.g. air attack, bee, ...)
1347 if (TargetPoint.X <> NoPointX) and (CurrentTeam <> nil) and (CurrentHedgehog <> nil) then
1348     begin
1349     with PHedgehog(CurrentHedgehog)^ do
1350         begin
1351         if CurAmmoType = amBee then
1352             spr:= sprTargetBee
1353         else
1354             spr:= sprTargetP;
1355         if replicateToLeft then
1356             begin
1357             ShiftWorld(-1);
1358             if spr = sprTargetP then
1359                 begin
1360                 if IsTooDarkToRead(Team^.Clan^.Color) then
1361                     DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
1362                 else
1363                     DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1364                 Tint(Team^.Clan^.Color shl 8 or $FF);
1365                 end;
1366             DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1367             if spr = sprTargetP then
1368                 untint;
1369             UnshiftWorld();
1370             end;
1371 
1372         if replicateToRight then
1373             begin
1374             ShiftWorld(1);
1375             if spr = sprTargetP then
1376                 begin
1377                 if IsTooDarkToRead(Team^.Clan^.Color) then
1378                     DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
1379                 else
1380                     DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1381                 Tint(Team^.Clan^.Color shl 8 or $FF);
1382                 end;
1383             DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1384             if spr = sprTargetP then
1385                 untint;
1386             UnshiftWorld();
1387             end;
1388 
1389         if spr = sprTargetP then
1390             begin
1391             if IsTooDarkToRead(Team^.Clan^.Color) then
1392                 DrawSpriteRotatedF(sprTargetPBackInv, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360)
1393             else
1394                 DrawSpriteRotatedF(sprTargetPBack, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1395             Tint(Team^.Clan^.Color shl 8 or $FF);
1396             end;
1397         DrawSpriteRotatedF(spr, TargetPoint.X + WorldDx, TargetPoint.Y + WorldDy, 0, 0, (RealTicks shr 3) mod 360);
1398         if spr = sprTargetP then
1399             untint;
1400         end;
1401     end;
1402 
1403 // Attack bar
1404 if replicateToLeft then
1405     begin
1406     ShiftWorld(-1);
1407     RenderAttackBar();
1408     UnshiftWorld();
1409     end;
1410 
1411 if replicateToRight then
1412     begin
1413     ShiftWorld(1);
1414     RenderAttackBar();
1415     UnshiftWorld();
1416     end;
1417 
1418 RenderAttackBar();
1419 
1420 // World edge
1421 RenderWorldEdge();
1422 
1423 // This scale is used to keep the various widgets at the same dimension at all zoom levels
1424 SetScale(cDefaultZoomLevel);
1425 
1426 isNotHiddenByCinematic:= true;
1427 // Cinematic Mode: Determine effects and state
1428 if CinematicScript or (InCinematicMode and autoCameraOn
1429     and ((CurrentHedgehog = nil) or CurrentHedgehog^.Team^.ExtDriven
1430     or (CurrentHedgehog^.BotLevel <> 0) or (GameType = gmtDemo))) then
1431     begin
1432     if CinematicSteps < 300 then
1433         begin
1434         inc(CinematicSteps, Lag);
1435         if CinematicSteps > 300 then
1436             begin
1437             CinematicSteps:= 300;
1438             isNotHiddenByCinematic:= false;
1439             end;
1440         end;
1441     end
1442 else if CinematicSteps > 0 then
1443     begin
1444     dec(CinematicSteps, Lag);
1445     if CinematicSteps < 0 then
1446         CinematicSteps:= 0;
1447     end;
1448 
1449 // Turn time
1450 if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
1451     begin
1452 {$IFDEF USE_TOUCH_INTERFACE}
1453     offsetX:= cScreenHeight - 13;
1454 {$ELSE}
1455     offsetX:= 48;
1456 {$ENDIF}
1457     offsetY:= cOffsetY;
1458     if ((TurnTimeLeft <> 0) and (TurnTimeLeft < 999000)) or (ReadyTimeLeft <> 0) then
1459         begin
1460         if ReadyTimeLeft <> 0 then
1461             i:= Succ(Pred(ReadyTimeLeft) div 1000)
1462         else
1463             i:= Succ(Pred(TurnTimeLeft) div 1000);
1464 
1465         if i>99 then
1466             t:= 112
1467         else if i>9 then
1468             t:= 96
1469         else
1470             t:= 80;
1471         DrawSprite(sprFrame, -(cScreenWidth shr 1) + t + offsetY, cScreenHeight - offsetX, 1);
1472         while i > 0 do
1473             begin
1474             dec(t, 32);
1475             if isPaused or (not IsClockRunning()) then
1476                 spr := sprBigDigitGray
1477             else if (ReadyTimeLeft <> 0) then
1478                 spr := sprBigDigitGreen
1479             else if IsGetAwayTime then
1480                 spr := sprBigDigitRed
1481             else
1482                 spr := sprBigDigit;
1483             DrawSprite(spr, -(cScreenWidth shr 1) + t + offsetY, cScreenHeight - offsetX, i mod 10);
1484             i:= i div 10
1485             end;
1486         DrawSprite(sprFrame, -(cScreenWidth shr 1) + t - 4 + offsetY, cScreenHeight - offsetX, 0);
1487         end;
1488 
1489     end;
1490 
1491 // Team bars
1492 if (UIDisplay = uiAll) and (isNotHiddenByCinematic) then
1493     RenderTeamsHealth;
1494 
1495 // Current hedgehog health in top left corner
1496 if ((UIDisplay = uiAll) or (UIDisplay = uiNoTeams)) and (isNotHiddenByCinematic) and
1497         (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and
1498         (CurrentHedgehog^.HealthTagTex <> nil) and
1499         ((CurrentHedgehog^.Gear^.State and gstHHDriven) <> 0) then
1500     begin
1501     t:= 11;
1502     i:= t;
1503 {$IFDEF USE_TOUCH_INTERFACE}
1504     i:= t + pauseButton.frame.y + pauseButton.frame.h;
1505 {$ENDIF}
1506 
1507     // Hide health and healh icons in gfInvulnerable mode (except heResurrectable)
1508     if ((GameFlags and gfInvulnerable) = 0) then
1509     begin
1510     // Health tag
1511     DrawTexture(cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - 16, i, CurrentHedgehog^.HealthTagTex);
1512     inc(t, CurrentHedgehog^.HealthTagTex^.h);
1513     cDemoClockFPSOffsetY:= t;
1514 
1515     t:= SpritesData[sprHealthHud].Width + 18;
1516     // Main health icon. Appearance depends on game mode and poisoning state
1517     if ((GameFlags and gfResetHealth) = 0) then
1518         if (CurrentHedgehog^.Effects[hePoisoned] <> 0) then
1519             DrawSprite(sprHealthPoisonHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
1520         else
1521             DrawSprite(sprHealthHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
1522     else
1523         if (CurrentHedgehog^.Effects[hePoisoned] <> 0) then
1524             DrawSprite(sprMedicPoisonHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
1525         else
1526             DrawSprite(sprMedicHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0);
1527     // Put halo above health icon for resurrectable hog
1528     if (CurrentHedgehog^.Effects[heResurrectable] <> 0) then
1529         DrawSprite(sprHaloHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t - 2), i - SpritesData[sprHaloHud].Height + 1, 0);
1530 
1531     // Additional health-related states
1532     inc(t, 2);
1533     // Invulnerable
1534     if (CurrentHedgehog^.Effects[heInvulnerable] <> 0) then
1535         begin
1536         inc(t, SpritesData[sprInvulnHud].Width + 2);
1537         DrawSprite(sprInvulnHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
1538         end
1539     // Karma
1540     else if ((GameFlags and gfKarma) <> 0) then
1541         begin
1542         inc(t, SpritesData[sprKarmaHud].Width + 2);
1543         DrawSprite(sprKarmaHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0)
1544         end;
1545     // Vampirism
1546     if cVampiric then
1547         begin
1548         inc(t, SpritesData[sprVampHud].Width + 2);
1549         DrawSprite(sprVampHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t), i, 0);
1550         end;
1551     end
1552     // in gfInvulnerable mode ...
1553     else if (CurrentHedgehog^.Effects[heResurrectable] <> 0) then
1554         // show halo for resurrectable hog
1555         DrawSprite(sprHaloHud, (cScreenWidth div 2 - CurrentHedgehog^.HealthTagTex^.w - t - 2), i, 0);
1556     end
1557 else
1558     cDemoClockFPSOffsetY:= 0;
1559 
1560 // Wind bar
1561 if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
1562     begin
1563 {$IFDEF USE_TOUCH_INTERFACE}
1564     offsetX:= cScreenHeight - 13;
1565     offsetY:= (cScreenWidth shr 1) + 74;
1566 {$ELSE}
1567     offsetX:= 30;
1568     offsetY:= 180;
1569 {$ENDIF}
1570     DrawSprite(sprWindBar, (cScreenWidth shr 1) - offsetY, cScreenHeight - offsetX, 0);
1571     if WindBarWidth > 0 then
1572         begin
1573         {$WARNINGS OFF}
1574         r.x:= 8 - (RealTicks shr 6) mod 9;
1575         {$WARNINGS ON}
1576         r.y:= 0;
1577         r.w:= WindBarWidth;
1578         r.h:= 13;
1579         DrawSpriteFromRect(sprWindR, r, (cScreenWidth shr 1) - offsetY + 77, cScreenHeight - offsetX + 2, 13, 0);
1580         end
1581     else
1582         if WindBarWidth < 0 then
1583         begin
1584         {$WARNINGS OFF}
1585         r.x:= (Longword(WindBarWidth) + RealTicks shr 6) mod 9;
1586         {$WARNINGS ON}
1587         r.y:= 0;
1588         r.w:= - WindBarWidth;
1589         r.h:= 13;
1590         DrawSpriteFromRect(sprWindL, r, (cScreenWidth shr 1) - offsetY + 74 + WindBarWidth, cScreenHeight - offsetX + 2, 13, 0);
1591         end
1592     end;
1593 
1594 // Indicators for global effects (extra damage, low gravity)
1595 if (UIDisplay <> uiNone) and (isNotHiddenByCinematic) then
1596     begin
1597 {$IFDEF USE_TOUCH_INTERFACE}
1598     offsetX:= (cScreenWidth shr 1) - 95;
1599     offsetY:= cScreenHeight - 21;
1600 {$ELSE}
1601     offsetX:= 45;
1602     offsetY:= 51;
1603 {$ENDIF}
1604 
1605     if cDamageModifier = _1_5 then
1606         begin
1607             DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
1608             DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amExtraDamage) - 1, 1, 32, 32);
1609 {$IFDEF USE_TOUCH_INTERFACE}
1610             offsetX := offsetX - 33
1611 {$ELSE}
1612             offsetX := offsetX + 33
1613 {$ENDIF}
1614         end;
1615     if (cLowGravity) or ((GameFlags and gfLowGravity) <> 0) then
1616         begin
1617             DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
1618             DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amLowGravity) - 1, 1, 32, 32);
1619 {$IFDEF USE_TOUCH_INTERFACE}
1620             offsetX := offsetX - 33
1621 {$ELSE}
1622             offsetX := offsetX + 33
1623 {$ENDIF}
1624         end;
1625     if cLaserSighting then
1626         begin
1627             DrawTextureF(ropeIconTex, 1, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, 0, 1, 32, 32);
1628             DrawTextureF(SpritesData[sprAMAmmos].Texture, 0.90, (cScreenWidth shr 1) - offsetX, cScreenHeight - offsetY, ord(amLaserSight) - 1, 1, 32, 32);
1629         end;
1630     end;
1631 
1632 // Cinematic Mode: Render black bars
1633 if CinematicSteps > 0 then
1634     begin
1635     r.x:= ViewLeftX;
1636     r.w:= ViewWidth;
1637     r.y:= ViewTopY;
1638     CinematicBarH:= (ViewHeight * CinematicSteps) div 2048;
1639     r.h:= CinematicBarH;
1640     DrawRect(r, 0, 0, 0, $FF, true);
1641     r.y:= ViewBottomY - r.h;
1642     DrawRect(r, 0, 0, 0, $FF, true);
1643     end;
1644 
1645 // Touchscreen interface widgets
1646 {$IFDEF USE_TOUCH_INTERFACE}
1647 DrawScreenWidget(@arrowLeft);
1648 DrawScreenWidget(@arrowRight);
1649 DrawScreenWidget(@arrowUp);
1650 DrawScreenWidget(@arrowDown);
1651 
1652 DrawScreenWidget(@fireButton);
1653 DrawScreenWidget(@jumpWidget);
1654 DrawScreenWidget(@AMWidget);
1655 DrawScreenWidget(@utilityWidget);
1656 DrawScreenWidget(@utilityWidget2);
1657 DrawScreenWidget(@pauseButton);
1658 {$ENDIF}
1659 
1660 // Captions
1661 if UIDisplay <> uiNone then
1662     DrawCaptions;
1663 
1664 // Lag alert
1665 if isInLag then
1666     DrawSprite(sprLag, 32 - (cScreenWidth shr 1), 32, (RealTicks shr 7) mod 12);
1667 
1668 // Chat
1669 DrawChat;
1670 
1671 
1672 // Mission panel
1673 if not isFirstFrame and (missionTimer <> 0) or isShowMission or isPaused or fastUntilLag or (GameState = gsConfirm) then
1674     begin
1675     if (ReadyTimeLeft = 0) and (missionTimer > 0) then
1676         dec(missionTimer, Lag);
1677     if missionTimer < 0 then
1678         missionTimer:= 0; // avoid subtracting below 0
1679     if missionTex <> nil then
1680         DrawTextureCentered(0, Min((cScreenHeight shr 1) + 100, cScreenHeight - 48 - missionTex^.h), missionTex);
1681     end;
1682 if missionTimer = 0 then
1683     isForceMission := false;
1684 
1685 // AmmoMenu
1686 if bShowAmmoMenu and ((AMState = AMHidden) or (AMState = AMHiding)) then
1687     begin
1688     if (AMState = AMHidden) then
1689         AMAnimStartTime:= RealTicks
1690     else
1691         AMAnimStartTime:= RealTicks - (AMAnimDuration - (RealTicks - AMAnimStartTime));
1692     AMState:= AMShowingUp;
1693     end;
1694 if (not bShowAmmoMenu) and ((AMstate = AMShowing) or (AMState = AMShowingUp)) then
1695     begin
1696     if (AMState = AMShowing) then
1697         AMAnimStartTime:= RealTicks
1698     else
1699         AMAnimStartTime:= RealTicks - (AMAnimDuration - (RealTicks - AMAnimStartTime));
1700     AMState:= AMHiding;
1701     end;
1702 
1703 if bShowAmmoMenu or (AMState = AMHiding) then
1704     ShowAmmoMenu;
1705 
1706 // Centered status/menu messages (synchronizing, auto skip, pause, etc.)
1707 if fastUntilLag then
1708     DrawTextureCentered(0, (cScreenHeight shr 1), SyncTexture)
1709 else if isAFK then
1710     DrawTextureCentered(0, (cScreenHeight shr 1), AFKTexture)
1711 else if isPaused then
1712     DrawTextureCentered(0, (cScreenHeight shr 1), PauseTexture);
1713 
1714 // Cursor
1715 if isCursorVisible and bShowAmmoMenu then
1716     DrawSprite(sprArrow, CursorPoint.X, cScreenHeight - CursorPoint.Y, (RealTicks shr 6) mod 8);
1717 
1718 // FPS and demo replay time
1719 {$IFDEF USE_TOUCH_INTERFACE}
1720 offsetY:= cDemoClockFPSOffsetY + 10 + pauseButton.frame.y + pauseButton.frame.h;
1721 {$ELSE}
1722 offsetY:= cDemoClockFPSOffsetY + 10;
1723 {$ENDIF}
1724 offsetX:= cOffsetY;
1725 if (RM = rmDefault) or (RM = rmRightEye) then
1726     begin
1727     inc(Frames);
1728 
1729     if cShowFPS or (GameType = gmtDemo) then
1730         inc(CountTicks, Lag);
1731 
1732     // Demo replay time
1733     if (GameType = gmtDemo) and (CountTicks >= 1000) then
1734         begin
1735         i:= GameTicks div 1000;
1736         t:= i mod 60;
1737         s:= inttostr(t);
1738         if t < 10 then
1739             s:= '0' + s;
1740         i:= i div 60;
1741         t:= i mod 60;
1742         s:= inttostr(t) + ':' + s;
1743         if t < 10 then
1744             s:= '0' + s;
1745         s:= inttostr(i div 60) + ':' + s;
1746 
1747 
1748         tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
1749         tmpSurface:= doSurfaceConversion(tmpSurface);
1750         FreeAndNilTexture(timeTexture);
1751         timeTexture:= Surface2Tex(tmpSurface, false);
1752         SDL_FreeSurface(tmpSurface)
1753         end;
1754 
1755     if (timeTexture <> nil) and (UIDisplay <> uiNone) then
1756         DrawTexture((cScreenWidth shr 1) - 20 - timeTexture^.w - offsetX, offsetY, timeTexture);
1757 
1758     // FPS counter
1759     if cShowFPS and (UIDisplay <> uiNone) then
1760         begin
1761         if CountTicks >= 1000 then
1762             begin
1763             FPS:= Frames;
1764             Frames:= 0;
1765             CountTicks:= 0;
1766             s:= Format(shortstring(trmsg[sidFPS]), inttostr(FPS));
1767             tmpSurface:= TTF_RenderUTF8_Blended(Fontz[CheckCJKFont(trmsg[sidFPS],fnt16)].Handle, Str2PChar(s), cWhiteColorChannels);
1768             tmpSurface:= doSurfaceConversion(tmpSurface);
1769             FreeAndNilTexture(fpsTexture);
1770             fpsTexture:= Surface2Tex(tmpSurface, false);
1771             SDL_FreeSurface(tmpSurface)
1772             end;
1773         if fpsTexture <> nil then
1774             begin
1775             if timeTexture <> nil then
1776                 i:= fpsTexture^.h + 5
1777             else
1778                 i:= 0;
1779             DrawTexture((cScreenWidth shr 1) - 20 - fpsTexture^.w - offsetX, offsetY + i, fpsTexture);
1780             end;
1781         end;
1782 end;
1783 
1784 // Quit Y/N question
1785 if GameState = gsConfirm then
1786     DrawTextureCentered(0, (cScreenHeight shr 1)-40, ConfirmTexture);
1787 
1788 if ScreenFade <> sfNone then
1789     begin
1790     if (not isFirstFrame) then
1791         case ScreenFade of
1792             sfToBlack, sfToWhite:     if ScreenFadeValue + Lag * ScreenFadeSpeed < sfMax then
1793                                           inc(ScreenFadeValue, Lag * ScreenFadeSpeed)
1794                                       else
1795                                           ScreenFadeValue:= sfMax;
1796             sfFromBlack, sfFromWhite: if ScreenFadeValue - Lag * ScreenFadeSpeed > 0 then
1797                                           dec(ScreenFadeValue, Lag * ScreenFadeSpeed)
1798                                       else
1799                                           ScreenFadeValue:= 0;
1800             end;
1801     if ScreenFade <> sfNone then
1802         begin
1803         r.x:= ViewLeftX;
1804         r.y:= ViewTopY;
1805         r.w:= ViewWidth;
1806         r.h:= ViewHeight;
1807 
1808         case ScreenFade of
1809             sfToBlack, sfFromBlack: DrawRect(r, 0, 0, 0, ScreenFadeValue * 255 div 1000, true);
1810             sfToWhite, sfFromWhite: DrawRect(r, $FF, $FF, $FF, ScreenFadeValue * 255 div 1000, true);
1811             end;
1812 
1813         if not isFirstFrame and ((ScreenFadeValue = 0) or (ScreenFadeValue = sfMax)) then
1814             ScreenFade:= sfNone
1815         end
1816     end;
1817 
1818 {$IFDEF USE_VIDEO_RECORDING}
1819 // During video prerecording draw red blinking circle and text 'rec'
1820 if flagPrerecording then
1821     begin
1822     if recTexture = nil then
1823         begin
1824         s:= 'rec';
1825         tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fntBig].Handle, Str2PChar(s), cWhiteColorChannels);
1826         tmpSurface:= doSurfaceConversion(tmpSurface);
1827         FreeAndNilTexture(recTexture);
1828         recTexture:= Surface2Tex(tmpSurface, false);
1829         SDL_FreeSurface(tmpSurface)
1830         end;
1831     DrawTexture( -(cScreenWidth shr 1) + 50, 20, recTexture);
1832 
1833     t:= -255 + ((RealTicks div 2) and 511);
1834     a:= Byte(min(255, abs(t)));
1835 
1836     // draw red circle
1837     DrawCircleFilled(-(cScreenWidth shr 1) + 30, 35, 10, $FF, $00, $00, a);
1838     end;
1839 {$ENDIF}
1840 
1841 SetScale(zoom);
1842 
1843 // Cursor
1844 if isCursorVisible and (not bShowAmmoMenu) then
1845     begin
1846     if not CurrentTeam^.ExtDriven then TargetCursorPoint:= CursorPoint;
1847     with CurrentHedgehog^ do
1848         if (Gear <> nil) and ((Gear^.State and gstChooseTarget) <> 0) then
1849             begin
1850         i:= GetCurAmmoEntry(CurrentHedgehog^)^.Pos;
1851         with Ammoz[CurAmmoType] do
1852             if PosCount > 0 then
1853                 begin
1854                 if (CurAmmoType = amGirder) or (CurAmmoType = amTeleport) then
1855                     begin
1856                 // pulsating transparency
1857                     if ((GameTicks div 16) mod $80) >= $40 then
1858                         Tint($FF, $FF, $FF, $C0 - (GameTicks div 16) mod $40)
1859                     else
1860                         Tint($FF, $FF, $FF, $80 + (GameTicks div 16) mod $40);
1861                     end;
1862                 DrawSprite(PosSprite, TargetCursorPoint.X - (SpritesData[PosSprite].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1),i);
1863                 Untint();
1864                 if (WorldEdge = weWrap) and (CurAmmoType = amBee) then
1865                     begin
1866                     if (TargetCursorPoint.X - WorldDx > rightX) then
1867                         DrawSprite(sprThroughWrap, TargetCursorPoint.X - (SpritesData[sprThroughWrap].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1) - SpritesData[sprThroughWrap].Height - 2, 0)
1868                     else if (TargetCursorPoint.X - WorldDx < leftX) then
1869                         DrawSprite(sprThroughWrap, TargetCursorPoint.X - (SpritesData[sprThroughWrap].Width shr 1), cScreenHeight - TargetCursorPoint.Y - (SpritesData[PosSprite].Height shr 1) - SpritesData[sprThroughWrap].Height - 2, 1);
1870                     end;
1871                 end;
1872             end;
1873     DrawTextureF(SpritesData[sprArrow].Texture, cDefaultZoomLevel / cScaleFactor, TargetCursorPoint.X + round(SpritesData[sprArrow].Width / cScaleFactor), cScreenHeight + round(SpritesData[sprArrow].Height / cScaleFactor) - TargetCursorPoint.Y, (RealTicks shr 6) mod 8, 1, SpritesData[sprArrow].Width, SpritesData[sprArrow].Height);
1874     end;
1875 
1876 // debug stuff
1877 if cViewLimitsDebug then
1878     begin
1879     r.x:= ViewLeftX;
1880     r.y:= ViewTopY;
1881     r.w:= ViewWidth;
1882     r.h:= ViewHeight;
1883     DrawRect(r, 255, 0, 0, 128, false);
1884     end;
1885 
1886 isFirstFrame:= false
1887 end;
1888 
1889 var PrevSentPointTime: LongWord = 0;
1890 
1891 procedure MoveCamera;
1892 var EdgesDist, wdy, shs,z, dstX: LongInt;
1893     inbtwnTrgtAttks: Boolean;
1894 begin
1895 {$IFNDEF MOBILE}
1896 if (not (CurrentTeam^.ExtDriven and isCursorVisible and (not bShowAmmoMenu) and autoCameraOn)) and cHasFocus and (GameState <> gsConfirm) then
1897     uCursor.updatePosition();
1898 {$ENDIF}
1899 z:= round(200/zoom);
1900 inbtwnTrgtAttks := ((GameFlags and gfInfAttack) <> 0) and (CurrentHedgehog <> nil) and ((CurrentHedgehog^.Gear = nil) or (CurrentHedgehog^.Gear <> FollowGear)) and ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) <> 0);
1901 if autoCameraOn and (not PlacingHogs) and (FollowGear <> nil) and (not isCursorVisible) and (not bShowAmmoMenu) and (not fastUntilLag) and (not inbtwnTrgtAttks) then
1902     if ((abs(CursorPoint.X - prevPoint.X) + abs(CursorPoint.Y - prevpoint.Y)) > 4) then
1903         begin
1904         FollowGear:= nil;
1905         prevPoint:= CursorPoint;
1906         exit
1907         end
1908     else
1909         begin
1910             dstX:= hwRound(FollowGear^.X) + hwSign(FollowGear^.dX) * z + WorldDx;
1911 
1912             if (WorldEdge = weWrap) then
1913                 begin
1914                     if dstX - prevPoint.X < (leftX - rightX) div 2 then
1915                         CursorPoint.X:= (prevPoint.X * 7 + dstX - (leftX - rightX)) div 8
1916                     else if dstX - prevPoint.X > (rightX - leftX) div 2 then
1917                         CursorPoint.X:= (prevPoint.X * 7 + dstX - (rightX - leftX)) div 8
1918                     else
1919                         CursorPoint.X:= (prevPoint.X * 7 + dstX) div 8;
1920                 end
1921             else // usual camera movement routine
1922                 begin
1923                     CursorPoint.X:= (prevPoint.X * 7 + dstX) div 8;
1924                 end;
1925 
1926         if isPhone() or (cScreenHeight < 600) or (hwFloat(FollowGear^.dY * z).Round < 10) then
1927             CursorPoint.Y:= (prevPoint.Y * 7 + cScreenHeight - (hwRound(FollowGear^.Y) + WorldDy)) div 8
1928         else
1929             CursorPoint.Y:= (prevPoint.Y * 7 + cScreenHeight - (hwRound(FollowGear^.Y) + hwSign(FollowGear^.dY) * z + WorldDy)) div 8;
1930         end;
1931 
1932 if (WorldEdge = weWrap) then
1933     begin
1934         if -WorldDx < leftX then
1935             WorldDx:= WorldDx - rightX + leftX
1936         else if -WorldDx > rightX then
1937             WorldDx:= WorldDx + rightX - leftX;
1938     end;
1939 
1940 wdy:= trunc(cScreenHeight / cScaleFactor) + cScreenHeight div 2 - cWaterLine - (cVisibleWater + trunc(CinematicBarH / (cScaleFactor / 2.0)));
1941 if WorldDy < wdy then
1942     WorldDy:= wdy;
1943 
1944 if ((CursorPoint.X = prevPoint.X) and (CursorPoint.Y = prevpoint.Y)) then
1945     exit;
1946 
1947 if (AMState = AMShowingUp) or (AMState = AMShowing) then
1948 begin
1949     if CursorPoint.X < AmmoRect.x + amNumOffsetX + 3 then//check left
1950         CursorPoint.X:= AmmoRect.x + amNumOffsetX + 3;
1951     if CursorPoint.X > AmmoRect.x + AmmoRect.w - 3 then//check right
1952         CursorPoint.X:= AmmoRect.x + AmmoRect.w - 3;
1953     if CursorPoint.Y > cScreenHeight - AmmoRect.y -amNumOffsetY - 1 then//check top
1954         CursorPoint.Y:= cScreenHeight - AmmoRect.y - amNumOffsetY - 1;
1955     if CursorPoint.Y < cScreenHeight - (AmmoRect.y + AmmoRect.h - AMSlotSize - 5) then//check bottom
1956         CursorPoint.Y:= cScreenHeight - (AmmoRect.y + AmmoRect.h - AMSlotSize - 5);
1957     prevPoint:= CursorPoint;
1958     exit
1959 end;
1960 
1961 if isCursorVisible then
1962     begin
1963     if (not CurrentTeam^.ExtDriven) and (GameTicks >= PrevSentPointTime + cSendCursorPosTime) then
1964         begin
1965         SendIPCXY('P', CursorPoint.X - WorldDx, cScreenHeight - CursorPoint.Y - WorldDy);
1966         PrevSentPointTime:= GameTicks
1967         end;
1968     EdgesDist:= cCursorEdgesDist
1969     end
1970 else
1971     EdgesDist:= cGearScrEdgesDist;
1972 
1973 // this generates the border around the screen that moves the camera when cursor is near it
1974 if (CurrentTeam^.ExtDriven and isCursorVisible and autoCameraOn) or
1975    (not CurrentTeam^.ExtDriven and isCursorVisible) or ((FollowGear <> nil) and autoCameraOn) then
1976     begin
1977     if CursorPoint.X < - cScreenWidth div 2 + EdgesDist then
1978         begin
1979         WorldDx:= WorldDx - CursorPoint.X - cScreenWidth div 2 + EdgesDist;
1980         CursorPoint.X:= - cScreenWidth div 2 + EdgesDist
1981         end
1982     else
1983         if CursorPoint.X > cScreenWidth div 2 - EdgesDist then
1984             begin
1985             WorldDx:= WorldDx - CursorPoint.X + cScreenWidth div 2 - EdgesDist;
1986             CursorPoint.X:= cScreenWidth div 2 - EdgesDist
1987             end;
1988 
1989     shs:= min(cScreenHeight div 2 - trunc(cScreenHeight / cScaleFactor) + EdgesDist, cScreenHeight - EdgesDist);
1990     if CursorPoint.Y < shs then
1991         begin
1992         WorldDy:= WorldDy + CursorPoint.Y - shs;
1993         CursorPoint.Y:= shs;
1994         end
1995     else
1996         if (CursorPoint.Y > cScreenHeight - EdgesDist) then
1997             begin
1998            WorldDy:= WorldDy + CursorPoint.Y - cScreenHeight + EdgesDist;
1999            CursorPoint.Y:= cScreenHeight - EdgesDist
2000             end;
2001     end
2002 else
2003     if cHasFocus then
2004         begin
2005         WorldDx:= WorldDx - CursorPoint.X + prevPoint.X;
2006         WorldDy:= WorldDy + CursorPoint.Y - prevPoint.Y;
2007         CursorPoint.X:= 0;
2008         CursorPoint.Y:= cScreenHeight div 2;
2009         end;
2010 
2011 // this moves the camera according to CursorPoint X and Y
2012 prevPoint:= CursorPoint;
2013 if WorldDy > LAND_HEIGHT + 1024 then
2014     WorldDy:= LAND_HEIGHT + 1024;
2015 if WorldDy < wdy then
2016     WorldDy:= wdy;
2017 if WorldDx < - LAND_WIDTH - 1024 then
2018     WorldDx:= - LAND_WIDTH - 1024;
2019 if WorldDx > 1024 then
2020     WorldDx:= 1024;
2021 end;
2022 
2023 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt);
2024 begin
2025     ShowMission(caption, subcaption, text, icon, time, false);
2026 end;
2027 
2028 procedure ShowMission(caption, subcaption, text: ansistring; icon, time : LongInt; forceDisplay : boolean);
2029 var r: TSDL_Rect;
2030 begin
2031 if cOnlyStats then exit;
2032 
2033 r.w:= 32;
2034 r.h:= 32;
2035 
2036 // If true, then mission panel cannot be hidden by releasing the mission panel key.
2037 // Is in effect until timer runs out, is hidden with HideMission or ShowMission is called with forceDisplay=false.
2038 isForceMission := forceDisplay;
2039 
2040 if time = 0 then
2041     time:= 5000;
2042 missionTimer:= time;
2043 FreeAndNilTexture(missionTex);
2044 
2045 if icon > -1 then
2046     begin
2047     r.x:= 0;
2048     r.y:= icon * 32;
2049     missionTex:= RenderHelpWindow(caption, subcaption, text, ansistring(''), 0, MissionIcons, @r)
2050     end
2051 else
2052     begin
2053     r.x:= ((-icon - 1) shr 4) * 32;
2054     r.y:= ((-icon - 1) mod 16) * 32;
2055     missionTex:= RenderHelpWindow(caption, subcaption, text, ansistring(''), 0, SpritesData[sprAMAmmos].Surface, @r)
2056     end;
2057 end;
2058 
2059 procedure HideMission;
2060 begin
2061     missionTimer:= 0;
2062     isForceMission:= false;
2063 end;
2064 
2065 procedure SetAmmoTexts(ammoType: TAmmoType; name: ansistring; caption: ansistring; description: ansistring; autoLabels: boolean);
2066 var
2067     ammoStrId: TAmmoStrId;
2068     ammoStr: ansistring;
2069     tmpsurf: PSDL_Surface;
2070 begin
2071     if cOnlyStats then exit;
2072 
2073     ammoStrId := Ammoz[ammoType].NameId;
2074 
2075     trluaammo[ammoStrId] := name;
2076     if length(trluaammo[ammoStrId]) > 0 then
2077         ammoStr:= trluaammo[ammoStrId]
2078     else
2079         ammoStr:= trammo[ammoStrId];
2080 
2081     if checkFails(length(ammoStr) > 0,'No default text/translation found for ammo type #' + intToStr(ord(ammoType)) + '!',true) then exit;
2082 
2083     tmpsurf:= TTF_RenderUTF8_Blended(Fontz[CheckCJKFont(ammoStr,fnt16)].Handle, PChar(ammoStr), cWhiteColorChannels);
2084     if checkFails(tmpsurf <> nil,'Name-texture creation for ammo type #' + intToStr(ord(ammoType)) + ' failed!',true) then exit;
2085     tmpsurf:= doSurfaceConversion(tmpsurf);
2086     FreeAndNilTexture(Ammoz[ammoType].NameTex);
2087     Ammoz[ammoType].NameTex:= Surface2Tex(tmpsurf, false);
2088     SDL_FreeSurface(tmpsurf);
2089 
2090     trluaammoc[ammoStrId] := caption;
2091     trluaammod[ammoStrId] := description;
2092     trluaammoe[ammoStrId] := autoLabels;
2093 end;
2094 
2095 procedure ShakeCamera(amount: LongInt);
2096 begin
2097 if isCursorVisible then
2098     exit;
2099 amount:= Max(1, round(amount*zoom/2));
2100 WorldDx:= WorldDx - amount + LongInt(random(1 + amount * 2));
2101 WorldDy:= WorldDy - amount + LongInt(random(1 + amount * 2));
2102 end;
2103 
2104 
2105 procedure onFocusStateChanged;
2106 begin
2107 {$IFDEF MOBILE}
2108 if (not cHasFocus) and (not isPaused) then
2109     ParseCommand('pause', true);
2110 // when created SDL receives an exposure event that calls UndampenAudio at full power, muting audio
2111 exit;
2112 {$ENDIF}
2113 if (not cHasFocus) and (GameState <> gsConfirm) then
2114     ParseCommand('quit', true);
2115 
2116 {$IFDEF USE_VIDEO_RECORDING}
2117 // do not change volume during prerecording as it will affect sound in video file
2118 if (not flagPrerecording) then
2119 {$ENDIF}
2120     begin
2121     if (not cHasFocus) then DampenAudio()
2122     else UndampenAudio();
2123     end;
2124 end;
2125 
2126 procedure updateCursorVisibility;
2127 begin
2128     if isPaused or isAFK or (GameState = gsConfirm) then
2129         begin
2130 {$IFNDEF USE_TOUCH_INTERFACE}
2131         SDL_SetRelativeMouseMode(SDL_FALSE);
2132 {$ENDIF}
2133         if SDL_ShowCursor(SDL_QUERY) = SDL_DISABLE then
2134             begin
2135             uCursor.resetPosition;
2136 {$IFNDEF USE_TOUCH_INTERFACE}
2137             SDL_ShowCursor(SDL_ENABLE);
2138 {$ENDIF}
2139             end;
2140         end
2141     else
2142         begin
2143         uCursor.resetPositionDelta;
2144 {$IFNDEF USE_TOUCH_INTERFACE}
2145         SDL_ShowCursor(SDL_DISABLE);
2146         SDL_SetRelativeMouseMode(SDL_TRUE);
2147 {$ENDIF}
2148         end;
2149 end;
2150 
2151 procedure updateTouchWidgets(ammoType: TAmmoType);
2152 begin
2153 {$IFDEF USE_TOUCH_INTERFACE}
2154 //show the aiming buttons + animation
2155 if (Ammoz[ammoType].Ammo.Propz and ammoprop_NeedUpDown) <> 0 then
2156     begin
2157     if (not arrowUp.show) then
2158         begin
2159         animateWidget(@arrowUp, true, true);
2160         animateWidget(@arrowDown, true, true);
2161         end;
2162     end
2163 else
2164     if arrowUp.show then
2165         begin
2166         animateWidget(@arrowUp, true, false);
2167         animateWidget(@arrowDown, true, false);
2168         end;
2169 SetUtilityWidgetState(ammoType);
2170 {$ELSE}
2171 ammoType:= ammoType; // avoid hint
2172 {$ENDIF}
2173 end;
2174 
2175 procedure SetUtilityWidgetState(ammoType: TAmmoType);
2176 begin
2177 {$IFDEF USE_TOUCH_INTERFACE}
2178 if(ammoType = amNothing)then
2179     ammoType:= CurrentHedgehog^.CurAmmoType;
2180 
2181 if(CurrentHedgehog <> nil)then
2182     if ((Ammoz[ammoType].Ammo.Propz and ammoprop_Timerable) <> 0) and (ammoType <> amDrillStrike) then
2183         begin
2184         utilityWidget.sprite:= sprTimerButton;
2185         if (not utilityWidget.show) then
2186             animateWidget(@utilityWidget, true, true);
2187         end
2188     else if (Ammoz[ammoType].Ammo.Propz and ammoprop_NeedTarget) <> 0 then
2189         begin
2190         utilityWidget.sprite:= sprTargetButton;
2191         if (not utilityWidget.show) then
2192             animateWidget(@utilityWidget, true, true);
2193         end
2194     else if ammoType = amSwitch then
2195         begin
2196         utilityWidget.sprite:= sprSwitchButton;
2197         if (not utilityWidget.show) then
2198             animateWidget(@utilityWidget, true, true);
2199         end
2200     else if utilityWidget.show then
2201         animateWidget(@utilityWidget, true, false);
2202 
2203     if ((Ammoz[ammoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
2204         begin
2205         utilityWidget2.sprite:= sprBounceButton;
2206         if (not utilityWidget2.show) then
2207             animateWidget(@utilityWidget2, true, true);
2208         end
2209     else if utilityWidget2.show then
2210         animateWidget(@utilityWidget2, true, false);
2211 {$ELSE}
2212 ammoType:= ammoType; // avoid hint
2213 {$ENDIF}
2214 end;
2215 
2216 procedure animateWidget(widget: POnScreenWidget; fade, showWidget: boolean);
2217 begin
2218 with widget^ do
2219     begin
2220     show:= showWidget;
2221     if fade then fadeAnimStart:= RealTicks;
2222 
2223     with moveAnim do
2224         begin
2225         animate:= true;
2226         startTime:= RealTicks;
2227         source.x:= source.x xor target.x; //swap source <-> target
2228         target.x:= source.x xor target.x;
2229         source.x:= source.x xor target.x;
2230         source.y:= source.y xor target.y;
2231         target.y:= source.y xor target.y;
2232         source.y:= source.y xor target.y;
2233         end;
2234     end;
2235 end;
2236 
2237 
2238 procedure initModule;
2239 begin
2240     fpsTexture:= nil;
2241     recTexture:= nil;
2242     FollowGear:= nil;
2243     WindBarWidth:= 0;
2244     bShowAmmoMenu:= false;
2245     bSelected:= false;
2246     bShowFinger:= false;
2247     Frames:= 0;
2248     WorldDx:= -512;
2249     WorldDy:= -256;
2250     PrevSentPointTime:= 0;
2251 
2252     FPS:= 0;
2253     CountTicks:= 0;
2254     SoundTimerTicks:= 0;
2255     prevPoint.X:= 0;
2256     prevPoint.Y:= 0;
2257     missionTimer:= 0;
2258     missionTex:= nil;
2259     cOffsetY:= 0;
2260     AMState:= AMHidden;
2261     isFirstFrame:= true;
2262 
2263     FillChar(WorldFade, sizeof(WorldFade), 0);
2264     WorldFade[0].a:= 255;
2265     WorldFade[1].a:= 255;
2266     FillChar(WorldEnd, sizeof(WorldEnd), 0);
2267     WorldEnd[0].a:= 255;
2268     WorldEnd[1].a:= 255;
2269     WorldEnd[2].a:= 255;
2270     WorldEnd[3].a:= 255;
2271 
2272     AmmoMenuTex:= nil;
2273     AmmoMenuInvalidated:= true
2274 end;
2275 
2276 procedure freeModule;
2277 begin
2278     ResetWorldTex();
2279 end;
2280 
2281 end.
2282