1 (* |
1 (* |
2 * Hedgewars, a free turn based strategy game |
2 * Hedgewars, a free turn based strategy game |
3 * Copyright (c) 2004-2013 Andrey Korotaev <unC0Rr@gmail.com> |
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com> |
4 * |
4 * |
5 * This program is free software; you can redistribute it and/or modify |
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 |
6 * it under the terms of the GNU General Public License as published by |
7 * the Free Software Foundation; version 2 of the License |
7 * the Free Software Foundation; version 2 of the License |
8 * |
8 * |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. |
12 * GNU General Public License for more details. |
13 * |
13 * |
14 * You should have received a copy of the GNU General Public License |
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 |
15 * along with this program; if not, write to the Free Software |
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
17 *) |
17 *) |
18 |
18 |
19 {$INCLUDE "options.inc"} |
19 {$INCLUDE "options.inc"} |
20 |
20 |
21 unit uTeams; |
21 unit uTeams; |
22 interface |
22 interface |
23 uses uConsts, uInputHandler, uRandom, uFloat, uStats, |
23 uses uConsts, uInputHandler, uRandom, uFloat, uStats, |
24 uCollisions, uSound, uStore, uTypes, uScript |
24 uCollisions, uSound, uStore, uTypes, uScript |
25 {$IFDEF USE_TOUCH_INTERFACE}, uWorld{$ENDIF}; |
25 {$IFDEF USE_TOUCH_INTERFACE}, uWorld{$ENDIF}; |
26 |
26 |
27 |
27 |
28 procedure initModule; |
28 procedure initModule; |
32 procedure SwitchHedgehog; |
32 procedure SwitchHedgehog; |
33 procedure AfterSwitchHedgehog; |
33 procedure AfterSwitchHedgehog; |
34 procedure InitTeams; |
34 procedure InitTeams; |
35 function TeamSize(p: PTeam): Longword; |
35 function TeamSize(p: PTeam): Longword; |
36 procedure RecountTeamHealth(team: PTeam); |
36 procedure RecountTeamHealth(team: PTeam); |
|
37 procedure RecountAllTeamsHealth(); |
37 procedure RestoreHog(HH: PHedgehog); |
38 procedure RestoreHog(HH: PHedgehog); |
38 |
39 |
39 procedure RestoreTeamsFromSave; |
40 procedure RestoreTeamsFromSave; |
40 function CheckForWin: boolean; |
41 function CheckForWin: boolean; |
41 procedure TeamGoneEffect(var Team: TTeam); |
42 procedure TeamGoneEffect(var Team: TTeam); |
79 if not GameOver then |
80 if not GameOver then |
80 begin |
81 begin |
81 if AliveCount = 0 then |
82 if AliveCount = 0 then |
82 begin // draw |
83 begin // draw |
83 AddCaption(trmsg[sidDraw], cWhiteColor, capgrpGameState); |
84 AddCaption(trmsg[sidDraw], cWhiteColor, capgrpGameState); |
84 SendStat(siGameResult, trmsg[sidDraw]); |
85 SendStat(siGameResult, shortstring(trmsg[sidDraw])); |
85 AddGear(0, 0, gtATFinishGame, 0, _0, _0, 3000) |
86 AddGear(0, 0, gtATFinishGame, 0, _0, _0, 3000) |
86 end |
87 end |
87 else // win |
88 else // win |
88 with AliveClan^ do |
89 with AliveClan^ do |
89 begin |
90 begin |
|
91 ts:= ansistring(Teams[0]^.TeamName); |
90 if TeamsNumber = 1 then |
92 if TeamsNumber = 1 then |
91 s:= Format(shortstring(trmsg[sidWinner]), Teams[0]^.TeamName) // team wins |
93 s:= FormatA(trmsg[sidWinner], ts) // team wins |
92 else |
94 else |
93 s:= Format(shortstring(trmsg[sidWinner]), Teams[0]^.TeamName); // clan wins |
95 s:= FormatA(trmsg[sidWinner], ts); // clan wins |
94 |
96 |
95 for j:= 0 to Pred(TeamsNumber) do |
97 for j:= 0 to Pred(TeamsNumber) do |
96 with Teams[j]^ do |
98 with Teams[j]^ do |
97 for i:= 0 to cMaxHHIndex do |
99 for i:= 0 to cMaxHHIndex do |
98 with Hedgehogs[i] do |
100 with Hedgehogs[i] do |
102 AddVoice(sndFlawless, Teams[0]^.voicepack) |
104 AddVoice(sndFlawless, Teams[0]^.voicepack) |
103 else |
105 else |
104 AddVoice(sndVictory, Teams[0]^.voicepack); |
106 AddVoice(sndVictory, Teams[0]^.voicepack); |
105 |
107 |
106 AddCaption(s, cWhiteColor, capgrpGameState); |
108 AddCaption(s, cWhiteColor, capgrpGameState); |
107 SendStat(siGameResult, s); |
109 SendStat(siGameResult, shortstring(s)); |
108 AddGear(0, 0, gtATFinishGame, 0, _0, _0, 3000) |
110 AddGear(0, 0, gtATFinishGame, 0, _0, _0, 3000) |
109 end; |
111 end; |
110 SendStats; |
112 SendStats; |
111 end; |
113 end; |
112 GameOver:= true |
114 GameOver:= true |
332 GetRandom(2); // needed to avoid extdriven desync |
334 GetRandom(2); // needed to avoid extdriven desync |
333 AddVoice(sndYesSir, CurrentTeam^.voicepack); |
335 AddVoice(sndYesSir, CurrentTeam^.voicepack); |
334 end; |
336 end; |
335 if cHedgehogTurnTime < 1000000 then |
337 if cHedgehogTurnTime < 1000000 then |
336 ReadyTimeLeft:= cReadyDelay; |
338 ReadyTimeLeft:= cReadyDelay; |
337 AddCaption(Format(shortstring(trmsg[sidReady]), CurrentTeam^.TeamName), cWhiteColor, capgrpGameState) |
339 s:= ansistring(CurrentTeam^.TeamName); |
|
340 AddCaption(FormatA(trmsg[sidReady], s), cWhiteColor, capgrpGameState) |
338 end |
341 end |
339 else |
342 else |
340 begin |
343 begin |
341 if TurnTimeLeft > 0 then |
344 if TurnTimeLeft > 0 then |
342 begin |
345 begin |
512 end; |
515 end; |
513 |
516 |
514 procedure TeamGoneEffect(var Team: TTeam); |
517 procedure TeamGoneEffect(var Team: TTeam); |
515 var i: LongInt; |
518 var i: LongInt; |
516 begin |
519 begin |
517 with Team do |
520 with Team do |
518 for i:= 0 to cMaxHHIndex do |
521 if skippedTurns < 3 then |
519 with Hedgehogs[i] do |
|
520 begin |
522 begin |
521 if Hedgehogs[i].GearHidden <> nil then |
523 inc(skippedTurns); |
522 RestoreHog(@Hedgehogs[i]); |
524 for i:= 0 to cMaxHHIndex do |
523 |
525 with Hedgehogs[i] do |
524 if Gear <> nil then |
526 if Gear <> nil then |
525 begin |
527 Gear^.State:= Gear^.State and (not gstHHDriven); |
526 Gear^.Hedgehog^.Effects[heInvulnerable]:= 0; |
528 |
527 Gear^.Damage:= Gear^.Health; |
529 ParseCommand('/skip', true); |
528 Gear^.State:= (Gear^.State or gstHHGone) and (not gstHHDriven) |
|
529 end |
|
530 end |
530 end |
|
531 else |
|
532 for i:= 0 to cMaxHHIndex do |
|
533 with Hedgehogs[i] do |
|
534 begin |
|
535 if Hedgehogs[i].GearHidden <> nil then |
|
536 RestoreHog(@Hedgehogs[i]); |
|
537 |
|
538 if Gear <> nil then |
|
539 begin |
|
540 Gear^.Hedgehog^.Effects[heInvulnerable]:= 0; |
|
541 Gear^.Damage:= Gear^.Health; |
|
542 Gear^.State:= (Gear^.State or gstHHGone) and (not gstHHDriven) |
|
543 end |
|
544 end |
531 end; |
545 end; |
532 |
546 |
533 procedure chAddHH(var id: shortstring); |
547 procedure chAddHH(var id: shortstring); |
534 var s: shortstring; |
548 var s: shortstring; |
535 Gear: PGear; |
549 Gear: PGear; |
536 begin |
550 begin |
537 s:= ''; |
551 s:= ''; |
538 if (not isDeveloperMode) or (CurrentTeam = nil) then |
552 if (not isDeveloperMode) then |
539 exit; |
553 exit; |
|
554 TryDo((CurrentTeam <> nil), 'Can''t add hedgehogs yet, add a team first!', true); |
540 with CurrentTeam^ do |
555 with CurrentTeam^ do |
541 begin |
556 begin |
|
557 TryDo(HedgehogsNumber<=cMaxHHIndex, 'Can''t add hedgehog to "' + TeamName + '"! (already ' + intToStr(HedgehogsNumber) + ' hogs)', true); |
542 SplitBySpace(id, s); |
558 SplitBySpace(id, s); |
543 SwitchCurrentHedgehog(@Hedgehogs[HedgehogsNumber]); |
559 SwitchCurrentHedgehog(@Hedgehogs[HedgehogsNumber]); |
544 CurrentHedgehog^.BotLevel:= StrToInt(id); |
560 CurrentHedgehog^.BotLevel:= StrToInt(id); |
545 CurrentHedgehog^.Team:= CurrentTeam; |
561 CurrentHedgehog^.Team:= CurrentTeam; |
546 Gear:= AddGear(0, 0, gtHedgehog, 0, _0, _0, 0); |
562 Gear:= AddGear(0, 0, gtHedgehog, 0, _0, _0, 0); |
583 if isDeveloperMode then |
602 if isDeveloperMode then |
584 begin |
603 begin |
585 SplitBySpace(s, cs); |
604 SplitBySpace(s, cs); |
586 SplitBySpace(cs, ts); |
605 SplitBySpace(cs, ts); |
587 Color:= StrToInt(cs); |
606 Color:= StrToInt(cs); |
588 TryDo(Color <> 0, 'Error: black team color', true); |
|
589 |
607 |
590 // color is always little endian so the mask must be constant also in big endian archs |
608 // color is always little endian so the mask must be constant also in big endian archs |
591 Color:= Color or $FF000000; |
609 Color:= Color or $FF000000; |
592 AddTeam(Color); |
610 AddTeam(Color); |
593 CurrentTeam^.TeamName:= ts; |
611 CurrentTeam^.TeamName:= ts; |
594 CurrentTeam^.PlayerHash:= s; |
612 CurrentTeam^.PlayerHash:= s; |
595 loadTeamBinds(ts); |
613 loadTeamBinds(ts); |
596 |
614 |
597 if GameType in [gmtDemo, gmtSave, gmtRecord] then |
615 if GameType in [gmtDemo, gmtSave, gmtRecord] then |
598 CurrentTeam^.ExtDriven:= true; |
616 CurrentTeam^.ExtDriven:= true; |
599 |
617 |
600 CurrentTeam^.voicepack:= AskForVoicepack('Default') |
618 CurrentTeam^.voicepack:= AskForVoicepack('Default') |
601 end |
619 end |
622 |
640 |
623 addBind(CurrentTeam^.Binds, id) |
641 addBind(CurrentTeam^.Binds, id) |
624 end; |
642 end; |
625 |
643 |
626 procedure chTeamGone(var s:shortstring); |
644 procedure chTeamGone(var s:shortstring); |
|
645 var t, i: LongInt; |
|
646 isSynced: boolean; |
|
647 begin |
|
648 isSynced:= s[1] = 's'; |
|
649 |
|
650 Delete(s, 1, 1); |
|
651 |
|
652 t:= 0; |
|
653 while (t < TeamsCount) and (TeamsArray[t]^.TeamName <> s) do |
|
654 inc(t); |
|
655 if t = TeamsCount then |
|
656 exit; |
|
657 |
|
658 TeamsArray[t]^.isGoneFlagPendingToBeSet:= true; |
|
659 |
|
660 if isSynced then |
|
661 begin |
|
662 for i:= 0 to Pred(TeamsCount) do |
|
663 with TeamsArray[i]^ do |
|
664 begin |
|
665 if (not hasGone) and isGoneFlagPendingToBeSet then |
|
666 begin |
|
667 AddChatString(#7 + '* '+ TeamName + ' is gone'); // TODO: localize |
|
668 if not CurrentTeam^.ExtDriven then SendIPC(_S'f' + s); |
|
669 hasGone:= true; |
|
670 skippedTurns:= 0; |
|
671 isGoneFlagPendingToBeSet:= false; |
|
672 RecountTeamHealth(TeamsArray[i]) |
|
673 end; |
|
674 if hasGone and isGoneFlagPendingToBeUnset then |
|
675 ParseCommand('/teamback s' + s, true) |
|
676 end |
|
677 end |
|
678 else |
|
679 begin |
|
680 //TeamsArray[t]^.isGoneFlagPendingToBeSet:= true; |
|
681 |
|
682 if (not CurrentTeam^.ExtDriven) or (CurrentTeam^.TeamName = s) or (CurrentTeam^.hasGone) then |
|
683 ParseCommand('/teamgone s' + s, true) |
|
684 end; |
|
685 end; |
|
686 |
|
687 procedure chTeamBack(var s:shortstring); |
627 var t: LongInt; |
688 var t: LongInt; |
628 begin |
689 isSynced: boolean; |
629 t:= 0; |
690 begin |
630 while (t < cMaxTeams) and (TeamsArray[t] <> nil) and (TeamsArray[t]^.TeamName <> s) do |
691 isSynced:= s[1] = 's'; |
631 inc(t); |
692 |
632 if (t = cMaxTeams) or (TeamsArray[t] = nil) then |
693 Delete(s, 1, 1); |
633 exit; |
694 |
634 |
695 t:= 0; |
635 with TeamsArray[t]^ do |
696 while (t < TeamsCount) and (TeamsArray[t]^.TeamName <> s) do |
636 if not hasGone then |
697 inc(t); |
637 begin |
698 if t = TeamsCount then |
638 AddChatString('** '+ TeamName + ' is gone'); |
699 exit; |
639 hasGone:= true; |
700 |
640 |
701 if isSynced then |
641 RecountTeamHealth(TeamsArray[t]) |
702 begin |
|
703 with TeamsArray[t]^ do |
|
704 if hasGone then |
|
705 begin |
|
706 AddChatString(#8 + '* '+ TeamName + ' is back'); |
|
707 if not CurrentTeam^.ExtDriven then SendIPC(_S'g' + s); |
|
708 hasGone:= false; |
|
709 |
|
710 RecountTeamHealth(TeamsArray[t]); |
|
711 |
|
712 if isGoneFlagPendingToBeUnset and (Owner = UserNick) then |
|
713 ExtDriven:= false; |
|
714 |
|
715 isGoneFlagPendingToBeUnset:= false; |
|
716 end; |
|
717 end |
|
718 else |
|
719 begin |
|
720 TeamsArray[t]^.isGoneFlagPendingToBeUnset:= true; |
|
721 |
|
722 if not CurrentTeam^.ExtDriven then |
|
723 ParseCommand('/teamback s' + s, true); |
642 end; |
724 end; |
643 end; |
725 end; |
644 |
726 |
645 |
727 |
646 procedure chFinish(var s:shortstring); |
728 procedure chFinish(var s:shortstring); |
647 var t: LongInt; |
729 var t: LongInt; |
648 begin |
730 begin |
649 // avoid compiler hint |
731 // avoid compiler hint |
650 s:= s; |
732 s:= s; |
651 |
733 |
|
734 isPaused:= false; |
|
735 |
652 t:= 0; |
736 t:= 0; |
653 while (t < cMaxTeams) and (TeamsArray[t] <> nil) do |
737 while t < TeamsCount do |
654 begin |
738 begin |
655 TeamsArray[t]^.hasGone:= true; |
739 TeamsArray[t]^.hasGone:= true; |
656 inc(t); |
740 inc(t) |
657 end; |
741 end; |
658 |
742 |
659 AddChatString('** Good-bye!'); |
743 AddChatString(#7 + '* Good-bye!'); |
660 RecountAllTeamsHealth(); |
744 RecountAllTeamsHealth(); |
661 end; |
745 end; |
662 |
746 |
663 procedure SwitchCurrentHedgehog(newHog: PHedgehog); |
747 procedure SwitchCurrentHedgehog(newHog: PHedgehog); |
664 var oldCI, newCI: boolean; |
748 var oldCI, newCI: boolean; |
670 newCI:= (newHog^.Gear <> nil) and (newHog^.Gear^.CollisionIndex >= 0); |
754 newCI:= (newHog^.Gear <> nil) and (newHog^.Gear^.CollisionIndex >= 0); |
671 if oldCI then DeleteCI(CurrentHedgehog^.Gear); |
755 if oldCI then DeleteCI(CurrentHedgehog^.Gear); |
672 if newCI then DeleteCI(newHog^.Gear); |
756 if newCI then DeleteCI(newHog^.Gear); |
673 oldHH:= CurrentHedgehog; |
757 oldHH:= CurrentHedgehog; |
674 CurrentHedgehog:= newHog; |
758 CurrentHedgehog:= newHog; |
675 if (CurrentHedgehog <> nil) and (CurrentHedgehog^.CurAmmoType = amKnife) then |
|
676 LoadHedgehogHat(CurrentHedgehog^, 'Reserved/chef'); |
|
677 if oldCI then AddCI(oldHH^.Gear); |
759 if oldCI then AddCI(oldHH^.Gear); |
678 if newCI then AddCI(newHog^.Gear) |
760 if newCI then AddCI(newHog^.Gear) |
679 end; |
761 end; |
680 |
762 |
681 |
763 |
740 RegisterVariable('addhh', @chAddHH, false); |
822 RegisterVariable('addhh', @chAddHH, false); |
741 RegisterVariable('addteam', @chAddTeam, false); |
823 RegisterVariable('addteam', @chAddTeam, false); |
742 RegisterVariable('hhcoords', @chSetHHCoords, false); |
824 RegisterVariable('hhcoords', @chSetHHCoords, false); |
743 RegisterVariable('bind', @chBind, true ); |
825 RegisterVariable('bind', @chBind, true ); |
744 RegisterVariable('teamgone', @chTeamGone, true ); |
826 RegisterVariable('teamgone', @chTeamGone, true ); |
|
827 RegisterVariable('teamback', @chTeamBack, true ); |
745 RegisterVariable('finish', @chFinish, true ); // all teams gone |
828 RegisterVariable('finish', @chFinish, true ); // all teams gone |
746 RegisterVariable('fort' , @chFort , false); |
829 RegisterVariable('fort' , @chFort , false); |
747 RegisterVariable('grave' , @chGrave , false); |
830 RegisterVariable('grave' , @chGrave , false); |
748 RegisterVariable('hat' , @chSetHat , false); |
831 RegisterVariable('hat' , @chSetHat , false); |
749 RegisterVariable('flag' , @chFlag , false); |
832 RegisterVariable('flag' , @chFlag , false); |
763 end; |
846 end; |
764 |
847 |
765 procedure freeModule; |
848 procedure freeModule; |
766 var i, h: LongWord; |
849 var i, h: LongWord; |
767 begin |
850 begin |
|
851 CurrentHedgehog:= nil; |
768 if TeamsCount > 0 then |
852 if TeamsCount > 0 then |
769 begin |
853 begin |
770 for i:= 0 to Pred(TeamsCount) do |
854 for i:= 0 to Pred(TeamsCount) do |
771 begin |
855 begin |
772 for h:= 0 to cMaxHHIndex do |
856 for h:= 0 to cMaxHHIndex do |
773 with TeamsArray[i]^.Hedgehogs[h] do |
857 with TeamsArray[i]^.Hedgehogs[h] do |
774 begin |
858 begin |
|
859 // if Gear <> nil then |
|
860 // DeleteGearStage(Gear, true); |
775 if GearHidden <> nil then |
861 if GearHidden <> nil then |
776 Dispose(GearHidden); |
862 Dispose(GearHidden); |
777 |
863 // DeleteGearStage(GearHidden, true); |
778 FreeTexture(NameTagTex); |
864 |
779 FreeTexture(HealthTagTex); |
865 FreeAndNilTexture(NameTagTex); |
780 FreeTexture(HatTex); |
866 FreeAndNilTexture(HealthTagTex); |
|
867 FreeAndNilTexture(HatTex) |
781 end; |
868 end; |
782 |
869 |
783 with TeamsArray[i]^ do |
870 with TeamsArray[i]^ do |
784 begin |
871 begin |
785 FreeTexture(NameTagTex); |
872 FreeAndNilTexture(NameTagTex); |
786 FreeTexture(GraveTex); |
873 FreeAndNilTexture(OwnerTex); |
787 FreeTexture(AIKillsTex); |
874 FreeAndNilTexture(GraveTex); |
788 FreeTexture(FlagTex); |
875 FreeAndNilTexture(AIKillsTex); |
|
876 FreeAndNilTexture(FlagTex); |
789 end; |
877 end; |
790 |
878 |
791 Dispose(TeamsArray[i]); |
879 Dispose(TeamsArray[i]) |
792 end; |
880 end; |
793 for i:= 0 to Pred(ClansCount) do |
881 for i:= 0 to Pred(ClansCount) do |
794 begin |
882 begin |
795 FreeTexture(ClansArray[i]^.HealthTex); |
883 FreeAndNilTexture(ClansArray[i]^.HealthTex); |
796 Dispose(ClansArray[i]); |
884 Dispose(ClansArray[i]) |
797 end |
885 end |
798 end; |
886 end; |
799 TeamsCount:= 0; |
887 TeamsCount:= 0; |
800 ClansCount:= 0; |
888 ClansCount:= 0; |
801 end; |
889 end; |