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" |
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 |