hedgewars/uGearsUtils.pas
changeset 10363 fc3aa563ab01
parent 10356 7d1044267b83
child 10417 8f66bb4dbf80
equal deleted inserted replaced
10362:a39e4fa3153e 10363:fc3aa563ab01
    22 interface
    22 interface
    23 uses uTypes, uFloat;
    23 uses uTypes, uFloat;
    24 
    24 
    25 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
    25 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline;
    26 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
    26 procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord);
       
    27 procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
    27 
    28 
    28 function  ModifyDamage(dmg: Longword; Gear: PGear): Longword;
    29 function  ModifyDamage(dmg: Longword; Gear: PGear): Longword;
    29 procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
    30 procedure ApplyDamage(Gear: PGear; AttackerHog: PHedgehog; Damage: Longword; Source: TDamageSource);
    30 procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
    31 procedure spawnHealthTagForHH(HHGear: PGear; dmg: Longword);
    31 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
    32 procedure HHHurt(Hedgehog: PHedgehog; Source: TDamageSource);
   355         Gear^.DirAngle := Gear^.DirAngle + 360
   356         Gear^.DirAngle := Gear^.DirAngle + 360
   356     else if 360 < Gear^.DirAngle then
   357     else if 360 < Gear^.DirAngle then
   357         Gear^.DirAngle := Gear^.DirAngle - 360
   358         Gear^.DirAngle := Gear^.DirAngle - 360
   358 end;
   359 end;
   359 
   360 
       
   361 procedure AddSplashForGear(Gear: PGear; justSkipping: boolean);
       
   362 var x, y, i, distL, distR, distB, minDist, maxDrops: LongInt;
       
   363     splash, particle: PVisualGear;
       
   364     speed, hwTmp: hwFloat;
       
   365     vi, vs, tmp: real; // impact speed and sideways speed
       
   366     isImpactH, isImpactRight: boolean;
       
   367 begin
       
   368 x:= hwRound(Gear^.X);
       
   369 y:= hwRound(Gear^.Y);
       
   370 
       
   371 splash:= AddVisualGear(x, y, vgtSplash);
       
   372 if splash = nil then
       
   373     exit;
       
   374 
       
   375 // correct position and angle
       
   376 
       
   377 distB:= cWaterline - y;
       
   378 
       
   379 if WorldEdge <> weSea then
       
   380     minDist:= distB
       
   381 else
       
   382     begin
       
   383     distL:= x - leftX;
       
   384     distR:= rightX - x;
       
   385     minDist:= min(distB, min(distL, distR));
       
   386     end;
       
   387 
       
   388 isImpactH:= (minDist <> distB);
       
   389 
       
   390 if not isImpactH then
       
   391     begin
       
   392     dec(y, distB);
       
   393     splash^.Y:= y;
       
   394     speed:= hwAbs(Gear^.dY);
       
   395     vs:= abs(hwFloat2Float(Gear^.dX));
       
   396     end
       
   397 else
       
   398     begin
       
   399     isImpactRight := minDist = distR;
       
   400     if isImpactRight then
       
   401         begin
       
   402         inc(x, distR);
       
   403         splash^.Angle:= -90;
       
   404         end
       
   405     else
       
   406         begin
       
   407         dec(x, distL);
       
   408         splash^.Angle:=  90;
       
   409         end;
       
   410     splash^.X:= x;
       
   411     speed:= hwAbs(Gear^.dX);
       
   412     vs:= abs(hwFloat2Float(Gear^.dY));
       
   413     end;
       
   414 
       
   415 vi:= hwFloat2Float(speed);
       
   416 
       
   417 // splash sound
       
   418 
       
   419 if justSkipping then
       
   420     PlaySound(sndSkip)
       
   421 else
       
   422     begin
       
   423     // adjust water impact sound based on gear speed and density
       
   424     hwTmp:= hwAbs(Gear^.Density * speed);
       
   425 
       
   426     if hwTmp > _1 then
       
   427         PlaySound(sndSplash)
       
   428     else if hwTmp > _0_5 then
       
   429         PlaySound(sndSkip)
       
   430     else
       
   431         PlaySound(sndDroplet2);
       
   432     end;
       
   433 
       
   434 with splash^ do
       
   435     begin
       
   436     Scale:= abs(hwFloat2Float(Gear^.Density / _3 * speed));
       
   437     if Scale > 1 then Scale:= power(Scale,0.3333)
       
   438     else Scale:= Scale + ((1-Scale) / 2);
       
   439     if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
       
   440     else Timer:= 1;
       
   441     // Low Gravity
       
   442     FrameTicks:= FrameTicks*Timer;
       
   443     end;
       
   444 
       
   445 
       
   446 // eject water drops
       
   447 
       
   448 maxDrops := (hwRound(Gear^.Density) * 3) div 2 + round((vi + vs) * hwRound(Gear^.Density) * 6);
       
   449 for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
       
   450     begin
       
   451     if isImpactH then
       
   452         particle := AddVisualGear(x, y - 3 + Random(7), vgtDroplet)
       
   453     else
       
   454         particle := AddVisualGear(x - 3 + Random(7), y, vgtDroplet);
       
   455 
       
   456     if particle <> nil then
       
   457         with particle^ do
       
   458             begin
       
   459             // dX and dY were initialized to have a random value on creation (see uVisualGearsList)
       
   460             if isImpactH then
       
   461                 begin
       
   462                 tmp:= dX;
       
   463                 if isImpactRight then
       
   464                     dX:=  dY - vi / 5
       
   465                 else
       
   466                     dX:= -dy + vi / 5;
       
   467                 dY:= tmp * (1 + vs / 10);
       
   468                 end
       
   469             else
       
   470                 begin
       
   471                 dX:= dX * (1 + vs / 10);
       
   472                 dY:= dY - vi / 5;
       
   473                 end;
       
   474 
       
   475             if splash <> nil then
       
   476                 begin
       
   477                 if splash^.Scale > 1 then
       
   478                     begin
       
   479                     dX:= dX * power(splash^.Scale, 0.3333); // tone down the droplet height further
       
   480                     dY:= dY * power(splash^.Scale, 0.3333);
       
   481                     end
       
   482                 else
       
   483                     begin
       
   484                     dX:= dX * splash^.Scale;
       
   485                     dY:= dY * splash^.Scale;
       
   486                     end;
       
   487                 end;
       
   488             end
       
   489     end;
       
   490 
       
   491 end;
       
   492 
   360 procedure DrownGear(Gear: PGear);
   493 procedure DrownGear(Gear: PGear);
   361 begin
   494 begin
   362 Gear^.doStep := @doStepDrowningGear;
   495 Gear^.doStep := @doStepDrowningGear;
   363 
   496 
   364 Gear^.Timer := 5000; // how long game should wait
   497 Gear^.Timer := 5000; // how long game should wait
   365 end;
   498 end;
   366 
   499 
   367 function CheckGearDrowning(var Gear: PGear): boolean;
   500 function CheckGearDrowning(var Gear: PGear): boolean;
   368 var
   501 var
   369     skipSpeed, skipAngle, skipDecay, hwTmp: hwFloat;
   502     skipSpeed, skipAngle, skipDecay: hwFloat;
   370     i, maxDrops, X, Y, dist2Water: LongInt;
   503     tmp, X, Y, dist2Water: LongInt;
   371     vdX, vdY, tmp: real;
   504     isSubmersible, isDirH, isImpact, isSkip: boolean;
   372     particle, splash: PVisualGear;
       
   373     isSubmersible, isImpactH, isImpactRight, isLeaving: boolean;
       
   374     s: ansistring;
   505     s: ansistring;
   375 begin
   506 begin
   376     // probably needs tweaking. might need to be in a case statement based upon gear type
   507     // probably needs tweaking. might need to be in a case statement based upon gear type
   377     X:= hwRound(Gear^.X);
   508     X:= hwRound(Gear^.X);
   378     Y:= hwRound(Gear^.Y);
   509     Y:= hwRound(Gear^.Y);
   379 
   510 
   380     dist2Water:= cWaterLine - (Y + Gear^.Radius);
   511     dist2Water:= cWaterLine - (Y + Gear^.Radius);
   381     isImpactH:= false;
   512     isDirH:= false;
   382 
   513 
   383     if WorldEdge = weSea then
   514     if WorldEdge = weSea then
   384         begin
   515         begin
   385         i:= dist2Water;
   516         tmp:= dist2Water;
   386         dist2Water:= min(dist2Water, min(X - Gear^.Radius - leftX, rightX - (X + Gear^.Radius)));
   517         dist2Water:= min(dist2Water, min(X - Gear^.Radius - leftX, rightX - (X + Gear^.Radius)));
   387         isImpactH:= i <> dist2Water;
   518         // if water on sides is closer than on bottom -> horizontal direction
   388         end;
   519         isDirH:= tmp <> dist2Water;
       
   520         end;
       
   521 
       
   522     isImpact:= false;
   389 
   523 
   390     if dist2Water < 0 then
   524     if dist2Water < 0 then
   391         begin
   525         begin
   392         // invisible gears will just be deleted
   526         // invisible gears will just be deleted
   393         // unless they are generic fallers, then they will be "respawned"
   527         // unless they are generic fallers, then they will be "respawned"
   402                 end
   536                 end
   403             else DeleteGear(Gear);
   537             else DeleteGear(Gear);
   404             exit(true)
   538             exit(true)
   405             end;
   539             end;
   406         isSubmersible:= ((Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.State and gstSubmersible <> 0)) or (Gear^.State and gstSubmersible <> 0);
   540         isSubmersible:= ((Gear = CurrentHedgehog^.Gear) and (CurAmmoGear <> nil) and (CurAmmoGear^.State and gstSubmersible <> 0)) or (Gear^.State and gstSubmersible <> 0);
       
   541 
   407         skipSpeed := _0_25;
   542         skipSpeed := _0_25;
   408         skipAngle := _1_9;
   543         skipAngle := _1_9;
   409         skipDecay := _0_87;
   544         skipDecay := _0_87;
   410         vdX:= abs(hwFloat2Float(Gear^.dX));
   545 
   411         vdY:= abs(hwFloat2Float(Gear^.dY));
       
   412 
   546 
   413         // skipping
   547         // skipping
   414 
   548 
   415         // check for -1 depth because if deeper, then it already had its chance of skipping
   549         if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
   416         if  (dist2Water = -1) and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > skipSpeed)
   550         and ( ((not isDirH) and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)))
   417         and ( ((not isImpactH) and (hwAbs(Gear^.dX) > skipAngle * hwAbs(Gear^.dY)))
   551           or (isDirH and (hwAbs(Gear^.dY) > skipAngle * hwAbs(Gear^.dX))) ) then
   418           or (isImpactH and (hwAbs(Gear^.dY) > skipAngle * hwAbs(Gear^.dX))) ) then
       
   419             begin
   552             begin
       
   553             isSkip:= true;
   420             // if skipping we move the gear out of water
   554             // if skipping we move the gear out of water
   421             if isImpactH then
   555             if isDirH then
   422                 begin
   556                 begin
   423                 Gear^.dX.isNegative := (not Gear^.dX.isNegative);
   557                 Gear^.dX.isNegative := (not Gear^.dX.isNegative);
   424                 Gear^.X:= Gear^.X + Gear^.dX;
   558                 Gear^.X:= Gear^.X + Gear^.dX;
   425                 end
   559                 end
   426             else
   560             else
   429                 Gear^.Y:= Gear^.Y + Gear^.dY;
   563                 Gear^.Y:= Gear^.Y + Gear^.dY;
   430                 end;
   564                 end;
   431             Gear^.dY := Gear^.dY * skipDecay;
   565             Gear^.dY := Gear^.dY * skipDecay;
   432             Gear^.dX := Gear^.dX * skipDecay;
   566             Gear^.dX := Gear^.dX * skipDecay;
   433             CheckGearDrowning := false;
   567             CheckGearDrowning := false;
   434             PlaySound(sndSkip)
       
   435             end
   568             end
   436         else // not skipping
   569         else // not skipping
   437             begin
   570             begin
       
   571             isImpact:= true;
       
   572             isSkip:= false;
   438             if not isSubmersible then
   573             if not isSubmersible then
   439                 begin
   574                 begin
   440                 CheckGearDrowning := true;
   575                 CheckGearDrowning := true;
   441                 Gear^.State := gstDrowning;
   576                 Gear^.State := gstDrowning;
   442                 Gear^.RenderTimer := false;
   577                 Gear^.RenderTimer := false;
   458                             AddCaption(FormatA(GetEventString(eidDrowned), s), cWhiteColor, capgrpMessage);
   593                             AddCaption(FormatA(GetEventString(eidDrowned), s), cWhiteColor, capgrpMessage);
   459                             end
   594                             end
   460                         end
   595                         end
   461                     else
   596                     else
   462                         DrownGear(Gear);
   597                         DrownGear(Gear);
   463                     if Gear^.Kind = gtFlake then
   598                     if (dist2Water < -1) or (Gear^.Kind = gtFlake) then
   464                         exit(true); // skip splashes
   599                         exit(true); // skip splashes
   465                 end
   600                 end
   466             // drown submersible grears if far below map
   601             else // submersible
   467             else if (Y > cWaterLine + cVisibleWater*4) and
   602                 begin
   468                     ((Gear <> CurrentHedgehog^.Gear) or (CurAmmoGear = nil) or (CurAmmoGear^.State and gstSubmersible = 0)) then
   603                 // drown submersible grears if far below map
   469                 DrownGear(Gear);
   604                 if (Y > cWaterLine + cVisibleWater*4) then
   470 
   605                     begin
   471             isImpactRight:= isImpactH and (abs(X - LongInt(leftX)) > abs(LongInt(rightX) - X));
   606                     DrownGear(Gear);
   472             isLeaving:= (isSubmersible and (dist2Water = -2 * Gear^.Radius) and (Gear = CurAmmoGear) and (CurAmmoGear^.Pos = 0)
   607                     exit(true); // no splashes needed
   473             and (((not isImpactH) and CurAmmoGear^.dY.isNegative) or (isImpactH and (isImpactRight = CurAmmoGear^.dX.isNegative))));
   608                     end;
   474 
   609 
   475             // splash sound
   610                 CheckGearDrowning := false;
   476 
   611 
   477             if ((not isSubmersible) and (dist2Water = -1))
   612                 // check if surface was penetrated
   478             or isLeaving then
   613 
   479                 begin
   614                 // no penetration if center's water distance not smaller than radius
   480                 // adjust water impact sound on gear speed and density
   615                 if  abs(dist2Water + Gear^.Radius) >= Gear^.Radius then
   481                 if isImpactH then
   616                     isImpact:= false
   482                     hwTmp:= hwAbs(Gear^.Density * Gear^.dX)
       
   483                 else
   617                 else
   484                     hwTmp:= hwAbs(Gear^.Density * Gear^.dY);
       
   485 
       
   486                 if hwTmp > _1 then
       
   487                     PlaySound(sndSplash)
       
   488                 else if hwTmp > _0_5 then
       
   489                     PlaySound(sndSkip)
       
   490                 else
       
   491                     PlaySound(sndDroplet2);
       
   492                 end;
       
   493             end;
       
   494 
       
   495         // splash animation
       
   496 
       
   497         if ((cReducedQuality and rqPlainSplash) = 0)
       
   498         and (((not isSubmersible) and (dist2Water = -1))
       
   499         or isLeaving) then
       
   500             begin
       
   501             splash:= AddVisualGear(X, Y, vgtSplash);
       
   502             if splash <> nil then
       
   503                 begin
       
   504                 if isImpactH then
       
   505                     begin
   618                     begin
   506                     splash^.Scale:= abs(hwFloat2Float((Gear^.Density / _3) * Gear^.dX));
   619                     // get distance to water of last tick
   507                     if isImpactRight then
   620                     if isDirH then
   508                         splash^.Angle:= -90
   621                         begin
       
   622                         tmp:= hwRound(Gear^.X - Gear^.dX);
       
   623                         tmp:= abs(min(tmp - leftX, rightX - tmp));
       
   624                         end
   509                     else
   625                     else
   510                         splash^.Angle:=  90;
   626                         begin
   511                     end
   627                         tmp:= hwRound(Gear^.Y - Gear^.dY);
   512                 else
   628                         tmp:= abs(cWaterLine - tmp);
   513                     splash^.Scale:= abs(hwFloat2Float(Gear^.Density / _3 * Gear^.dY));
   629                         end;
   514                 with splash^ do
   630 
   515                     begin
   631                     // there was an impact if distance was same as radius
   516                     if Scale > 1 then Scale:= power(Scale,0.3333)
   632                     isImpact:= (tmp = Gear^.Radius)
   517                     else Scale:= Scale + ((1-Scale) / 2);
       
   518                     if Scale > 1 then Timer:= round(min(Scale*0.0005/cGravityf,4))
       
   519                     else Timer:= 1;
       
   520                     // Low Gravity
       
   521                     FrameTicks:= FrameTicks*Timer;
       
   522                     end;
   633                     end;
   523                 end;
   634                 end; // end of submersible
   524 
   635             end; // end of not skipping
   525             // eject water drops
   636 
   526 
   637         // splash sound animation and droplets
   527             maxDrops := (hwRound(Gear^.Density) * 3) div 2 + round(vdX * hwRound(Gear^.Density) * 6) + round(vdY * hwRound(Gear^.Density) * 6);
   638         if isImpact or isSkip then
   528             for i:= max(maxDrops div 3, min(32, Random(maxDrops))) downto 0 do
   639             addSplashForGear(Gear, isSkip);
   529                 begin
   640 
   530                 if isImpactH then
       
   531                     begin
       
   532                     if isImpactRight then
       
   533                         particle := AddVisualGear(RightX, Y - 3 + Random(7), vgtDroplet)
       
   534                     else
       
   535                         particle := AddVisualGear(LeftX, Y - 3 + Random(7), vgtDroplet)
       
   536                     end
       
   537                 else
       
   538                     particle := AddVisualGear(X - 3 + Random(7), cWaterLine, vgtDroplet);
       
   539 
       
   540                 if particle <> nil then
       
   541                     with particle^ do
       
   542                         begin
       
   543                         // dX and dY were initialized to have a random value on creation (see uVisualGearsList)
       
   544                         if isImpactH then
       
   545                             begin
       
   546                             tmp:= dX;
       
   547                             if isImpactRight then
       
   548                                 dX:=  dY - vdX / 5
       
   549                             else
       
   550                                 dX:= -dy + vdX / 5;
       
   551                             dY:= tmp * (1 + vdY / 10);
       
   552                             end
       
   553                         else
       
   554                             begin
       
   555                             dX:= dX * (1 + vdX / 10);
       
   556                             dY:= dY - vdY / 5;
       
   557                             end;
       
   558 
       
   559                         if splash <> nil then
       
   560                             begin
       
   561                             if splash^.Scale > 1 then
       
   562                                 begin
       
   563                                 dX:= dX * power(splash^.Scale,0.3333); // tone down the droplet height further
       
   564                                 dY:= dY * power(splash^.Scale, 0.3333)
       
   565                                 end
       
   566                             else
       
   567                                 begin
       
   568                                 dX:= dX * splash^.Scale;
       
   569                                 dY:= dY * splash^.Scale
       
   570                                 end
       
   571                             end;
       
   572                         end
       
   573                 end
       
   574             end;
       
   575         if isSubmersible and (Gear = CurAmmoGear) and (CurAmmoGear^.Pos = 0) then
       
   576             CurAmmoGear^.Pos := 1000
       
   577         end
   641         end
   578     else
   642     else
   579         begin
   643         begin
   580         if (not ((Gear^.Kind = gtJetpack) or (Gear^.Kind = gtBee))) then
   644         if (not ((Gear^.Kind = gtJetpack) or (Gear^.Kind = gtBee))) then
   581             Gear^.State:= (Gear^.State and (not gstSubmersible));  // making it temporary for most gears is more attractive I think
   645             Gear^.State:= (Gear^.State and (not gstSubmersible));  // making it temporary for most gears is more attractive I think