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