diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0719acb --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Created by .ignore support plugin (hsz.mobi) +.idea +bin +data +doc +*.log +*.log.* +milestones +reports +SafariBowl.iml \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..cc9a523 --- /dev/null +++ b/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SafariBowl]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build_client.xml b/build_client.xml new file mode 100644 index 0000000..98e4bef --- /dev/null +++ b/build_client.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SafariBowl]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build_server.xml b/build_server.xml new file mode 100644 index 0000000..578cdcd --- /dev/null +++ b/build_server.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SafariBowl]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar new file mode 100644 index 0000000..9d5fe16 Binary files /dev/null and b/lib/hamcrest-core-1.3.jar differ diff --git a/lib/junit-javadoc.jar b/lib/junit-javadoc.jar new file mode 100644 index 0000000..f7bdb82 Binary files /dev/null and b/lib/junit-javadoc.jar differ diff --git a/lib/junit.jar b/lib/junit.jar new file mode 100644 index 0000000..3a7fc26 Binary files /dev/null and b/lib/junit.jar differ diff --git a/lib/vecmath.jar b/lib/vecmath.jar new file mode 100755 index 0000000..88f649a Binary files /dev/null and b/lib/vecmath.jar differ diff --git a/resources/images/background.png b/resources/images/background.png new file mode 100644 index 0000000..eea790e Binary files /dev/null and b/resources/images/background.png differ diff --git a/resources/images/band_aid.png b/resources/images/band_aid.png new file mode 100644 index 0000000..11515f9 Binary files /dev/null and b/resources/images/band_aid.png differ diff --git a/resources/images/band_aid_double.png b/resources/images/band_aid_double.png new file mode 100644 index 0000000..9e8f687 Binary files /dev/null and b/resources/images/band_aid_double.png differ diff --git a/resources/images/defaultL.png b/resources/images/defaultL.png new file mode 100644 index 0000000..e22b38b Binary files /dev/null and b/resources/images/defaultL.png differ diff --git a/resources/images/defaultR.png b/resources/images/defaultR.png new file mode 100644 index 0000000..cdfb5af Binary files /dev/null and b/resources/images/defaultR.png differ diff --git a/resources/images/dice/die_attacker_down.png b/resources/images/dice/die_attacker_down.png new file mode 100644 index 0000000..ece949a Binary files /dev/null and b/resources/images/dice/die_attacker_down.png differ diff --git a/resources/images/dice/die_both_down.png b/resources/images/dice/die_both_down.png new file mode 100644 index 0000000..69ffa6e Binary files /dev/null and b/resources/images/dice/die_both_down.png differ diff --git a/resources/images/dice/die_defender_down.png b/resources/images/dice/die_defender_down.png new file mode 100644 index 0000000..964e967 Binary files /dev/null and b/resources/images/dice/die_defender_down.png differ diff --git a/resources/images/dice/die_defender_stumbles.png b/resources/images/dice/die_defender_stumbles.png new file mode 100644 index 0000000..ce74014 Binary files /dev/null and b/resources/images/dice/die_defender_stumbles.png differ diff --git a/resources/images/dice/die_pushed.png b/resources/images/dice/die_pushed.png new file mode 100644 index 0000000..1a61802 Binary files /dev/null and b/resources/images/dice/die_pushed.png differ diff --git a/resources/images/football.png b/resources/images/football.png new file mode 100644 index 0000000..59cd972 Binary files /dev/null and b/resources/images/football.png differ diff --git a/resources/images/logo-100x68.png b/resources/images/logo-100x68.png new file mode 100644 index 0000000..2569071 Binary files /dev/null and b/resources/images/logo-100x68.png differ diff --git a/resources/images/logo-500x341.png b/resources/images/logo-500x341.png new file mode 100644 index 0000000..e69de29 diff --git a/resources/images/moderators/turtle.png b/resources/images/moderators/turtle.png new file mode 100644 index 0000000..b8f5ee8 Binary files /dev/null and b/resources/images/moderators/turtle.png differ diff --git a/resources/images/moderators/walrus.png b/resources/images/moderators/walrus.png new file mode 100644 index 0000000..fdeca61 Binary files /dev/null and b/resources/images/moderators/walrus.png differ diff --git a/resources/images/red_card.png b/resources/images/red_card.png new file mode 100644 index 0000000..92e67a6 Binary files /dev/null and b/resources/images/red_card.png differ diff --git a/resources/images/remove_player.png b/resources/images/remove_player.png new file mode 100644 index 0000000..8d93e82 Binary files /dev/null and b/resources/images/remove_player.png differ diff --git a/resources/images/rhino-100x78-rotated.png b/resources/images/rhino-100x78-rotated.png new file mode 100644 index 0000000..8ebd9e9 Binary files /dev/null and b/resources/images/rhino-100x78-rotated.png differ diff --git a/resources/images/rhino-100x78.png b/resources/images/rhino-100x78.png new file mode 100644 index 0000000..2be9f7e Binary files /dev/null and b/resources/images/rhino-100x78.png differ diff --git a/resources/images/rhino-250x195.png b/resources/images/rhino-250x195.png new file mode 100644 index 0000000..46c1581 Binary files /dev/null and b/resources/images/rhino-250x195.png differ diff --git a/resources/images/stunned_star.png b/resources/images/stunned_star.png new file mode 100644 index 0000000..f9f9c37 Binary files /dev/null and b/resources/images/stunned_star.png differ diff --git a/resources/teams/Apes/BlondCapuchin.js b/resources/teams/Apes/BlondCapuchin.js new file mode 100644 index 0000000..e1d1bb0 --- /dev/null +++ b/resources/teams/Apes/BlondCapuchin.js @@ -0,0 +1,146 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Throwable.", + "Very small and easily hurt.", + "Just pushed on 'defender stumbles'.", + "Very good at passing through tackle", + "zones, ignores helping tackle zones", + "of other enemies, when dodging." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var alreadyDodgedKey = "hasAlreadyDodged"; + var throwableKey = "throwable"; + + function setPrice(player, price) { + return 40000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 16; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(alreadyDodgedKey, "false"); + player.addSpecialStat(throwableKey, "true"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockDodgeStunty = Java.extend(RuleBlock); + var ruleBlockDodgeStunty = new RuleBlockDodgeStunty(rulesArray[1].getActor()) { + beingBlockedDefenderStumbles: function(message, attacker, firstThrowModifier, injuryRollModifier){ + var _super_ = Java.super(ruleBlockDodgeStunty); + _super_.beingBlockedPushed(message, attacker, player.getPosition()); + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockDodgeStunty; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveDodgeStunty = Java.extend(RuleMove); + var ruleMoveDodgeStunty = new RuleMoveDodgeStunty(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveDodgeStunty); + if(!(_super_.stayFine(0))){ + if(player.getSpecialStat(alreadyDodgedKey) == "false"){ + player.setSpecialStat(alreadyDodgedKey, "true"); + if(!(_super_.stayFine(0))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }else{ + return _super_.beingTackled(message, i, path); + } + }else{ + return true; + } + } + }; + rulesArray[0] = ruleMoveDodgeStunty; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStunty = Java.extend(RuleThrow); + var ruleThrowStunty = new RuleThrowStunty(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStunty); + _super_.findThrowModificator(problems - 1, distance); + } + }; + rulesArray[3] = ruleThrowStunty; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/BlondCapuchinL.png b/resources/teams/Apes/BlondCapuchinL.png new file mode 100644 index 0000000..bc045c1 Binary files /dev/null and b/resources/teams/Apes/BlondCapuchinL.png differ diff --git a/resources/teams/Apes/BlondCapuchinR.png b/resources/teams/Apes/BlondCapuchinR.png new file mode 100644 index 0000000..8ed0ae8 Binary files /dev/null and b/resources/teams/Apes/BlondCapuchinR.png differ diff --git a/resources/teams/Apes/Chimpanzee.js b/resources/teams/Apes/Chimpanzee.js new file mode 100644 index 0000000..63b48af --- /dev/null +++ b/resources/teams/Apes/Chimpanzee.js @@ -0,0 +1,271 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Cheater.", + "Cannot pick up the ball.", + "Throws coconuts at his enemies.", + "Just pushed on 'defender stumbles'.", + "Very good at passing through tackle", + "zones, ignores helping tackle zones", + "of other enemies, when dodging." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var cheatedKey = "cheated"; + var alreadyDodgedKey = "hasAlreadyDodged"; + var noHandsString = "Sorry, but I have no hands left!"; + var API_AIM_FUNCTION_NAME = "apiAim"; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + function apiAim(player, destX, destY, userIndex){ + var Math = Java.type("java.lang.Math"); + var distance = Math.sqrt((player.getPos().x - destX)*(player.getPos().x - destX) + (player.getPos().y - destY)*(player.getPos().y - destY)); + var problems = -(player.getMatch().getOpposingTeam(player.getTeam()).getTacklezones(player.getPos())); + var mod = player.getRule(3).findThrowModificator(problems, distance); + if(player.getRule(3).geTest(mod)){ + coconutHail(player, destX, destY); + }else{ + notWellThrownCoconut(player, destX, destY); + } + player.invokeSetRemainingBe(0); + player.getMatch().setGamePhase(3); + } + function notWellThrownCoconut(player, destX, destY){ + var Vector2d = Java.type("javax.vecmath.Vector2d"); + var destPos = new Vector2d(destX, destY); + for(var i = 0; i < 3; i++){ + destPos.add(player.getMatch().scatter()); + } + coconutHail(player, destPos.x, destPos.y); + } + function coconutHail(player, destX, destY){ + var PitchField = Java.type("gameLogic.PitchField"); + playerDownCoconut(player, destX, destY); + var neighboursArray = Java.from(player.getMatch().getPitch().getNeighbours(destX, destY)); + for(var i = 0; i < neighboursArray.length; i++){ + if(player.getMatch().d6.throwDie() > 3){ + playerDownCoconut(player, neighboursArray[i].getPos().x, neighboursArray[i].getPos().y); + } + } + } + function playerDownCoconut(player, destX, destY){ + if(player.getMatch().getPitch().getFields()[destX][destY].getPlayer() != null){ + var defender = player.getMatch().getPitch().getFields()[destX][destY].getPlayer(); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + defenderCondition = defender.invokeGetPlayerCondition(); + defenderPosition = defender.getPosition(); + defender.getRule(1).playerDown(message, defender, ENEMY_DOWN); + if(defenderCondition == PlayerCondition.STUNNED && defender.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + defender.invokeSetPlayerCondition(PlayerCondition.STUNNED); + } + } + } + + function setPrice(player, price) { + return 40000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(alreadyDodgedKey, "false"); + player.addSpecialStat(cheatedKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockDodgeStunty = Java.extend(RuleBlock); + var ruleBlockDodgeStunty = new RuleBlockDodgeStunty(rulesArray[1].getActor()) { + beingBlockedDefenderStumbles: function(message, attacker, firstThrowModifier, injuryRollModifier){ + var _super_ = Java.super(ruleBlockDodgeStunty); + _super_.beingBlockedPushed(message, attacker, player.getPosition()); + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockDodgeStunty; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveDodgeStuntyNoHandsCheater = Java.extend(RuleMove); + var ruleMoveDodgeStuntyNoHandsCheater = new RuleMoveDodgeStuntyNoHandsCheater(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveDodgeStuntyNoHandsCheater); + if(!(_super_.stayFine(problems))){ + if(player.getSpecialStat(alreadyDodgedKey) == "false"){ + player.setSpecialStat(alreadyDodgedKey, "true"); + if(!(_super_.stayFine(problems))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }else{ + return _super_.beingTackled(message, i, path); + } + }else{ + return true; + } + }, + tryToPickUpBall: function(message, i, path){ + var _super_ = Java.super(ruleMoveDodgeStuntyNoHandsCheater); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + return _super_.faildToPickUpBall(message); + } + }; + rulesArray[0] = ruleMoveDodgeStuntyNoHandsCheater; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStuntyNoHands = Java.extend(RuleThrow); + var ruleThrowStuntyNoHands = new RuleThrowStuntyNoHands(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.findThrowModificator(problems - 1, distance); + }, + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + } + }; + rulesArray[3] = ruleThrowStuntyNoHands; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchNoHands = Java.extend(RuleCatch); + var ruleCatchNoHands = new RuleCatchNoHands(rulesArray[4].getActor()){ + apply: function(successfulThrow){ + var actingUserIndex; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + var _super_ = Java.super(ruleCatchNoHands); + _super_.scatterBallAround(actingUserIndex); + }, + giveBall: function(message){ + var _super_ = Java.super(ruleCatchNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE); + } + }; + rulesArray[4] = ruleCatchNoHands; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_SETUP_YOUR_TEAM){ + if(player.getSpecialStat(cheatedKey).equals("true")){ + player.invokeSetRedCard(true); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) { + var SpecialRule = Java.type("gameLogic.rules.SpecialRule"); + var specialRulesArray = []; + + var SpecialRuleCoconutHail = Java.extend(SpecialRule); + var specialRuleCoconutHail = new SpecialRuleCoconutHail(player, "Hail Coconut") { + apply: function(message){ + var _super_ = Java.super(specialRuleCoconutHail); + var _super_super_ = Java.super(_super_); + _super_.checkForMovingPlayer(player); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetPlayerCondition() != PlayerCondition.FINE){ + _super_.returnFailureMessage(message, FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + } + if(player.invokeGetRemainingBe() == player.invokeGetBe()){ + player.setSpecialStat(cheatedKey, "true"); + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_AIM; + parameterArray[1] = API_AIM_FUNCTION_NAME; + parameterArray[2] = player.getId() - 1; + parameterArray[3] = player.getRule(3).getLONG_BOMB(); + player.getMatch().setGamePhase(5); + player.getMatch().setCurrentActorWaitingForAnswer(player); + _super_.sendMessage(message, SBProtocolCommand.EVENT, parameterArray); + }else{ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "Ask again next turn!"); + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + } + } + }; + specialRulesArray[0] = specialRuleCoconutHail; + + var specialRulesToReturn = Java.to(specialRulesArray, Java.type("gameLogic.rules.SpecialRule[]")); + return specialRulesToReturn; + } + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/ChimpanzeeL.png b/resources/teams/Apes/ChimpanzeeL.png new file mode 100644 index 0000000..e84f511 Binary files /dev/null and b/resources/teams/Apes/ChimpanzeeL.png differ diff --git a/resources/teams/Apes/ChimpanzeeR.png b/resources/teams/Apes/ChimpanzeeR.png new file mode 100644 index 0000000..de18f03 Binary files /dev/null and b/resources/teams/Apes/ChimpanzeeR.png differ diff --git a/resources/teams/Apes/Gorilla.js b/resources/teams/Apes/Gorilla.js new file mode 100644 index 0000000..b4f21cc --- /dev/null +++ b/resources/teams/Apes/Gorilla.js @@ -0,0 +1,393 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "High chance of hurting enemies when blocking.", + "Can throw players with the attribute 'throwable',", + "but sometimes crushes them accidentally.", + "Stupid, so sometimes she just forgets", + "what he was about to do. This can be", + "helped by setting other players to his side." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var checkForStupidKey = "checkForStupid"; + var stupidKey = "isStupid"; + var stupidString = "HHNG"; + var YOU_SUCK = "YOU ARE "; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + var ENEMY_DOWN = "YOUR ENEMY IS "; + var YOU_SUCK = "YOUR ARE "; + var FAILD_NO_THROWABLE_PLAYER_NEXT_TO_ME = "NO THROWABLE PLAYER NEXT TO ME"; + var FAILD_CLUMSY = "THE GORILLA WAS TOO CLUMSY AND SQUEEZED THE TEAMMATE TO DEATH WITH HIS BARE HANDS"; + var throwableKey = "throwable"; + var API_CHOICE_FUNCTION_NAME = "apiChoice"; + var API_AIM_FUNCTION_NAME = "apiAim"; + function apiChoice(player, teamIndex, playerToBeThrownIndex, userIndex){ + var playerToBeThrown = player.getTeam().getPlayers().get(playerToBeThrownIndex); + player.getMatch().setCurrentDefenderWaitingForAnswer(playerToBeThrown); + var playerIndex = player.findPlayerIndex(); + if(!(player.getMatch().d6.throwDie() == 1 && player.getMatch().d6.throwDie() == 1)){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_AIM; + parameterArray[1] = API_AIM_FUNCTION_NAME; + parameterArray[2] = playerIndex; + parameterArray[3] = 8; //MaxRange + player.getMatch().sendMessage(player.getMatch().getUser(userIndex), SBProtocolCommand.EVENT, parameterArray); + }else{ + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var Pitch = Java.type("gameLogic.Pitch"); + playerToBeThrown.invokeSetPlayerCondition(PlayerCondition.DEAD); + playerToBeThrown.invokeSetPosition(Pitch.THE_VOID); + var parameterArray = []; + parameterArray[0] = FAILD_CLUMSY; + player.getMatch().sendMessage(player.getMatch().getUser(userIndex), SBProtocolCommand.EVENT, parameterArray); + player.getMatch().setGamePhase(3); + } + player.invokeSetRemainingBe(0); + } + function apiAim(player, destX, destY, userIndex){ + var x = destX; + var y = destY; + for(var i = 0; i < 3; i++){ + scatterRoll = player.getMatch().scatter(); + x += scatterRoll.x; + y += scatterRoll.y; + } + if(player.getMatch().getPitch().isOnField(x, y)){ + playerLanding(player.getMatch().getCurrentDefenderWaitingForAnswer(), userIndex, x, y, false); + }else{ + player.getRule(2).crowdBeatsUpPlayer(player.getMatch().getCurrentDefenderWaitingForAnswer()); + } + player.getMatch().setGamePhase(3); + } + function playerLanding(player, userIndex, x, y, willFailForSure){ + if(player.getMatch().getPitch().getFields()[x][y].getPlayer() == null){ + player.invokeSetPosition(x, y); + if(willFailForSure){ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getRule(1).playerDown(message, player, YOU_SUCK); + }else{ + var enemyIndex = -1; + if(userIndex == 0){ + enemyIndex = 1; + }else if(userIndex == 1){ + enemyIndex = 0; + } + var mod = -(player.getMatch().getTeam(enemyIndex).getTacklezones(x, y)); + if(!(player.getRule(0).geTest(mod))){ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getRule(1).playerDown(message, player, YOU_SUCK); + } + } + }else{ + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + playerCondition = player.getMatch().getPitch().getFields()[x][y].getPlayer().invokeGetPlayerCondition(); + defender = player.getMatch().getPitch().getFields()[x][y].getPlayer(); + player.getMatch().getPitch().getFields()[x][y].getPlayer().getRule(1).playerDown(message, player.getMatch().getPitch().getFields()[x][y].getPlayer(), ENEMY_DOWN); + if(playerCondition.equals(PlayerCondition.STUNNED) && defender.invokeGetPlayerCondition().equals(PlayerCondition.PRONE)){ + defender.invokeSetPlayerCondition(PlayerCondition.STUNNED); + } + var scatterRoll = player.getMatch().scatter(); + var newX = x + scatterRoll.x; + var newY = y + scatterRoll.y; + if(player.getMatch().getPitch().isOnField(newX, newY)){ + playerLanding(player, userIndex, newX, newY, true); + }else{ + player.getRule(2).crowdBeatsUpPlayer(player); + } + } + } + + function checkForStupid(player){ + var difficulty = 4; + if(player.getTeam().getTacklezones(player.getPos()) > 0){ + difficulty = 2; + } + if(player.getSpecialStat(stupidKey) == "false"){ + if(player.getTeam().getMatch().d6.throwDie() < difficulty){ + player.setSpecialStat(stupidKey, "true"); + player.updateActiveTackleZone(); + } + } + } + + function setPrice(player, price) { + return 110000; + } + function setGe(player, ge) { + return 1; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 5; + } + function setBe(player, be) { + return 4; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 2; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + player.addSpecialStat(stupidKey, "false"); + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveStupid = Java.extend(RuleMove); + var ruleMoveStupid = new RuleMoveStupid(rulesArray[0].getActor()){ + apply: function(message, path){ + var _super_ = Java.super(ruleMoveStupid); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var pathArray = Java.from(path); + if(pathArray.length < 2){ + if(player.invokeGetPlayerCondition() == PlayerCondition.FINE){ + return; + } + } + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), stupidString); + }else{ + _super_.apply(message, path); + } + } + }; + rulesArray[0] = ruleMoveStupid; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockStupidMightyBlowRegeneration = Java.extend(RuleBlock); + var ruleBlockStupidMightyBlowRegeneration = new RuleBlockStupidMightyBlowRegeneration(rulesArray[1].getActor()) { + apply: function(message, defender){ + var _super_ = Java.super(ruleBlockStupidMightyBlowRegeneration); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), stupidString); + }else{ + _super_.apply(message, defender); + } + }, + defenderDown: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderDown(message, player, defender, 1, 1); + return true; + }, + defenderStumbles: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderStumbles(message, player, defender, 1, 1); + return true; + }, + bothDown: function(message, defender) { + var _super_ = Java.super(ruleBlockStupidMightyBlowRegeneration); + defender.getRule(1).beingBlockedBothDown(message, 1, 1); + _super_.playerDown(message, player, YOU_SUCK, 0, 0); + _super_.clearHighlightFields(); + return true; + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie(); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + if(player.getMatch().d6.throwDie() > 3){ + player.invokeSetPlayerCondition(PlayerCondition.FINE); + s += ", BUT REGENERATED"; + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockStupidMightyBlowRegeneration; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStupid = Java.extend(RuleThrow); + var ruleThrowStupid = new RuleThrowStupid(rulesArray[3].getActor()){ + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStupid); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), stupidString); + }else{ + _super_.apply(message, destination); + } + } + }; + rulesArray[3] = ruleThrowStupid; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchStupid = Java.extend(RuleCatch); + var ruleCatchStupid = new RuleCatchStupid(rulesArray[4].getActor()){ + apply: function(successfulThrow){ + var _super_ = Java.super(ruleCatchStupid); + if(player.getSpecialStat(stupidKey) == "true"){ + var actingUserIndex = -1; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + _super_.scatterBallAround(actingUserIndex); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), stupidString); + }else{ + _super_.apply(successfulThrow); + } + } + }; + rulesArray[4] = ruleCatchStupid; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_YOUR_TURN || eventString == SBProtocolMessage.EVENT_ENDED_TURN){ + var difficulty = 3; + if(player.getTeam().getTacklezones(player.getPos()) > 0){ + difficulty = 1; + } + if(player.getTeam().getMatch().d6.throwDie() > difficulty){ + player.setSpecialStat(stupidKey, "false"); + player.updateActiveTackleZone(); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) { + var SpecialRule = Java.type("gameLogic.rules.SpecialRule"); + var specialRulesArray = []; + + var SpecialRuleThrowTeammate = Java.extend(SpecialRule); + var specialRuleThrowTeammate = new SpecialRuleThrowTeammate(player, "Throw Teammate") { + apply: function(message){ + var _super_ = Java.super(specialRuleThrowTeammate); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetRemainingBe() == 0 && !(player.getTeam().getMovingPlayer() == player)){ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + return; + } + if(player.invokeGetPlayerCondition() != PlayerCondition.FINE){ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + } + _super_.checkForMovingPlayer(player); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), stupidString); + }else{ + if(player.invokeGetRemainingBe() > 0){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_CHOICE; + parameterArray[1] = API_CHOICE_FUNCTION_NAME; + var playerCounter = 0; + var PitchField = Java.type("gameLogic.PitchField"); + var neighboursArray = Java.from(player.getMatch().getPitch().getNeighbours(player.getPos())); + for(var i = 0; i < neighboursArray.length; i++){ + var field = neighboursArray[i]; + if(field.getPlayer() != null){ + if(player.getTeam() == field.getPlayer().getTeam() && field.getPlayer().invokeGetPlayerCondition().equals(PlayerCondition.FINE)){ + var throwable = false; + throwable = field.getPlayer().getSpecialStat(throwableKey); + if(throwable == "true"){ + var teamIndex = -1; + if(player.getTeam() == field.getPlayer().getMatch().getTeam(0)){ + teamIndex = 0; + }else if(player.getTeam() == field.getPlayer().getMatch().getTeam(1)){ + teamIndex = 1; + } + parameterArray[2*playerCounter+2] = teamIndex; + parameterArray[2*playerCounter+3] = field.getPlayer().getId() - 1; + playerCounter++; + } + } + } + } + if(playerCounter > 0){ + player.getMatch().setGamePhase(5); + player.getMatch().setCurrentActorWaitingForAnswer(player); + _super_.sendMessage(message, SBProtocolCommand.EVENT, parameterArray); + }else{ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I can't find any throwable player!"); + _super_.returnFailureMessage(message, FAILD_NO_THROWABLE_PLAYER_NEXT_TO_ME); + } + }else{ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + } + } + } + }; + specialRulesArray[0] = specialRuleThrowTeammate; + + var specialRulesToReturn = Java.to(specialRulesArray, Java.type("gameLogic.rules.SpecialRule[]")); + return specialRulesToReturn; + } + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) { + var Vector = Java.type("java.util.Vector"); + if(player.getSpecialStat(stupidKey) == "true"){ + return new Vector(); + } + } + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/GorillaL.png b/resources/teams/Apes/GorillaL.png new file mode 100644 index 0000000..e5c8479 Binary files /dev/null and b/resources/teams/Apes/GorillaL.png differ diff --git a/resources/teams/Apes/GorillaR.png b/resources/teams/Apes/GorillaR.png new file mode 100644 index 0000000..44fc53a Binary files /dev/null and b/resources/teams/Apes/GorillaR.png differ diff --git a/resources/teams/Apes/HowlerMonkey.js b/resources/teams/Apes/HowlerMonkey.js new file mode 100644 index 0000000..f1a115a --- /dev/null +++ b/resources/teams/Apes/HowlerMonkey.js @@ -0,0 +1,357 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Cheater.", + "Cannot pick up the ball.", + "Whirls across the field with another monkey", + "blocking everyone in his path." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var cheatedKey = "cheated"; + var noHandsString = "Sorry, but I have no hands left!"; + var ONLY_TORNADO = "SRY, BUT I'M SO HYPERACTIVE, THAT I CAN ONLY USE MY SPECIAL ABILITY TORNADO"; + var API_FIELD_FUNCTION_NAME = "apiField"; + var ENEMY_DOWN = "YOUR ENEMY IS "; + var YOU_SUCK = "YOU ARE "; + var FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION = "PLAYER IS NOT IN A GOOD CONDITION"; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + function apiField(player, x, y, userIndex){ + var directionThrow = player.getMatch().d3.throwDie(); + var newX = x; + var newY = y; + if(x == player.getPos().x){ + newX = directionThrow - 2 + x; + }else if(y == player.getPos().y){ + newY = directionThrow - 2 + y; + } + if(player.getMatch().getPitch().isOnField(newX, newY)){ + if(player.getMatch().getPitch().getFields()[newX][newY].getPlayer() == null){ + player.invokeSetPosition(newX, newY); + if(player.getMatch().getPitch().getBallPos().x == newX && player.getMatch().getPitch().getBallPos().y == newY){ + player.getRule(4).scatterBallAround(userIndex); + } + endMove(player); + }else{ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + blockFromMove(player, message, player.getMatch().getPitch().getFields()[newX][newY].getPlayer()); + } + }else{ + player.getRule(2).crowdBeatsUpPlayer(player); + player.getMatch().setGamePhase(3); + } + } + function endMove(player){ + player.getMatch().setGamePhase(5); + player.invokeCountDownRemainingBe(1); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetRemainingBe() > 0 && player.invokeGetPlayerCondition() == PlayerCondition.FINE){ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getSpecialRule(0).apply(message); + }else{ + player.getMatch().setGamePhase(3); + } + } + function playerDownInAttack(message, p, s){ + armorRoll = p.getMatch().d6.throwDie() + p.getMatch().d6.throwDie(); + s = injuryRollInAttack(p, 0); + p.getRule(1).sendMessageShowMe(p.toString(), "I am " + s + "!"); + p.getRule(1).returnSuccessMessage(message, s); + var teamIndex = -1; + if(p.getTeam().equals(p.getMatch().getTeam(0))){ + teamIndex = 0; + }else if(p.getTeam().equals(p.getMatch().getTeam(1))){ + teamIndex = 1; + }else{ + return; + } + p.getMatch().endTurn(teamIndex); + } + function injuryRollInAttack(player, modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "INJURED"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "DEAD"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + return s; + } + function blockFromMove(player, message, defender){ + player.setSpecialStat(cheatedKey, "true"); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + playerIsFine = (player.getMatch().getPitch().isOnField(player.getPos()) && player.invokeGetPlayerCondition() == PlayerCondition.FINE); + playerHasRemainingBe = (player.invokeGetRemainingBe() > 0); + playerWantsToBlitz = (player.invokeGetRemainingBe() != player.invokeGetBe()); + if(playerIsFine && playerHasRemainingBe){ + // Set fields for defender and message so they are available in attackerDown() and bothDown() + this.message = message; + if (player.getMatch().getPitch().isAdjacent(player.getPosition(), defender.getPosition())){ + if(defender.invokeGetPlayerCondition() == PlayerCondition.FINE){ + player.getRule(1).throwDice(message, defender); + }else{ + player.getRule(1).beatHim(defender, message, true); + } + } else player.getRule(1).returnFailureMessage(message, SBProtocolMessage.FAILD_BLOCKING_NOT_POSSIBLE); + } else player.getRule(1).returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_CANNOT_TAKE_ACTION); + } + function followWithoutQuestion(player, defenderField, message){ + player.invokeSetPosition(defenderField); + player.getRule(2).returnSuccessMessage(message, SBProtocolMessage.WORKD_FOLLOWED); + endMove(player); + } + + function setPrice(player, price) { + return 70000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 7; + } + function setBe(player, be) { + return 3; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(cheatedKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockStuntyCheaterTornado = Java.extend(RuleBlock); + var ruleBlockStuntyCheaterTornado = new RuleBlockStuntyCheaterTornado(rulesArray[1].getActor()) { + apply: function(message, defender){ + var _super_ = Java.super(ruleBlockStuntyCheaterTornado); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I can only use my tornado!"); + _super_.returnFailureMessage(message, ONLY_TORNADO); + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + }, + beatHim: function(defender, message, playerWantsToBlitz){ + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + defenderCondition = defender.invokeGetPlayerCondition(); + defenderPosition = defender.getPosition(); + defender.getRule(1).playerDown(message, defender, ENEMY_DOWN); + if(defenderCondition == PlayerCondition.STUNNED && defender.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + defender.invokeSetPlayerCondition(PlayerCondition.STUNNED); + } + player.getRule(2).apply(message, defender, defenderPosition, player.getTeam().getCoach().getUID(), player, defenderPosition.getPos()); + }, + attackerDown: function(message) { + playerDownInAttack(message, player, YOU_SUCK); + player.getMatch().setGamePhase(3); + player.getMatch().clearCurrentHighlitedFields(); + var _super_ = Java.super(ruleBlockStuntyCheaterTornado); + _super_.clearHighlightFields(); + return true; + }, + bothDown: function(message, defender) { + defender.getRule(1).beingBlockedBothDown(message, 0, 0); + playerDownInAttack(message, player, YOU_SUCK); + player.getMatch().clearCurrentHighlitedFields(); + var _super_ = Java.super(ruleBlockStuntyCheaterTornado); + _super_.clearHighlightFields(); + player.getMatch().setGamePhase(3); + return true; + } + }; + rulesArray[1] = ruleBlockStuntyCheaterTornado; + + var RulePush = Java.type("gameLogic.rules.RulePush"); + var RulePushTornado = Java.extend(RulePush); + var rulePushTornado = new RulePushTornado(rulesArray[2].getActor()){ + backUp: function(defenderField, message, playerBackingUp){ + var _super_ = Java.super(rulePushTornado); + player.getRule(1).clearHighlightFields(); + followWithoutQuestion(player, defenderField, message); + } + }; + rulesArray[2] = rulePushTornado; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveNoHandsTornado = Java.extend(RuleMove); + var ruleMoveNoHandsTornado = new RuleMoveNoHandsTornado(rulesArray[0].getActor()){ + apply: function(message, path){ + var _super_ = Java.super(ruleMoveNoHandsTornado); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I can only use my tornado!"); + _super_.returnFailureMessage(message, ONLY_TORNADO); + }, + tryToPickUpBall: function(message, i, path){ + var _super_ = Java.super(ruleMoveNoHandsTornado); + return _super_.faildToPickUpBall(message); + } + }; + rulesArray[0] = ruleMoveNoHandsTornado; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStuntyNoHands = Java.extend(RuleThrow); + var ruleThrowStuntyNoHands = new RuleThrowStuntyNoHands(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.findThrowModificator(problems - 1, distance); + }, + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I can only use my tornado!"); + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, player.toString(), noHandsString); + } + }; + rulesArray[3] = ruleThrowStuntyNoHands; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchNoHands = Java.extend(RuleCatch); + var ruleCatchNoHands = new RuleCatchNoHands(rulesArray[4].getActor()){ + apply: function(successfulThrow){ + var actingUserIndex; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + var _super_ = Java.super(ruleCatchNoHands); + _super_.scatterBallAround(actingUserIndex); + }, + giveBall: function(message){ + var _super_ = Java.super(ruleCatchNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE); + } + }; + rulesArray[4] = ruleCatchNoHands; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_SETUP_YOUR_TEAM){ + if(player.getSpecialStat(cheatedKey).equals("true")){ + player.invokeSetRedCard(true); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) { + var SpecialRule = Java.type("gameLogic.rules.SpecialRule"); + var specialRulesArray = []; + + var SpecialRuleTornado = Java.extend(SpecialRule); + var specialRuleTornado = new SpecialRuleTornado(player, "Tornado") { + apply: function(message){ + var _super_ = Java.super(specialRuleTornado); + _super_.checkForMovingPlayer(player); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetPlayerCondition() != PlayerCondition.FINE){ + _super_.returnFailureMessage(message, FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + } + if(player.invokeGetRemainingBe() > 0){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_FIELD; + parameterArray[1] = API_FIELD_FUNCTION_NAME; + var fieldCounter = 0; + for(var j = -1; j < 2; j++){ + for(var i = -1; i < 2; i++){ + var posX = player.getPos().x+i; + var posY = player.getPos().y+j; + if(player.getMatch().getPitch().isOnField(posX, posY)){ + if((i == 0 && j != 0) || (i != 0 && j == 0)){ + parameterArray[2*fieldCounter+2] = posX; + parameterArray[2*fieldCounter+3] = posY; + fieldCounter++; + } + } + } + } + player.getMatch().setGamePhase(5); + player.getMatch().setCurrentActorWaitingForAnswer(player); + _super_.sendMessage(message, SBProtocolCommand.EVENT, parameterArray); + }else{ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + } + } + }; + specialRulesArray[0] = specialRuleTornado; + + var specialRulesToReturn = Java.to(specialRulesArray, Java.type("gameLogic.rules.SpecialRule[]")); + return specialRulesToReturn; + } + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/HowlerMonkeyL.png b/resources/teams/Apes/HowlerMonkeyL.png new file mode 100644 index 0000000..11edd9b Binary files /dev/null and b/resources/teams/Apes/HowlerMonkeyL.png differ diff --git a/resources/teams/Apes/HowlerMonkeyR.png b/resources/teams/Apes/HowlerMonkeyR.png new file mode 100644 index 0000000..db8917d Binary files /dev/null and b/resources/teams/Apes/HowlerMonkeyR.png differ diff --git a/resources/teams/Apes/Marsupilami.js b/resources/teams/Apes/Marsupilami.js new file mode 100644 index 0000000..528fb0f --- /dev/null +++ b/resources/teams/Apes/Marsupilami.js @@ -0,0 +1,257 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Cheater.", + "Sneaky when fouling.", + "Easily hurt.", + "Can jump over enemies using his tail.", + "Just pushed on 'defender stumbles'.", + "Very good at passing through tackle", + "zones, ignores helping tackle zones", + "of other enemies, when dodging." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var alreadyDodgedKey = "hasAlreadyDodged"; + var cheatedKey = "cheated"; + var API_FIELD_FUNCTION_NAME = "apiField"; + var FAILD_NO_EMPTY_SQUARE_TO_LEAP_TO = "NO EMPTY SQUARE TO LEAP TO"; + var FAILD_NOT_ENOUGH_REMAINING_BE = "NOT ENOUGH REMAINING BE"; + var ENEMY_FOULED = "YOU HAVE BEATEN UP YOUR ENEMY. YOUR ENEMY IS "; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + function apiField(player, x, y, userIndex){ + player.getMatch().setGamePhase(3); + player.invokeSetPosition(x, y); + player.invokeCountDownRemainingBe(2); + if(player.getRule(0).geTest(1)){ + }else{ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getRule(1).playerDown(message, player, player.getRule(1).YOU_SUCK); + player.invokeSetRemainingBe(0); + } + } + + function setPrice(player, price) { + return 40000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 7; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(alreadyDodgedKey, "false"); + player.addSpecialStat(cheatedKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockDodgeStuntyDirtyPlayer = Java.extend(RuleBlock); + var ruleBlockDodgeStuntyDirtyPlayer = new RuleBlockDodgeStuntyDirtyPlayer(rulesArray[1].getActor()) { + beingBlockedDefenderStumbles: function(message, attacker, firstThrowModifier, injuryRollModifier){ + var _super_ = Java.super(ruleBlockDodgeStuntyDirtyPlayer); + _super_.beingBlockedPushed(message, attacker, player.getPosition()); + }, + beatHim: function(defender, message){ + var _super_ = Java.super(ruleBlockDodgeStuntyDirtyPlayer); + if(player.getTeam().getFoul()){ + armorRollModifier = player.getTeam().getTacklezones(defender.getPos()) -1; + armorRollModifier += defender.getTeam().getTacklezones(player.getPos()); + defender.getRule(1).playerDown(message, defender, ENEMY_FOULED, armorRollModifier + 1, 1); + player.getTeam().setFoul(false); + _super_.refereeTriesToKeepSurvey(message); + } + else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_FOUL_LEFT); + } + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockDodgeStuntyDirtyPlayer; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveDodgeStuntyCheater = Java.extend(RuleMove); + var ruleMoveDodgeStuntyCheater = new RuleMoveDodgeStuntyCheater(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveDodgeStuntyCheater); + if(!(_super_.stayFine(problems))){ + if(player.getSpecialStat(alreadyDodgedKey) == "false"){ + player.setSpecialStat(alreadyDodgedKey, "true"); + if(!(_super_.stayFine(problems))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }else{ + return _super_.beingTackled(message, i, path); + } + }else{ + return true; + } + } + }; + rulesArray[0] = ruleMoveDodgeStuntyCheater; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStuntyLongTail = Java.extend(RuleThrow); + var ruleThrowStuntyLongTail = new RuleThrowStuntyLongTail(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStuntyLongTail); + _super_.findThrowModificator(problems - 1, distance); + }, + intercept: function(thrower, teamIndex, playerIndex, userIndex){ + player.getMatch().setGamePhase(3); + var mod = -1; + mod -= player.getMatch().getOpposingTeam(player.getMatch().getTeam(teamIndex)).getTacklezones(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + if(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getRule(3).geTest(mod)){ + player.invokeSetIsHoldingBall(false); + player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).invokeSetIsHoldingBall(true); + player.getMatch().getPitch().adjustBallPos(palyer.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + var actingUserIndex = -1; + if(userIndex == 0){ + actingUserIndex = 1; + }else if(userIndex == 1){ + actingUserIndex = 0; + } + sendMessageShowMe(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).toString(), "Successfully intercepted!"); + player.getMatch().endTurn(actingUserIndex); + }else{ + thrower.getRule(3).throwBall(player.getMatch().getCurrentMessageWaitingForAnswer(), player.getMatch().getCurrentDefenderFieldWaitingForAnser(), player.getMatch().getCurrentModificatorWaitingForAnser()); + } + } + }; + rulesArray[3] = ruleThrowStuntyLongTail; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_SETUP_YOUR_TEAM){ + if(player.getSpecialStat(cheatedKey).equals("true")){ + player.invokeSetRedCard(true); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) { + var SpecialRule = Java.type("gameLogic.rules.SpecialRule"); + var specialRulesArray = []; + + var SpecialRuleLeapLongTail = Java.extend(SpecialRule); + var specialRuleLeapLongTail = new SpecialRuleLeapLongTail(player, "Leap") { + apply: function(message){ + var _super_ = Java.super(specialRuleLeapLongTail); + _super_.checkForMovingPlayer(player); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetPlayerCondition() != PlayerCondition.FINE){ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + } + if(player.invokeGetRemainingBe() > 1){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_FIELD; + parameterArray[1] = API_FIELD_FUNCTION_NAME; + var fieldCounter = 0; + for(var j = -2; j < 3; j++){ + for(var i = -2; i < 3; i++){ + var posX = player.getPos().x+i; + var posY = player.getPos().y+j; + if(player.getMatch().getPitch().isOnField(posX, posY)){ + if(player.getMatch().getPitch().getFields()[posX][posY].getPlayer() == null && !(i==0 && j==0)){ + parameterArray[2*fieldCounter+2] = posX; + parameterArray[2*fieldCounter+3] = posY; + fieldCounter++; + } + } + } + } + if(fieldCounter > 0){ + player.setSpecialStat(cheatedKey, "true"); + player.getMatch().setGamePhase(5); + player.getMatch().setCurrentActorWaitingForAnswer(player); + _super_.sendMessage(message, SBProtocolCommand.EVENT, parameterArray); + }else{ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "There is no Square I can leap to!"); + _super_.returnFailureMessage(message, FAILD_NO_EMPTY_SQUARE_TO_LEAP_TO); + } + }else{ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I'm exhausted!"); + _super_.returnFailureMessage(message, FAILD_NOT_ENOUGH_REMAINING_BE); + } + } + }; + specialRulesArray[0] = specialRuleLeapLongTail; + + var specialRulesToReturn = Java.to(specialRulesArray, Java.type("gameLogic.rules.SpecialRule[]")); + return specialRulesToReturn; + } + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/MarsupilamiL.png b/resources/teams/Apes/MarsupilamiL.png new file mode 100644 index 0000000..c273d9e Binary files /dev/null and b/resources/teams/Apes/MarsupilamiL.png differ diff --git a/resources/teams/Apes/MarsupilamiR.png b/resources/teams/Apes/MarsupilamiR.png new file mode 100644 index 0000000..d70b423 Binary files /dev/null and b/resources/teams/Apes/MarsupilamiR.png differ diff --git a/resources/teams/Apes/Orangutan.js b/resources/teams/Apes/Orangutan.js new file mode 100644 index 0000000..c810739 --- /dev/null +++ b/resources/teams/Apes/Orangutan.js @@ -0,0 +1,206 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Cheater.", + "Cannot pick up the ball.", + "Has a rock to beat his enemies,", + "so the risk of injury is high" + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var cheatedKey = "cheated"; + var noHandsString = "Sorry, but I have no hands left!"; + var ENEMY_DOWN = "YOU HAVE ROCKED YOUR ENEMY, HE IS "; + var YOU_SUCK = "YOU HAVE BEEN ROCKED, YOU ARE "; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + function setPrice(player, price) { + return 40000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(cheatedKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockStuntyCheaterRockAttack = Java.extend(RuleBlock); + var ruleBlockStuntyCheaterRockAttack = new RuleBlockStuntyCheaterRockAttack(rulesArray[1].getActor()) { + apply: function(message, defender){ + player.setSpecialStat(cheatedKey, "true"); + var _super_ = Java.super(ruleBlockStuntyCheaterRockAttack); + _super_.apply(message, defender); + }, + throwDice: function(message, defender){ + player.getMatch().addCurrentHighlitedFields(player.getPos()); + player.getMatch().addCurrentHighlitedFields(defender.getPos()); + var _super_ = Java.super(ruleBlockStuntyCheaterRockAttack); + _super_.sendHighlightFields(player.getMatch().getCurrentHighlitedFields()); + var attackThrow = player.getMatch().d6.throwDie(); + if(attackThrow > 1){ + defenderField = defender.getPos(); + defender.getRule(1).playerDown(message, defender, ENEMY_DOWN, 3, 0); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(defender.invokeGetPlayerCondition().equals(PlayerCondition.DEAD) || defender.invokeGetPlayerCondition().equals(PlayerCondition.INJURED) || defender.invokeGetPlayerCondition().equals(PlayerCondition.KO)){ + player.getRule(2).backUp(defenderField, message, player); + } + _super_.clearHighlightFields(); + }else{ + _super_.playerDown(message, player, YOU_SUCK, 3, 0); + _super_.clearHighlightFields(); + } + }, + beatHim: function(defender, message){ + var _super_ = Java.super(ruleBlockStuntyCheaterRockAttack); + var armorRollModifier = player.getTeam().getTacklezones(defender.getPos()) + 2; + armorRollModifier += defender.getTeam().getTacklezones(player.getPos()); + defender.getRule(1).playerDown(message, defender, RuleBlock.ENEMY_FOULED, armorRollModifier, 0); + player.getTeam().setFoul(false); + _super_.refereeTriesToKeepSurvey(message); + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockStuntyCheaterRockAttack; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveStuntyNoHands = Java.extend(RuleMove); + var ruleMoveStuntyNoHands = new RuleMoveStuntyNoHands(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveStuntyNoHands); + if(!(_super_.stayFine(0))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }, + tryToPickUpBall: function(message, i, path){ + var _super_ = Java.super(ruleMoveStuntyNoHands); + return _super_.faildToPickUpBall(message); + } + }; + rulesArray[0] = ruleMoveStuntyNoHands; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStuntyNoHands = Java.extend(RuleThrow); + var ruleThrowStuntyNoHands = new RuleThrowStuntyNoHands(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.findThrowModificator(problems - 1, distance); + }, + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStuntyNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, player.toString(), noHandsString); + } + }; + rulesArray[3] = ruleThrowStuntyNoHands; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchNoHands = Java.extend(RuleCatch); + var ruleCatchNoHands = new RuleCatchNoHands(rulesArray[4].getActor()){ + apply: function(successfulThrow){ + var actingUserIndex; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + var _super_ = Java.super(ruleCatchNoHands); + _super_.scatterBallAround(actingUserIndex); + }, + giveBall: function(message){ + var _super_ = Java.super(ruleCatchNoHands); + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, noHandsString); + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE); + } + }; + rulesArray[4] = ruleCatchNoHands; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_SETUP_YOUR_TEAM){ + if(player.getSpecialStat(cheatedKey).equals("true")){ + player.invokeSetRedCard(true); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Apes/OrangutanL.png b/resources/teams/Apes/OrangutanL.png new file mode 100644 index 0000000..6e923f5 Binary files /dev/null and b/resources/teams/Apes/OrangutanL.png differ diff --git a/resources/teams/Apes/OrangutanR.png b/resources/teams/Apes/OrangutanR.png new file mode 100644 index 0000000..89a7328 Binary files /dev/null and b/resources/teams/Apes/OrangutanR.png differ diff --git a/resources/teams/PolarRegion/ArcticHare.js b/resources/teams/PolarRegion/ArcticHare.js new file mode 100644 index 0000000..df01e75 --- /dev/null +++ b/resources/teams/PolarRegion/ArcticHare.js @@ -0,0 +1,118 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Very good at picking up", + "the ball and passing it." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 70000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 8; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 5; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 2; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveSureHands = Java.extend(RuleMove); + var ruleMoveSureHands = new RuleMoveSureHands(rulesArray[0].getActor()) { + tryToPickUpBall: function(message, i, path){ + var _super_ = Java.super(ruleMoveSureHands); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var Weather = Java.type("gameLogic.Weather"); + var mod = 1-(player.getMatch().getOpposingTeam(player.getTeam()).getTacklezones(path[i].getPos())); + if(player.getMatch().getWeather() == Weather.POURING_RAIN){ + mod--; + } + if(_super_.geTest(mod) && player.invokeGetPlayerCondition()==PlayerCondition.FINE){ + return _super_.pickedUpBall(message); + }else{ + if(_super_.geTest(mod)){ + return _super_.pickedUpBall(message); + }else{ + return _super_.faildToPickUpBall(message); + } + } + } + }; + rulesArray[0] = ruleMoveSureHands; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowPass = Java.extend(RuleThrow); + var ruleThrowPass = new RuleThrowPass(rulesArray[3].getActor()) { + throwBall: function(message, destinationField, problems){ + var _super_ = Java.super(ruleThrowPass); + if(_super_.geTest(problems)){ + _super_.successfulThrow(message, destinationField); + }else{ + if(_super_.geTest(problems)){ + _super_.successfulThrow(message, destinationField); + }else{ + _super_.notWellThrown(destinationField, message); + } + } + player.invokeSetIsHoldingBall(false); + player.invokeSetRemainingBe(0); + player.getTeam().setPass(false); + player.getMatch().sendBallPos(); + if(player.getMatch().findTeamHoldingTheBall() != player.getMatch().findUserIndex(message)){ + player.getMatch().endTurn(message); + } + player.getMatch().sendPlayer(player); + } + }; + rulesArray[3] = ruleThrowPass; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/ArcticHareL.png b/resources/teams/PolarRegion/ArcticHareL.png new file mode 100644 index 0000000..b75ab44 Binary files /dev/null and b/resources/teams/PolarRegion/ArcticHareL.png differ diff --git a/resources/teams/PolarRegion/ArcticHareR.png b/resources/teams/PolarRegion/ArcticHareR.png new file mode 100644 index 0000000..d8a7545 Binary files /dev/null and b/resources/teams/PolarRegion/ArcticHareR.png differ diff --git a/resources/teams/PolarRegion/Penguin.js b/resources/teams/PolarRegion/Penguin.js new file mode 100644 index 0000000..3db00fb --- /dev/null +++ b/resources/teams/PolarRegion/Penguin.js @@ -0,0 +1,62 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Walks funny." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 50000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 5; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 16; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) {} + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/PenguinL.png b/resources/teams/PolarRegion/PenguinL.png new file mode 100644 index 0000000..3343752 Binary files /dev/null and b/resources/teams/PolarRegion/PenguinL.png differ diff --git a/resources/teams/PolarRegion/PenguinR.png b/resources/teams/PolarRegion/PenguinR.png new file mode 100644 index 0000000..cbe5e5c Binary files /dev/null and b/resources/teams/PolarRegion/PenguinR.png differ diff --git a/resources/teams/PolarRegion/PolarBear.js b/resources/teams/PolarRegion/PolarBear.js new file mode 100644 index 0000000..d1584a7 --- /dev/null +++ b/resources/teams/PolarRegion/PolarBear.js @@ -0,0 +1,62 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Guy" + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 80000; + } + function setGe(player, ge) { + return 2; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 4; + } + function setBe(player, be) { + return 4; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 4; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) {} + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/PolarBearL.png b/resources/teams/PolarRegion/PolarBearL.png new file mode 100644 index 0000000..542cae1 Binary files /dev/null and b/resources/teams/PolarRegion/PolarBearL.png differ diff --git a/resources/teams/PolarRegion/PolarBearR.png b/resources/teams/PolarRegion/PolarBearR.png new file mode 100644 index 0000000..c52d30b Binary files /dev/null and b/resources/teams/PolarRegion/PolarBearR.png differ diff --git a/resources/teams/PolarRegion/Puffin.js b/resources/teams/PolarRegion/Puffin.js new file mode 100644 index 0000000..2045b4b --- /dev/null +++ b/resources/teams/PolarRegion/Puffin.js @@ -0,0 +1,151 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + var alreadyDodgedKey = "hasAlreadyDodged"; + var throwableKey = "throwable"; + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Throwable.", + "Very small and easily hurt.", + "Just pushed on 'defender stumbles'.", + "Very good at passing through tackle", + "zones, ignores helping tackle zones", + "of other enemies, when dodging." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 40000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 4; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(alreadyDodgedKey, "false"); + player.addSpecialStat(throwableKey, "true"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockDodgeStunty = Java.extend(RuleBlock); + var ruleBlockDodgeStunty = new RuleBlockDodgeStunty(rulesArray[1].getActor()) { + beingBlockedDefenderStumbles: function(message, attacker, firstThrowModifier, injuryRollModifier){ + var _super_ = Java.super(ruleBlockDodgeStunty); + _super_.beingBlockedPushed(message, attacker, player.getPosition()); + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie() + 1; + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockDodgeStunty; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveDodgeStunty = Java.extend(RuleMove); + var ruleMoveDodgeStunty = new RuleMoveDodgeStunty(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveDodgeStunty); + if(!(_super_.stayFine(0))){ + if(player.getSpecialStat(alreadyDodgedKey) == "false"){ + player.setSpecialStat(alreadyDodgedKey, "true"); + if(!(_super_.stayFine(0))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }else{ + return _super_.beingTackled(message, i, path); + } + }else{ + return true; + } + } + }; + rulesArray[0] = ruleMoveDodgeStunty; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStunty = Java.extend(RuleThrow); + var ruleThrowStunty = new RuleThrowStunty(rulesArray[3].getActor()){ + findThrowModificator: function(problems, distance){ + var _super_ = Java.super(ruleThrowStunty); + _super_.findThrowModificator(problems - 1, distance); + } + }; + rulesArray[3] = ruleThrowStunty; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_YOUR_TURN){ + player.setSpecialStat(alreadyDodgedKey, "false"); + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/PuffinL.png b/resources/teams/PolarRegion/PuffinL.png new file mode 100644 index 0000000..9d1c1a7 Binary files /dev/null and b/resources/teams/PolarRegion/PuffinL.png differ diff --git a/resources/teams/PolarRegion/PuffinR.png b/resources/teams/PolarRegion/PuffinR.png new file mode 100644 index 0000000..86ca3d2 Binary files /dev/null and b/resources/teams/PolarRegion/PuffinR.png differ diff --git a/resources/teams/PolarRegion/Reindeer.js b/resources/teams/PolarRegion/Reindeer.js new file mode 100644 index 0000000..d07b551 --- /dev/null +++ b/resources/teams/PolarRegion/Reindeer.js @@ -0,0 +1,80 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Doesn't tumble on 'both down'." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 80000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 4; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockBlock = Java.extend(RuleBlock); + var ruleBlockBlock = new RuleBlockBlock(rulesArray[1].getActor()) { + beingBlockedBothDown: function(message){}, + bothDown: function(message, defender){ + defender.getRule(1).beingBlockedBothDown(message, 0, 0); + var _super_ = Java.super(ruleBlockBlock); + _super_.clearHighlightFields(); + return true; + } + }; + + rulesArray[1] = ruleBlockBlock; + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/ReindeerL.png b/resources/teams/PolarRegion/ReindeerL.png new file mode 100644 index 0000000..aed83fd Binary files /dev/null and b/resources/teams/PolarRegion/ReindeerL.png differ diff --git a/resources/teams/PolarRegion/ReindeerR.png b/resources/teams/PolarRegion/ReindeerR.png new file mode 100644 index 0000000..1d49514 Binary files /dev/null and b/resources/teams/PolarRegion/ReindeerR.png differ diff --git a/resources/teams/PolarRegion/Seacow.js b/resources/teams/PolarRegion/Seacow.js new file mode 100644 index 0000000..da479b7 --- /dev/null +++ b/resources/teams/PolarRegion/Seacow.js @@ -0,0 +1,394 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "High chance of hurting enemies when blocking.", + "Can throw players with the attribute 'throwable',", + "but sometimes crushes them accidentally.", + "Heals faster than other players.", + "Stupid, so sometimes she just forgets", + "what she was about to do. This can be", + "helped by setting other players to her side." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + var checkForStupidKey = "checkForStupid"; + var stupidKey = "isStupid"; + var stupidString = "HHNG"; + var YOU_SUCK = "YOU ARE "; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + var ENEMY_DOWN = "YOUR ENEMY IS "; + var YOU_SUCK = "YOUR ARE "; + var FAILD_NO_THROWABLE_PLAYER_NEXT_TO_ME = "NO THROWABLE PLAYER NEXT TO ME"; + var FAILD_CLUMSY = "THE SEACOW WAS TOO CLUMSY, ROLLED ON THE TEAMMATE AND CRUMPLED HIM TO DEATH"; + var throwableKey = "throwable"; + var API_CHOICE_FUNCTION_NAME = "apiChoice"; + var API_AIM_FUNCTION_NAME = "apiAim"; + function apiChoice(player, teamIndex, playerToBeThrownIndex, userIndex){ + var playerToBeThrown = player.getTeam().getPlayers().get(playerToBeThrownIndex); + player.getMatch().setCurrentDefenderWaitingForAnswer(playerToBeThrown); + var playerIndex = player.findPlayerIndex(); + if(!(player.getMatch().d6.throwDie() == 1 && player.getMatch().d6.throwDie() == 1)){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_AIM; + parameterArray[1] = API_AIM_FUNCTION_NAME; + parameterArray[2] = playerIndex; + parameterArray[3] = 8; //MaxRange + player.getMatch().sendMessage(player.getMatch().getUser(userIndex), SBProtocolCommand.EVENT, parameterArray); + }else{ + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var Pitch = Java.type("gameLogic.Pitch"); + playerToBeThrown.invokeSetPlayerCondition(PlayerCondition.DEAD); + playerToBeThrown.invokeSetPosition(Pitch.THE_VOID); + var parameterArray = []; + parameterArray[0] = FAILD_CLUMSY; + player.getMatch().sendMessage(player.getMatch().getUser(userIndex), SBProtocolCommand.EVENT, parameterArray); + player.getMatch().setGamePhase(3); + } + player.invokeSetRemainingBe(0); + } + function apiAim(player, destX, destY, userIndex){ + var x = destX; + var y = destY; + for(var i = 0; i < 3; i++){ + scatterRoll = player.getMatch().scatter(); + x += scatterRoll.x; + y += scatterRoll.y; + } + if(player.getMatch().getPitch().isOnField(x, y)){ + playerLanding(player.getMatch().getCurrentDefenderWaitingForAnswer(), userIndex, x, y, false); + }else{ + player.getRule(2).crowdBeatsUpPlayer(player.getMatch().getCurrentDefenderWaitingForAnswer()); + } + player.getMatch().setGamePhase(3); + } + function playerLanding(player, userIndex, x, y, willFailForSure){ + if(player.getMatch().getPitch().getFields()[x][y].getPlayer() == null){ + player.invokeSetPosition(x, y); + if(willFailForSure){ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getRule(1).playerDown(message, player, YOU_SUCK); + }else{ + var enemyIndex = -1; + if(userIndex == 0){ + enemyIndex = 1; + }else if(userIndex == 1){ + enemyIndex = 0; + } + var mod = -(player.getMatch().getTeam(enemyIndex).getTacklezones(x, y)); + if(!(player.getRule(0).geTest(mod))){ + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + player.getRule(1).playerDown(message, player, YOU_SUCK); + } + } + }else{ + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var message = new SBProtocolMessage(player.getTeam().getCoach().getUID(), SBProtocolCommand.EVENT, " "); + playerCondition = player.getMatch().getPitch().getFields()[x][y].getPlayer().invokeGetPlayerCondition(); + defender = player.getMatch().getPitch().getFields()[x][y].getPlayer(); + player.getMatch().getPitch().getFields()[x][y].getPlayer().getRule(1).playerDown(message, player.getMatch().getPitch().getFields()[x][y].getPlayer(), ENEMY_DOWN); + if(playerCondition.equals(PlayerCondition.STUNNED) && defender.invokeGetPlayerCondition().equals(PlayerCondition.PRONE)){ + defender.invokeSetPlayerCondition(PlayerCondition.STUNNED); + } + var scatterRoll = player.getMatch().scatter(); + var newX = x + scatterRoll.x; + var newY = y + scatterRoll.y; + if(player.getMatch().getPitch().isOnField(newX, newY)){ + playerLanding(player, userIndex, newX, newY, true); + }else{ + player.getRule(2).crowdBeatsUpPlayer(player); + } + } + } + + function checkForStupid(player){ + var difficulty = 4; + if(player.getTeam().getTacklezones(player.getPos()) > 0){ + difficulty = 2; + } + if(player.getSpecialStat(stupidKey) == "false"){ + if(player.getTeam().getMatch().d6.throwDie() < difficulty){ + player.setSpecialStat(stupidKey, "true"); + player.updateActiveTackleZone(); + } + } + } + + function setPrice(player, price) { + return 110000; + } + function setGe(player, ge) { + return 1; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 5; + } + function setBe(player, be) { + return 4; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + player.addSpecialStat(stupidKey, "false"); + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveStupid = Java.extend(RuleMove); + var ruleMoveStupid = new RuleMoveStupid(rulesArray[0].getActor()){ + apply: function(message, path){ + var _super_ = Java.super(ruleMoveStupid); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var pathArray = Java.from(path); + if(pathArray.length < 2){ + if(player.invokeGetPlayerCondition() == PlayerCondition.FINE){ + return; + } + } + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stupidString); + }else{ + _super_.apply(message, path); + } + } + }; + rulesArray[0] = ruleMoveStupid; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockStupidMightyBlowRegeneration = Java.extend(RuleBlock); + var ruleBlockStupidMightyBlowRegeneration = new RuleBlockStupidMightyBlowRegeneration(rulesArray[1].getActor()) { + apply: function(message, defender){ + var _super_ = Java.super(ruleBlockStupidMightyBlowRegeneration); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stupidString); + }else{ + _super_.apply(message, defender); + } + }, + defenderDown: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderDown(message, player, defender, 1, 1); + return true; + }, + defenderStumbles: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderStumbles(message, player, defender, 1, 1); + return true; + }, + bothDown: function(message, defender) { + var _super_ = Java.super(ruleBlockStupidMightyBlowRegeneration); + defender.getRule(1).beingBlockedBothDown(message, 1, 1); + _super_.playerDown(message, player, YOU_SUCK, 0, 0); + _super_.clearHighlightFields(); + return true; + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie(); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 8){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + if(player.getMatch().d6.throwDie() > 3){ + player.invokeSetPlayerCondition(PlayerCondition.FINE); + s += ", BUT REGENERATED"; + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockStupidMightyBlowRegeneration; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStupid = Java.extend(RuleThrow); + var ruleThrowStupid = new RuleThrowStupid(rulesArray[3].getActor()){ + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStupid); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stupidString); + }else{ + _super_.apply(message, destination); + } + } + }; + rulesArray[3] = ruleThrowStupid; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchStupid = Java.extend(RuleCatch); + var ruleCatchStupid = new RuleCatchStupid(rulesArray[4].getActor()){ + apply: function(successfulThrow){ + var _super_ = Java.super(ruleCatchStupid); + if(player.getSpecialStat(stupidKey) == "true"){ + var actingUserIndex = -1; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + _super_.scatterBallAround(actingUserIndex); + _super_.sendMessageShowMe(player.toString(), stupidString); + }else{ + _super_.apply(successfulThrow); + } + } + }; + rulesArray[4] = ruleCatchStupid; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_YOUR_TURN || eventString == SBProtocolMessage.EVENT_ENDED_TURN){ + var difficulty = 3; + if(player.getTeam().getTacklezones(player.getPos()) > 0){ + difficulty = 1; + } + if(player.getTeam().getMatch().d6.throwDie() > difficulty){ + player.setSpecialStat(stupidKey, "false"); + player.updateActiveTackleZone(); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) { + var SpecialRule = Java.type("gameLogic.rules.SpecialRule"); + var specialRulesArray = []; + + var SpecialRuleThrowTeammate = Java.extend(SpecialRule); + var specialRuleThrowTeammate = new SpecialRuleThrowTeammate(player, "Throw Teammate") { + apply: function(message){ + var _super_ = Java.super(specialRuleThrowTeammate); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(player.invokeGetRemainingBe() == 0 && !(player.getTeam().getMovingPlayer() == player)){ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + return; + } + if(player.invokeGetPlayerCondition() != PlayerCondition.FINE){ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + } + _super_.checkForMovingPlayer(player); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStupidKey, player); + } + if(player.getSpecialStat(stupidKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stupidString); + }else{ + if(player.invokeGetRemainingBe() > 0){ + var parameterArray = []; + parameterArray[0] = SBProtocolMessage.EVENT_API_CHOICE; + parameterArray[1] = API_CHOICE_FUNCTION_NAME; + var playerCounter = 0; + var PitchField = Java.type("gameLogic.PitchField"); + var neighboursArray = Java.from(player.getMatch().getPitch().getNeighbours(player.getPos())); + for(var i = 0; i < neighboursArray.length; i++){ + var field = neighboursArray[i]; + if(field.getPlayer() != null){ + if(player.getTeam() == field.getPlayer().getTeam() && field.getPlayer().invokeGetPlayerCondition().equals(PlayerCondition.FINE)){ + var throwable = false; + throwable = field.getPlayer().getSpecialStat(throwableKey); + if(throwable == "true"){ + var teamIndex = -1; + if(player.getTeam() == field.getPlayer().getMatch().getTeam(0)){ + teamIndex = 0; + }else if(player.getTeam() == field.getPlayer().getMatch().getTeam(1)){ + teamIndex = 1; + } + parameterArray[2*playerCounter+2] = teamIndex; + parameterArray[2*playerCounter+3] = field.getPlayer().getId() - 1; + playerCounter++; + } + } + } + } + if(playerCounter > 0){ + player.getMatch().setGamePhase(5); + player.getMatch().setCurrentActorWaitingForAnswer(player); + _super_.sendMessage(message, SBProtocolCommand.EVENT, parameterArray); + }else{ + _super_.sendMessageShowMe(player.getTeam().getCoach(), player.toString(), "I can't find any throwable players"); + _super_.returnFailureMessage(message, FAILD_NO_THROWABLE_PLAYER_NEXT_TO_ME); + } + }else{ + _super_.returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + } + } + } + }; + specialRulesArray[0] = specialRuleThrowTeammate; + + var specialRulesToReturn = Java.to(specialRulesArray, Java.type("gameLogic.rules.SpecialRule[]")); + return specialRulesToReturn; + } + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) { + var Vector = Java.type("java.util.Vector"); + if(player.getSpecialStat(stupidKey) == "true"){ + return new Vector(); + } + } + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/PolarRegion/SeacowL.png b/resources/teams/PolarRegion/SeacowL.png new file mode 100644 index 0000000..294d099 Binary files /dev/null and b/resources/teams/PolarRegion/SeacowL.png differ diff --git a/resources/teams/PolarRegion/SeacowR.png b/resources/teams/PolarRegion/SeacowR.png new file mode 100644 index 0000000..47e194b Binary files /dev/null and b/resources/teams/PolarRegion/SeacowR.png differ diff --git a/resources/teams/Savanna/Elephant.js b/resources/teams/Savanna/Elephant.js new file mode 100644 index 0000000..7e2188c --- /dev/null +++ b/resources/teams/Savanna/Elephant.js @@ -0,0 +1,226 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + var checkForStubbornKey = "checkForStubborn"; + var stubbornKey = "isStubborn"; + var stubbornString = "I'm stubborn!"; + var YOU_SUCK = "YOU ARE "; + var SBProtocolCommand = Java.type("network.SBProtocolCommand"); + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + + function checkForStubborn(player){ + if(player.getSpecialStat(stubbornKey) == "false"){ + if(player.getTeam().getMatch().d6.throwDie() < 2){ + player.setSpecialStat(stubbornKey, "true"); + player.updateActiveTackleZone(); + } + } + } + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "High chance of hurting enemies when blocking.", + "Very tough and not easily injured.", + "Stubborn, so sometimes he'll refuse to do", + "anything." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 140000; + } + function setGe(player, ge) { + return 2; + } + function setRs(player, rs) { + return 9; + } + function setSt(player, st) { + return 5; + } + function setBe(player, be) { + return 5; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(stubbornKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveStubborn = Java.extend(RuleMove); + var ruleMoveStubborn = new RuleMoveStubborn(rulesArray[0].getActor()) { + apply: function(message, path){ + var _super_ = Java.super(ruleMoveStubborn); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var pathArray = Java.from(path); + if(pathArray.length < 2){ + if(player.invokeGetPlayerCondition() == PlayerCondition.FINE){ + return; + } + } + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStubbornKey, player); + } + if(player.getSpecialStat(stubbornKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stubbornString); + }else{ + _super_.apply(message, path); + } + + } + }; + rulesArray[0] = ruleMoveStubborn; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockStubbornMightyBlowThickSkull = Java.extend(RuleBlock); + var ruleBlockStubbornMightyBlowThickSkull = new RuleBlockStubbornMightyBlowThickSkull(rulesArray[1].getActor()) { + apply: function(message, defender){ + var _super_ = Java.super(ruleBlockStubbornMightyBlowThickSkull); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStubbornKey, player); + } + if(player.getSpecialStat(stubbornKey) == "true"){ + _super_.sendMessageShowMe(player.toString(), stubbornString); + }else{ + _super_.apply(message, defender); + } + }, + defenderDown: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderDown(message, player, defender, 1, 1); + return true; + }, + defenderStumbles: function(message, defender) { + var PitchField = Java.type("gameLogic.PitchField"); + var defenderField = new PitchField(defender.getPos()); + defender.getRule(1).beingBlockedDefenderStumbles(message, player, defender, 1, 1); + return true; + }, + bothDown: function(message, defender) { + var _super_ = Java.super(ruleBlockStubbornMightyBlowThickSkull); + defender.getRule(1).beingBlockedBothDown(message, 1, 1); + _super_.playerDown(message, player, YOU_SUCK, 0, 0); + _super_.clearHighlightFields(); + return true; + }, + injuryRoll: function(modifier){ + s = ""; + injuryRoll = player.getMatch().d6.throwDie() + player.getMatch().d6.throwDie(); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + if(injuryRoll + modifier < 9){ + player.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + player.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + casultyRoll = player.getMatch().d6.throwDie() * 10 + player.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + player.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + player.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + player.getMatch().addCasualty(player); + } + player.invokeClearPosition(); + } + return s; + } + }; + rulesArray[1] = ruleBlockStubbornMightyBlowThickSkull; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowStubborn = Java.extend(RuleThrow); + var ruleThrowStubborn = new RuleThrowStubborn(rulesArray[3].getActor()) { + apply: function(message, destination){ + var _super_ = Java.super(ruleThrowStubborn); + if(player.getTeam().getMovingPlayer() != player){ + player.invokeFunctionByName(checkForStubbornKey, player); + } + if(player.getSpecialStat(stubbornKey) == "true"){ + _super_.sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, player.toString() + " of " + player.getTeam().toString(), stubbornString); + }else{ + _super_.apply(message, destination); + } + } + }; + rulesArray[3] = ruleThrowStubborn; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchStubborn = Java.extend(RuleCatch); + var ruleCatchStubborn = new RuleCatchStubborn(rulesArray[4].getActor()) { + apply: function(successfulThrow){ + var _super_ = Java.super(ruleCatchStubborn); + if(player.getSpecialStat(stubbornKey) == "true"){ + var actingUserIndex = -1; + if(player.getTeam() == player.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(player.getTeam() == player.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + _super_.scatterBallAround(actingUserIndex); + _super_.sendMessageShowMe(player.toString(), stubbornString); + }else{ + _super_.apply(successfulThrow); + } + } + }; + rulesArray[4] = ruleCatchStubborn; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function setSpecialRules(player) {} + function setActiveTackleZone(player, activeTackleZone) { + var Vector = Java.type("java.util.Vector"); + if(player.getSpecialStat(stubbornKey) == "true"){ + return new Vector(); + } + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 1; + } + function eventHappened(player, eventString){ + if(eventString == SBProtocolMessage.EVENT_YOUR_TURN){ + if(player.getTeam().getMatch().d6.throwDie() > 1){ + player.setSpecialStat(stubbornKey, "false"); + player.updateActiveTackleZone(); + } + } + } + function setRemainingBe(player, remainingBe) {} + function setTackleZone(player, tackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Savanna/ElephantL.png b/resources/teams/Savanna/ElephantL.png new file mode 100644 index 0000000..e59e379 Binary files /dev/null and b/resources/teams/Savanna/ElephantL.png differ diff --git a/resources/teams/Savanna/ElephantR.png b/resources/teams/Savanna/ElephantR.png new file mode 100644 index 0000000..ad29426 Binary files /dev/null and b/resources/teams/Savanna/ElephantR.png differ diff --git a/resources/teams/Savanna/Giraffe.js b/resources/teams/Savanna/Giraffe.js new file mode 100644 index 0000000..a61c28f --- /dev/null +++ b/resources/teams/Savanna/Giraffe.js @@ -0,0 +1,118 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Very good at picking up the ball and passing it." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 70000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 8; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 2; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveSureHands = Java.extend(RuleMove); + var ruleMoveSureHands = new RuleMoveSureHands(rulesArray[0].getActor()) { + tryToPickUpBall: function(message, i, path){ + var _super_ = Java.super(ruleMoveSureHands); + var PlayerCondition = Java.type("gameLogic.PlayerCondition"); + var Weather = Java.type("gameLogic.Weather"); + var mod = 1-(player.getMatch().getOpposingTeam(player.getTeam()).getTacklezones(path[i].getPos())); + if(player.getMatch().getWeather() == Weather.POURING_RAIN){ + mod--; + } + if(_super_.geTest(mod) && player.invokeGetPlayerCondition()==PlayerCondition.FINE){ + return _super_.pickedUpBall(message); + }else{ + if(_super_.geTest(mod)){ + return _super_.pickedUpBall(message); + }else{ + return _super_.faildToPickUpBall(message); + } + } + } + }; + rulesArray[0] = ruleMoveSureHands; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowPass = Java.extend(RuleThrow); + var ruleThrowPass = new RuleThrowPass(rulesArray[3].getActor()) { + throwBall: function(message, destinationField, problems){ + var _super_ = Java.super(ruleThrowPass); + if(_super_.geTest(problems)){ + _super_.successfulThrow(message, destinationField); + }else{ + if(_super_.geTest(problems)){ + _super_.successfulThrow(message, destinationField); + }else{ + _super_.notWellThrown(destinationField, message); + } + } + player.invokeSetIsHoldingBall(false); + player.invokeSetRemainingBe(0); + player.getTeam().setPass(false); + player.getMatch().sendBallPos(); + if(player.getMatch().findTeamHoldingTheBall() != player.getMatch().findUserIndex(message)){ + player.getMatch().endTurn(message); + } + player.getMatch().sendPlayer(player); + } + }; + rulesArray[3] = ruleThrowPass; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} + +} \ No newline at end of file diff --git a/resources/teams/Savanna/GiraffeL.png b/resources/teams/Savanna/GiraffeL.png new file mode 100644 index 0000000..0e0836b Binary files /dev/null and b/resources/teams/Savanna/GiraffeL.png differ diff --git a/resources/teams/Savanna/GiraffeR.png b/resources/teams/Savanna/GiraffeR.png new file mode 100644 index 0000000..6da75ad Binary files /dev/null and b/resources/teams/Savanna/GiraffeR.png differ diff --git a/resources/teams/Savanna/Pelican.js b/resources/teams/Savanna/Pelican.js new file mode 100644 index 0000000..4f66ddf --- /dev/null +++ b/resources/teams/Savanna/Pelican.js @@ -0,0 +1,174 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + var alreadyDodgedKey = "hasAlreadyDodged"; + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Only pushed on 'defender stumbles'.", + "Very good at passing through tackle zones.", + "Very good at catching the ball." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 70000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 7; + } + function setSt(player, st) { + return 2; + } + function setBe(player, be) { + return 8; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 4; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + player.addSpecialStat(alreadyDodgedKey, "false"); + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockDodge = Java.extend(RuleBlock); + var ruleBlockDodge = new RuleBlockDodge(rulesArray[1].getActor()) { + beingBlockedDefenderStumbles: function(message, attacker, firstThrowModifier, injuryRollModifier){ + var _super_ = Java.super(ruleBlockDodge); + _super_.beingBlockedPushed(message, attacker, player.getPosition()); + } + }; + rulesArray[1] = ruleBlockDodge; + + var RuleMove = Java.type("gameLogic.rules.RuleMove"); + var RuleMoveDodge = Java.extend(RuleMove); + var ruleMoveDodge = new RuleMoveDodge(rulesArray[0].getActor()){ + tackleTest: function(message, problems, i, path){ + var _super_ = Java.super(ruleMoveDodge); + if(!(_super_.stayFine(problems))){ + if(player.getSpecialStat(alreadyDodgedKey) == "false"){ + player.setSpecialStat(alreadyDodgedKey, "true"); + if(!(_super_.stayFine(problems))){ + return _super_.beingTackled(message, i, path); + }else{ + return true; + } + }else{ + return _super_.beingTackled(message, i, path); + } + }else{ + return true; + } + } + }; + rulesArray[0] = ruleMoveDodge; + + var RuleThrow = Java.type("gameLogic.rules.RuleThrow"); + var RuleThrowCatch = Java.extend(RuleThrow); + var ruleThrowCatch = new RuleThrowCatch(rulesArray[3].getActor()){ + intercept: function(thrower, teamIndex, playerIndex, userIndex){ + player.getMatch().setGamePhase(3); + var mod = -2; + mod -= player.getMatch().getOpposingTeam(player.getMatch().getTeam(teamIndex)).getTacklezones(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + if(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getRule(3).geTest(mod)){ + player.invokeSetIsHoldingBall(false); + player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).invokeSetIsHoldingBall(true); + player.getMatch().getPitch().adjustBallPos(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + var actingUserIndex = -1; + if(userIndex == 0){ + actingUserIndex = 1; + }else if(userIndex == 1){ + actingUserIndex = 0; + } + player.getMatch().endTurn(actingUserIndex); + }else{ + if(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getRule(3).geTest(mod)){ + player.invokeSetIsHoldingBall(false); + player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).invokeSetIsHoldingBall(true); + player.getMatch().getPitch().adjustBallPos(player.getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + var actingUserIndex = -1; + if(userIndex == 0){ + actingUserIndex = 1; + }else if(userIndex == 1){ + actingUserIndex = 0; + } + player.getMatch().endTurn(actingUserIndex); + }else{ + thrower.getRule(3).throwBall(player.getMatch().getCurrentMessageWaitingForAnswer(), player.getMatch().getCurrentDefenderFieldWaitingForAnser(), player.getMatch().getCurrentModificatorWaitingForAnser()); + } + } + } + } + rulesArray[3] = ruleThrowCatch; + + var RuleCatch = Java.type("gameLogic.rules.RuleCatch"); + var RuleCatchCatch = Java.extend(RuleCatch); + var ruleCatchCatch = new RuleCatchCatch(rulesArray[4].getActor()){ + catchBall: function(successfulThrow, actingUserIndex){ + var _super_ = Java.super(ruleCatchCatch); + var mod = -(player.getMatch().getOpposingTeam(player.getTeam()).getTacklezones(player.getPos())); + if(successfulThrow){ + mod = mod+1; + } + var Weather = Java.type("gameLogic.Weather"); + if(player.getMatch().getWeather() == Weather.POURING_RAIN){ + mod--; + } + if(_super_.geTest(mod)){ + _super_.ballCatched(actingUserIndex); + }else{ + if(_super_.geTest(mod)){ + _super_.ballCatched(actingUserIndex); + }else{ + _super_.scatterBallAround(actingUserIndex); + } + } + } + }; + rulesArray[4] = ruleCatchCatch; + + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function eventHappened(player, eventString){ + var SBProtocolMessage = Java.type("network.SBProtocolMessage"); + if(eventString == SBProtocolMessage.EVENT_YOUR_TURN){ + player.setSpecialStat(alreadyDodgedKey, "false"); + } + } + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Savanna/PelicanL.png b/resources/teams/Savanna/PelicanL.png new file mode 100644 index 0000000..6051dab Binary files /dev/null and b/resources/teams/Savanna/PelicanL.png differ diff --git a/resources/teams/Savanna/PelicanR.png b/resources/teams/Savanna/PelicanR.png new file mode 100644 index 0000000..5344b00 Binary files /dev/null and b/resources/teams/Savanna/PelicanR.png differ diff --git a/resources/teams/Savanna/Rhino.js b/resources/teams/Savanna/Rhino.js new file mode 100644 index 0000000..ea27a07 --- /dev/null +++ b/resources/teams/Savanna/Rhino.js @@ -0,0 +1,80 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Doesn't tumble on 'both down'." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 90000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 8; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 7; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) { + var rulesArray = [ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch]; + + var RuleBlock = Java.type("gameLogic.rules.RuleBlock"); + var RuleBlockBlock = Java.extend(RuleBlock); + var ruleBlockBlock = new RuleBlockBlock(rulesArray[1].getActor()) { + beingBlockedBothDown: function(message){}, + bothDown: function(message, defender){ + defender.getRule(1).beingBlockedBothDown(message, 0, 0); + var _super_ = Java.super(ruleBlockBlock); + _super_.clearHighlightFields(); + return true; + } + }; + + rulesArray[1] = ruleBlockBlock; + var rulesToReturn = Java.to(rulesArray, Java.type("gameLogic.rules.Rule[]")); + return rulesToReturn; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 4; + } + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Savanna/RhinoL.png b/resources/teams/Savanna/RhinoL.png new file mode 100644 index 0000000..4a42e34 Binary files /dev/null and b/resources/teams/Savanna/RhinoL.png differ diff --git a/resources/teams/Savanna/RhinoR.png b/resources/teams/Savanna/RhinoR.png new file mode 100644 index 0000000..891e6ab Binary files /dev/null and b/resources/teams/Savanna/RhinoR.png differ diff --git a/resources/teams/Savanna/Zebra.js b/resources/teams/Savanna/Zebra.js new file mode 100644 index 0000000..a340874 --- /dev/null +++ b/resources/teams/Savanna/Zebra.js @@ -0,0 +1,62 @@ +load("nashorn:mozilla_compat.js"); +var imports = new JavaImporter(Packages.client, + Packages.client.display, + Packages.client.logic, + Packages.server, + Packages.server.display, + Packages.server.logic, + Packages.gameLogic, + Packages.gameLogic.dice, + Packages.gameLogic.rules, + Packages.GUI, + Packages.network, + Packages.util); +with(imports) { + + function setDescriptionLines(player, descriptionLines) { + var lines = [ + "Just a Zebra." + ]; + return Java.to(lines, Java.type("java.lang.String[]")); + } + + function setPrice(player, price) { + return 50000; + } + function setGe(player, ge) { + return 3; + } + function setRs(player, rs) { + return 8; + } + function setSt(player, st) { + return 3; + } + function setBe(player, be) { + return 6; + } + function adjustMaxHeadcount(player, maxHeadcount) { + return 16; + } + function setRules(player, ruleMove, ruleBlock, rulePush, ruleThrow, ruleCatch) {} + function eventHappened(player, eventString){} + function setRemainingBe(player, remainingBe) {} + function setSpecialRules(player) {} + function setTackleZone(player, tackleZone) {} + function setActiveTackleZone(player, activeTackleZone) {} + function setPlayerCondition(player, playerCondition) {} + function setPosition(player, position) {} + function setIsHoldingBall(player, isHoldingBall) {} + function setRedCard(player, redCard) {} + + function adjustTeam(player, team) {} + function adjustPosition(player, position) {} + function adjustRemainingBe(player, be) {} + function adjustMinHeadcount(player, minHeadcount) {} + function getGe(player, ge) {} + function getRs(player, rs) {} + function getSt(player, st) {} + function getBe(player, be) {} + function getRemainingBe(player, remainingBe) {} + function getPlayerCondition(player, playerCondition) {} +} \ No newline at end of file diff --git a/resources/teams/Savanna/ZebraL.png b/resources/teams/Savanna/ZebraL.png new file mode 100644 index 0000000..fcb28e3 Binary files /dev/null and b/resources/teams/Savanna/ZebraL.png differ diff --git a/resources/teams/Savanna/ZebraR.png b/resources/teams/Savanna/ZebraR.png new file mode 100644 index 0000000..2e6ce82 Binary files /dev/null and b/resources/teams/Savanna/ZebraR.png differ diff --git a/src/GUI/SBColor.java b/src/GUI/SBColor.java new file mode 100644 index 0000000..3f16c7e --- /dev/null +++ b/src/GUI/SBColor.java @@ -0,0 +1,53 @@ +package GUI; + +import java.awt.*; + +/** + * The colour palette + */ +public class SBColor { + public static final Color YELLOW = new Color(255, 255, 0), + YELLOW_80 = new Color(255, 255, 0, 80), + YELLOW_20 = new Color(255, 255, 0, 20), + + ORANGE_BRIGHT = new Color(255, 200, 0), + ORANGE_BRIGHT_80 = new Color(255, 200, 0, 80), + ORANGE_BRIGHT_20 = new Color(255, 200, 0, 20), + ORANGE_MEDIUM = new Color(255, 168, 3), + ORANGE_MEDIUM_80 = new Color(255, 168, 3, 80), + ORANGE_MEDIUM_20 = new Color(255, 168, 3, 20), + ORANGE_DARK = new Color(255, 140, 0), + ORANGE_DARK_80 = new Color(255, 140, 0, 80), + ORANGE_DARK_20 = new Color(255, 140, 0, 20), + + RED = new Color(255, 60, 0), + RED_80 = new Color(255, 60, 0, 80), + RED_20 = new Color(255, 60, 0, 20), + + GREEN_DARK = new Color(50, 130, 40), + GREEN_DARK_80 = new Color(50, 130, 40, 80), + GREEN_BRIGHT = new Color(100, 180, 80), + GREEN_BRIGHT_80 = new Color(100, 180, 80, 80), + + BLUE_DARK = new Color(16, 16, 119), + BLUE_DARK_80 = new Color(16, 16, 119, 80), + BLUE_BRIGHT = new Color(39, 93, 179), + BLUE_BRIGHT_80 = new Color(39, 93, 179, 80), + + BLACK = new Color(0, 0, 0), + BLACK_180 = new Color(0, 0, 0, 180), + BLACK_90 = new Color(0, 0, 0, 90), + BLACK_80 = new Color(0, 0, 0, 80), + BLACK_60 = new Color(0, 0, 0, 60), + BLACK_40 = new Color(0, 0, 0, 40), + BLACK_20 = new Color(0, 0, 0, 20), + WHITE_20 = new Color(255, 255, 255, 20), + WHITE_40 = new Color(255, 255, 255, 40), + WHITE_60 = new Color(255, 255, 255, 60), + WHITE_80 = new Color(255, 255, 255, 80), + WHITE = new Color(255, 255, 255), + + BROWN_BRIGHT = new Color(128, 100, 77), + BROWN = new Color(82, 52, 39), + BROWN_80 = new Color(82, 52, 39, 80); +} diff --git a/src/GUI/SBFrame.java b/src/GUI/SBFrame.java new file mode 100644 index 0000000..ac6b9c3 --- /dev/null +++ b/src/GUI/SBFrame.java @@ -0,0 +1,118 @@ +package GUI; + +import javax.swing.*; +import java.awt.*; + +/** + * A standard class for all frames in SafariBowl. + * Created by milan on 18.3.15. + */ +public abstract class SBFrame extends JFrame { + + private static final Dimension DEFAULT_DIMENSIONS = new Dimension(700, 500); + private static final int DEFAULT_CLOSE_OPERATION = WindowConstants.EXIT_ON_CLOSE; + + /** + * Create a new frame with a title. + * @param title The frame title. + */ + public SBFrame(String title) { + super(title); + setBounds(0, 0, DEFAULT_DIMENSIONS.width, DEFAULT_DIMENSIONS.height); + setDefaultCloseOperation(DEFAULT_CLOSE_OPERATION); + } + + /** + * Create a new frame with a title and a size. + * @param title The frame title. + * @param size The frame size. + */ + public SBFrame(String title, Dimension size) { + super(title); + setBounds(0, 0, size.width, size.height); + setDefaultCloseOperation(DEFAULT_CLOSE_OPERATION); + } + + /** + * Set the size of this frame. + * @param size The size to resize this frame to. + */ + public void setSize(Dimension size) { + setBounds(getX(), getY(), size.width, size.height); + } + + /** + * Center the frame on the screen. + */ + public void center() { + int left = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().width/2 - getWidth()/2; + int top = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().height/2 - getHeight()/2; + setBounds(left, top, getWidth(), getHeight()); + } + + public static void center(JFrame frame) { + int left = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().width/2 - frame.getWidth()/2; + int top = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().height/2 - frame.getHeight()/2; + frame.setBounds(left, top, frame.getWidth(), frame.getHeight()); + } + + public Dimension getAvailableSpaceOnScreen() { +// Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); +// Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration()); +// return new Dimension(screenSize.width - screenInsets.left - screenInsets.right, screenSize.height - screenInsets.top - screenInsets.bottom); + int screenWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().width, + screenHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds().height; + return new Dimension(screenWidth, screenHeight); + } + + /** + * A scroller class that scrolls textareas in scroll panes. + */ + private static class Scroller extends Thread { + private JTextArea area; + private JScrollPane pane; + + public Scroller(JTextArea area, JScrollPane pane) { + super(); + this.area = area; + this.pane = pane; + } + + @Override + public void run() { + int oldTextLength = 0; + while(area != null) { + int newTextLength = area.getText().length(); + if(newTextLength > oldTextLength) { + oldTextLength = newTextLength; + scrollToBottom(); + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + /** + * Scrolls the chat area to the bottom. + */ + private void scrollToBottom() { + pane.getVerticalScrollBar().setValue(pane.getVerticalScrollBar().getMaximum()); + pane.getHorizontalScrollBar().setValue(pane.getHorizontalScrollBar().getMinimum()); + pane.revalidate(); + pane.repaint(); + } + } + + /** + * Add a new scroller for the given area and scroll pane. + * @param area The area to scroll. + * @param pane The scroll pane to scroll on. + */ + public static void addScroller(JTextArea area, JScrollPane pane) { + (new Scroller(area, pane)).start(); + } + +} \ No newline at end of file diff --git a/src/GUI/SBGUIPanel.java b/src/GUI/SBGUIPanel.java new file mode 100644 index 0000000..329a5fe --- /dev/null +++ b/src/GUI/SBGUIPanel.java @@ -0,0 +1,27 @@ +package GUI; + +import javax.swing.*; +import java.awt.*; + +/** + * A superclass for GUI panels. + */ +public class SBGUIPanel extends JPanel { + + protected static final int DEFAULT_PADDING = 40; + + /** + * Create new GUI panel. + */ + public SBGUIPanel() { + super(); + } + + /** + * Create new GUI panel with a layout. + * @param layout The layout to create the GUI panel with. + */ + public SBGUIPanel(LayoutManager layout) { + super(layout); + } +} diff --git a/src/GUI/SBGamePanel.java b/src/GUI/SBGamePanel.java new file mode 100644 index 0000000..5a29410 --- /dev/null +++ b/src/GUI/SBGamePanel.java @@ -0,0 +1,28 @@ +package GUI; + +import javax.swing.*; +import java.awt.*; + +/** + * A superclass for Game panels. + */ +public class SBGamePanel extends JPanel { + + public static final int DEFAULT_PADDING = 40; + + /** + * Create new game panel. + */ + public SBGamePanel() { + super(); + } + + /** + * Create new game panel with a layout. + * @param layout The layout to create the game panel with. + */ + public SBGamePanel(LayoutManager layout) { + super(layout); + } + +} diff --git a/src/client/Client.java b/src/client/Client.java new file mode 100644 index 0000000..7ecf223 --- /dev/null +++ b/src/client/Client.java @@ -0,0 +1,629 @@ +package client; + +import client.display.*; +import client.logic.ClientMessageProcessor; +import client.logic.ClientProtocolManager; +import client.logic.ClientSocketManager; +import client.logic.ClientMatch; +import gameLogic.GameController; +import network.*; +import util.SBApplication; +import util.SBLogger; + +import java.net.InetAddress; +import java.util.UUID; +import java.util.logging.Level; + +/** + * The main server application + * Created by milan on 18.3.15. + */ +public class Client extends SBApplication { + + private static final SBLogger L = new SBLogger(Client.class.getName(), util.SBLogger.LOG_LEVEL); + public static final Level LOG_OUTPUT_LEVEL = Level.INFO; + + private ClientSocketManager socketManager; + private ClientProtocolManager protocolManager; + private ClientMessageProcessor messageProcessor; + private ClientFrame frame; + private boolean loggedIn = false, playingMatch = false; + private String username = ""; + private ClientMatch match = null; + private int clientRole = -1; + + /** + * The main method of the client. + * @param args Command line arguments. + */ + public static void main(String[] args) { + + // run client + Client client = new Client(); + client.runClient(); + + } + + /** + * Start the client application. + */ + public void runClient() { + // assign a UID to this client + UID = UUID.randomUUID(); + L.log(Level.INFO, "My UID is " + UID); + + // prepare and show connect pane + frame = new ClientFrame(this); + frame.showConnectPanel("Loading teams..."); + frame.getConnectPanel().setControlsEnabled(false); + frame.center(); + frame.setVisible(true); + loadAvailableTeamsLocally(); // prepare team manager while loading is displayed + frame.showConnectPanel("Connect to SafariBowl server"); + frame.getConnectPanel().setControlsEnabled(true); + + // prepare the message processor + messageProcessor = new ClientMessageProcessor(this); + + // prepare the protocol and socket managers + protocolManager = new ClientProtocolManager(this); + socketManager = new ClientSocketManager(this, protocolManager); + + // read old games from file + getLoggedGames(GameController.CLIENT_GAMES_FILE.getPath()); + + } + + // MESSAGE & ANSWER PROCESSING + + /** + * Tell the message processor to process any new incoming messages. + */ + public void processMessages() { + while(getProtocolManager() == null) { // sometimes accessing getProtocolManager() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + while(getProtocolManager().getMessagesToProcess() == null) { // sometimes accessing getMessagesToProcess() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + + while(getProtocolManager().getMessagesToProcess().size() > 0) { + SBProtocolMessage message = getProtocolManager().getNextMessageToProcessAndStoreIt(); + if(message.getSocket() != null) getMessageProcessor().processMessage(message); + else L.log(Level.WARNING, "Message "+message.toStringShortenUUID()+" has no socket to return answers to. Dropping."); + } + } + + /** + * Tell the answer processor to process any new incoming answers. + */ + public void processAnswers() { + while(getProtocolManager() == null) { // sometimes accessing getProtocolManager() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + while(getProtocolManager().getAnswersToProcess() == null) { // sometimes accessing getAnswersToProcess() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + + while(getProtocolManager().getAnswersToProcess().size() > 0) { + SBProtocolMessage answer = getProtocolManager().getAnswersToProcess().poll(); + if(answer.getSocket() != null) getMessageProcessor().processAnswer(answer); + else L.log(Level.WARNING, "Message "+answer.toStringShortenUUID()+" has no socket to return answers to. Dropping."); + } + } + + // ACTIONS + + /** + * Connect client to address and port. + * @param address The address to connect to. + * @param port The port to connect to. + */ + public void connect(String address, int port) { + try { + L.log(Level.INFO, "Connecting to "+address+" on port "+port+"."); + socketManager.startClient(InetAddress.getByName(address), port); + } catch (Exception e) { + L.log(Level.WARNING, "Could not connect to " + address + " on port " + port + ". "+e.toString()); + frame.showConnectPanel("Could not connect to server. Try again."); + frame.getConnectPanel().focusAddressField(); + } + } + + /** + * Try to log a user in. + * @param name The name of the user to log in. + * @param password The encrypted password of the user to log in. + */ + public void login(String name, String password) { + if(name.length() > 0) { + getMessageProcessor().loggedInWithUsername = name; + getMessageProcessor().loggedInWithEncryptedPassword = password; + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.LOGIN, new SBProtocolParameterArray(name, password))); + } + } + + /** + * Send a chat message to another user or to all users. + * @param recipientName The name of the user to send the message to. If this is 'all', the message is sent as a broadcast message to everyone. + * @param message The message to send. + */ + public void chat(String recipientName, String message) { + if(recipientName.equals("all")) socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.BDCST, new SBProtocolParameterArray(username, message))); + else socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.SENDM, new SBProtocolParameterArray(recipientName, message))); + L.log(Level.INFO, "Sent message to " + recipientName + ": " + message); + } + + /** + * Send a game message to the server. + * @param message The message to send. + */ + public void sendGameMessage(SBProtocolMessage message) { + log(Level.FINER, " Sending game message " + + message.getMID().toString().substring(0, 5) + + ": " + message.getParameters().toStringUnescaped()); + socketManager.sendMessage(message); + } + + /** + * Send a game answer to the server. + * @param beginningOfMID The beginning of the MID of the message to answer to. + * @param answerType WORKD or FAILD + * @param answerParams The params to pass with the answer. + */ + public void sendGameAnswer(String beginningOfMID, SBProtocolCommand answerType, SBProtocolParameterArray answerParams) { + SBProtocolMessage messageToRemove = null; + for(SBProtocolMessage message: getProtocolManager().getProcessedMessages()) { + if(message.getMID().toString().startsWith(beginningOfMID)) { + messageToRemove = message; + log(Level.INFO, "Sending game answer for " + + message.getMID().toString().substring(0, 5) + + ": " + answerParams.toStringUnescaped()); + if(answerType == SBProtocolCommand.FAILD) message.returnFailureMessage(getUID(), answerParams); + else message.returnSuccessMessage(getUID(), answerParams); + break; + } + } + if(messageToRemove == null) { + log(Level.WARNING, "Message with MID beginning with "+beginningOfMID+" not found."); + } + getProtocolManager().removeUnansweredMessage(messageToRemove); + } + + /** + * Surrender to your opponent. + */ + public void surrender() { + if(isPlayingMatch()) socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.SRNDR)); + else log(Level.WARNING, "Client is not in game. Won't surrender!"); + } + + /** + * End your turn. + */ + public void endTurn() { + if(isPlayingMatch()) socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.EVENT, new SBProtocolParameterArray(SBProtocolMessage.EVENT_END_TURN))); + else log(Level.WARNING, "Client is not in game. Cannot end turn."); + } + + /** + * Set up team in a cool lineup + * @param side >0 = right side, <0 = left side. + */ + public void coolLineup(int side) { + if(isPlayingMatch()) { + getMatch().settingCoolLayout = side; + int posX = -1, posY = -1; + if(side > 0) { + if(side > 10) getMatch().settingCoolLayout = 0; + else { + switch (side) { + case 1: + posX = 13; posY = 6; break; + case 2: + posX = 13; posY = 7; break; + case 3: + posX = 13; posY = 8; break; + case 4: + posX = 14; posY = 5; break; + case 5: + posX = 14; posY = 9; break; + case 6: + posX = 15; posY = 3; break; + case 7: + posX = 15; posY = 11; break; + case 8: + posX = 17; posY = 6; break; + case 9: + posX = 17; posY = 8; break; + case 10: + posX = 20; posY = 7; break; + } + side -= 1; + } + } else if(side < 0) { + if(side < -10) getMatch().settingCoolLayout = 0; + else { + switch (side) { + case -1: + posX = 12; posY = 6; break; + case -2: + posX = 12; posY = 7; break; + case -3: + posX = 12; posY = 8; break; + case -4: + posX = 11; posY = 5; break; + case -5: + posX = 11; posY = 9; break; + case -6: + posX = 10; posY = 3; break; + case -7: + posX = 10; posY = 11; break; + case -8: + posX = 8; posY = 6; break; + case -9: + posX = 8; posY = 8; break; + case -10: + posX = 5; posY = 7; break; + } + side += 1; + } + } + if(posX >= 0 && posY >= 0) socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.ACTIO, new SBProtocolParameterArray("SET PLAYER", Math.abs(side)+"", posX+"", posY+""))); + } + } + + /** + * Tell the server to put this client on a waiting list for a new game against a random opponent. + */ + public void startGame() { + if(!isPlayingMatch()) { // only start a new game if not currently in another game + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.START, new SBProtocolParameterArray())); + getMessageProcessor().waitingForGame = true; + getMessageProcessor().invitedOpponent = ""; + + getFrame().getLobbyPanel().setGameStartComponentsEnabled(false); + getFrame().getLobbyPanel().writeMessage("Waiting for a random coach to lose against you."); + L.log(Level.INFO, "Waiting for a random coach to lose against you."); + } else { + L.log(Level.WARNING, "Tried to start a new game while being in another game currently. Ignoring attempt."); + startingGameFailed(false); + } + } + + public void stopWaitingForGame() { + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.START, new SBProtocolParameterArray())); + getMessageProcessor().waitingForGame = false; + getMessageProcessor().invitedOpponent = ""; + + getFrame().getLobbyPanel().setGameStartComponentsEnabled(true); + getFrame().getLobbyPanel().writeMessage("Stopped waiting for someone to join your game."); + L.log(Level.INFO, "Stopped waiting for someone to join your game."); + } + + /** + * Tell the server to invite another player to play against this client. + * @param opponent The name of the opponent to invite. + */ + public void invitePlayer(String opponent) { + if(!isPlayingMatch()) { // only start a new game if not currently in another game + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.START, new SBProtocolParameterArray(opponent))); + getMessageProcessor().invitedOpponent = opponent; + setPlayingMatch(true); // set playing so other invitations are ignored + + getFrame().getLobbyPanel().writeMessage("Inviting " + opponent + " to lose against you."); + L.log(Level.INFO, "Inviting " + opponent + " to lose against you."); + } else { + L.log(Level.WARNING, "Tried to start a new game while being in another game currently. Ignoring attempt."); + startingGameFailed(false); + } + } + + /** + * Start a match against an opponent. + * @param opponent The name of the opponent to play against. + */ + public void createGame(String opponent) { + clientRole = 0; // 0: was invited and accepted + if(opponent.equals(getMessageProcessor().invitedOpponent)) clientRole = 1; // 1: invited other player + if(getMessageProcessor().waitingForGame) clientRole = 2; // 2: was waiting for game + + String creationMessage = "Starting match against "+opponent+"."; + if(clientRole == 1) creationMessage = opponent+" accepted invitation. Starting match."; + L.log(Level.INFO, creationMessage); + + getMessageProcessor().waitingForGame = false; + getMessageProcessor().invitedOpponent = ""; + getFrame().getLobbyPanel().setGameStartComponentsEnabled(true); + if(clientRole == 0 || clientRole == 2) setMatch(new ClientMatch(this, opponent, getUsername())); // user was invited or was waiting for game therefore is second user + else setMatch(new ClientMatch(this, getUsername(), opponent)); + setPlayingMatch(true); + + getFrame().writeMessage(creationMessage); + getFrame().showGamePanel(true); + getFrame().getGamePanel().setPitchOnCanvas(getMatch().getPitch()); + + } + + /** + * Notifies the user that starting a game has failed. + * @param resetPlayingMatch Whether the playingMatch-field should be reset to false. (Which it shouldn't if starting a new match failed because the client was already in a game) + */ + public void startingGameFailed(boolean resetPlayingMatch) { + String failureMessage = "Failed to start a game."; + if(getMessageProcessor().invitedOpponent.length() > 0) failureMessage = "Failed to start a game against "+getMessageProcessor().invitedOpponent+"."; + + getMessageProcessor().waitingForGame = false; + getMessageProcessor().invitedOpponent = ""; + if(resetPlayingMatch) setPlayingMatch(false); + getFrame().getLobbyPanel().setGameStartComponentsEnabled(true); + + L.log(Level.WARNING, failureMessage); + getFrame().getLobbyPanel().writeMessage(failureMessage); + } + + /** + * Log out this client. + */ + public void logout() { + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.LOGUT, new SBProtocolParameterArray())); + } + + /** + * A game has finished. Update UI. + */ + public void finishedGame() { + if(isPlayingMatch() && getMatch() != null) { + if(getMatch().getWinnerString().equals(getUsername())) + getFrame().showLobbyPanel("Won match against " + match.getOpponentString(getUsername()) + "!"); + else if(getMatch().getWinnerString().equals("")) + getFrame().showLobbyPanel("Tied against " + match.getOpponentString(getUsername()) + "!"); + else + getFrame().showLobbyPanel("Lost match against " + match.getOpponentString(getUsername()) + "!"); + int clientTeamIndex = match.getOpponentString(0).equals(getUsername()) ? 0 : 1; +// getGameFrame().writeMessage("Stats:"); + getFrame().writeMessage("Final score: " + match.getScoreFromTeam(clientTeamIndex) + ":" + match.getScoreFromTeam(clientTeamIndex==1?0:1)); + + setMatch(null); + setPlayingMatch(false); + } + } + + /** + * Change the name of the user this client is logged in as. + * @param newName The name to change to. + */ + public void changeName(String newName) { + if(username.length() > 0) { + getMessageProcessor().potentialNewUsername = newName; // store new name until name change is confirmed + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.CHNGE, new SBProtocolParameterArray(username, newName))); + } + } + + /** + * This is run whenever this client looses connection to the server. + */ + public void lostConnection() { + setLoggedIn(false); + getFrame().showConnectPanel("Lost connection. Connect again."); + } + + /** + * This is run whenever this client tries to connect to a server. + */ + public void isConnecting() { + getFrame().showConnectPanel("Connecting..."); + } + + /** + * This is run whenever this client has been connected to a server or a connection attempt has failed. + * @param address The address of the server this client has tried to connecte to. + * @param port The port of the server this client has tried to connecte to. + * @param connected Whether the connection attempt was successful. + */ + public void hasConnected(InetAddress address, int port, boolean connected) { + if (connected) { + L.log(Level.INFO, "Successfully connected to " + address + " on port " + port + "."); + try { Thread.sleep(100); } catch (InterruptedException ignored) {} + getSocketManager().startConnectionListener(); + frame.showLoginPanel("Connected to server. Log in."); + if(autologin) login("milan", "30f6bee6387a90c504c0116b968db626"); // logs in automatically to speed up testing + } else { + L.log(Level.WARNING, "Could not connect to " + address + " on port " + port + "."); + try { Thread.sleep(100); } catch (InterruptedException ignored) {} + frame.showConnectPanel("Could not connect to server. Try again."); + frame.getConnectPanel().focusAddressField(); + } + } + + /** + * Request a list with all logged-in users from the server. + */ + public void getUsersList() { + socketManager.sendMessage(new SBProtocolMessage(UID, SBProtocolCommand.LSUSR, new SBProtocolParameterArray())); + } + + /** + * Request a list with all running and not running games from the server. + */ + public void getGamesList() { + socketManager.sendMessage(new SBProtocolMessage(UID, SBProtocolCommand.LSGAM, new SBProtocolParameterArray())); + } + + public void updateUsersList(SBProtocolParameterArray users) { + String[][] usersString = new String[users.size()][]; + for(int i = 0; i < users.size(); i++) { + SBProtocolParameterArray user = users.getParameter(i).toArray(); + if(user.size() == 1) + usersString[i] = new String[]{user.getParameter(0).getContent()}; + else if(user.size() == 2) + usersString[i] = new String[]{user.getParameter(0).getContent(), + user.getParameter(1).getContent()}; + else if(user.size() == 3) + usersString[i] = new String[]{user.getParameter(0).getContent(), + user.getParameter(1).getContent(), + user.getParameter(2).getContent()}; + } + getFrame().updateUsersList(usersString); + } + + public void updateGamesList(SBProtocolParameterArray games) { + String[][] gamesString = new String[games.size()][]; + for(int i = 0; i < games.size(); i++) { + SBProtocolParameterArray game = games.getParameter(i).toArray(); + if(game.size() == 1) + gamesString[i] = new String[]{game.getParameter(0).getContent()}; + else if(game.size() == 4) + gamesString[i] = new String[]{game.getParameter(0).getContent(), + game.getParameter(1).getContent(), + game.getParameter(2).getContent(), + game.getParameter(3).getContent()}; + } + getFrame().updateGamesList(gamesString); + } + + public void updateHighscoreTable(SBProtocolParameterArray scores) { + String[][] scoresString = new String[scores.size()][]; + for(int i = 0; i < scores.size(); i++) { + SBProtocolParameterArray score = scores.getParameter(i).toArray(); + if(score.size() == LobbyPanel.SCORE_COLUMN_COUNT) { + String[] scoreString = new String[LobbyPanel.SCORE_COLUMN_COUNT]; + for(int j = 0; j < LobbyPanel.SCORE_COLUMN_COUNT; j++) + scoreString[j] = score.getParameter(j).getContent(); + scoresString[i] = scoreString; + } + } + getFrame().updateHighscoreList(scoresString); + } + + // HELPERS + + public void log(Level level, String message) { + if(getFrame() != null) { + if(getFrame().getLobbyPanel() != null) + if(level.intValue() >= LOG_OUTPUT_LEVEL.intValue()) getFrame().getLobbyPanel().writeMessage(message); + if(getFrame().getGamePanel() != null) + if(level.intValue() >= LOG_OUTPUT_LEVEL.intValue()) getFrame().getGamePanel().writeMessage(message); + } + if(!message.endsWith("....")) L.log(level, message); + } + + /** + * Checks whether a name is allowed. (contains no spaces, longer than 0, not 'all'). + * @param name The name to be checked. + * @return Whether the name is allowed. + */ + public boolean checkName(String name) { + return !name.contains(" ") && name.length() > 0 && !name.toLowerCase().equals("all"); + } + + /** + * Return a success answer for a received message. + * @param returnTo The message to return an answer to. + * @param parameters The parameters to send with the answer. + */ + public void returnSuccessMessage(SBProtocolMessage returnTo, String... parameters) { + returnTo.returnSuccessMessage(UID, new SBProtocolParameterArray(parameters)); + } + + /** + * Return a success answer for a received message. + * @param returnTo The message to return an answer to. + * @param parameters The parameters to send with the answer. + */ + public void returnFailureMessage(SBProtocolMessage returnTo, String... parameters) { + returnTo.returnFailureMessage(UID, new SBProtocolParameterArray(parameters)); + } + + /** + * Checks on the server whether a user exists. + * @param name The name to check. + * @return Whether the user with the given name exists. + */ + public boolean checkIfUserExists(String name) { + getMessageProcessor().setUserExists(-1); + socketManager.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.EXIST, new SBProtocolParameterArray(name))); + while(getMessageProcessor().getUserExists() < 0) { + if(socketManager.getSocket() != null) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + break; + } + } + } + int exists = getMessageProcessor().getUserExists(); + getMessageProcessor().setUserExists(-1); + if(exists == 0) return false; + else if(exists == 1) return true; + else { + L.log(Level.SEVERE, "Error while looking if user exists."); + return true; + } + } + + /** + * On the client this method just sets this client as logged-out. + */ + public void logOutAllUsers() { + setLoggedIn(false); + } + + // GETTERS & SETTERS + + public ClientMessageProcessor getMessageProcessor() { + return messageProcessor; + } + + public UUID getUID() { + return UID; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public SBSocketManager getSocketManager() { + return socketManager; + } + + public ClientProtocolManager getProtocolManager() { + return protocolManager; + } + + public ClientFrame getFrame() { + return frame; + } + + public boolean isPlayingMatch() { + return playingMatch; + } + + public void setPlayingMatch(boolean playingMatch) { + this.playingMatch = playingMatch; + } + + public boolean isLoggedIn() { + return loggedIn; + } + + public void setLoggedIn(boolean loggedIn) { + this.loggedIn = loggedIn; + } + + public ClientMatch getMatch() { + return match; + } + + public void setMatch(ClientMatch match) { + this.match = match; + } + + public int getClientRole() { + return clientRole; + } + + // UNUSED + + public void logOutUserWithUID(UUID UID) {} +} diff --git a/src/client/display/ClientFrame.java b/src/client/display/ClientFrame.java new file mode 100644 index 0000000..d4cbd45 --- /dev/null +++ b/src/client/display/ClientFrame.java @@ -0,0 +1,378 @@ +package client.display; + +import GUI.SBFrame; +import client.Client; +import gameLogic.Player; +import gameLogic.Team; +import util.ResourceManager; + +import javax.swing.*; +import java.awt.*; +import java.util.logging.Level; + +/** + * The main frame for the main client application. + * Created by milan on 23.3.15. + */ +public class ClientFrame extends SBFrame { + private static final String MAIN_TITLE = "SafariBowl", + CONNECT_TITLE = "Connect to a SafariBowl game server", + LOGIN_TITLE = "Log in to SafariBowl or create a new account", + LOBBY_TITLE = "Lobby", + GAME_TITLE = "Match"; + private JPanel mainPanel; + private CardLayout cardLayout; + private ConnectPanel connectPanel; + private LoginPanel loginPanel; + private LobbyPanel lobbyPanel; + private GameFrame gameFrame; + private Client parent; + public boolean wasPutToFront; + + /** + * Create a new client frame with a parent client. + * @param parent The client that is parent of this frame. + */ + public ClientFrame(Client parent) { + super(MAIN_TITLE); + setUp(parent); + } + + /** + * Create a new client frame with a parent client and a frame title. + * @param parent The client that is parent of this frame. + * @param title The title for the frame. + */ + public ClientFrame(Client parent, String title) { + super(title); + setUp(parent); + } + + /** + * Set the frame up. + * @param parent The client that is parent of this frame. + */ + private void setUp(Client parent) { + this.parent = parent; + + mainPanel = new JPanel(); + cardLayout = new CardLayout(); + mainPanel.setLayout(cardLayout); + + // create panels + connectPanel = new ConnectPanel(this); + loginPanel = new LoginPanel(this); + lobbyPanel = new LobbyPanel(this); + + // add panels + mainPanel.add(CONNECT_TITLE, connectPanel); + mainPanel.add(LOGIN_TITLE, loginPanel); + mainPanel.add(LOBBY_TITLE, lobbyPanel); + cardLayout.addLayoutComponent(connectPanel, CONNECT_TITLE); + cardLayout.addLayoutComponent(loginPanel, LOGIN_TITLE); + cardLayout.addLayoutComponent(lobbyPanel, LOBBY_TITLE); + add(mainPanel); + } + + // PANELS + + /** + * Show the connect panel. + * @param message The message to display on the panel. + */ + public void showConnectPanel(String message) { + if(gameFrame != null) { + gameFrame.exitFullscreen(); + gameFrame.setVisible(false); + } + setMinimumSize(new Dimension(0, 0)); + setSize(new Dimension(350, 200)); + setResizable(false); + connectPanel.setMessage(message); + cardLayout.show(mainPanel, CONNECT_TITLE); + connectPanel.focusAddressField(); + setVisible(true); + } + + /** + * Show the login panel. + * @param message The message to display on the panel. + */ + public void showLoginPanel(String message) { + if(gameFrame != null) { + gameFrame.exitFullscreen(); + gameFrame.setVisible(false); + } + setMinimumSize(new Dimension(0, 0)); + setSize(new Dimension(350, 200)); + setResizable(false); + loginPanel.setMessage(message); + cardLayout.show(mainPanel, LOGIN_TITLE); + loginPanel.focusNameField(); + setVisible(true); + } + + /** + * Show the lobby panel. + * @param message The message to display on the panel. + */ + public void showLobbyPanel(String message) { + if(gameFrame != null) { + gameFrame.exitFullscreen(); + gameFrame.setVisible(false); + } + setSize(new Dimension(749, 750)); + setResizable(true); + writeMessage(message); + cardLayout.show(mainPanel, LOBBY_TITLE); + center(); + lobbyPanel.focusChatField(); + lobbyPanel.setInvitePlayerName("someone"); + lobbyPanel.setGameStartComponentsEnabled(true); + setMinimumSize(new Dimension(400, 800)); + setVisible(true); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + lobbyPanel.setSizes(getSize()); + try { + Thread.sleep(50); + } catch(InterruptedException ignored) {} + setSize(new Dimension(750, 850)); + } + }); + } + + /** + * Show the game panel. + */ + public void showGamePanel(boolean newGame) { + setMinimumSize(new Dimension(0, 0)); + if(newGame) { + gameFrame = new GameFrame(this); + gameFrame.setSize(new Dimension(16 * 100 - 1, 10 * 100)); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + gameFrame.setSizes(gameFrame.getSize(), true); + try { + Thread.sleep(50); + } catch(InterruptedException ignored) {} + gameFrame.setSize(new Dimension(16 * 100, 10 * 100)); + } + }); + } + if(gameFrame != null) { + setVisible(false); + gameFrame.setVisible(true); + } else getClient().log(Level.WARNING, "Tried to go back into game panel without having been in it before."); + } + + // HELPERS + + /** + * Send a chat message to some- or everybody. + * @param chatField The field where the message is stored. + * @return Whether the chat recipient was auto-filled. + */ + public boolean sendMessage(JTextField chatField) { + String message, recipient; + String text = chatField.getText(); + if(chatField.getText().startsWith("@")) { + // "@milan hello there!" -> message = "hello there!" recipient = "milan" + message = text.replaceAll("^@\\S+", ""); + if(message.length() > 1) { + message = message.substring(1, message.length()); + recipient = text.substring(1, text.length() - message.length() - 1); + } + else return false; // don't send if message was empty + } else { + // check if it is a command + if(text.toLowerCase().equals("/games") || text.toLowerCase().equals("/g")) { // get games list command + writeMessage("Requesting games list..."); + getClient().log(Level.FINE, "Requesting games list."); + getClient().getGamesList(); + chatField.setText(""); + return false; + } else if(text.toLowerCase().equals("/users") || text.toLowerCase().equals("/u")) { // get users list command + writeMessage("Requesting list of users online..."); + getClient().log(Level.FINE, "Requesting list of users online."); + getClient().getUsersList(); + chatField.setText(""); + return false; + } else if(text.toLowerCase().equals("/full") && getClient().isPlayingMatch()) { // toggle fullscreen + writeMessage("Toggling fullscreen"); + getClient().log(Level.FINE, "Toggling fullscreen."); + gameFrame.toggleFullscreen(); + return false; + } else if(text.toLowerCase().equals("/arschloch") && getClient().isPlayingMatch()) { // toggle fullscreen + writeMessage("Al Capone: Fuck off."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + System.exit(-1); + } + System.exit(0); + return false; + } else if(text.toLowerCase().equals("/teams") && getClient().isPlayingMatch()) { // get list of available teams + writeMessage("Available teams:"); + for(Team availableTeam: parent.getTeamManager().getTeamBlueprints()) writeMessage(availableTeam.getType()); + chatField.setText(""); + return false; + } else if(text.toLowerCase().startsWith("/players ") && getClient().isPlayingMatch()) { // get list of available players in team + String teamName = text.substring("/players ".length()); + for(Team availableTeam: parent.getTeamManager().getTeamBlueprints()) { + if(availableTeam.getType().equalsIgnoreCase(teamName)) { + writeMessage("Available players in team " + teamName + ":"); + for(Player availablePlayer: availableTeam.getAvailablePlayers()) writeMessage(availablePlayer.getName()); + chatField.setText(""); + return false; + } + } + writeMessage("Team " + teamName + " does not exist."); + return false; + } else { + message = text; + recipient = "all"; + } + } + addChatMessage("@"+recipient.trim(), message); + getClient().chat(recipient.toLowerCase().trim(), message); + if(!recipient.equals("all")) { + chatField.setText("@"+recipient+" "); + return true; + } else { + chatField.setText(""); + return false; + } + } + + /** + * Reset the lobby panel. (Clear fields, etc.) + */ + public void resetLobbyPanel() { + lobbyPanel.resetLobby(); + } + + public void updateUsersList(String[][] users) { + if(lobbyPanel != null) { + lobbyPanel.removeAllUsersFromList(); + for(String[] user: users) + lobbyPanel.addUserToList(user); + } + } + + public void updateGamesList(String[][] games) { + if(lobbyPanel != null) { + lobbyPanel.removeAllGamesFromList(); + for(String[] game: games) + lobbyPanel.addGameToList(game); + } + } + + public void updateHighscoreList(String[][] scores) { + if(lobbyPanel != null) { + lobbyPanel.removeAllHighscoresFromTable(); + for(String[] score: scores) { + lobbyPanel.addHighscoreToTable(score); + } + } + } + + /** + * Reset the game panel. (Clear fields, etc.) + */ + public void resetGamePanel() { + getGamePanel().resetGamePanel(); + } + + /** + * Prompt the client to accept a game invitation. + * @param userInviting The name of the user inviting this client. + * @return Whether the client accepted the invitation. + */ + public boolean getInvitedAnswer(String userInviting) { + return JOptionPane.showConfirmDialog(this, + userInviting.substring(0, 1).toUpperCase() + userInviting.substring(1) + " and team invited you to a game of SafariBowl.\nAccept their invitation and show them who's the real walrus here!", + "Game invitation from " + userInviting, + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + new ImageIcon(ResourceManager.MODERATOR_WALRUS)) == 0; + } + + /** + * Prompt the client to confirm surrender. + * @return Whether the client confirmed surrender. If the client is not in a game, this will return false. + */ + public boolean getSurrenderAnswer() { + JFrame frame = gameFrame.isFullscreen() ? gameFrame.getFullscreenFrame() : gameFrame; + return parent.isPlayingMatch() + && JOptionPane.showInternalConfirmDialog(frame.getContentPane(), + "Do you really want to surrender?", + "Surrender?", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + new ImageIcon(ResourceManager.MODERATOR_TURTLE)) == 0; + } + + /** + * Write a chat message to both the lobby and the game panel. + * @param sender The sender of the message. + * @param message The message to write. + */ + public void addChatMessage(String sender, String message) { + getLobbyPanel().addChatMessage(sender, message); + if(getGamePanel() != null) getGamePanel().addChatMessage(sender, message); + } + + /** + * Write a message to both the lobby and the game panel. + * @param message The message to write. + */ + public void writeMessage(String message) { + getLobbyPanel().writeMessage(message); + if(getGamePanel() != null) getGamePanel().writeMessage(message); + } + + // GETTERS & SETTERS + + /** + * Get the parent client. + * @return The client that is parent of this frame. + */ + public Client getClient() { + return parent; + } + + /** + * Get the connect panel. + * @return The connect panel of this frame. + */ + public ConnectPanel getConnectPanel() { + return connectPanel; + } + + /** + * Get the login panel. + * @return The login panel of this frame. + */ + public LoginPanel getLoginPanel() { + return loginPanel; + } + + /** + * Get the lobby panel. + * @return The lobby panel of this frame. + */ + public LobbyPanel getLobbyPanel() { + return lobbyPanel; + } + + /** + * Get the game panel. + * @return The game panel of this frame. + */ + public GamePanel getGamePanel() { + return gameFrame == null ? null : gameFrame.getGamePanel(); + } +} diff --git a/src/client/display/ConnectPanel.java b/src/client/display/ConnectPanel.java new file mode 100644 index 0000000..3a06ef7 --- /dev/null +++ b/src/client/display/ConnectPanel.java @@ -0,0 +1,153 @@ +package client.display; + +import GUI.SBGUIPanel; +import server.Server; +import util.SBLogger; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.logging.Level; + +/** + * The client connect panel + * Created by milan on 23.3.15. + */ +public class ConnectPanel extends SBGUIPanel { + + private final JLabel messageLabel; + private final JTextField addressField, portField; + private final JButton connectButton; + private ClientFrame parent; + + /** + * Create a panel with client connect GUI to be displayed in a client frame. + * @param parent The client frame to display this panel in. + */ + public ConnectPanel(ClientFrame parent) { + super(new GridBagLayout()); + this.parent = parent; + // prepare layout + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + setLayout(layout); + + // prepare components + messageLabel = new JLabel("Connect to SafariBowl server."); + JLabel addressLabel = new JLabel("IP Address"); + addressField = new JTextField("localhost", 10); + JLabel portLabel = new JLabel("Port"); + portField = new JTextField(Server.DEFAULT_PORT+"", 10); + connectButton = new JButton("Connect"); + + // set constraints + constraints.fill = GridBagConstraints.HORIZONTAL; + + constraints.insets = new Insets(0, DEFAULT_PADDING, 0, 0); + constraints.weightx = 16; + layout.setConstraints(addressLabel, constraints); + layout.setConstraints(portLabel, constraints); + + constraints.insets = new Insets(0, 0, 0, DEFAULT_PADDING); + constraints.gridwidth = GridBagConstraints.REMAINDER; + layout.setConstraints(addressField, constraints); + layout.setConstraints(portField, constraints); + + constraints.weightx = 0; + + constraints.insets = new Insets(DEFAULT_PADDING/4, DEFAULT_PADDING, 0, DEFAULT_PADDING); + layout.setConstraints(connectButton, constraints); + + constraints.insets = new Insets(0, DEFAULT_PADDING, DEFAULT_PADDING/2, DEFAULT_PADDING); + layout.setConstraints(messageLabel, constraints); + + // add event listeners + addressField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + portField.requestFocusInWindow(); + } + }); + portField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + connectButton.doClick(); + } + }); + connectButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if(connectButton.isEnabled()) submitConnectForm(); + } + }); + connectButton.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + // submit form if enter was typed on connectButton + if (e.getKeyCode() == KeyEvent.VK_ENTER && connectButton.isEnabled()) submitConnectForm(); + } + }); + + // add components + add(messageLabel); + add(addressLabel); + add(addressField); + add(portLabel); + add(portField); + add(connectButton); + } + + /** + * Set the message that is being thrown at the user above the connect field. + * @param message The story to tell. Please don't be rude to our users. + */ + public void setMessage(String message) { + messageLabel.setText(message); + } + + /** + * Set focus to the address field. + */ + public void focusAddressField() { + addressField.requestFocusInWindow(); + } + + /** + * Set focus to the port field. + */ + public void focusPortField() { + portField.requestFocusInWindow(); + } + + /** + * Enable or disable the connect button. + * @param enable Whether the connect button should be enabled or disabled. + */ + public void setControlsEnabled(boolean enable) { + connectButton.setEnabled(enable); + } + + /** + * Tell parent to connect to server. + */ + private void submitConnectForm() { + // get address and port from fields + String address = addressField.getText(); + int port = Integer.parseInt(portField.getText()); + // submit form if valid + if(address.length() <= 0) addressField.requestFocusInWindow(); + else if(port < 0 || port > 65535) portField.requestFocusInWindow(); + else parent.getClient().connect(address, port); + } +} diff --git a/src/client/display/GameCanvas.java b/src/client/display/GameCanvas.java new file mode 100644 index 0000000..0e92b3f --- /dev/null +++ b/src/client/display/GameCanvas.java @@ -0,0 +1,673 @@ +package client.display; + +import GUI.SBColor; +import client.Client; +import client.logic.GameRenderer; +import client.logic.PitchMouseLogic; +import gameLogic.*; +import gameLogic.dice.BlockDie; +import gameLogic.rules.SpecialRule; +import network.SBProtocolMessage; +import util.ResourceManager; + +import javax.swing.*; +import javax.vecmath.Vector2d; +import java.awt.*; +import java.awt.event.*; +import java.util.LinkedList; +import java.util.Vector; +import java.util.logging.Level; + +/** + * The container in which the pitch is drawn. + */ +public class GameCanvas extends JPanel { + + public static final int MAX_TIMER_VALUE = 360, TIMER_DELAY = 10, + SHOW_ME_ALPHA = 300, SHOW_ME_ALPHA_DELAY = 200; + + private Pitch pitch = null; + private GameFrame frame; + private GameRenderer gameRenderer; + private PitchMouseLogic pitchMouseLogic; + + // API fields + private SBProtocolMessage APIMessage; + private Vector2d[] highlightAPIPositions; + private Color[] highlightAPIColors; + private int showMeAlpha; + private String showMeNow, showMeNowPlayer; + private LinkedList showMe = new LinkedList(), showMePlayer = new LinkedList(); + private boolean fieldAPI, choiceAPI, aimAPI, diceAPI; + private int aimAPIIndex = -1, aimAPIDistance; + private Player[] choiceAPIPlayers; + private Vector2d[] fieldAPIFields; + private int[] diceAPIDice; + + private Player playerChoosingSpecialRuleFor = null; + private int[] specialRuleMenuPosition, specialRuleMenuSize; + private int specialRuleMenuNameHeight, specialRuleMenuItemHeight; + + private double w, h, pW, t, pWSquared, pitchWidth, pitchHeight; + private Player movingPlayer = null; + private int[] mousePosition; + private boolean canKickOff, canSetUp, canGiveBall; + private boolean choseTeamType, choseTeam, sentChooseTeam, hasSetUpTeam; + private boolean yourTurn; + private int remainingMoney = GameController.MAX_MONEY_TO_SPEND; + /** + * shiftPressed is no boolean because if left and right shift + * are both pressed and then right shift is being released again, + * a boolean would be false even tough left shift is still pressed down. + */ + private int shiftPressed = 0; + public int frameCount = 0, frameRate = GamePanel.FRAME_RATE; + public int timer = 0; + private Team teamChosen = null; + private Vector playersChosen = new Vector(), + playersOnBench = new Vector(), + playersOnOpponentBench = new Vector(); + + public GameCanvas(GameFrame frame) { + this.frame = frame; + for (int i = 0; i < GameFrame.OLD_POSITIONS_STORED; i++) + getGameFrame().getOldMousePositions().add(new int[]{-1, -1}); + + // prepare and start mouse logic + this.pitchMouseLogic = new PitchMouseLogic(this); + + final GameCanvas thisCanvas = this; + java.awt.EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + gameRenderer = new GameRenderer(thisCanvas); + getGameFrame().getGamePanel().getInputMap().put(KeyStroke.getKeyStroke("released Y"), "releasedY"); + getGameFrame().getGamePanel().getActionMap().put("releasedY", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + shiftPressed = 0; + } + }); + getGameFrame().getGamePanel().getInputMap().put(KeyStroke.getKeyStroke("pressed Y"), "pressedShift"); + getGameFrame().getGamePanel().getActionMap().put("pressedShift", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + shiftPressed = 1; + } + }); + getGameFrame().getGamePanel().getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "pressedEnter"); + getGameFrame().getGamePanel().getActionMap().put("pressedEnter", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if(getGamePhase() == 0 && choseTeamType() && !sentChooseTeam) { // finished choosing team + chooseTeam(); + } + } + }); + getGameFrame().getGamePanel().getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressedSpace"); + getGameFrame().getGamePanel().getActionMap().put("pressedSpace", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if(isChoosingSpecialRule()) { + setPlayerChoosingSpecialRuleFor(null); + } else if(getPitchMouseLogic().isHoveringOwnPlayer() + && getPitchMouseLogic().getHoveringPlayer().getSpecialRules().length > 0 + && !getPlayersOnBench().contains(getPitchMouseLogic().getHoveringPlayer()) + && getGamePhase() == 3) { + setPlayerChoosingSpecialRuleFor(getPitchMouseLogic().getHoveringPlayer()); + setSpecialRuleMenuPosition(getPitchMouseLogic().getMXYCoord()); + } + } + }); + getGameFrame().getGamePanel().getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "pressedEscape"); + getGameFrame().getGamePanel().getActionMap().put("pressedEscape", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + setPlayerChoosingSpecialRuleFor(null); + } + }); + } + }); + addMouseListener(pitchMouseLogic.createMouseActionLogic()); + addMouseMotionListener(pitchMouseLogic.createMouseMotionLogic()); + + // prepare and start the renderer + setDoubleBuffered(true); + ActionListener renderer = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + repaint(); + frameCount++; + } + }; + (new Timer(1000/GamePanel.FRAME_RATE, renderer)).start(); + + // framerate counter + (new Timer(1000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + frameRate = frameCount; + frameCount = 0; + } + })).start(); + + // framerate counter + (new Timer(TIMER_DELAY, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + timer++; + if(timer > MAX_TIMER_VALUE) timer = 0; + } + })).start(); + } + + // HELPERS + + private String promptForTeamName() { + JFrame frame = getGameFrame().isFullscreen() ? getGameFrame().getFullscreenFrame() : getGameFrame(); + return JOptionPane.showInternalInputDialog(frame.getContentPane(), "Choose a name for your team.", "Name Team", JOptionPane.QUESTION_MESSAGE); + } + + public void chooseTeam() { + if(teamChosen != null) { + String[] playersChosenNames = new String[playersChosen.size()]; + for (int i = 0; i < playersChosen.size(); i++) playersChosenNames[i] = playersChosen.get(i).getName(); + String name = promptForTeamName(); + if(name != null) getGameFrame().chooseTeam(name, teamChosen.getType(), playersChosenNames); + } else getClient().log(Level.WARNING, "Tried to choose null team."); + } + + // DRAWING + + @Override + public void paint(Graphics g) { + Graphics2D g2D = (Graphics2D) g; + render(g2D); + } + + public void render(Graphics2D g) { + try { + if(getClient().getMatch().getWeather() != null && !getGameRenderer().preparedWeather()) getGameRenderer().prepareWeather(getClient().getMatch().getWeather(), getW(), getH(), getPitchHeight()); + + if(getPitch() != null) { + + movingPlayer = pitchMouseLogic.getMovingPlayer(); + getGameFrame().getOldMousePositions().remove(0); + getGameFrame().getOldMousePositions().add(mousePosition); + mousePosition = pitchMouseLogic.getMXYCoord(); + + // 0: Team Choosing Phase, 1: Team Setup Phase, 2: Kick Phase, 3: Normal Playing Phase, 4: Finishing Phase, 5: Waiting Phase + getGameFrame().setGamePhase(getClient().getMatch().getGamePhase()); + + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + + g.setPaint(SBColor.WHITE); + g.fillRect(0, 0, (int) getW(), (int) getH()); + GameRenderer r = getGameRenderer(); + int phase = getGamePhase(); + + if(r != null) { + + if(phase >= 1) { + r.drawGUI(g); + // r.drawPitch(g); + if(getHighlightAPIPositions() != null && getHighlightAPIColors() != null) + r.drawHighlightAPI(g, getHighlightAPIPositions(), getHighlightAPIColors()); + r.drawFieldsReachable(g); + r.drawPlayers(g); + r.drawPlayersBench(g); + } + + r.drawGameActionFields(g); + + if(phase >= 2) { + if(requestedDiceAPI()) r.drawDiceAPI(g, getDiceAPIDice()); // Dice API + } + + if(phase >= 1) { + if(canSetUp()) r.drawSetPlayer(g); + } + + if(phase >= 2) { + r.drawPath(g); + r.drawFieldAimingAt(g); + if(canGiveBall()) r.drawGiveBall(g); + // API + if(requestedAimAPI()) r.drawAimAPI(g, getAimAPIIndex(), getAimAPIDistance()); + if(requestedChoiceAPI()) r.drawChoiceAPI(g, getChoiceAPIPlayers()); + if(requestedFieldAPI()) r.drawFieldAPI(g, getFieldAPIFields()); + } + + if(phase >= 3) { + r.drawMovingPlayer(g); + } + + if(phase >= 1) { + r.drawScore(g); + r.drawRoundCount(g); + r.drawPositionMarker(g); + r.drawFrameRate(g); + r.drawWeather(g); + } + + r.drawGamePhaseInfo(g); + + if(getPitchMouseLogic().getHoveringPlayer() != null || isChoosingSpecialRule()) + r.drawTooltip(g, getPitchMouseLogic().getHoveringPlayer(), getPitchMouseLogic().getMXYCoord()); + else if(getPitchMouseLogic().getHoveringPlayerOnBench() != null) + r.drawTooltip(g, getPitchMouseLogic().getHoveringPlayerOnBench(), getPitchMouseLogic().getMXYCoord()); + + if(showMe.size() >= 0) { + if(showMeAlpha > 0 && showMeNow != null && showMeNowPlayer != null) { + r.drawShowMe(g, showMeNowPlayer, showMeNow, showMeAlpha); + if(showMeAlpha > SHOW_ME_ALPHA_DELAY) showMeAlpha -= 1; + else { + double sub = (SHOW_ME_ALPHA_DELAY + 1 - showMeAlpha) / 8; + showMeAlpha -= sub > 0 ? sub : 1; + } + } else if(showMe.size() > 0) { + showMeNow = showMe.poll(); + showMeNowPlayer = showMePlayer.poll(); + showMeAlpha = SHOW_ME_ALPHA; + } + } + + } + } + } catch(NullPointerException e) { + getClient().log(Level.WARNING, "Received nullpointer in PitchCanvas but continuing anyway."); + e.printStackTrace(); + } + } + + // API + + public void setHighlightAPIPositionsAndFields(Vector2d[] positions, Color[] colors) { + this.highlightAPIPositions = positions; + this.highlightAPIColors = colors; + } + + public Vector2d[] getHighlightAPIPositions() { + return highlightAPIPositions; + } + + public Color[] getHighlightAPIColors() { + return highlightAPIColors; + } + + public void showMe(String player, String showMe) { + if(player != null && showMe != null) { + this.showMe.add(showMe); + this.showMePlayer.add(player); + } + } + + public void setAPIMessage(SBProtocolMessage APIMessage) { + this.APIMessage = APIMessage; + } + + public SBProtocolMessage getAPIMessage() { + return APIMessage; + } + + public void setFieldAPI(boolean fieldAPI) { + this.fieldAPI = fieldAPI; + } + + public boolean requestedFieldAPI() { + return fieldAPI; + } + + public void setFieldAPIFields(Vector2d[] fieldAPIFields) { + this.fieldAPIFields = fieldAPIFields; + } + + public Vector2d[] getFieldAPIFields() { + return fieldAPIFields; + } + + public void setChoiceAPI(boolean choiceAPI) { + this.choiceAPI = choiceAPI; + } + + public boolean requestedChoiceAPI() { + return choiceAPI; + } + + public void setChoiceAPIPlayers(Player[] choiceAPIPlayers) { + this.choiceAPIPlayers = choiceAPIPlayers; + } + + public Player[] getChoiceAPIPlayers() { + return choiceAPIPlayers; + } + + public void setAimAPI(boolean aimAPI) { + this.aimAPI = aimAPI; + } + + public boolean requestedAimAPI() { + return aimAPI; + } + + public void setAimAPIIndex(int aimAPIIndex) { + this.aimAPIIndex = aimAPIIndex; + } + + public int getAimAPIIndex() { + return aimAPIIndex; + } + + public void setAimAPIDistance(int aimAPIDistance) { + this.aimAPIDistance = aimAPIDistance; + } + + public int getAimAPIDistance() { + return aimAPIDistance; + } + + public void setDiceAPI(boolean diceAPI) { + this.diceAPI = diceAPI; + } + + public boolean requestedDiceAPI() { + return diceAPI; + } + + public void setDiceAPIDice(int[] diceAPIDice) { + this.diceAPIDice = diceAPIDice; + } + + public int[] getDiceAPIDice() { + return diceAPIDice; + } + + // GETTERS & SETTERS + + public Client getClient() { + return getGameFrame().getClient(); + } + + public PitchMouseLogic getPitchMouseLogic() { + return pitchMouseLogic; + } + + public GameRenderer getGameRenderer() { + return gameRenderer; + } + + /** + * Set the size at which this pitch will be drawn. + * @param size The size at which this pitch will be drawn. + */ + public void setPreferredSize(Dimension size) { + super.setPreferredSize(size); + w = size.getWidth(); + h = size.getHeight(); + pitchWidth = w; + pitchHeight = h / 1.5; + pW = w/26; + t = pW / ResourceManager.PEDESTAL_WIDTH; + pWSquared = pW*pW; + + // move camera + GameRenderer r = getGameRenderer(); + if(r != null) { + double pitchHeight = getPitchHeight(); + r.vX = size.getWidth()/2; + r.dY = pitchHeight; + r.vZ = 2.04*pitchHeight; + r.vY = 2.814*pitchHeight; + } + + if(getGameRenderer() != null) getGameRenderer().emptyImageBuffer(); + } + + /** + * Set a pitch to draw on this pitch canvas. If pitch is null, the pitch is not drawn. + * @param pitch The pitch to draw. + */ + public void setPitch(Pitch pitch) { + this.pitch = pitch; + } + + public Pitch getPitch() { + return pitch; + } + + public GameFrame getGameFrame() { + return frame; + } + + public double getW() { + return w; + } + + public double getH() { + return h; + } + + public double getPW() { + return pW; + } + + public double getPH() { + return pW; + } + + public double getT() { + return t; + } + + public double getPitchWidth() { + return pitchWidth; + } + + public double getPitchHeight() { + return pitchHeight; + } + + public double getPWSquared() { + return pWSquared; + } + + public int[] getStoredMousePosition() { + return mousePosition; + } + + public int[] getSpecialRuleMenuPosition() { + return specialRuleMenuPosition; + } + + public void setSpecialRuleMenuPosition(int[] specialRuleMenuPosition) { + this.specialRuleMenuPosition = specialRuleMenuPosition; + } + + public int[] getSpecialRuleMenuSize() { + return specialRuleMenuSize; + } + + public void setSpecialRuleMenuSize(int[] specialRuleMenuSize) { + this.specialRuleMenuSize = specialRuleMenuSize; + } + + public int getSpecialRuleMenuNameHeight() { + return specialRuleMenuNameHeight; + } + + public void setSpecialRuleMenuNameHeight(int specialRuleMenuNameHeight) { + this.specialRuleMenuNameHeight = specialRuleMenuNameHeight; + } + + public int getSpecialRuleMenuItemHeight() { + return specialRuleMenuItemHeight; + } + + public void setSpecialRuleMenuItemHeight(int specialRuleMenuItemHeight) { + this.specialRuleMenuItemHeight = specialRuleMenuItemHeight; + } + + public Player getMovingPlayer() { + return movingPlayer; + } + + public PitchField getFieldAimingAt() { + return getPitchMouseLogic().getFieldAimingAt(); + } + + public int getClientIndex() { + return getGameFrame().getClientIndex(); + } + + public void setCanKickOff(boolean canKickOff) { + this.canKickOff = canKickOff; + } + + public boolean canKickOff() { + return canKickOff; + } + + public void setIsYourTurn(boolean yourTurn) { + this.yourTurn = yourTurn; + } + + public void setPlayerChoosingSpecialRuleFor(Player playerChoosingSpecialRuleFor) { + this.playerChoosingSpecialRuleFor = playerChoosingSpecialRuleFor; + if(playerChoosingSpecialRuleFor == null) setSpecialRuleMenuPosition(new int[]{-1000, -1000}); + } + + public void setCanSetUp(boolean canSetUp) { + this.canSetUp = canSetUp; + } + + public boolean canSetUp() { + return canSetUp; + } + + public void setChoseTeam(boolean choseTeam) { + this.choseTeam = choseTeam; + } + + public boolean choseTeam() { + return choseTeam; + } + + public void setHasSentChooseTeam(boolean sentChooseTeam) { + this.sentChooseTeam = sentChooseTeam; + } + + public boolean hasSentChooseTeam() { + return sentChooseTeam; + } + + public boolean canGiveBall() { + return canGiveBall; + } + + public void setCanGiveBall(boolean canGiveBall) { + this.canGiveBall = canGiveBall; + } + + public boolean choseTeamType() { + return choseTeamType; + } + + public void setChoseTeamType(boolean choseTeamType) { + this.choseTeamType = choseTeamType; + } + + public int getRemainingMoney() { + return remainingMoney; + } + + public void setRemainingMoney(int remainingMoney) { + this.remainingMoney = remainingMoney; + } + + public boolean isYourTurn() { + return yourTurn; + } + + public boolean isChoosingSpecialRule() { + return getPlayerChoosingSpecialRuleFor() != null; + } + + public Player getPlayerChoosingSpecialRuleFor() { + return playerChoosingSpecialRuleFor; + } + + public boolean isShiftPressed() { + return shiftPressed > 0; + } + + public Team getTeamChosen() { + return teamChosen; + } + + public void addPlayerChosen(Player player) { + if(playersChosen.size() < Team.MAX_TEAM_SIZE) { + if(getRemainingMoney() - player.getPrice() >= 0) { + playersChosen.add(player); + setRemainingMoney(getRemainingMoney() - player.getPrice()); + } + } + if(playersChosen.size() >= Team.MAX_TEAM_SIZE) chooseTeam(); + } + + public void removePlayerChosen(int index) { + if(index >= 0 && index < playersChosen.size()) { + Player playerRemoved = playersChosen.remove(index); + if(playerRemoved != null) + setRemainingMoney(getRemainingMoney() + playerRemoved.getPrice()); + } + } + + public void resetPlayersChosen() { + this.playersChosen = new Vector(); + this.remainingMoney = GameController.MAX_MONEY_TO_SPEND; + } + + public Vector getPlayersChosen() { + return playersChosen; + } + + public void addPlayerOnBench(Player player) { + if(player != null) playersOnBench.add(player); + } + + public Vector getPlayersOnBench() { + return playersOnBench; + } + + public void addPlayerOnOpponentBench(Player player) { + if(player != null) playersOnOpponentBench.add(player); + } + + public Vector getPlayersOnOpponentBench() { + return playersOnOpponentBench; + } + + public void setTeamChosen(Team teamChosen) { + this.teamChosen = teamChosen; + } + + public void setHasSetUpTeam(boolean hasSetUpTeam) { + this.hasSetUpTeam = hasSetUpTeam; + } + + public boolean hasSetUpTeam() { + return hasSetUpTeam; + } + + public int getGamePhase() { + return getGameFrame().getGamePhase(); + } + + public boolean isLeft() { + return getClientIndex() == 0; + } +} diff --git a/src/client/display/GameFrame.java b/src/client/display/GameFrame.java new file mode 100644 index 0000000..0eafd0e --- /dev/null +++ b/src/client/display/GameFrame.java @@ -0,0 +1,457 @@ +package client.display; + +import GUI.SBFrame; +import client.Client; +import client.logic.DrawingPath; +import gameLogic.Pitch; +import gameLogic.PitchField; +import gameLogic.Player; +import gameLogic.Team; +import gameLogic.rules.SpecialRule; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameter; +import network.SBProtocolParameterArray; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; +import java.util.logging.Level; + +/** + * The frame in which the game is played. + */ +public class GameFrame extends SBFrame { + + public static final double MAX_DRAG_ROTATION = Math.PI/6; + public static final int OLD_POSITIONS_STORED = 2; + + private GraphicsDevice device; + private JFrame fullscreenFrame; + private GameCanvas gameCanvas; + private GamePanel gamePanel; + private ClientFrame parent; + + private ArrayList oldMousePositions = new ArrayList(); // a list with old [x, y] coordinates of the mouse pointer + private int gameActionEventOrAnswer = 0; // 0: action, 1: event, 2: answer + private String guiGameMessage = "[]"; + private SBProtocolCommand guiGameCommand; + + private DrawingPath drawingPath = null; + private int gamePhase = -1, clientIndex = -1; + + public GameFrame(ClientFrame parent) { + super("SafariBowl"); + + // fullscreen + device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); + fullscreenFrame = new JFrame(); + fullscreenFrame.setUndecorated(true); + fullscreenFrame.setResizable(false); + + this.parent = parent; + this.gamePanel = new GamePanel(this); + this.gameCanvas = new GameCanvas(this); + + this.clientIndex = getClient().getClientRole() == 1 ? 0 : 1; + for (int i = 0; i < OLD_POSITIONS_STORED; i++) + oldMousePositions.add(new int[]{-1, -1}); + + addListeners(); + + setLayout(new BoxLayout(getContentPane(), BoxLayout.X_AXIS)); + fullscreenFrame.setLayout(new BoxLayout(fullscreenFrame.getContentPane(), BoxLayout.X_AXIS)); + + setVisible(true); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + add(gameCanvas); + add(gamePanel); + } + }); + } + + public boolean isFullscreen() { + return device.getFullScreenWindow() != null; + } + + /** + * Toggle fullscreen mode. + */ + public void toggleFullscreen() { + if(!isFullscreen()) { + setVisible(false); + device.setFullScreenWindow(fullscreenFrame); + fullscreenFrame.add(gameCanvas); + fullscreenFrame.add(gamePanel); + setSizes(fullscreenFrame.getContentPane().getSize(), false); + } else { + exitFullscreen(); + } + } + + /** + * Exit fullscreen mode. + */ + public void exitFullscreen() { + device.setFullScreenWindow(null); + setVisible(true); + setSizes(getContentPane().getSize(), true); + add(gameCanvas); + add(gamePanel); + } + + public JFrame getFullscreenFrame() { + return fullscreenFrame; + } + + public void setSizes(Dimension size, final boolean resizeFrame) { + double heightRatio = (double) Pitch.PITCH_LENGTH / ((double) Pitch.PITCH_WIDTH * 1.5); + final Dimension canvasNewSize = new Dimension((int) (heightRatio * size.getHeight()), (int) size.getHeight()), + panelNewSize = new Dimension((int) (size.getWidth() - canvasNewSize.getWidth()), + (int) size.getHeight()), + frameBorderSizes = new Dimension(getWidth() - getContentPane().getWidth(), + getHeight() - getContentPane().getHeight()), + frameNewSize = new Dimension((int) (size.getWidth()+frameBorderSizes.getWidth()), + (int) (size.getHeight()+frameBorderSizes.getHeight())); + +// if(canvasNewSize.getWidth() > maxSize.getWidth()) { +// canvasNewSize.setSize(maxSize.getWidth(), maxSize.getWidth() * heightRatio); +// frameNewSize.setSize(canvasNewSize.getWidth()+frameBorderSizes.getWidth(), +// canvasNewSize.getHeight()+frameBorderSizes.getHeight()); +// } +// if(canvasNewSize.getHeight() > maxSize.getHeight()) { +// canvasNewSize.setSize(maxSize.getHeight() / heightRatio, maxSize.getHeight()); +// frameNewSize.setSize(canvasNewSize.getWidth()+frameBorderSizes.getWidth(), +// canvasNewSize.getHeight()+frameBorderSizes.getHeight()); +// } + + if(resizeFrame) setSize(frameNewSize); + getGameCanvas().setPreferredSize(canvasNewSize); + getGamePanel().setSizes(panelNewSize); + } + + /** + * Outsourced method for setting any handlers to avoid monolith constructor. + */ + private void addListeners() { + addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if(!isFullscreen()) setSizes(getContentPane().getSize(), true); + } + }); + } + }); + } + + // GUI GAME MESSAGES + + /** + * Send a game action or event message to the server. + */ + public void sendGameMessage() { + String messageString; + SBProtocolCommand actionOrEvent; + switch (gameActionEventOrAnswer) { + case 0: + messageString = getGamePanel().getGameActionBoxSelected(); + if(messageString.startsWith("COOL LINEUP")) { + getClient().coolLineup(messageString.endsWith("RIGHT") ? 1 : -1); + return; + } + if(messageString.startsWith("SET TEAM")) messageString = "SET TEAM"; + actionOrEvent = SBProtocolCommand.ACTIO; + break; + case 1: + messageString = getGamePanel().getGameEventBoxSelected(); + actionOrEvent = SBProtocolCommand.EVENT; + break; + case 2: + String wholeString = getGamePanel().getGameActionOrEventFieldText(), answerTypeString = getGamePanel().getGameAnswerBoxSelected(); + String answerString = wholeString.replaceFirst("^\\S+ ", ""), + MID = wholeString.substring(0, wholeString.length()-answerString.length()-1); + SBProtocolParameterArray answerParams = SBProtocolParameterArray.paraception(answerString); + if(answerParams != null) { + SBProtocolCommand answerType = null; + if(answerTypeString.equals("SUC WORKD")) answerType = SBProtocolCommand.WORKD; + else if(answerTypeString.equals("FAI FAILD")) answerType = SBProtocolCommand.FAILD; + if(answerType != null) getClient().sendGameAnswer(MID, answerType, answerParams); + else { + getClient().log(Level.SEVERE, "This should not happen! GamePanel.java line 330."); + getGamePanel().writeMessage("Illegal answer type null. Not sending answer."); + } + } else { // there was an error parsing the params + getGamePanel().writeMessage("Error parsing parameters. Check for typos."); + getGamePanel().focusGameActionOrEventField(); + } + return; + default: + return; + } + SBProtocolParameterArray actionParams = SBProtocolParameterArray.paraception(getGamePanel().getGameActionOrEventFieldText()); + if(actionParams != null) { + // prepare message params + SBProtocolParameterArray messageParams = new SBProtocolParameterArray(messageString); + for(int i = 0; i < actionParams.size(); i++){ + messageParams.addParameter(actionParams.getParameter(i)); + } + // prepare message itself and send + SBProtocolMessage message = new SBProtocolMessage(getClient().getUID(), actionOrEvent, messageParams); + getClient().sendGameMessage(message); + } else { // there was an error parsing the params + getGamePanel().writeMessage("Error parsing parameters. Check for typos."); + getGamePanel().focusGameActionOrEventField(); + } + } + + public void sendPreparedGUIGameMessage() { + SBProtocolParameterArray messageParams = SBProtocolParameterArray.paraception(guiGameMessage); + if(messageParams != null) { + if(guiGameCommand != null) { + // prepare message itself and send + SBProtocolMessage message = new SBProtocolMessage(getClient().getUID(), guiGameCommand, messageParams); + getClient().sendGameMessage(message); + } else { + getGamePanel().writeMessage("Error sending game message. This should not happen, but has."); + getGamePanel().focusGameActionOrEventField(); + } + } else { // there was an error parsing the params + getGamePanel().writeMessage("Error parsing parameters. Check for typos."); + getGamePanel().focusGameActionOrEventField(); + } + } + + /** + * Prompts the user if they really want to surrender. + */ + public void surrender() { + if(getGamePanel().getClientFrame().getSurrenderAnswer()) { + getGamePanel().writeMessage("You gave up. You turtle."); + getClient().surrender(); + } + } + + /** + * End your turn. + */ + public void endTurn() { + getClient().endTurn(); + } + + public void chooseTeam(String teamName, String teamType, String... players) { + if(players.length >= Team.MIN_TEAM_SIZE && players.length <= Team.MAX_TEAM_SIZE) { + String s = "\", \""; + guiGameMessage = "[\"SET&space;TEAM\", \"" + SBProtocolParameter.escape(teamName) + s + SBProtocolParameter.escape(teamType) + s; + for (String player : players) + guiGameMessage += SBProtocolParameter.escape(player) + s; + guiGameMessage = guiGameMessage.substring(0, guiGameMessage.length() - s.length()) + "\"]"; + guiGameCommand = SBProtocolCommand.ACTIO; + sendPreparedGUIGameMessage(); + setHasSentChooseTeam(true); + } + } + + public void setDrawingPath(DrawingPath path, Player player) { + if(path == null) resetDrawingPath(); + else { + this.drawingPath = path; + updateDrawingPathString(player); + } + } + + public void resetDrawingPath() { + this.drawingPath = null; + } + + private void updateDrawingPathString(Player player) { + String s = "\", \""; + guiGameMessage = "[\"" + SBProtocolMessage.ACTIO_MOVE + s + (player.getId()-1) + "\""; + if(drawingPath.getPath().size() == 0) guiGameMessage = "[\"" + SBProtocolMessage.ACTIO_MOVE + s + (player.getId()-1) + s + player.getPos().x + s + player.getPos().y + "\""; + for(PitchField field: drawingPath.getPath()) + guiGameMessage += ", \"" + (int) field.getPos().x + s + (int) field.getPos().y + "\""; + guiGameMessage += "]"; + guiGameCommand = SBProtocolCommand.ACTIO; + } + + /** + * Send a blitz or block message to the server. + * @param path The path the player moves before the blitz or block. + * @param destination The destination of the blitz or block. + * @param actor The player blitzing or blocking. + */ + public void blitzOrBlock(DrawingPath path, PitchField destination, Player actor) { + if(path != null && destination != null && actor != null) { + try { + if(destination.getPlayer() != null) { + SBProtocolParameterArray params; + if (path.getPath().size() > 1) { // is blitz + params = new SBProtocolParameterArray(SBProtocolMessage.ACTIO_MOVE, (actor.getId() - 1) + ""); + for (PitchField field : path.getPath()) { + params.addParameter(new SBProtocolParameter((int) field.getPos().x + "")); + params.addParameter(new SBProtocolParameter((int) field.getPos().y + "")); + } + params.addParameter(new SBProtocolParameter((int) destination.getPos().x + "")); + params.addParameter(new SBProtocolParameter((int) destination.getPos().y + "")); + } else { // is block + params = new SBProtocolParameterArray(SBProtocolMessage.ACTIO_BLCK, (actor.getId() - 1) + ""); + params.addParameter(new SBProtocolParameter((destination.getPlayer().getId() - 1) + "")); + } + getClient().sendGameMessage(new SBProtocolMessage(getClient().getUID(), SBProtocolCommand.ACTIO, params)); + } + } catch (NullPointerException e) { + getClient().log(Level.WARNING, "Catched nullpointer while blitzing! (But continuing)"); + e.printStackTrace(); + } + } + } + + public void throwBall(PitchField throwDestination, Player thrower) { + if(throwDestination != null && thrower != null) { + try { + SBProtocolParameterArray params = new SBProtocolParameterArray( + "THRW", + (thrower.getId() - 1) + "", + (int) throwDestination.getPos().x + "", + (int) throwDestination.getPos().y + ""); + getClient().sendGameMessage(new SBProtocolMessage(getClient().getUID(), SBProtocolCommand.ACTIO, params)); + } catch (NullPointerException e) { + getClient().log(Level.WARNING, "Catched nullpointer while throwing! (But continuing)"); + e.printStackTrace(); + } + } + } + + public void kick(PitchField kickDestination) { + if(kickDestination != null) { + setCanKickOff(false); + getClient().getMatch().kick(kickDestination); + } + } + + public void specialRule(Player player, int specialRuleIndex) { + if(specialRuleIndex >= 0 && specialRuleIndex < player.getSpecialRules().length) { + SpecialRule specialRule = player.getSpecialRules()[specialRuleIndex]; + + if(specialRule != null) { + SBProtocolParameterArray params = new SBProtocolParameterArray(SBProtocolMessage.ACTIO_SPCL, (player.getId()-1)+"", specialRule.getName()); + getClient().sendGameMessage(new SBProtocolMessage(getClient().getUID(), SBProtocolCommand.ACTIO, params)); + getGameCanvas().setPlayerChoosingSpecialRuleFor(null); + } + } + } + + public void giveBall(Player playerReceivingBall) { + if(playerReceivingBall != null) { + setCanGiveBall(false); + getClient().getMatch().giveBall(playerReceivingBall); + } + } + + public void setPlayer(Player player, PitchField destination) { + boolean left = gameCanvas.getClientIndex() == 0; + if(player != null) { + guiGameCommand = SBProtocolCommand.ACTIO; + String s = "\", \""; + boolean send = false; + if(left && getGameCanvas().getPitch().isOnLeftHalf(destination.getPos()) + || !left && getGameCanvas().getPitch().isOnRightHalf(destination.getPos()) + || destination.equals(Pitch.THE_VOID)) // player can and wants to set on same half or back on bench + send = true; + if(send) { + guiGameMessage = "[\"" + SBProtocolParameter.escape(SBProtocolMessage.ACTIO_SET_PLAYER) + s + (player.getId()-1) + s + (int) destination.getPos().x + s + (int) destination.getPos().y + "\"]"; + sendPreparedGUIGameMessage(); + } + } + } + + public void finishedSettingPlayers() { + getGameCanvas().setHasSetUpTeam(true); + getClient().sendGameMessage(new SBProtocolMessage(getClient().getUID(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_ALL_PLAYERS_SET)); + } + + // GETTERS & SETTERS + + public Client getClient() { + return getGamePanel().getClient(); + } + + public GamePanel getGamePanel() { + return gamePanel; + } + + public GameCanvas getGameCanvas() { + return gameCanvas; + } + + public ClientFrame getClientFrame() { + return parent; + } + + public String getGuiGameMessage() { + return guiGameMessage; + } + + public void setGameActionEventOrAnswer(int gameActionEventOrAnswer) { + this.gameActionEventOrAnswer = gameActionEventOrAnswer; + } + + public int getGamePhase() { + return gamePhase; + } + + public void setGamePhase(int gamePhase) { + this.gamePhase = gamePhase; + } + + public int getClientIndex() { + return clientIndex; + } + + public ArrayList getOldMousePositions() { + return oldMousePositions; + } + + /** + * Set the pitch for the pitch canvas to draw. + * @param pitch The pitch to draw on the pitch canvas. + */ + public void setPitchOnCanvas(Pitch pitch) { + gameCanvas.setPitch(pitch); + } + + public void setCanKickOff(boolean canKickOff) { + gameCanvas.setCanKickOff(canKickOff); + } + + public void setIsYourTurn(boolean yourTurn) { + gameCanvas.setIsYourTurn(yourTurn); + } + + public void setCanSetUp(boolean canSetUp) { + gameCanvas.setCanSetUp(canSetUp); + } + + public void setHasSetUpTeam(boolean hasSetUpTeam) { + gameCanvas.setHasSetUpTeam(hasSetUpTeam); + } + + public void setHasSentChooseTeam(boolean hasSentChooseTeam) { + gameCanvas.setHasSentChooseTeam(hasSentChooseTeam); + } + + public void setChoseTeam(boolean choseTeam) { + gameCanvas.setChoseTeam(choseTeam); + } + + public void setCanGiveBall(boolean canGiveBall) { + gameCanvas.setCanGiveBall(canGiveBall); + } + +} diff --git a/src/client/display/GamePanel.java b/src/client/display/GamePanel.java new file mode 100644 index 0000000..a8bf0f4 --- /dev/null +++ b/src/client/display/GamePanel.java @@ -0,0 +1,555 @@ +package client.display; + +import GUI.SBFrame; +import GUI.SBGamePanel; +import client.Client; +import gameLogic.Pitch; +import gameLogic.Player; +import network.SBProtocolMessage; +import util.ResourceManager; + +import javax.swing.*; +import javax.vecmath.Vector2d; + +import java.awt.*; +import java.awt.event.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.logging.Level; + +/** + * The client game panel + * Created by milan on 23.3.15. + */ +public class GamePanel extends SBGamePanel { + + public static final int D = 10, CONTROLS_HEIGHT = 240, FRAME_RATE = 60; // frame rate in fps + + private JTextField chatField, gameActionOrEventField; + private JTextArea chatArea; + private JScrollPane chatAreaPane; + private JButton sendMessageButton, sendGameActionOrEventButton, surrenderButton, endTurnButton, fullscreenButton; + private JComboBox gameAnswerBox, gameActionBox, gameEventBox, chooseActionOrEventBox; + private CardLayout gameActionOrEventLayout; + private JPanel gameActionOrEventPanel, chatPanel, gameMessagePanel, otherControlsPanel, gameMessageCommandPanel; + private GameFrame gameFrame; + private HashMap gameActions = new HashMap(), + gameEvents = new HashMap(), + gameAnswers = new HashMap(); + private ClientFrame clientFrame; + private boolean autoFilledChatRecipient; + + /** + * Create a panel with client login GUI to be displayed in a game frame. + * @param gameFrame The game frame to display this panel in. + */ + public GamePanel(GameFrame gameFrame) { + super(new GridBagLayout()); + this.gameFrame = gameFrame; + this.clientFrame = gameFrame.getClientFrame(); + setUp(); + } + + private void setUp() { + // add actions, events and their descriptions + putActions(); + putEvents(); + putAnswers(); + + // prepare areas + prepareChatArea(); + prepareChatPanel(); +// prepareGameMessagePanel(); + prepareOtherControls(); + + // DOSTUFF™ + prepareLayouts(); + unsetOpaque(); + addComponents(); + addListeners(); +// showGameActionBox(); + SBFrame.addScroller(chatArea, chatAreaPane); + } + + public void setSizes(Dimension size) { + int w = size.width, h = size.height; + + chatAreaPane.setPreferredSize(new Dimension(w / 2, h - 4 * D - 5 * D)); + chatPanel.setPreferredSize(new Dimension(w / 2, 4 * D)); +// gameMessagePanel.setPreferredSize(new Dimension(w / 2, 4 * D)); +// gameMessageCommandPanel.setPreferredSize(new Dimension(w / 2, 4 * D)); + otherControlsPanel.setPreferredSize(new Dimension(w / 2, 5 * D)); + + chatField.setPreferredSize(new Dimension(chatPanel.getWidth() - sendMessageButton.getWidth() - 3 * D, 3 * D)); +// gameActionOrEventField.setPreferredSize(new Dimension(gameMessagePanel.getWidth() - sendGameActionOrEventButton.getWidth() - 3 * D, 3 * D)); + setPreferredSize(size); + } + + /** + * Set focus to the chat field. + */ + public void focusChatField() { + chatField.requestFocusInWindow(); + } + + /** + * Set focus to the change name field. + */ + public void focusGameActionOrEventField() { + gameActionOrEventField.requestFocusInWindow(); + } + + public void showGameActionBox() { + gameFrame.setGameActionEventOrAnswer(0); + gameActionOrEventLayout.show(gameActionOrEventPanel, "ACTIONBOX"); + gameActionBox.requestFocusInWindow(); + gameActionOrEventPanel.setPreferredSize(gameActionBox.getPreferredSize()); + } + + public void showGameEventBox() { + gameFrame.setGameActionEventOrAnswer(1); + gameActionOrEventLayout.show(gameActionOrEventPanel, "EVENTBOX"); + gameEventBox.requestFocusInWindow(); + gameActionOrEventPanel.setPreferredSize(gameEventBox.getPreferredSize()); + } + + public void showGameAnswerBox() { + gameFrame.setGameActionEventOrAnswer(2); + gameActionOrEventLayout.show(gameActionOrEventPanel, "ANSWERBOX"); + gameAnswerBox.requestFocusInWindow(); + gameActionOrEventPanel.setPreferredSize(gameAnswerBox.getPreferredSize()); + } + + /** + * Append a message without a sender to the message panel. + * @param message The message to append. + */ + public void writeMessage(String message) { + if(chatArea.getText().length() <= 0) chatArea.append(message); + else chatArea.append("\n" + message); + } + + /** + * Append a chat message to the message panel. + * @param sender The name of the sender of this message. + * @param message The message itself. + */ + public void addChatMessage(String sender, String message) { + if(chatArea.getText().length() <= 0) chatArea.append(sender + ": " + message); + else chatArea.append("\n" + sender + ": " + message); + } + + /** + * Reset the fields and re-enable game-start-components. + */ + public void resetGamePanel() { + focusGameActionOrEventField(); + chatField.setText(""); + chatArea.setText(""); + gameActionOrEventField.setText(""); + } + + /** + * Prompt the client to follow a pushed player. + * @return Whether the client wants to follow. + */ + public boolean getFollowAnswer() { + JFrame frame = gameFrame.isFullscreen() ? gameFrame.getFullscreenFrame() : gameFrame; + return JOptionPane.showInternalConfirmDialog(frame.getContentPane(), + "Follow pushed player?", + "Follow?", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, + new ImageIcon(ResourceManager.MODERATOR_WALRUS)) == 0; + } + + // SETUP HELPERS + + /** + * Outsourced method for setting any handlers to avoid monolith constructor. + */ + private void addListeners() { + + chatField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + autoFilledChatRecipient = clientFrame.sendMessage(chatField); + } + }); + chatField.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE && autoFilledChatRecipient) chatField.setText(""); + if (e.getKeyCode() != KeyEvent.VK_ENTER) autoFilledChatRecipient = false; + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + sendMessageButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + autoFilledChatRecipient = clientFrame.sendMessage(chatField); + } + }); + sendMessageButton.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent e) { + } + + public void keyPressed(KeyEvent e) { + } + + public void keyReleased(KeyEvent e) { + // submit form if enter was typed on logoutButton + if (e.getKeyCode() == KeyEvent.VK_ENTER) autoFilledChatRecipient = clientFrame.sendMessage(chatField); + } + }); +// gameActionOrEventField.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// gameFrame.sendGameMessage(); +// } +// }); +// sendGameActionOrEventButton.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// gameFrame.sendGameMessage(); +// } +// }); +// sendGameActionOrEventButton.addKeyListener(new KeyListener() { +// public void keyTyped(KeyEvent e) { +// } +// +// public void keyPressed(KeyEvent e) { +// } +// +// public void keyReleased(KeyEvent e) { + // submit form if enter was typed on logoutButton +// if (e.getKeyCode() == KeyEvent.VK_ENTER) gameFrame.sendGameMessage(); +// } +// }); +// chooseActionOrEventBox.addActionListener(new ActionListener() { +// @SuppressWarnings("SuspiciousMethodCalls") +// @Override +// public void actionPerformed(ActionEvent e) { +// if (chooseActionOrEventBox.getSelectedItem().equals("GAM EVENT")) { // show event box +// showGameEventBox(); +// gameActionOrEventField.setText(gameEvents.get(gameEventBox.getSelectedItem())); +// } else if (chooseActionOrEventBox.getSelectedItem().equals("GAM ACTIO")) { // show action box +// showGameActionBox(); +// gameActionOrEventField.setText(gameActions.get(gameActionBox.getSelectedItem())); +// } else { // show answer box +// showGameAnswerBox(); +// gameActionOrEventField.setText(gameAnswers.get(gameAnswerBox.getSelectedItem())); +// } +// } +// }); +// gameActionBox.addActionListener(new ActionListener() { +// @Override +// public void actionPerformed(ActionEvent e) { +// String actionString = (String) gameActionBox.getSelectedItem(); +// for (String action : gameActions.keySet()) { +// if (actionString.equals(action)) { +// gameActionOrEventField.setText(gameActions.get(action)); +// } +// } +// } +// }); +// gameEventBox.addActionListener(new ActionListener() { +// @Override +// public void actionPerformed(ActionEvent e) { +// String actionString = (String) gameEventBox.getSelectedItem(); +// for (String action : gameEvents.keySet()) { +// if (actionString.equals(action)) { +// gameActionOrEventField.setText(gameEvents.get(action)); +// } +// } +// } +// }); +// surrenderButton.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// gameFrame.surrender(); +// } +// }); +// endTurnButton.addActionListener(new ActionListener() { +// public void actionPerformed(ActionEvent e) { +// gameFrame.endTurn(); +// } +// }); + fullscreenButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + gameFrame.toggleFullscreen(); + } + }); + + chatArea.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + focusChatField(); + } + }); + + } + + private void prepareChatArea() { + chatArea = new JTextArea(); + chatAreaPane = new JScrollPane(chatArea); + chatArea.setFont(new Font("monospaced", Font.PLAIN, 12)); + chatArea.setEditable(false); + } + + private void prepareChatPanel() { + chatField = new JTextField(); + sendMessageButton = new JButton("Send"); + + chatPanel = new JPanel(); + } + + @SuppressWarnings("unchecked") + private void prepareGameMessagePanel() { + chooseActionOrEventBox = new JComboBox(new String[]{"GAM ACTIO", "GAM EVENT", "ANSWER"}); + + String[] gameActionsArray = gameActions.keySet().toArray(new String[gameActions.size()]); + Arrays.sort(gameActionsArray); + gameActionBox = new JComboBox(gameActionsArray); + String[] gameEventsArray = gameEvents.keySet().toArray(new String[gameEvents.size()]); + Arrays.sort(gameEventsArray); + gameEventBox = new JComboBox(gameEventsArray); + String[] gameAnswersArray = gameAnswers.keySet().toArray(new String[gameAnswers.size()]); + // switch fail and success so success is selected first + String fai = gameAnswersArray[0]; + gameAnswersArray[0] = gameAnswersArray[1]; + gameAnswersArray[1] = fai; + gameAnswerBox = new JComboBox(gameAnswersArray); + + gameActionOrEventPanel = new JPanel(); + gameActionOrEventLayout = new CardLayout(); + gameActionOrEventPanel.setLayout(gameActionOrEventLayout); + gameActionOrEventPanel.add("ACTIONBOX", gameActionBox); + gameActionOrEventPanel.add("EVENTBOX", gameEventBox); + gameActionOrEventPanel.add("ANSWERBOX", gameAnswerBox); + gameActionOrEventLayout.addLayoutComponent(gameActionBox, "ACTIONBOX"); + gameActionOrEventLayout.addLayoutComponent(gameEventBox, "EVENTBOX"); + gameActionOrEventLayout.addLayoutComponent(gameAnswerBox, "ANSWERBOX"); + + gameActionOrEventField = new JTextField(); + + sendGameActionOrEventButton = new JButton("Send"); + + gameMessagePanel = new JPanel(); + gameMessageCommandPanel = new JPanel(); + } + + private void prepareOtherControls() { + fullscreenButton = new JButton("Fullscreen"); +// surrenderButton = new JButton("Surrender"); +// endTurnButton = new JButton("End Turn"); + + otherControlsPanel = new JPanel(); + } + + private void prepareLayouts() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + chatPanel.setLayout(new BoxLayout(chatPanel, BoxLayout.X_AXIS)); +// gameMessageCommandPanel.setLayout(new BoxLayout(gameMessageCommandPanel, BoxLayout.X_AXIS)); +// gameMessagePanel.setLayout(new BoxLayout(gameMessagePanel, BoxLayout.X_AXIS)); + otherControlsPanel.setLayout(new BoxLayout(otherControlsPanel, BoxLayout.X_AXIS)); + } + + private void unsetOpaque() { + chatAreaPane.setOpaque(false); + chatPanel.setOpaque(false); +// gameMessageCommandPanel.setOpaque(false); +// gameMessagePanel.setOpaque(false); + otherControlsPanel.setOpaque(false); + } + + private void addComponents() { + chatAreaPane.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + + chatPanel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + chatPanel.add(chatField); + chatPanel.add(Box.createRigidArea(new Dimension(D, 0))); + chatPanel.add(sendMessageButton); + +// gameMessageCommandPanel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); +// gameMessageCommandPanel.add(Box.createHorizontalGlue()); +// gameMessageCommandPanel.add(chooseActionOrEventBox); +// gameMessageCommandPanel.add(Box.createRigidArea(new Dimension(D, 0))); +// gameMessageCommandPanel.add(gameActionOrEventPanel); + +// gameMessagePanel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); +// gameMessagePanel.add(gameActionOrEventField); +// gameMessagePanel.add(Box.createRigidArea(new Dimension(D, 0))); +// gameMessagePanel.add(sendGameActionOrEventButton); + + otherControlsPanel.setBorder(BorderFactory.createEmptyBorder(D, D, D, D)); + otherControlsPanel.add(fullscreenButton); +// otherControlsPanel.add(Box.createHorizontalGlue()); +// otherControlsPanel.add(surrenderButton); +// otherControlsPanel.add(Box.createRigidArea(new Dimension(D, 0))); +// otherControlsPanel.add(endTurnButton); + + add(chatAreaPane); + add(chatPanel); +// add(gameMessageCommandPanel); +// add(gameMessagePanel); + add(otherControlsPanel); + + } + + /** + * Add game actions and their descriptions to the game actions map + */ + private void putActions() { + gameActions.put("", ""); + gameActions.put("MOVE", "[\"playerID\", \"position1\", \"positionN\"]"); + gameActions.put("THRW", "[\"playerID\", \"coordX\", \"coordY\"]"); + gameActions.put("BLCK", "[\"playerID\", \"defenderID\"]"); + gameActions.put("PUSH", "[\"playerID\", \"defenderID\"]"); + gameActions.put("SPCL", "[\"playerID\", \"action\", \"param1\", \"paramN\"]"); + gameActions.put("SET TEAM POLARREGION", "[\"ICECOLD&space;BEASTS\", \"PolarRegion\", \"Penguin\", \"Puffin\", \"ArcticHare\", \"PolarBear\", \"Reindeer\", \"Seacow\",\"Penguin\",\"Puffin\",\"Polarbear\",\"Reindeer\"]"); + gameActions.put("SET TEAM SAVANNA", "[\"DUST-DRY&space;BEASTS\", \"Savanna\", \"Zebra\", \"Pelican\", \"Giraffe\", \"Rhino\", \"Elephant\",\"Zebra\",\"Pelican\", \"Giraffe\", \"Rhino\", \"Elephant\"]"); + gameActions.put("SET TEAM APES", "[\"FUCKIN&space;BEASTS\", \"Apes\", \"Gorilla\", \"BlackCappedSquirrelMonkey\", \"Chimpanzee\", \"Orangutan\", \"HowlerMonkey\", \"Marsupilami\",\"Gorilla\", \"BlackCappedSquirrelMonkey\", \"Chimpanzee\", \"Orangutan\"]"); + gameActions.put("SET PLAYER", "[\"playerID\", \"coordX\", \"coordY\"]"); + gameActions.put("KICK", "[\"coordX\", \"coordY\"]"); + gameActions.put("GIVE BALL", "[\"playerID\"]"); + gameActions.put("COOL LINEUP RIGHT", ""); + gameActions.put("COOL LINEUP LEFT", ""); + } + + /** + * Add game actions and their descriptions to the game actions map + */ + private void putEvents() { + gameEvents.put("", ""); + gameEvents.put("ALL PLAYERS SET", ""); + gameEvents.put(SBProtocolMessage.EVENT_END_TURN, ""); + } + + /** + * Add game actions and their descriptions to the game actions map + */ + private void putAnswers() { + gameAnswers.put("SUC WORKD", "MIDcopyfromabove [\"param1\"]"); + gameAnswers.put("FAI FAILD", "MIDcopyfromabove [\"param1\"]"); + } + + public String getGameActionBoxSelected() { + return (String) gameActionBox.getSelectedItem(); + } + + public String getGameEventBoxSelected() { + return (String) gameEventBox.getSelectedItem(); + } + + public String getGameAnswerBoxSelected() { + return (String) gameAnswerBox.getSelectedItem(); + } + + public String getGameActionOrEventFieldText() { + return gameActionOrEventField.getText(); + } + + public Client getClient() { + return clientFrame.getClient(); + } + + public ClientFrame getClientFrame() { + return clientFrame; + } + + /** + * Set the pitch for the pitch canvas to draw. + * @param pitch The pitch to draw on the pitch canvas. + */ + public void setPitchOnCanvas(Pitch pitch) { + gameFrame.setPitchOnCanvas(pitch); + } + + public void setCanKickOff(boolean canKickOff) { + gameFrame.setCanKickOff(canKickOff); + } + + public void setIsYourTurn(boolean yourTurn) { + gameFrame.setIsYourTurn(yourTurn); + } + + public void setCanSetUp(boolean canSetUp) { + gameFrame.setCanSetUp(canSetUp); + } + + public void setHasSetUpTeam(boolean hasSetUpTeam) { + gameFrame.setHasSetUpTeam(hasSetUpTeam); + } + + public void setHasSentChooseTeam(boolean hasSentChooseTeam) { + gameFrame.setHasSentChooseTeam(hasSentChooseTeam); + } + + public void setChoseTeam(boolean choseTeam) { + gameFrame.setChoseTeam(choseTeam); + } + + public void setCanGiveBall(boolean canGiveBall) { + gameFrame.setCanGiveBall(canGiveBall); + } + + // API + + public void setHighlightAPIPositionsAndColors(Vector2d[] fields, Color[] colors){ + if(fields.length == colors.length) + gameFrame.getGameCanvas().setHighlightAPIPositionsAndFields(fields, colors); + else + getClient().log(Level.SEVERE, "Highlight API: fields and colours length do not match!"); + } + + public void clearHighlightAPIPositionsAndColors() { + gameFrame.getGameCanvas().setHighlightAPIPositionsAndFields(null, null); + } + + public void showMe(String player, String showMe) { + gameFrame.getGameCanvas().showMe(player, showMe); + } + + public void setAPIMessage(SBProtocolMessage message) { + gameFrame.getGameCanvas().setAPIMessage(message); + } + + public void setDiceAPI(boolean set) { + gameFrame.getGameCanvas().setDiceAPI(set); + } + + public void setDiceAPIDice(int[] dice){ + gameFrame.getGameCanvas().setDiceAPIDice(dice); + } + + public void setAimAPI(boolean set){ + gameFrame.getGameCanvas().setAimAPI(set); + } + + public void setAimAPIIndex(int index){ + gameFrame.getGameCanvas().setAimAPIIndex(index); + } + + public void setAimAPIDistance(int distance){ + gameFrame.getGameCanvas().setAimAPIDistance(distance); + } + + public void setChoiceAPI(boolean set){ + gameFrame.getGameCanvas().setChoiceAPI(set); + } + + public void setChoiceAPIPlayers(Player[] players){ + gameFrame.getGameCanvas().setChoiceAPIPlayers(players); + } + + public void setFieldAPI(boolean set){ + gameFrame.getGameCanvas().setFieldAPI(set); + } + + public void setFieldAPIFields(Vector2d[] fields){ + gameFrame.getGameCanvas().setFieldAPIFields(fields); + } +} diff --git a/src/client/display/LobbyPanel.java b/src/client/display/LobbyPanel.java new file mode 100644 index 0000000..07f1ff6 --- /dev/null +++ b/src/client/display/LobbyPanel.java @@ -0,0 +1,472 @@ +package client.display; + +import GUI.SBFrame; +import GUI.SBGUIPanel; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableModel; +import java.awt.*; +import java.awt.event.*; + +/** + * The client lobby panel + * Created by milan on 23.3.15. + */ +public class LobbyPanel extends SBGUIPanel { + + private static final int D = 10; + public static final int SCORE_COLUMN_COUNT = 8; + private JPanel chatPanel, usersAndGamesPanel, usersPanel, gamesPanel, otherControlsPanel; + @SuppressWarnings("FieldCanBeLocal") + private JTable highscoreTable; + private DefaultTableModel highscoreTableModel; + private JLabel gamesLabel, usersLabel, highscoreLabel; + @SuppressWarnings("FieldCanBeLocal") + private JList gamesList, usersList; + private DefaultListModel gamesListModel, usersListModel; + private JTextField chatField, newNameField; + private JTextArea chatArea; + private JScrollPane chatAreaPane, usersListPane, gamesListPane, highscorePane; + private JButton logoutButton, sendMessageButton, changeNameButton, startGameButton, inviteButton; + private ClientFrame parent; + private boolean autoFilledChatRecipient; + private int lastSelectedUserIndex = -1; + + /** + * Create a panel with client login GUI to be displayed in a client frame. + * @param parent The client frame to display this panel in. + */ + public LobbyPanel(ClientFrame parent) { + super(new GridBagLayout()); + this.parent = parent; + setUp(); + } + + private void setUp() { + // prepare areas + prepareChatArea(); + prepareChatPanel(); + prepareUsersAndGames(); + prepareHighscore(); + prepareOtherControls(); + + // DOSTUFF™ + prepareLayouts(); + addComponents(); + addListeners(); + SBFrame.addScroller(chatArea, chatAreaPane); + } + + public void setSizes(Dimension size) { + int w = size.width, h = size.height; + + chatAreaPane.setPreferredSize(new Dimension(w / 2, h - 4 * D - 5 * D - 28 * D - 14 * D)); + chatPanel.setPreferredSize(new Dimension(w / 2, 4 * D)); + chatField.setPreferredSize(new Dimension(chatPanel.getWidth() - sendMessageButton.getWidth() - 3 * D, 3 * D)); + + otherControlsPanel.setPreferredSize(new Dimension(w / 2, 5 * D)); + + int wThird = w/3 - D/2, wTwoThird = 2*w/3 - D/2; + usersAndGamesPanel.setPreferredSize(new Dimension(w / 2, 28 * D)); + usersPanel.setPreferredSize(new Dimension(wThird, 28 * D)); + gamesPanel.setPreferredSize(new Dimension(wTwoThird, 28 * D)); + + usersLabel.setPreferredSize(new Dimension(wThird - 2 * D, 3 * D)); + usersListPane.setPreferredSize(new Dimension(wThird - 2 * D, 22 * D)); + inviteButton.setPreferredSize(new Dimension(wThird - 2 * D, 3 * D)); + + highscoreLabel.setPreferredSize(new Dimension(w / 2, 3 * D)); + highscorePane.setPreferredSize(new Dimension(w / 2, 14 * D)); + + gamesLabel.setPreferredSize(new Dimension(wTwoThird - 2 * D, 3 * D)); + gamesListPane.setPreferredSize(new Dimension(wTwoThird - 2 * D, 22 * D)); + startGameButton.setPreferredSize(new Dimension(wTwoThird - 2 * D, 3 * D)); + + setPreferredSize(size); + } + + /** + * Set focus to the chat field. + */ + public void focusChatField() { + chatField.requestFocusInWindow(); + } + + /** + * Set focus to the change name field. + */ + public void focusChangeNameField() { + newNameField.requestFocusInWindow(); + } + + /** + * Some cool magic stuff. + * @param name The name of someone. Maybe you? + */ + public void setInvitePlayerName(String name) { + if(name.length() <= 0) name = "someone"; + inviteButton.setText("Play against " + name); + if(inviteButton.getText().equals("someone")) inviteButton.setEnabled(false); + else if(name.equals(parent.getClient().getUsername())) { + inviteButton.setText("This is you"); + inviteButton.setEnabled(false); + } else if(name.matches("[^ ]+ – in game.*")) { + inviteButton.setText("Already in game"); + inviteButton.setEnabled(false); + } else if(!startGameButton.getText().startsWith("Stop")) { + inviteButton.setEnabled(true); + } + } + + /** + * Append a message without a sender to the message panel. + * @param message The message to append. + */ + public void writeMessage(String message) { + if(chatArea.getText().length() <= 0) chatArea.append(message); + else chatArea.append("\n" + message); + if(message.startsWith("Loading")) parent.getConnectPanel().setMessage(message); + } + + /** + * Append a chat message to the message panel. + * @param sender The name of the sender of this message. + * @param message The message itself. + */ + public void addChatMessage(String sender, String message) { + if(chatArea.getText().length() <= 0) chatArea.append(sender + ": " + message); + else chatArea.append("\n" + sender + ": " + message); + } + + /** + * Reset the fields and re-enable game-start-components. + */ + public void resetLobby() { + focusChatField(); + chatField.setText(""); + chatArea.setText(""); + newNameField.setText(""); + setGameStartComponentsEnabled(true); + } + + /** + * Invite a player to start a game. + */ + public void invitePlayerToGame() { + String opponent = usersList.getSelectedValue().replaceFirst(" – .*", ""); + if(opponent != null) { + opponent = opponent.trim().toLowerCase(); + if(parent.getClient().checkName(opponent)) { + startGameButton.setText("Waiting for answer"); + startGameButton.setEnabled(false); + inviteButton.setText("Invited " + usersList.getSelectedValue().replaceFirst(" – .*", "")); + inviteButton.setEnabled(false); + parent.getClient().invitePlayer(opponent); + } else { + writeMessage(opponent + " is no valid name."); + } + } + } + + /** + * Log out. + */ + private void logout() { + parent.getClient().logout(); + } + + /** + * Start a new game. + */ + private void startGameOrStopWaiting() { + if(startGameButton.getText().toLowerCase().startsWith("start")) { + parent.getClient().startGame(); + focusChatField(); + } else { + parent.getClient().stopWaitingForGame(); + focusChatField(); + } + } + + /** + * Change the name of the logged-in user. + */ + public void changeName() { + String newName = newNameField.getText().toLowerCase().trim(); + parent.getClient().changeName(newName); + newNameField.setText(""); + focusChatField(); + } + + /** + * Let the user to start a new game or not. + * @param set Whether the user should be able to start a new game from the GUI. + */ + public void setGameStartComponentsEnabled(boolean set) { + if(usersList.getSelectedValue() != null) { + setInvitePlayerName(usersList.getSelectedValue().replaceFirst(" – waiting.*", "")); + inviteButton.setEnabled(true); + } else { + setInvitePlayerName("someone"); + inviteButton.setEnabled(false); + + } + if(set) { + startGameButton.setText("Start Game"); + startGameButton.setEnabled(true); + } else { + startGameButton.setText("Stop Waiting"); + } + } + + // SETUP HELPERS + + private void prepareChatArea() { + chatArea = new JTextArea(); + chatAreaPane = new JScrollPane(chatArea); + chatAreaPane.setOpaque(false); +// chatArea.setFont(new Font("monospaced", Font.PLAIN, 12)); + chatArea.setEditable(false); + } + + private void prepareChatPanel() { + chatField = new JTextField(); + sendMessageButton = new JButton("Send"); + + chatPanel = new JPanel(); + } + + private void prepareUsersAndGames() { + gamesLabel = new JLabel("Games:"); + gamesLabel.setHorizontalAlignment(SwingConstants.LEFT); + gamesListModel = new DefaultListModel(); + gamesList = new JList(gamesListModel); + gamesList.setEnabled(false); + gamesList.setLayoutOrientation(JList.VERTICAL); + gamesList.setVisibleRowCount(-1); + gamesListPane = new JScrollPane(gamesList); + gamesListPane.setOpaque(false); + + usersLabel = new JLabel("Users online:"); + usersLabel.setHorizontalAlignment(SwingConstants.LEFT); + usersListModel = new DefaultListModel(); + usersList = new JList(usersListModel); + usersList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + usersList.setLayoutOrientation(JList.VERTICAL); + usersList.setVisibleRowCount(-1); + usersListPane = new JScrollPane(usersList); + usersListPane.setOpaque(false); + + startGameButton = new JButton("Start Game"); + inviteButton = new JButton(); + setInvitePlayerName("someone"); + + usersPanel = new JPanel(); + gamesPanel = new JPanel(); + usersAndGamesPanel = new JPanel(); + } + + private void prepareHighscore() { + highscoreLabel = new JLabel("Highscores:"); + highscoreLabel.setHorizontalAlignment(SwingConstants.LEFT); + highscoreTableModel = new DefaultTableModel(new Object[0][SCORE_COLUMN_COUNT+1], new String[]{"Rank", "Name", "Wins", "Losses", "Ratio", "Scored", "Received", "Ratio", "Casualties"}); + highscoreTable = new JTable(highscoreTableModel); + highscoreTable.setEnabled(false); + + highscorePane = new JScrollPane(highscoreTable); + highscorePane.setOpaque(false); + } + + private void prepareOtherControls() { + newNameField = new JTextField(10); + changeNameButton = new JButton("Change username"); + logoutButton = new JButton("Logout"); + + otherControlsPanel = new JPanel(); + } + + private void prepareLayouts() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + chatPanel.setLayout(new BoxLayout(chatPanel, BoxLayout.X_AXIS)); + usersAndGamesPanel.setLayout(new BoxLayout(usersAndGamesPanel, BoxLayout.X_AXIS)); + usersPanel.setLayout(new BoxLayout(usersPanel, BoxLayout.Y_AXIS)); + gamesPanel.setLayout(new BoxLayout(gamesPanel, BoxLayout.Y_AXIS)); + otherControlsPanel.setLayout(new BoxLayout(otherControlsPanel, BoxLayout.X_AXIS)); + } + + private void addComponents() { + chatAreaPane.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + + chatPanel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + chatPanel.add(chatField); + chatPanel.add(Box.createRigidArea(new Dimension(D, 0))); + chatPanel.add(sendMessageButton); + + usersPanel.add(usersLabel); + usersPanel.add(Box.createRigidArea(new Dimension(0, D))); + usersPanel.add(usersListPane); + usersPanel.add(Box.createRigidArea(new Dimension(0, D))); + usersPanel.add(inviteButton); + gamesPanel.add(gamesLabel); + gamesPanel.add(Box.createRigidArea(new Dimension(0, D))); + gamesPanel.add(gamesListPane); + gamesPanel.add(Box.createRigidArea(new Dimension(0, D))); + gamesPanel.add(startGameButton); + + highscoreLabel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + highscorePane.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + + usersAndGamesPanel.setBorder(BorderFactory.createEmptyBorder(D, D, 0, D)); + usersAndGamesPanel.add(usersPanel); + usersAndGamesPanel.add(Box.createRigidArea(new Dimension(D, D))); + usersAndGamesPanel.add(gamesPanel); + + otherControlsPanel.setBorder(BorderFactory.createEmptyBorder(D, D, D, D)); + otherControlsPanel.add(newNameField); + otherControlsPanel.add(Box.createRigidArea(new Dimension(D, 0))); + otherControlsPanel.add(changeNameButton); + otherControlsPanel.add(Box.createHorizontalGlue()); + otherControlsPanel.add(logoutButton); + + add(chatAreaPane); + add(chatPanel); + add(usersAndGamesPanel); + add(highscoreLabel); + add(highscorePane); + add(otherControlsPanel); + } + + /** + * Outsourced method for setting any handlers to avoid monolith constructor. + */ + private void addListeners() { + + chatField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + autoFilledChatRecipient = parent.sendMessage(chatField); + } + }); + chatField.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE && autoFilledChatRecipient) chatField.setText(""); + if(e.getKeyCode() != KeyEvent.VK_ENTER) autoFilledChatRecipient = false; + } + }); + sendMessageButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + autoFilledChatRecipient = parent.sendMessage(chatField); + } + }); + sendMessageButton.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent e) { + // submit form if enter was typed on logoutButton + if(e.getKeyCode() == KeyEvent.VK_ENTER) autoFilledChatRecipient = parent.sendMessage(chatField); + } + }); + startGameButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + startGameOrStopWaiting(); + } + }); + inviteButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + invitePlayerToGame(); + } + }); + usersList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if(!e.getValueIsAdjusting()) { + if(usersList.getSelectedValue() != null) { + setInvitePlayerName(usersList.getSelectedValue().replaceFirst(" – waiting.*", "")); + } + } + } + }); + usersList.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_ENTER) invitePlayerToGame(); + } + }); + changeNameButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + changeName(); + } + }); + logoutButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + logout(); + } + }); + + // listener for size changes + addHierarchyBoundsListener(new HierarchyBoundsListener() { + @Override + public void ancestorMoved(HierarchyEvent e) { + } + + @Override + public void ancestorResized(HierarchyEvent e) { + if(e.getChanged() == getParent()) setSizes(e.getChanged().getSize()); + } + }); + } + + // GETTERS & SETTERS + + public void addUserToList(String[] user) { + switch(user.length) { + case 1: // in lobby + usersListModel.addElement(user[0]); + break; + case 2: // waiting for game + usersListModel.addElement(user[0] + " – waiting for game"); + break; + case 3: // in game + usersListModel.addElement(user[0] + " – in game: " + user[1] + ":" + user[2]); + break; + } + usersList.setSelectedIndex(lastSelectedUserIndex); + } + + public void removeAllUsersFromList() { + lastSelectedUserIndex = usersList.getSelectedIndex(); + usersListModel.removeAllElements(); + } + + public void addGameToList(String[] game) { + switch(game.length) { + case 1: // user waiting for game + gamesListModel.addElement(game[0] + " is waiting for someone to join"); + break; + case 4: // running game + gamesListModel.addElement(game[0] + " is playing against " + game[1] + ". " + game[2] + ":" + game[3]); + break; + } + } + + public void removeAllHighscoresFromTable() { + for(int i = 0; i < highscoreTableModel.getRowCount(); i++) { + highscoreTableModel.removeRow(0); + } + highscoreTableModel.setRowCount(0); + } + + public void addHighscoreToTable(String[] score) { + if(score.length == SCORE_COLUMN_COUNT) { + String[] newData = new String[SCORE_COLUMN_COUNT+1]; + newData[0] = (highscoreTableModel.getRowCount()+1) + ""; + System.arraycopy(score, 0, newData, 1, SCORE_COLUMN_COUNT); + highscoreTableModel.addRow(newData); + } + } + + public void removeAllGamesFromList() { + gamesListModel.removeAllElements(); + } + +} diff --git a/src/client/display/LoginPanel.java b/src/client/display/LoginPanel.java new file mode 100644 index 0000000..60ba573 --- /dev/null +++ b/src/client/display/LoginPanel.java @@ -0,0 +1,231 @@ +package client.display; + +import GUI.SBGUIPanel; +import util.SBLogger; + +import javax.swing.*; +import javax.xml.bind.DatatypeConverter; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.security.MessageDigest; +import java.util.logging.Level; + +/** + * The client login panel + * Created by milan on 23.3.15. + */ +public class LoginPanel extends SBGUIPanel { + + private static final SBLogger L = new SBLogger(LoginPanel.class.getName(), util.SBLogger.LOG_LEVEL); + + private final JLabel messageLabel; + private final JTextField nameField; + private final JPasswordField passwordField; + private final JButton loginButton; + private ClientFrame parent; + + /** + * Create a panel with client login GUI to be displayed in a client frame. + * @param parent The client frame to display this panel in. + */ + public LoginPanel(ClientFrame parent) { + super(new GridBagLayout()); + this.parent = parent; + // prepare layout + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + setLayout(layout); + + // prepare components + messageLabel = new JLabel("Log in to SafariBowl."); + JLabel nameLabel = new JLabel("Name"); + nameField = new JTextField(System.getProperty("user.name"), 10); + JLabel passwordLabel = new JLabel("Password"); + passwordField = new JPasswordField(10); + loginButton = new JButton("Login or Sign up"); + + // set constraints + constraints.fill = GridBagConstraints.HORIZONTAL; + + constraints.insets = new Insets(0, DEFAULT_PADDING, 0, 0); + constraints.weightx = 16; + layout.setConstraints(nameLabel, constraints); + layout.setConstraints(passwordLabel, constraints); + + constraints.insets = new Insets(0, 0, 0, DEFAULT_PADDING); + constraints.gridwidth = GridBagConstraints.REMAINDER; + layout.setConstraints(nameField, constraints); + layout.setConstraints(passwordField, constraints); + + constraints.weightx = 0; + + constraints.insets = new Insets(DEFAULT_PADDING/4, DEFAULT_PADDING, 0, DEFAULT_PADDING); + layout.setConstraints(loginButton, constraints); + + constraints.insets = new Insets(0, DEFAULT_PADDING, DEFAULT_PADDING/2, DEFAULT_PADDING); + layout.setConstraints(messageLabel, constraints); + + // add event listeners + nameField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + passwordField.requestFocusInWindow(); + } + }); + passwordField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + loginButton.doClick(); + } + }); + loginButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + submitLoginForm(); + } + }); + loginButton.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) {} + + @Override + public void keyPressed(KeyEvent e) {} + + @Override + public void keyReleased(KeyEvent e) { + // submit form if enter was typed on loginButton + if(e.getKeyCode() == KeyEvent.VK_ENTER) submitLoginForm(); + } + }); + + // add components + add(messageLabel); + add(nameLabel); + add(nameField); + add(passwordLabel); + add(passwordField); + add(loginButton); + } + + /** + * Prompt the user to retype his password to confirm account creation. + * @param userToCreate The name of the new user to create. + * @param password The password to check the retyped password against. + * @return Whether the user wanted to sign up or not. + */ + public boolean promptForCreationConfirmation(String userToCreate, String password) { + // prepare stuff + String passwordConfirmation = ""; + JPanel passwordPromptPanel = new JPanel(); + JPasswordField passwordPromptField = new JPasswordField(10); + JLabel passwordPromptLabel = new JLabel("User "+userToCreate+" does not yet exist.\nRetype password to create new user."); + + passwordPromptPanel.setLayout(new BorderLayout(0, DEFAULT_PADDING/4)); + passwordPromptPanel.add(passwordPromptLabel, BorderLayout.NORTH); + passwordPromptPanel.add(passwordPromptField, BorderLayout.SOUTH); + + // show initial dialog + int logIn = JOptionPane.showOptionDialog(this, + passwordPromptPanel, + "Confirm to sign up as "+userToCreate, + JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, + new String[]{"Sign up", "Return to login"}, passwordPromptField); + + // get the typed in password confirmation + char[] passwordArray = passwordPromptField.getPassword(); + for (char passwordChar: passwordArray) passwordConfirmation += passwordChar; + + // loop until user types in correct password or aborts + while(logIn == 0 && !passwordConfirmation.equals(password)) { + passwordPromptLabel.setText("Passwords do not match.\nRetype the password you chose in the login form."); + logIn = JOptionPane.showOptionDialog(this, + passwordPromptPanel, + "Confirm to sign up as "+userToCreate, + JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, + new String[]{"Sign up", "Return to login"}, passwordPromptField); + // get the typed in password confirmation + passwordArray = passwordPromptField.getPassword(); + for (char passwordChar: passwordArray) passwordConfirmation += passwordChar; + } + + // return whether the user aborted or typed in the correct password + return logIn == 0; + } + + /** + * Set the message that is being thrown at the user above the login field. + * @param message The story to tell. Please don't be rude to our users. + */ + public void setMessage(String message) { + messageLabel.setText(message); + } + + /** + * Change the name that was saved. I don't remember why exactly but that probably is exactly why I need to save names to avoid forgetting them. Have I mentioned I forget a lot? + * @param newName What was your name again? + */ + public void changeSavedName(String newName) { + nameField.setText(newName); + } + + /** + * Set focus to the name field. + */ + public void focusNameField() { + nameField.requestFocusInWindow(); + } + + /** + * Submit the login form. Duh. + */ + private void submitLoginForm() { + // get name and password from fields + String name = nameField.getText().toLowerCase().trim(), password = ""; + char[] passwordArray = passwordField.getPassword(); + for (char passwordChar: passwordArray) password += passwordChar; + // submit form only if valid + if(name.length() <= 0) { + setMessage("Name cannot be empty."); + nameField.requestFocusInWindow(); + } + else if(password.length() <= 0) { + setMessage("Password cannot be empty."); + passwordField.requestFocusInWindow(); + } + else { + // check if the chosen username exists already + if(!parent.getClient().checkIfUserExists(name)) { // username does not yet exist + // check if user really wants to create a new user + if (promptForCreationConfirmation(name, password)) { + sendLoginForm(name, password); // try to sign up + } + } else sendLoginForm(name, password); // if name does exist already, try to log in + } + } + + /** + * Send the login form. Duh. + * @param name Who wants to be logged in? + * @param password What is the password of that humble person (or pokémon)? + */ + private void sendLoginForm(String name, String password) { + // encrypt password + MessageDigest md; + byte[] digest = null; + try { + md = MessageDigest.getInstance("MD5"); + md.update(password.getBytes("UTF-8")); + digest = md.digest(password.getBytes("UTF-8")); + } catch (Exception e) { + e.printStackTrace(); + } + password = DatatypeConverter.printHexBinary(digest).toLowerCase(); + // submit form + L.log(Level.INFO, "Submitting login form for " + name); + if (parent != null) parent.getClient().login(name, password); + else L.log(Level.SEVERE, "Who are my parents?"); + } +} diff --git a/src/client/logic/ClientMatch.java b/src/client/logic/ClientMatch.java new file mode 100644 index 0000000..f066bc4 --- /dev/null +++ b/src/client/logic/ClientMatch.java @@ -0,0 +1,671 @@ +package client.logic; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.SimpleDateFormat; +import java.util.UUID; +import java.util.logging.Level; + +import javax.swing.*; +import javax.vecmath.Vector2d; + +import GUI.SBFrame; +import client.Client; +import gameLogic.*; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameterArray; +import server.logic.User; +import util.SBApplication; + +/** + * A game on a client. + */ +public class ClientMatch extends GameController { + + protected String[] coachesNames = new String[2]; // the logged-in users coaching the opposing teams. + protected String winner; + public int settingCoolLayout = 0; + SBProtocolMessage initiateKickMessage = null, giveBallMessage = null; + + /** + * The client does not have access to the Users so it needs to create a match with only the names of the coaches. + * @param parent The client that plays this game. + * @param coach1 The name of the first coach. + * @param coach2 The name of the second coach. + */ + public ClientMatch(SBApplication parent, String coach1, String coach2) { + super(parent, new User(coach1), new User(coach2)); + for(Team team: parent.getTeamManager().getTeamBlueprints()) addAvailableTeam(team); + this.coachesNames = new String[]{coach1, coach2}; + setUp(); + } + + /** + * The method called by all constructors. + */ + private void setUp() { + setRunning(true); + // start message listener + start(); + } + + @Override + public void run() { + MessageListener messageListener = new MessageListener(this) { + @Override + public void processMessage(SBProtocolMessage message) { + try{ + switch(message.getCommand()){ + case ACTIO: + processMessageACTIO(message); + break; + case EVENT: + processMessageEVENT(message); + break; + case SRNDR: + processMessageSRNDR(message); + break; + default: + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + break; + } + }catch(IndexOutOfBoundsException e){ + e.printStackTrace(); + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + } + } + + @Override + public void processAnswer(SBProtocolMessage answer) { + try { + SBProtocolMessage message = null; + for(SBProtocolMessage messageThatIsPotentiallyAnswered: getClient().getProtocolManager().getUnansweredMessages()) // for all unanswered messages + if(messageThatIsPotentiallyAnswered.getMID().equals(UUID.fromString(answer.getParameterContent(0)))) { // get the message whose MID equals the MID in the answer + message = messageThatIsPotentiallyAnswered; + break; + } + getClient().getProtocolManager().removeUnansweredMessage(message); + + if(message != null) { + + switch (answer.getModule()) { + case SUC: + processAnswerSUC(answer, message); + break; + case FAI: + processAnswerFAI(answer, message); + break; + default: + break; + } + + } else getParent().log(Level.FINER, "Received answer but found no message it belonged to: " + answer.toStringShortenUUID() + " Ignoring it."); + + } catch (IndexOutOfBoundsException e) { // Don't return failure message because answers don't expect (e.g. ignore) answers anyway + getParent().log(Level.WARNING, "Index out of bounds at process answer."); + } + } + + private void processMessageSRNDR(SBProtocolMessage message) { + + + } + + private void processMessageEVENT(final SBProtocolMessage message) { + String eventString = message.getParameterContent(0); + + if(eventString.equals(SBProtocolMessage.EVENT_INITIATE_KICK)) { + + if(getClient().getFrame().getGamePanel() != null) { + getClient().getFrame().getGamePanel().setCanKickOff(true); + getClient().getFrame().getGamePanel().setIsYourTurn(false); + } + initiateKickMessage = message; + + } else if(eventString.equals(SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE)) { + + if(getClient().getFrame().getGamePanel() != null) { + getClient().getFrame().getGamePanel().setCanGiveBall(true); + } + giveBallMessage = message; + + } else if(eventString.equals(SBProtocolMessage.EVENT_SETUP_YOUR_TEAM)) { + + if(getClient().getFrame().getGamePanel() != null) { + getClient().getFrame().getGamePanel().setCanSetUp(true); + getClient().getFrame().getGamePanel().setHasSetUpTeam(false); + } + + } else if(eventString.equals(SBProtocolMessage.EVENT_OPPONENT_SURRENDERED)) { + setWinnerString(getClient().getUsername()); + finishGame(); + + } else if(eventString.equals(SBProtocolMessage.EVENT_WON_GAME)) { + setWinnerString(getClient().getUsername()); + finishGame(); + + } else if(eventString.equals(SBProtocolMessage.EVENT_LOST_GAME)) { + setWinnerString(getOpponentString(getClient().getUsername())); + finishGame(); + + } else if(eventString.equals(SBProtocolMessage.EVENT_DRAW)) { + setWinnerString(""); + finishGame(); + + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_NEW_TEAM)) { + setNewTeam(message); + + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_PLAYER)) { + adjustPlayer(message); + + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_ROUND_COUNT)) { + setRoundCount(message); + + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_WEATHER)){ + Weather newWeather = Weather.valueOf(message.getParameterContent(1)); + setWeather(newWeather); + checkForBlizzard(); + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_MOVING_PLAYER)){ + try{ + int teamIndex = Integer.parseInt(message.getParameterContent(1)); + int movingPlayerIndex = Integer.parseInt(message.getParameterContent(2)); + if(movingPlayerIndex == -1){ + teams[teamIndex].setMovingPlayer(null); + }else{ + teams[teamIndex].setMovingPlayer(teams[teamIndex].getPlayers().get(movingPlayerIndex)); + } + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + }catch(IndexOutOfBoundsException e) { + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_BALL_POS)) { + try{ + int x = Integer.parseInt(message.getParameterContent(1)); + int y = Integer.parseInt(message.getParameterContent(2)); + getPitch().adjustBallPos(new Vector2d(x, y)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_SCORE)) { + try{ + score[0] = Integer.parseInt(message.getParameterContent(1)); + score[1] = Integer.parseInt(message.getParameterContent(2)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_BLITZ_PASS_FOUL)){ + try{ + int teamIndex = Integer.parseInt(message.getParameterContent(1)); + if(Integer.parseInt(message.getParameterContent(2)) == 1){ + getTeam(teamIndex).setBlitz(true); + }else{ + getTeam(teamIndex).setBlitz(false); + } + if(Integer.parseInt(message.getParameterContent(3)) == 1){ + getTeam(teamIndex).setPass(true); + }else{ + getTeam(teamIndex).setPass(false); + } + if(Integer.parseInt(message.getParameterContent(4)) == 1){ + getTeam(teamIndex).setFoul(true); + }else{ + getTeam(teamIndex).setFoul(false); + } + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_YOUR_TURN)) { + + if(getClient().getFrame().getGamePanel() != null) getClient().getFrame().getGamePanel().setIsYourTurn(true); + + } else if(eventString.equals(SBProtocolMessage.EVENT_ENDED_TURN)) { + + if(getClient().getFrame().getGamePanel() != null) getClient().getFrame().getGamePanel().setIsYourTurn(false); + + } else if(eventString.equals(SBProtocolMessage.EVENT_SEND_GAME_PHASE)) { + + setGamePhase(Integer.parseInt(message.getParameterContent(1))); + + } else if(eventString.equals(SBProtocolMessage.EVENT_WHAT_DIE)) { + int numberOfChoices = message.getParameters().size()-1; + if(numberOfChoices > 0) { // needs at least one die to choose from + + int[] dieToChooseFrom = new int[numberOfChoices]; + for(int i = 0; i < numberOfChoices; i++) { + try { + dieToChooseFrom[i] = Integer.parseInt(message.getParameterContent(i + 1)); + } catch(NumberFormatException e) { + getClient().log(Level.WARNING, "Received illegal die choice. Ignoring."); + } + } + + getClient().getFrame().getGamePanel().setDiceAPIDice(dieToChooseFrom); + getClient().getFrame().getGamePanel().setDiceAPI(true); + getClient().getFrame().getGamePanel().setAPIMessage(message); + + } + } else if(eventString.equals(SBProtocolMessage.EVENT_WHICH_DIRECTION)) { + + int directionButtonSize = 100; + final JFrame directionChoiceFrame = new JFrame("Choose direction"); + String[] directionStrings = new String[]{"up left", "up", "up right", "left", "right", "down left", "down", "down right"}; + final int[][] directionAnswers = {{-1, -1}, {0, -1}, {1, -1}, {-1, 0}, {1, 0}, {-1, 1}, {0, 1}, {1, 1}}; + Box row1 = Box.createHorizontalBox(), row2 = Box.createHorizontalBox(), row3 = Box.createHorizontalBox(); + for (int i = 0; i < 8; i++) { + JButton directionButton = new JButton(directionStrings[i]); + final int returnI = i; + directionButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + message.returnSuccessMessage(getClient().getUID(), SBProtocolMessage.WORKD_DIRECTION, directionAnswers[returnI][0] + "", directionAnswers[returnI][1] + ""); + directionChoiceFrame.setVisible(false); + directionChoiceFrame.dispose(); + } + }); + directionButton.setMinimumSize(new Dimension(directionButtonSize, directionButtonSize)); + directionButton.setPreferredSize(new Dimension(directionButtonSize, directionButtonSize)); + if(i < 3) row1.add(directionButton); + else if(i < 5) row2.add(directionButton); + else row3.add(directionButton); + if(i == 3) row2.add(Box.createRigidArea(new Dimension(directionButtonSize, directionButtonSize))); + } + row1.setSize(new Dimension(3*directionButtonSize, directionButtonSize)); + row2.setSize(new Dimension(3*directionButtonSize, directionButtonSize)); + row3.setSize(new Dimension(3*directionButtonSize, directionButtonSize)); + directionChoiceFrame.add(row1); + directionChoiceFrame.add(row2); + directionChoiceFrame.add(row3); + directionChoiceFrame.setLayout(new BoxLayout(directionChoiceFrame.getContentPane(), BoxLayout.Y_AXIS)); + directionChoiceFrame.setUndecorated(true); + directionChoiceFrame.setSize(new Dimension(3 * directionButtonSize, 3 * directionButtonSize)); + SBFrame.center(directionChoiceFrame); + directionChoiceFrame.setVisible(true); + directionChoiceFrame.toFront(); + + } else if(eventString.equals(SBProtocolMessage.EVENT_FOLLOW)) { + + if(getClient().getFrame().getGamePanel().getFollowAnswer()) + message.returnSuccessMessage(getClient().getUID(), SBProtocolMessage.WORKD_DECIDED, "1"); + else + message.returnSuccessMessage(getClient().getUID(), SBProtocolMessage.WORKD_DECIDED, "0"); + + } else if(eventString.equals(SBProtocolMessage.EVENT_API_AIM)){ + try{ + int playerIndex = Integer.parseInt(message.getParameterContent(2)), + distance = Integer.parseInt(message.getParameterContent(3)); + getClient().getFrame().getGamePanel().setAimAPIIndex(playerIndex); + getClient().getFrame().getGamePanel().setAimAPIDistance(distance); + getClient().getFrame().getGamePanel().setAimAPI(true); + getClient().getFrame().getGamePanel().setAPIMessage(message); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_API_CHOICE)){ + try{ + Player[] players = new Player[(message.getParameters().size()-2)/2]; + for(int i = 0; i < (message.getParameters().size()-2)/2; i++){ + players[i] = teams[Integer.parseInt(message.getParameterContent(i*2+2))].getPlayers().get(Integer.parseInt(message.getParameterContent(i*2+3))); + } + getClient().getFrame().getGamePanel().setChoiceAPIPlayers(players); + getClient().getFrame().getGamePanel().setChoiceAPI(true); + getClient().getFrame().getGamePanel().setAPIMessage(message); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_API_FIELD)){ + try{ + Vector2d[] fields = new Vector2d[(message.getParameters().size()-2)/2]; + for(int i = 0; i < (message.getParameters().size()-2)/2; i++){ + fields[i] = new Vector2d(Integer.parseInt(message.getParameterContent(i*2+2)), Integer.parseInt(message.getParameterContent(i*2+3))); + } + getClient().getFrame().getGamePanel().setFieldAPIFields(fields); + getClient().getFrame().getGamePanel().setFieldAPI(true); + getClient().getFrame().getGamePanel().setAPIMessage(message); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_API_HIGHLIGHT)){ + try{ + if(message.getParameters().size()-1 < 1){ + getClient().getFrame().getGamePanel().clearHighlightAPIPositionsAndColors(); + }else{ + Vector2d[] fields = new Vector2d[(message.getParameters().size()-1)/6]; + Color[] colors = new Color[(message.getParameters().size()-1)/6]; + for(int i = 0; i < fields.length; i++){ + fields[i] = new Vector2d(Integer.parseInt(message.getParameterContent(i*6+1)), Integer.parseInt(message.getParameterContent(i*6+2))); + colors[i] = new Color(Integer.parseInt(message.getParameterContent(i*6+3)), Integer.parseInt(message.getParameterContent(i*6+4)), Integer.parseInt(message.getParameterContent(i*6+5)), Integer.parseInt(message.getParameterContent(i*6+6))); + } + getClient().getFrame().getGamePanel().setHighlightAPIPositionsAndColors(fields, colors); + } + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(eventString.equals(SBProtocolMessage.EVENT_SHOW_ME)){ + + getClient().getFrame().getGamePanel().showMe(message.getParameterContent(1), message.getParameterContent(2)); + + } + } + + private void processMessageACTIO(SBProtocolMessage message) { + + + + } + + private void processAnswerSUC(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case SRNDR: + setWinnerString(getOpponentString(getClient().getUsername())); + finishGame(); + break; + case ACTIO: + String actionString = message.getParameterContent(0); + + if(actionString.equals(SBProtocolMessage.ACTIO_SET_PLAYER) && settingCoolLayout != 0) { + + if(settingCoolLayout > 0) getClient().coolLineup(settingCoolLayout + 1); + else getClient().coolLineup(settingCoolLayout - 1); + + } else if(actionString.equals(SBProtocolMessage.ACTIO_SET_TEAM)) { + + if(getClient().getFrame().getGamePanel() != null) { + getClient().getFrame().getGamePanel().setChoseTeam(true); + getClient().getFrame().getGamePanel().setIsYourTurn(false); + } + + } + break; + } + } + + private void processAnswerFAI(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case SRNDR: + getParent().log(Level.WARNING, "Surrender failed. You're an even greater turtle than I thought at first."); + break; + case ACTIO: +// if(message.getParameterContent(1).equals(SBProtocolMessage.ACTIO_SET_PLAYER) && settingCoolLayout < 0) // REMOVE THIS +// getClient().coolLineup(1); // REMOVE THIS + if(answer.getParameterContent(1).equals(SBProtocolMessage.FAILD_NOT_ENOUGH_PLAYERS)) { + getClient().getFrame().getGamePanel().setChoseTeam(false); + getClient().getFrame().getGamePanel().setHasSentChooseTeam(false); + }else if(answer.getParameterContent(1).equals(SBProtocolMessage.FAILD_NOT_ENOUGH_MONEY)) { + getClient().getFrame().getGamePanel().setChoseTeam(false); + getClient().getFrame().getGamePanel().setHasSentChooseTeam(false); + } + break; + case EVENT: + if(answer.getParameterContent(1).equals(SBProtocolMessage.FAILD_INVALID_TEAMSETUP)) { + // set back to false so player can try setting up again + getClient().getFrame().getGamePanel().setHasSetUpTeam(false); + } + break; + } + } + + }; + messageListener.start(); + + while(isRunning()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + // ACTIONS + + public void giveBall(Player playerReceivingBall) { + if(giveBallMessage != null) { + SBProtocolParameterArray kickParams = new SBProtocolParameterArray(SBProtocolMessage.WORKD_GIVE_BALL, (playerReceivingBall.getId()-1)+""); + giveBallMessage.returnSuccessMessage(getClient().getUID(), kickParams); + } else getClient().log(Level.WARNING, "Did not get permission to give ball. Not giving ball then."); + } + + public void kick(PitchField kickDestination) { + if(initiateKickMessage != null) { + SBProtocolParameterArray kickParams = new SBProtocolParameterArray(SBProtocolMessage.WORKD_KICK, (int) kickDestination.getPos().x+"", (int) kickDestination.getPos().y+""); + initiateKickMessage.returnSuccessMessage(getClient().getUID(), kickParams); + } else getClient().log(Level.WARNING, "Did not get permission to kick. Not kicking then."); + } + + // HELPERS + + public SBProtocolMessage sendMessage(SBProtocolCommand command, String... parameters){ + SBProtocolMessage message = new SBProtocolMessage(getParent().UID, command, parameters); + sendMessage(new UUID(0, 0), message); + return message; + } + + @Override + public void sendMessage(User destinationUser, SBProtocolCommand command, String... parameters) { + sendMessage(command, parameters); + } + + @Override + public void touchdown(Team t) { + + } + + /** + * Create a string of this game that can be written to file and read again. + * @return The string that represents this game. + */ + public String toLogString() { + String beginParam = "\", \"", beginArray = "\", [\"", endArray = "\"], \"", beginArrayFromArray = "\"], [\""; + /*String r = "[[\""+escape(coachesNames[0]) + beginArray + escape(getTeam(0).getName()) + beginParam + escape(getTeam(0).getType()) + endArray + escape(score[0]+"") + beginArrayFromArray + + escape(coachesNames[1]) + beginArray + escape(getTeam(1).getName()) + beginParam + escape(getTeam(1).getType()) + endArray + escape(score[1]+"") + endArray + + escape(winner) + beginParam + + escape((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(finishedOn));*/ + String team1Coach="", team1Name="", team1Type="", team1Score=escape(score[0]+""), + team2Coach="", team2Name="", team2Type="", team2Score=escape(score[1]+""), + winnerName="", finishedOnString=""; + if(coaches[0] != null) team1Coach = escape(coachesNames[0]); + if(teams[0] != null) team1Name = escape(teams[0].getName()); + if(teams[0] != null) team1Type = escape(teams[0].getType()); + if(coaches[1] != null) team2Coach = escape(coachesNames[1]); + if(teams[1] != null) team2Name = escape(teams[1].getName()); + if(teams[1] != null) team2Type = escape(teams[1].getType()); + if(winner != null) winnerName = escape(winner); + if(finishedOn != null) finishedOnString = escape((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(finishedOn)); + + String r = "[[\"" + team1Coach + beginArray + team1Name + beginParam + team1Type + endArray + team1Score + beginArrayFromArray + + team2Coach + beginArray + team2Name + beginParam + team2Type + endArray + team2Score + endArray + + winnerName + beginParam + + finishedOnString; + if(casualties.size() > 0) { + r += beginArray; // add the beginning of the casualties array + for (Player casualty: casualties) r += escape(casualty.toString()) + beginParam; // add all casualties + r = r.replaceAll(beginParam+"$", "\"]]"); // replace the last beginParam with the end of the string + } else r += "\", []]"; // add an empty casualties array + return r; + } + + /** + * Write this client match to file. + */ + public void logClientMatch() { + logGame(CLIENT_GAMES_FILE); + } + + /** + * initiates everything that is necessary at the end of a game + */ + private void finishGame(){ + if(getWinnerString().equals("")){ + getParent().log(Level.INFO, "The match ended in a Draw!"); + }else{ + getParent().log(Level.INFO, "The match ended and the winner is " + getWinnerString() + "!"); + } + setRunning(false); + getClient().finishedGame(); + logClientMatch(); + } + + public Client getClient() { + return (Client) getParent(); + } + + /** + * The client match always returns null here because it has no access to Users. + * @param user The user whose opponent is searched. Is ignored in client match. + * @return Always null because the client match has no access to Users. + */ + public User getOpponent(User user) { + getParent().log(Level.WARNING, "Tried to get opponent from within client match. Returning null because client doesn't know the opponent user object."); + return null; + } + + /** + * The client match always returns null here because it has no access to Users. + * @param index The index of the coach to get (0 or 1). Is ignored in client match. + * @return Always null because the client match has no access to Users. + */ + public User getOpponent(int index) { + getParent().log(Level.WARNING, "Tried to get opponent from within client match. Returning null because client doesn't know the opponent user object."); + return null; + } + + /** + * The client match stores the coaches as strings. Get the name of the opponent of the coach at given index. + * @param index The index of the coach whose opponent is searched. + * @return The name of the opponent of the coach at given index. Null if index is not 0 or 1. + */ + public String getOpponentString(int index) { + if(index == 0) return coachesNames[1]; + if(index == 1) return coachesNames[0]; + else return null; + } + + /** + * The client match stores the coaches as strings. Get the name of the opponent of the coach with a given name. + * @param coachName The name of the coach whose opponent is searched. + * @return The name of the opponent of the coach with given name. Null if index the coach is not in this game. + */ + public String getOpponentString(String coachName) { + if(coachesNames[0].equals(coachName)) return getOpponentString(0); + if(coachesNames[1].equals(coachName)) return getOpponentString(1); + else return null; + } + + public String getWinnerString() { + return winner; + } + + public void setWinnerString(String winner) { + this.winner = winner; + } + + private void setNewTeam(SBProtocolMessage message){ + int actingUserIndex; + try{ + actingUserIndex = Integer.parseInt(message.getParameterContent(1)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + return; + } + if(!(actingUserIndex == 0 || actingUserIndex == 1)){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + return; + } + Team newTeam = setTeamType(message, 3); + if(newTeam!=null){ + newTeam = new Team(newTeam, message.getParameterContent(2), this, getUser(actingUserIndex)); + int existingPlayers = 0; + for(int i = 4; i < message.getParameters().size(); i++){ + for(int j = 0; j < newTeam.getAvailablePlayers().size(); j++){ + if(message.getParameterContent(i).equalsIgnoreCase(newTeam.getAvailablePlayers().get(j).getName())){ + newTeam.addPlayer(newTeam.getAvailablePlayers().get(j)); + existingPlayers++; + } + } + } + setTeam(actingUserIndex, newTeam); + if(existingPlayers == message.getParameters().size()-4){ + getPitch().setTeam(actingUserIndex, newTeam); + }else{ + newTeam.clearPlayers(); + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } + } + + private void adjustPlayer(SBProtocolMessage message){ + try{ + int actingUserIndex = Integer.parseInt(message.getParameterContent(1)); + int playerIndex = Integer.parseInt(message.getParameterContent(2)); + int playerPosX = Integer.parseInt(message.getParameterContent(10)); + int playerPosY = Integer.parseInt(message.getParameterContent(11)); + if(!(actingUserIndex == 0 || actingUserIndex == 1)){ + return; + } + if(playerIndex < 0 || playerIndex >= teams[actingUserIndex].getPlayers().size() || playerPosX < -1 || playerPosX > 25 || playerPosY < -1 || playerPosY > 14){ + return; + } + teams[actingUserIndex].getPlayers().get(playerIndex).setName(message.getParameterContent(3)); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetBe(Integer.parseInt(message.getParameterContent(4))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetSt(Integer.parseInt(message.getParameterContent(5))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetGe(Integer.parseInt(message.getParameterContent(6))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetRs(Integer.parseInt(message.getParameterContent(7))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetRemainingBe(Integer.parseInt(message.getParameterContent(8))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetPlayerCondition(PlayerCondition.valueOf(message.getParameterContent(9))); + try{ + if(playerPosX == -1 && playerPosY == -1){ + teams[actingUserIndex].getPlayers().get(playerIndex).invokeClearPosition(); + }else{ + teams[actingUserIndex].getPlayers().get(playerIndex).getPosition().adjustPlayer(null); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeAdjustPosition(getPitch().getFields()[playerPosX][playerPosY]); + } + }catch(NullPointerException e){ + e.printStackTrace(); + } + teams[actingUserIndex].getPlayers().get(playerIndex).getPosition().adjustPlayer(teams[actingUserIndex].getPlayers().get(playerIndex)); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetIsHoldingBall(Boolean.parseBoolean(message.getParameterContent(12))); + teams[actingUserIndex].getPlayers().get(playerIndex).invokeSetRedCard(Boolean.parseBoolean(message.getParameterContent(13))); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } + + private void setRoundCount(SBProtocolMessage message){ + try{ + roundCount = Integer.parseInt(message.getParameterContent(1)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } + + public void sendSpecialRule(Player player, String name){ + int playerIndex = -1; + for(int i = 0; i < player.getTeam().getPlayers().size(); i++){ + if(player == player.getTeam().getPlayers().get(i)){ + playerIndex = i; + } + } + sendMessage(SBProtocolCommand.ACTIO, SBProtocolMessage.ACTIO_SPCL, playerIndex + "", name); + } + + // API + + public void sendDiceAPI(SBProtocolMessage message, int choice){ + returnSuccessMessage(message, SBProtocolMessage.WORKD_DIE_CHOSEN, choice+""); + } + + public void sendAimAPI(SBProtocolMessage message, Vector2d destination){ + returnSuccessMessage(message, SBProtocolMessage.EVENT_API_AIM, (int)destination.x + "", (int)destination.y + ""); + } + + public void sendChoiceAPI(SBProtocolMessage message, int playerIndex){ + returnSuccessMessage(message, SBProtocolMessage.EVENT_API_CHOICE, playerIndex + ""); + } + + public void sendFieldAPI(SBProtocolMessage message, Vector2d choice){ + returnSuccessMessage(message, SBProtocolMessage.EVENT_API_FIELD, (int)choice.x + "", (int)choice.y + ""); + } +} diff --git a/src/client/logic/ClientMessageProcessor.java b/src/client/logic/ClientMessageProcessor.java new file mode 100644 index 0000000..54df8da --- /dev/null +++ b/src/client/logic/ClientMessageProcessor.java @@ -0,0 +1,443 @@ +package client.logic; + +import client.display.ClientFrame; +import gameLogic.GameController; +import network.*; +import client.Client; +import util.MessageProcessor; + +import java.util.UUID; +import java.util.logging.Level; + +/** + * The message processor on the client side. + * Created by milan on 1.4.15. + */ +public class ClientMessageProcessor implements MessageProcessor { + + private final Client client; + public String potentialNewUsername = "", loggedInWithUsername = "", loggedInWithEncryptedPassword = "", invitedOpponent = ""; + public boolean waitingForGame = false; + private int userExists = -1; // user by checkIfUserExists() to wait for an answer. States: -1: waiting, 0: doesn't exist, 1: exists, 100: error + + public ClientMessageProcessor(Client client) { + this.client = client; + } + + /** + * Sorts messages to process by module and continues in processMessageMODULE(message). + * @param message The message to process + */ + @Override + public void processMessage(SBProtocolMessage message) { + try { + + boolean processMessage; + switch (message.getModule()) { + case CHT: + processMessage = processMessageCHT(message); + break; + case AUT: + processMessage = processMessageAUT(message); + break; + case GAM: + processMessage = processMessageGAM(message); + break; + default: + processMessage = false; + break; + } + if(!processMessage) returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + + } catch (IndexOutOfBoundsException e) { returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); } + } + + /** + * Sorts answers to process by module and continues in processAnswerMODULE(answer). + * @param answer The answer to process + */ + @Override + public void processAnswer(SBProtocolMessage answer) { + try { + + SBProtocolMessage message = null; + for(SBProtocolMessage messageThatIsPotentiallyAnswered: getProtocolManager().getUnansweredMessages()) // for all unanswered messages + if(messageThatIsPotentiallyAnswered.getMID().equals(UUID.fromString(answer.getParameterContent(0)))) { // get the message whose MID equals the MID in the answer + message = messageThatIsPotentiallyAnswered; + break; + } + getProtocolManager().removeUnansweredMessage(message); + + if(message != null) { + + switch (answer.getModule()) { + case SUC: + processAnswerSUC(answer, message); + break; + case FAI: + processAnswerFAI(answer, message); + break; + } + + } else getClient().log(Level.FINER, "Received answer but found no message it belonged to: " + answer.toStringShortenUUID() + " Ignoring it."); + + } catch (IndexOutOfBoundsException e) { /* Don't return failure message because answers don't expect (e.g. ignore) answers anyway */ } + } + + // MESSAGE PROCESSORS + + /** + * Processes messages in the module CHT. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageCHT(SBProtocolMessage message) { + switch (message.getCommand()) { + case SENDM: + getFrame().addChatMessage(message.getParameterContent(0), message.getParameterContent(1)); + return true; + + case BDCST: + if(!message.getParameterContent(0).toLowerCase().equals(getUsername().toLowerCase())) // if was not sent by this client + getFrame().addChatMessage(message.getParameterContent(0)+"@all", message.getParameterContent(1)); + return true; + + default: + return false; + } + } + + /** + * Processes messages in the module AUT. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageAUT(SBProtocolMessage message) { + switch (message.getCommand()) { + case UPGAM: + getClient().updateGamesList(message.getParameters()); + return true; + + case UPUSR: + getClient().updateUsersList(message.getParameters()); + return true; + + case SCORE: + getClient().updateHighscoreTable(message.getParameters()); + return true; + + default: + return false; + } + } + + /** + * Processes messages in the module GAM. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageGAM(SBProtocolMessage message) { + switch (message.getCommand()) { + case START: // server asks user to start game against player in message parameter 0 + String opponent = message.getParameterContent(0); + + if (waitingForGame) { // client was waiting for a random game. decline invitations + returnFailureMessage(message); + + } else { // client was not waiting for a game. message is an invitation + if(!getClient().isPlayingMatch()) { // client is not in a match currently + if(getFrame().getInvitedAnswer(opponent)) { // user accepted invitation + getClient().createGame(opponent); + returnSuccessMessage(message); + } else { // user declined invitation + returnFailureMessage(message); + } + } else returnFailureMessage(message); // client was playing already + } + return true; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(message); + return true; + + default: + return false; + } + } + + // ANSWER PROCESSORS + + /** + * Processes success answers. + * @param answer The answer to process. + * @param message The original message that was sent by this client. + */ + private void processAnswerSUC(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case LOGIN: + if (answer.getParameterContent(1).equals("LOGGED IN")) { + getClient().log(Level.INFO, "Received login success answer."); + if (!isLoggedIn()) { // client is not logged in yet + setLoggedIn(true); // log in + if (loggedInWithUsername.length() > 0) { + if(loggedInWithUsername.equals(getUsername())) { // was logged in with the same user before + if(getClient().isPlayingMatch()) { // client lost connection during match + getFrame().showGamePanel(false); + } + } else { // logged in with new username + setUsername(loggedInWithUsername); // set username + getClient().setPlayingMatch(false); + getFrame().resetLobbyPanel(); + getFrame().showLobbyPanel(Client.MODERATOR_NAME + ": Hello " + getUsername() + "!"); + if(getClient().automatchstart) { // starts a new match automatically to speed up testing + if (getUsername().equals("milan")) getClient().startGame(); + else if (getUsername().equals("pikachu")) getClient().invitePlayer("milan"); + } + } + + } else getFrame().showLobbyPanel(Client.MODERATOR_NAME + ": Hello " + getUsername() + "!"); + } + } else if (answer.getParameterContent(1).equals("CREATED USER")) { + getClient().log(Level.INFO, "Received sign up success answer, you can log in now."); + getFrame().getLoginPanel().setMessage("Signed up successfully. Log in now."); + if (!isLoggedIn()) // log in automatically + answer.returnMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.LOGIN, message.getParameters())); + } + break; + + case LOGUT: + if (isLoggedIn()) { + getClient().log(Level.INFO, "Received logout success answer."); + setLoggedIn(false); + setUsername(""); + getFrame().showLoginPanel("Successfully logged out."); + } + break; + + case CHNGE: + if (potentialNewUsername.length() > 0) { + setUsername(potentialNewUsername); + potentialNewUsername = ""; + getFrame().getLoginPanel().changeSavedName(getUsername()); + getFrame().getLobbyPanel().writeMessage("Changed username to " + getUsername() + "."); + } + break; + + case EXIST: + if (answer.getParameterContent(1).equals("EXISTS")) userExists = 1; + else if (answer.getParameterContent(1).equals("EXISTS NOT")) userExists = 0; + else userExists = 100; // unknown answer + break; + + case START: + getClient().createGame(answer.getParameterContent(1)); + break; + + case LSUSR: + for(int i = 1; i < answer.getParameters().size(); i++) { + if(answer.getParameter(i).isArray()) + if(answer.getParameter(i).toArray().size() == 2) { + // write the user to the lobby + String userString = answer.getParameter(i).toArray().getParameter(0).getContent(); + if(answer.getParameter(i).toArray().getParameter(1).getContent().equals("true")) userString += " – in game"; + getFrame().writeMessage(userString); + } else getClient().log(Level.WARNING, "Received invalid parameter in users list: "+answer.getParameter(i)); + } + break; + + case LSGAM: + for(int i = 1; i < answer.getParameters().size(); i++) { + if(answer.getParameter(i).isArray()) + if(answer.getParameter(i).toArray().size() == 5) { // is finished game + // write the game info to the lobby + SBProtocolParameterArray a = answer.getParameter(i).toArray(); + String winner = a.getParameter(2).getContent(); + String looser = a.getParameter(0).getContent(); // set looser to first opponent (is checked on line below) + if(winner.equals(looser)) looser = a.getParameter(1).getContent(); // if looser was second opponent, set to second opponent + String score1 = a.getParameter(3).getContent(); + String score2 = a.getParameter(4).getContent(); + if(winner.equals("")) { + getFrame().writeMessage(a.getParameter(0).getContent() + " tied against " + a.getParameter(1).getContent() + ". " + score1 + ":" + score2); + } else { + getFrame().writeMessage(winner + " won against " + looser + ". " + score1 + ":" + score2); + } + } else if(answer.getParameter(i).toArray().size() == 4) { // is running game + // write the game info to the lobby + String opponent1 = answer.getParameter(i).toArray().getParameter(0).getContent(); + String opponent2 = answer.getParameter(i).toArray().getParameter(1).getContent(); + String score1 = answer.getParameter(i).toArray().getParameter(2).getContent(); + String score2 = answer.getParameter(i).toArray().getParameter(3).getContent(); + getFrame().writeMessage(opponent1 + " is playing against " + opponent2 + ". " + score1 + ":" + score2); + } else if(answer.getParameter(i).toArray().size() == 1) { // is player waiting for opponent + // write the game info to the lobby + String playerWaitingForGame = answer.getParameter(i).toArray().getParameter(0).getContent(); + getFrame().writeMessage(playerWaitingForGame + " is waiting for someone to join his game."); + } else getClient().log(Level.WARNING, "Received invalid parameter in games list: "+answer.getParameter(i)); + } + if(answer.getParameters().size() <= 1) { // no games returned + getFrame().writeMessage("No games on the server right now."); + getClient().log(Level.INFO, "Received empty games list."); + } + break; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(answer); + getProtocolManager().addUnansweredMessage(message); // add answered message back to unanswered messages so it isn't lost + break; + + } + } + + /** + * Processes failure answers. + * @param answer The answer to process. + * @param message The original message that was sent by this client. + */ + private void processAnswerFAI(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case LOGIN: + if (answer.getParameterContent(1).equals("ALREADY LOGGED IN")) { + getClient().log(Level.INFO, loggedInWithUsername + " is already logged in."); + getFrame().showLoginPanel(loggedInWithUsername + " is already logged in."); + if(getClient().autologin) { // logs in automatically to speed up testing + if(loggedInWithUsername.equals("milan")) + client.login("pikachu", "33a9113983a8ec726fc4165fd99915eb"); + else client.login("milan", "30f6bee6387a90c504c0116b968db626"); + } + + } else if (answer.getParameterContent(1).equals("WRONG PASS")) { + getClient().log(Level.INFO, "Wrong password."); + getFrame().showLoginPanel("Wrong password."); + + } else if (answer.getParameterContent(1).equals("ILLEGAL NAME")) { + getClient().log(Level.INFO, "Name not allowed."); + getFrame().showLoginPanel("Name not allowed. Please, PLEASE don't break my logic with spaces in your name."); + + } else if (answer.getParameterContent(1).equals("EMPTY FIELD")) { + if (message.getParameterContent(0).length() <= 0) { // if name was empty + getClient().log(Level.INFO, "Name cannot be empty."); + getFrame().showLoginPanel("Name cannot be empty."); + + } else if (message.getParameterContent(1).equals("d41d8cd98f00b204e9800998ecf8427e")) { // if password was empty (empty string digested by md5 = d41d8cd98f00b204e9800998ecf8427e) + getClient().log(Level.INFO, "Password cannot be empty."); + getFrame().showLoginPanel("Password cannot be empty."); + + } else { // if something strange happened + getClient().log(Level.INFO, "Empty login field. Is there a ghost in here?"); + getFrame().showLoginPanel("Empty login field. Is there a ghost in here?"); + } + } + break; + + case LOGUT: + if (isLoggedIn()) { + getClient().log(Level.WARNING, "Could not log out. Maybe, "+Client.MODERATOR_NAME+" has a bad day today."); + getFrame().getLobbyPanel().writeMessage("Could not log out. Maybe, " + Client.MODERATOR_NAME + " has a bad day today."); + } + break; + + case CHNGE: + getFrame().getLobbyPanel().writeMessage("Could not change username to " + potentialNewUsername + "."); + getFrame().getLobbyPanel().focusChangeNameField(); + break; + + case START: + getClient().startingGameFailed(true); + break; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(answer); + getProtocolManager().addUnansweredMessage(message); // add answered message back to unanswered messages so it isn't lost + break; + + } + } + + /** + * Add an incoming game message or answer to the match incoming messages or answers queue. + * @param messageOrAnswer The message or answer to forward. + */ + private void addMessageOrAnswerToMatch(SBProtocolMessage messageOrAnswer) { + if(getClient().isPlayingMatch() && getClient().getMatch() != null) { // client is in match + if (messageOrAnswer.getModule() == SBProtocolCommand.SBProtocolModule.GAM) // is message + getClient().getMatch().addIncomingMessage(messageOrAnswer); // add message to incoming messages in match + else // is answer + getClient().getMatch().addIncomingAnswer(messageOrAnswer); // add answer to incoming answers in match + } else returnFailureMessage(messageOrAnswer); + } + + // HELPERS + + /** + * Returns a success message for the given message with parameters. + * @param returnTo The message to answer. + * @param parameters The parameters to send back with the answer. + */ + @Override + public void returnSuccessMessage(SBProtocolMessage returnTo, String... parameters) { + getClient().returnSuccessMessage(returnTo, parameters); + } + + /** + * Returns a failure message for the given message with parameters. + * @param returnTo The message to answer. + * @param parameters The parameters to send back with the answer. + */ + @Override + public void returnFailureMessage(SBProtocolMessage returnTo, String... parameters) { + getClient().returnFailureMessage(returnTo, parameters); + } + + // GETTERS & SETTERS + + public int getUserExists() { + return userExists; + } + + public void setUserExists(int userExists) { + this.userExists = userExists; + } + + private String getUsername() { + return getClient().getUsername(); + } + + private void setUsername(String name) { + getClient().setUsername(name); + } + + private ClientFrame getFrame() { + return getClient().getFrame(); + } + + private SBSocketManager getSocketManager() { + return getClient().getSocketManager(); + } + + private SBProtocolManager getProtocolManager() { + return getClient().getProtocolManager(); + } + + private UUID getUID() { + return getClient().UID; + } + + public Client getClient() { + return client; + } + + public boolean isLoggedIn() { + return getClient().isLoggedIn(); + } + + public void setLoggedIn(boolean loggedIn) { + getClient().setLoggedIn(loggedIn); + } + +} diff --git a/src/client/logic/ClientProtocolManager.java b/src/client/logic/ClientProtocolManager.java new file mode 100644 index 0000000..01727fb --- /dev/null +++ b/src/client/logic/ClientProtocolManager.java @@ -0,0 +1,20 @@ +package client.logic; + +import network.SBProtocolManager; +import util.SBApplication; + +/** + * A protocol manager that will communicate with a server socket manager and handle messages for a client. + * Created by milan on 28.3.15. + */ +public class ClientProtocolManager extends SBProtocolManager { + + /** + * Create new client protocol manager. + * @param parent The client that is parent of this protocol manager. + */ + public ClientProtocolManager(SBApplication parent) { + super(parent); + } + +} diff --git a/src/client/logic/ClientSocketManager.java b/src/client/logic/ClientSocketManager.java new file mode 100644 index 0000000..3568521 --- /dev/null +++ b/src/client/logic/ClientSocketManager.java @@ -0,0 +1,188 @@ +package client.logic; + +import client.Client; +import network.SBNetworkException; +import network.SBProtocolMessage; +import network.SBSocket; +import network.SBSocketManager; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.Date; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +/** + * A socket manager that will handle the sending and recieving of messages for a client. + * Created by milan on 25.3.15. + */ +public class ClientSocketManager extends SBSocketManager { + + private InetAddress address; + private SBSocket socket; + + /** + * Create a new client socket manager for client. + * @param client The client to create this socket manager for. + * @param protocolManager The protocol manager for this client. + */ + public ClientSocketManager(Client client, ClientProtocolManager protocolManager) { + super(client, protocolManager); + } + + /** + * Start the client and connect to address on port. + * @param address The address to connect to. + * @param port The port to connect to. + */ + public void startClient(InetAddress address, int port) { + this.port = port; + this.address = address; + // create socket creator thread + SBSocketConnector connector = new SBSocketConnector(address, port, getParent().UID, this); + connector.start(); + getParent().isConnecting(); + } + + /** + * The thread that connects to a server in the background. + */ + private class SBSocketConnector extends Thread { + private InetAddress address; + private int port; + private UUID UID; + private SBSocketManager manager; + + SBSocketConnector(InetAddress address, int port, UUID UID, SBSocketManager manager) { + this.address = address; + this.port = port; + this.UID = UID; + this.manager = manager; + } + + public void run() { + try { + SBSocket socket = new SBSocket(address, port, UID, manager); + connectorFinished(socket); + } catch (SBNetworkException e) { + connectorFinished(null); + } + } + } + + /** + * This method is run when the connector finished connecting to a server. + * @param socket The socket that is connected to the server. + */ + void connectorFinished(SBSocket socket) { + if(socket != null) { + this.socket = socket; + getParent().hasConnected(address, port, true); + } else { + getParent().hasConnected(address, port, false); + } + } + + /** + * Stop the client. + */ + public void stopClient() { + removeSocket(socket); + address = null; + port = 0; + } + + /** + * Get the address to which the client is currently connected to. + * @return The address. null if the client is not connected. + */ + public InetAddress getAddress() { + return address; + } + + /** + * Close the socket. On the client the socket passed is ignored, because there exists only one socket. + * @param socket On the client this is ignored, because there exists only one socket. + */ + public void removeSocket(SBSocket socket) { + try { + getParent().log(Level.SEVERE, "Closing socket."); + socket.close(); + getParent().lostConnection(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Error while closing socket " + socket.getUID().toString().substring(0, 8) + ". Leaving open."); + } + this.socket = null; + } + + public Vector getSockets() { + Vector sockets = new Vector(); + sockets.add(socket); + return sockets; + } + + /** + * Get the socket if its UID equals the given UID. + * @param UID The UID to check the UID of the socket against. + * @return The socket if its UUD equals the given UID or null if not. + */ + public SBSocket getSocket(UUID UID) { + if(socket != null) if(socket.getUID().equals(UID)) return socket; + return null; + } + + public SBSocket getSocket() { + return socket; + } + + /** + * Send a message to the socket if it is connected. + * @param UID The client socket manager ignores this, because there is only one socket to send messages to. + * @param message The protocol message to send. + */ + public void sendMessage(UUID UID, SBProtocolMessage message) { + if(socket != null) socket.sendMessage(message); + } + + /** + * Send a message to the socket if it is connected. + * @param message The protocol message to send. + */ + public void sendMessage(SBProtocolMessage message) { + if(socket != null) socket.sendMessage(message); + } + + /** + * Get whether the socket is connected to a server. + * @return Whether the socket is connected to a server. + */ + public boolean isConnected() { + return socket != null && socket.isConnected(); + } + + /** + * Start a connection listener to wait for ping timeouts. + */ + public void startConnectionListener() { + // start connection listener + (new Thread(new Runnable() { + @Override + public void run() { + setLastPingAnswered(new Date(System.currentTimeMillis())); + // check if any ping has been received in the last PING_MESSAGE_TIMEOUT milliseconds + while(socket != null) { + if(getLastPingAnswered().before(new Date(System.currentTimeMillis()-PING_MESSAGE_TIMEOUT))) { + getParent().log(Level.SEVERE, "Timeout while waiting for a ping. Closing socket."); + removeSocket(socket); + } + try { + Thread.sleep(PING_SENDER_INTERVAL); + } catch (InterruptedException e) { + getParent().log(Level.WARNING, "Interrupted while waiting until checking last received ping. Checking now instead."); + } + } + } + })).start(); + } +} diff --git a/src/client/logic/DrawingPath.java b/src/client/logic/DrawingPath.java new file mode 100644 index 0000000..806dc9e --- /dev/null +++ b/src/client/logic/DrawingPath.java @@ -0,0 +1,35 @@ +package client.logic; + +import gameLogic.Pitch; +import gameLogic.PitchField; + +import java.util.ArrayList; + +/** + * Used to store drawn path elements when the move of a Player is planned. + */ +public class DrawingPath { + + private ArrayList path = new ArrayList(); + private Pitch pitch; + + public DrawingPath(Pitch pitch, PitchField firstPos) { + this.pitch = pitch; + path.add(firstPos); + } + + public boolean addPathElement(PitchField element) { + if (pitch.isAdjacent(path.get(path.size() - 1), element)) { // new path element is adjacent to last added path element + path.add(element); + return true; + } else return false; + } + + public void removeLastPathElement() { + path.remove(path.size() - 1); + } + + public ArrayList getPath() { + return path; + } +} diff --git a/src/client/logic/GameRenderer.java b/src/client/logic/GameRenderer.java new file mode 100644 index 0000000..5f365f3 --- /dev/null +++ b/src/client/logic/GameRenderer.java @@ -0,0 +1,1446 @@ +package client.logic; + +import GUI.SBColor; +import client.Client; +import client.display.GameCanvas; +import client.display.GameFrame; +import gameLogic.*; +import gameLogic.dice.BlockDie; +import gameLogic.rules.RuleThrow; +import gameLogic.rules.SpecialRule; +import util.ResourceManager; + +import javax.vecmath.Vector2d; +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.RescaleOp; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; +import java.util.logging.Level; + +/** + * The Engine calculating rendering. + */ +public class GameRenderer { + + private static final int MAX_IMAGE_BUFFER_SIZE = 200, + STANDARD_BEACON_ALPHA = 30, + DIE_PADDING = 5; + public static final String CURRENCY_SYMBOL = "₪"; + + GameCanvas gameCanvas; + + public double vX = 500, vY = 2220, vZ = 1590, dY = 590, pZ = 0; + + private volatile HashMap> imageBuffer = new HashMap>(); + private volatile HashMap> imageFilterBuffer = new HashMap>(); + + private int aimingFieldDistanceClass = 0; + private Weather weather = null; + private int[][] weatherParticles; + private int[] weatherParticleSizes, weatherParticleVelocity; + + public GameRenderer(GameCanvas gameCanvas) { + this.gameCanvas = gameCanvas; + } + + public void drawPitch(Graphics2D g) { + if(g != null) { + g.setStroke(new BasicStroke((int) (getPW()/24), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + for (int x = 0; x < getGameCanvas().getPitch().getFields().length; x++) { +// for (int y = 0; y < getGameCanvas().getPitch().getFields()[x].length; y++) { +// PitchField field = getGameCanvas().getPitch().getFields()[x][y]; +// if (field != null) { + // draw background +// if(x == 0 || x == 25) g.setPaint(SBColor.GREEN_DARK); +// else g.setPaint(SBColor.GREEN_BRIGHT); +// fillRectangleOnPitch(g, x * getPW(), y * getPW(), getPW(), getPW()); +// } +// } + // draw vertical lines + g.setPaint(SBColor.WHITE); + drawLineOnPitch(g, x * getPW(), 0, x * getPW(), getPitchHeight()); + } + // draw last vertical line + g.setPaint(SBColor.WHITE); + drawLineOnPitch(g, 26 * getPW(), 0, 26 * getPW(), getPitchHeight()); + // draw horizontal lines + for (int y = 0; y < getGameCanvas().getPitch().getFields()[0].length+1; y++) + drawLineOnPitch(g, 0, y * getPW(), getPitchWidth(), y * getPW()); + // draw extra stuff + g.setStroke(new BasicStroke((int) (getPW() / 12), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + drawLineOnPitch(g, getPitchWidth() / 2, 0, getPitchWidth() / 2, getPitchHeight()); + drawLineOnPitch(g, 0, 4 * getPW(), getPitchWidth(), 4 * getPW()); + drawLineOnPitch(g, 0, 11 * getPW(), getPitchWidth(), 11 * getPW()); + } + } + + public void drawPlayers(Graphics2D g) { + Pitch pitch = getGameCanvas().getPitch(); + PitchField[][] fields = pitch.getFields(); + int ballX = (int) pitch.getBallPos().x, + ballY = (int) pitch.getBallPos().y; + for (int x = 0; x < fields.length; x++) { + for (int y = 0; y < fields[x].length; y++) { + if(fields[x][y] != null) { + // draw player + Player player = fields[x][y].getPlayer(); + if(player != null) drawPlayer(g, player); + // draw ball + if(x == ballX && y == ballY) drawBall(g, ballX, ballY); + } + } + } + } + + public void drawPlayer(Graphics2D g, Player player) { + boolean left = player.getTeam().equals(player.getMatch().getTeam(0)); + + if(getGameCanvas().isYourTurn() + && getGameCanvas().getGamePhase() < 4 + && (player.$GUIgetRemainingBe() > 0 || player.isHoldingBall() && player.getTeam().getPass()) + && isOwnPlayer(player) + && getGameCanvas().getGamePhase() >= 3 + && !( + player != getPitch().getTeam(getGameCanvas().getClientIndex()).getMovingPlayer() + && player.$GUIgetRemainingBe() == 0 + )) { + + highlightFieldEdges(g, player.getPos().x, player.getPos().y, SBColor.GREEN_DARK); + + } + + BufferedImage sprite; + if (left) sprite = player.getSpriteR(); + else sprite = player.getSpriteL(); + if (sprite == null) sprite = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + + double gap = (ResourceManager.IMAGE_WIDTH - ResourceManager.PEDESTAL_WIDTH)/2 * ((Pitch.PITCH_WIDTH+1)/(player.getPos().y+1)/20 + 1), + tx = player.getPos().x * ResourceManager.PEDESTAL_WIDTH - gap, + ty = player.getPos().y * ResourceManager.PEDESTAL_WIDTH;// + ResourceManager.PEDESTAL_WIDTH - ResourceManager.IMAGE_HEIGHT; + if(left) tx -= gap; + + float[] scaleFactors = new float[]{1, 1, 1, 1}; + if(getGameCanvas().getClientIndex() != (player.getTeam().equals(player.getMatch().getTeam(0)) ? 0 : 1)) scaleFactors = new float[]{0.8f, 0.8f, 0.8f, 1}; + RescaleOp rescaleOp = new RescaleOp(scaleFactors, new float[4], null); + + drawImageWithOp(g, sprite, rescaleOp, tx, ty, getT(), 0, true); + + if(player.$GUIgetPlayerCondition() == PlayerCondition.PRONE || player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw star if proned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + -(left ? 0 : ResourceManager.IMAGE_WIDTH/2)+tx+ResourceManager.IMAGE_WIDTH-(double) ResourceManager.PROP_STAR.getWidth(), ty + ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + if(player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw second star if stunned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + -(left ? 0 : ResourceManager.IMAGE_WIDTH/2)+tx+ResourceManager.IMAGE_WIDTH-3*(double) ResourceManager.PROP_STAR.getWidth()/2, ty + ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + + // if this player holds the ball, draw it right in front of it + double ballX = getPitch().getBallPos().x, + ballY = getPitch().getBallPos().y; + if(player.getTeam().getMatch().getPitch().getBallPos().equals(player.getPos())) drawBall(g, ballX, ballY); + } + + public void drawPlayersBench(Graphics2D g) { + + double playersInRow = Team.MAX_TEAM_SIZE / 2, + seatsInRow = playersInRow + 2, + playerScale = getPitchWidth() / 2 / seatsInRow / ResourceManager.IMAGE_WIDTH; + int playersOffBench = 0; + + // set players on bench + getGameCanvas().getPlayersOnBench().removeAllElements(); + getGameCanvas().getPlayersOnOpponentBench().removeAllElements(); + try { + for (Player player : getClient().getMatch().getTeam(getGameCanvas().getClientIndex()).getPlayers()) + if (player.getPosition().equals(Pitch.THE_VOID)) // player is outside of field + getGameCanvas().addPlayerOnBench(player); + for (Player player : getClient().getMatch().getTeam(getGameCanvas().getClientIndex() == 0 ? 1 : 0).getPlayers()) + if (player.getPosition().equals(Pitch.THE_VOID)) // player is outside of field + getGameCanvas().addPlayerOnOpponentBench(player); + } catch(NullPointerException e) { // trap for strange nullpointer + getClient().log(Level.SEVERE, "Send the following information to milan!"); + if(getClient().getMatch() != null) { + if(getGameCanvas() != null) { + if(getClient().getMatch().getTeam(getGameCanvas().getClientIndex()) != null) { + if(getClient().getMatch().getTeam(getGameCanvas().getClientIndex()).getPlayers() != null) { + if(getClient().getMatch().getTeam(getGameCanvas().getClientIndex()).getPlayers().get(0) != null) { + if(getClient().getMatch().getTeam(getGameCanvas().getClientIndex()).getPlayers().get(0).getPosition() != null) { + getClient().log(Level.SEVERE, "strange"); + } else getClient().log(Level.SEVERE, "position of player at 0 is null"); + } else getClient().log(Level.SEVERE, "player at 0 is null"); + } else getClient().log(Level.SEVERE, "players are null"); + } else getClient().log(Level.SEVERE, "team is null"); + } else getClient().log(Level.SEVERE, "game canvas is null"); + } else getClient().log(Level.SEVERE, "match is null"); + } + + // draw players on own bench + for (int i = 1; i <= getGameCanvas().getPlayersOnBench().size(); i++) { + Player playerToDraw = getGameCanvas().getPlayersOnBench().get(i-1); + + if(playerToDraw.$GUIisKOInjuredOrDead()) + playersOffBench++; + + drawPlayerOnOwnBench(g, playerToDraw, playersInRow, seatsInRow, i-playersOffBench, playerScale); + } + + // draw players on opponent bench + playersOffBench = 0; + for (int i = 1; i <= getGameCanvas().getPlayersOnOpponentBench().size(); i++) { + Player playerToDraw = getGameCanvas().getPlayersOnOpponentBench().get(i-1); + + if(playerToDraw.$GUIisKOInjuredOrDead()) + playersOffBench++; + + drawPlayerOnOpponentBench(g, playerToDraw, playersInRow, seatsInRow, i-playersOffBench, playerScale); + } + + } + + private void drawPlayerOnOwnBench(Graphics2D g, Player player, double playersInRow, double seatsInRow, int i, double scale) { + BufferedImage sprite = isLeft() ? player.getSpriteR() : player.getSpriteL(); + + if(player.$GUIisKOInjuredOrDead()) { // draw players in hospital or on graveyard + + drawInjuredOrDeadPlayer(g, player, sprite, isLeft(), scale); + + } else { + + double tx, ty = getPitchHeight()/scale + 2*ResourceManager.IMAGE_HEIGHT/5; + if(i <= playersInRow) tx = (isLeft() ? i : i+seatsInRow) * ResourceManager.IMAGE_WIDTH; + else { + tx = (isLeft() ? i-playersInRow : i+seatsInRow-playersInRow) * ResourceManager.IMAGE_WIDTH; + ty += ResourceManager.IMAGE_HEIGHT; + } + + drawImageWithOp(g, sprite, null, tx, ty, scale, 0, false); // draw on bench + if(player.getRedCard()) // draw red card if player was expelled from match + drawImageWithOp(g, ResourceManager.PROP_RED_CARD, null, tx, ty, scale, 0, false); + if(player.$GUIgetPlayerCondition() == PlayerCondition.PRONE || player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw star if proned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + tx+ResourceManager.IMAGE_WIDTH, ty + 3*ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + if(player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw second star if stunned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + tx+ResourceManager.IMAGE_WIDTH-(double) ResourceManager.PROP_STAR.getWidth()/2, ty + 3*ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + + } + } + + private void drawPlayerOnOpponentBench(Graphics2D g, Player player, double playersInRow, double seatsInRow, int i, double scale) { + BufferedImage sprite = isLeft() ? player.getSpriteL() : player.getSpriteR(); + + if(player.$GUIisKOInjuredOrDead()) { // draw players in hospital or on graveyard + + drawInjuredOrDeadPlayer(g, player, sprite, !isLeft(), scale); + + } else { + + double tx, ty = getPitchHeight()/scale + 2*ResourceManager.IMAGE_HEIGHT/5; + if(i <= playersInRow) tx = (isLeft() ? i+seatsInRow : i) * ResourceManager.IMAGE_WIDTH; + else { + tx = (isLeft() ? i+seatsInRow-playersInRow : i-playersInRow) * ResourceManager.IMAGE_WIDTH; + ty += ResourceManager.IMAGE_HEIGHT; + } + + drawImageWithOp(g, sprite, new RescaleOp(new float[]{0.8f, 0.8f, 0.8f, 1}, new float[4], null), tx, ty, scale, 0, false); // draw on bench + if(player.getRedCard()) // draw red card if player was expelled from match + drawImageWithOp(g, ResourceManager.PROP_RED_CARD, null, tx, ty, scale, 0, false); + if(player.$GUIgetPlayerCondition() == PlayerCondition.PRONE || player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw star if proned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + tx+ResourceManager.IMAGE_WIDTH, ty + 3*ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + if(player.$GUIgetPlayerCondition() == PlayerCondition.STUNNED) { // draw second star if stunned + drawImageWithOp( + g, ResourceManager.PROP_STAR, null, + tx+ResourceManager.IMAGE_WIDTH-(double) ResourceManager.PROP_STAR.getWidth()/2, ty + 3*ResourceManager.IMAGE_HEIGHT/4, + getT(), 0, true); + } + + } + } + + private void drawInjuredOrDeadPlayer(Graphics2D g, Player player, BufferedImage sprite, boolean left, double scale) { + double pitchWidthHalved = getPitchWidth() / 2 / scale, + actionFieldSize = getPitchHeight() / 4 / scale, + seatWidth = pitchWidthHalved / (Team.MAX_TEAM_SIZE / 2 + 2), + graveyardAndHospitalWidth = pitchWidthHalved - seatWidth - actionFieldSize, + hospitalWidth = 5*graveyardAndHospitalWidth/9, // don't change this ratio because of drawn graveyard and hospital! + tx, ty; + + int addX = (new Random(player.hashCode()*player.getId())).nextInt((int) (actionFieldSize - ResourceManager.IMAGE_WIDTH/2)), + addY = (new Random(player.hashCode()*player.getId())).nextInt((int) (actionFieldSize)); + ty = getPitchHeight()/scale + actionFieldSize + addY/2 + ResourceManager.IMAGE_HEIGHT*scale*4; + + if(player.$GUIgetPlayerCondition() == PlayerCondition.INJURED + || player.$GUIgetPlayerCondition() == PlayerCondition.KO) { // player is injured or ko + + if(left) tx = actionFieldSize + addX - ResourceManager.IMAGE_WIDTH*scale*2; + else tx = getW()/scale - actionFieldSize - hospitalWidth + addX - ResourceManager.IMAGE_WIDTH*scale*2; + + drawImageWithOp(g, sprite, null, tx, ty, scale, 0, false); + BufferedImage bandAid = player.$GUIgetPlayerCondition() == PlayerCondition.INJURED ? ResourceManager.PROP_BAND_AID_DOUBLE : ResourceManager.PROP_BAND_AID; + drawImageWithOp(g, bandAid, null, tx + bandAid.getWidth() / 2, ty + ResourceManager.IMAGE_HEIGHT - bandAid.getHeight(), scale, 0, false); + + } else { // player is dead + + if(left) tx = actionFieldSize + hospitalWidth + addX - ResourceManager.IMAGE_WIDTH*scale*4; + else tx = getW()/scale - actionFieldSize - graveyardAndHospitalWidth + addX - ResourceManager.IMAGE_WIDTH*scale*4; + + drawImageWithOp(g, sprite, null, tx, ty, scale, 0, false); + + } + + } + + public void drawGUI(Graphics2D g) { + BufferedImage background = ResourceManager.BACKGROUND; + double scale = getW()/background.getWidth(); + background = scaleImage(background, background.hashCode(), scale); + g.drawImage(background, null, 0, 0); + } + + public void drawTooltip(Graphics2D g, Player player, int[] mXY) { + if(getGameCanvas().isChoosingSpecialRule()) + player = getGameCanvas().getPlayerChoosingSpecialRuleFor(); + + Font textFont = new Font("SansSerif", Font.PLAIN, (int) (12*10*getT())), + textFontSmall = new Font("Monospaced", Font.PLAIN, (int) (9*10*getT())), + textFontStats = new Font("Monospaced", Font.PLAIN, (int) (12*10*getT())), + textFontBold = new Font("SansSerif", Font.BOLD, (int) (12*10*getT())), + textFontBig = new Font("SansSerif", Font.PLAIN, (int) (14*10*getT())), + textFontBigBold = new Font("SansSerif", Font.BOLD, (int) (14*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFont), + metricsSmall = g.getFontMetrics(textFontSmall), + metricsStats = g.getFontMetrics(textFontStats), + metricsBold = g.getFontMetrics(textFontBold), + metricsBig = g.getFontMetrics(textFontBig), + metricsBigBold = g.getFontMetrics(textFontBigBold); + float padding = (int) (75*getT()), + wN = metricsBold.stringWidth(player.toString()), + hN = metricsBold.getHeight(), + hD = metricsSmall.getHeight(), + hS = metricsStats.getHeight(), + hO = metrics.getHeight(), + hR = metricsBig.getHeight(), + corr = 0, + width = wN; + + if(getGameCanvas().isChoosingSpecialRule()) { // special rules menu + + int[] pos = getGameCanvas().getSpecialRuleMenuPosition(); + int hovering = getGameCanvas().getPitchMouseLogic().getSpecialRuleChoiceHovering(); + ArrayList specialRules = new ArrayList(); + for(SpecialRule specialRule : player.getSpecialRules()) + specialRules.add("◆ " + specialRule.getName() + " ◆"); + for(int i = 0; i < specialRules.size(); i++) { + int w; + if(hovering == i) w = metricsBigBold.stringWidth(specialRules.get(i)); + else w = metricsBig.stringWidth(specialRules.get(i)); + if(w > width) width = w; + } + width += 2 * padding; + + if(getW() - pos[0] - width < 0) corr = width; + + g.setPaint(SBColor.BLACK_180); + float height = 2 * padding + hN + specialRules.size() * (padding + hR); + g.fillRect((int) (pos[0] + padding / 2 - corr), (int) (pos[1] + padding / 2), (int) width, (int) height); + + g.setPaint(SBColor.WHITE); + g.setFont(textFontBold); + g.drawString(player.toString(), pos[0] + 3 * padding / 2 - corr, pos[1] + padding + hN); + + for(int i = 0; i < specialRules.size(); i++) { + float dy = pos[1] + padding + 2 * hN + (i + 1) * padding + i * hR; + if(hovering == i) g.setFont(textFontBigBold); + else g.setFont(textFontBig); + + g.drawString(specialRules.get(i), pos[0] + 3 * padding / 2 - corr, dy); + } + + // set sizes for mouse listeners + getGameCanvas().setSpecialRuleMenuSize(new int[]{(int) width, (int) height}); + getGameCanvas().setSpecialRuleMenuNameHeight((int) (padding + 2 * hN)); + getGameCanvas().setSpecialRuleMenuItemHeight((int) (padding + hR)); + + } else { // normal tooltip + + String[] description = player.$GUIgetDescriptionLines(); + for(String string : description) { + int w = metricsSmall.stringWidth(string); + if(w > width) width = w; + } + + String[] stats = new String[]{ + "BE: " + player.$GUIgetRemainingBe() + "/" + player.$GUIgetBe() + " ST: " + player.$GUIgetSt(), + "GE: " + player.$GUIgetGe() + " RS: " + player.$GUIgetRs()}; + for(String string : stats) { + int w = metricsStats.stringWidth(string); + if(w > width) width = w; + } + + ArrayList other = new ArrayList(); + if(player.getRedCard()) other.add("Banned from game!"); + other.add(player.$GUIgetPlayerCondition().randomConditionDescription(player)); + for(SpecialRule specialRule : player.getSpecialRules()) + other.add("◆ Can " + specialRule.getName()); + if(player.isHoldingBall()) { + if(player.getTeam().getPass()) other.add("I wanna toss this ball!"); + else other.add("I can't pass this turn."); + } + for(String string : other) { + int w = metrics.stringWidth(string); + if(w > width) width = w; + } + + width += 2 * padding; + if(getW() - mXY[0] - width < 0) corr = width; + + g.setPaint(SBColor.BLACK_180); + float height = 2 * padding + hN + description.length * hD + stats.length * (padding + hS) + other.size() * (padding + hO); + g.fillRect((int) (mXY[0] + padding / 2 - corr), (int) (mXY[1] + padding / 2), (int) width, (int) height); + + g.setPaint(SBColor.WHITE); + g.setFont(textFontBold); + g.drawString(player.toString(), mXY[0] + 3 * padding / 2 - corr, mXY[1] + padding + hN); + + g.setFont(textFontSmall); + for(int i = 0; i < description.length; i++) { + float dy = mXY[1] + padding + 2 * hN + i * hD; + g.drawString(description[i], mXY[0] + 3 * padding / 2 - corr, dy); + } + + g.setFont(textFontStats); + for(int i = 0; i < stats.length; i++) { + float dy = mXY[1] + padding + 2 * hN + description.length * hD + (i + 1) * padding + i * hS; + g.drawString(stats[i], mXY[0] + 3 * padding / 2 - corr, dy); + } + + g.setFont(textFont); + for(int i = 0; i < other.size(); i++) { + float dy = mXY[1] + padding + 2 * hN + description.length * hD + (stats.length + 1) * padding + stats.length * hS + i * padding + i * hO; + g.drawString(other.get(i), mXY[0] + 3 * padding / 2 - corr, dy); + } + } + } + + public void drawMovingPlayer(Graphics2D g) { + if(getGameCanvas().getMovingPlayer() != null) { + AffineTransform transform = new AffineTransform(); + double tx = getGameCanvas().getStoredMousePosition()[0] / getT(), + ty = getGameCanvas().getStoredMousePosition()[1] / getT(); + + BufferedImage sprite; + if(isLeft()) sprite = getGameCanvas().getMovingPlayer().getSpriteR(); + else sprite = getGameCanvas().getMovingPlayer().getSpriteL(); + if(sprite == null) sprite = new BufferedImage(100, 200, BufferedImage.TYPE_INT_ARGB); + + transform.scale(getT(), getT()); + transform.translate(tx, ty); + double rotation = -(getGameCanvas().getGameFrame().getOldMousePositions().get(0)[0]-getGameCanvas().getGameFrame().getOldMousePositions().get(1)[0]) / getPW(); + if(rotation > GameFrame.MAX_DRAG_ROTATION) rotation = GameFrame.MAX_DRAG_ROTATION; + else if(rotation < -GameFrame.MAX_DRAG_ROTATION) rotation = -GameFrame.MAX_DRAG_ROTATION; + transform.rotate(rotation); + g.transform(transform); + + g.drawImage(sprite, new RescaleOp(new float[]{0.8f, 0.8f, 0.8f, 0.6f}, new float[4], null), -ResourceManager.IMAGE_WIDTH / 2, 0); + + try { g.transform(transform.createInverse()); } + catch (NoninvertibleTransformException ignored) {} + } + } + + public void drawSetPlayer(Graphics2D g) { + drawMovingPlayer(g); + + if(getGameCanvas().getMovingPlayer() != null && getGameCanvas().getFieldAimingAt() != null) { + + if(getGameCanvas().getFieldAimingAt().getPlayer() == null) { // there is nobody on this field yet + highlightField(g, + getPitchMouseLogic().getFieldAimingAt().getPos().x, + getPitchMouseLogic().getFieldAimingAt().getPos().y, + SBColor.YELLOW_80); + } + + } + } + + public void drawBall(Graphics2D g, double x, double y) { + if(getPitch()!= null) { + if(x >= 0 && x < Pitch.PITCH_LENGTH && y >= 0 && y < Pitch.PITCH_WIDTH) { + double tx = x * ResourceManager.PEDESTAL_WIDTH * 2, + ty = y * ResourceManager.PEDESTAL_WIDTH * 2; + // draw beacon + drawBeacon(g, x, y, SBColor.BLUE_BRIGHT); + // draw ball + Player playerWithBall = getPitch().getFields()[(int) x][(int) y].getPlayer(); + if(playerWithBall == null) { // no player has the ball + tx += ResourceManager.PEDESTAL_WIDTH/4; + ty += 3*ResourceManager.PEDESTAL_WIDTH/2; + } else { // a player has the ball + boolean left = playerWithBall.getTeam().equals(getPitch().getTeam(0)); + tx = left ? tx + ResourceManager.PEDESTAL_WIDTH/2 : tx - ResourceManager.PEDESTAL_WIDTH/8; + ty += ResourceManager.PEDESTAL_WIDTH/2; + } + drawImageWithOp(g, ResourceManager.PROP_FOOTBALL, null, tx, ty, getT() / 2, 0, true); + } + } + } + + public void drawPath(Graphics2D g) { + // draw path + if(getPitchMouseLogic().getDrawingPath() != null) { + for (int i = 1; i < getPitchMouseLogic().getDrawingPath().getPath().size(); i++) { + PitchField field = getPitchMouseLogic().getDrawingPath().getPath().get(i); + if(field != null) { + highlightField(g, field.getPos().x, field.getPos().y, SBColor.YELLOW_80); + } + } + } + + // draw blitz field + if(getPitchMouseLogic().getFieldBlitzing() != null) { + highlightField(g, + getPitchMouseLogic().getFieldBlitzing().getPos().x, + getPitchMouseLogic().getFieldBlitzing().getPos().y, + SBColor.BLACK_60); + } + } + + public void drawFieldsReachable(Graphics2D g) { + Player aimingPlayer = getPitchMouseLogic().getAimingPlayer(); + + if(getPitchMouseLogic().getFieldAimingAt() != null && aimingPlayer != null) { + if(!getPitchMouseLogic().isAimingKickoff()) { // only draw radii and color differently for normal passes + + double mX = getPitchMouseLogic().getMXY()[0], + mY = getPitchMouseLogic().getMXY()[1]; + Color paint; + + for(int y = 0; y < Pitch.PITCH_WIDTH; y++) { + for(int x = 0; x < Pitch.PITCH_LENGTH; x++) { + PitchField field = getPitch().getFields()[x][y]; + if(field != null) { + + Vector2d dist = (Vector2d) aimingPlayer.getPos().clone(); + dist.sub(field.getPos()); + + paint = SBColor.YELLOW_80; + if (dist.length() > RuleThrow.SHORT_PASS) paint = SBColor.ORANGE_BRIGHT_80; + if (dist.length() > RuleThrow.LONG_PASS) paint = SBColor.ORANGE_DARK_80; + if (dist.length() > RuleThrow.LONG_BOMB) paint = SBColor.RED_80; + + if(dist.length() <= RuleThrow.LONG_BOMB) + highlightField(g, field.getPos().x, field.getPos().y, paint); + + } + } + } + + // field + Vector2d dist = (Vector2d) aimingPlayer.getPos().clone(); + dist.sub(new Vector2d(mX, mY)); + + paint = SBColor.YELLOW_80; + aimingFieldDistanceClass = 0; + if (dist.length() > RuleThrow.SHORT_PASS) { + paint = SBColor.ORANGE_BRIGHT_80; + aimingFieldDistanceClass = 1; + } + if (dist.length() > RuleThrow.LONG_PASS) { + paint = SBColor.ORANGE_DARK_80; + aimingFieldDistanceClass = 2; + } + if (dist.length() > RuleThrow.LONG_BOMB) { + paint = SBColor.RED_80; + aimingFieldDistanceClass = 3; + } + + if(getPitchMouseLogic().getFieldAimingAt() != null) + highlightField(g, + getPitchMouseLogic().getFieldAimingAt().getPos().x, + getPitchMouseLogic().getFieldAimingAt().getPos().y, + paint); + + } + } + } + + public void drawFieldAimingAt(Graphics2D g) { + Player aimingPlayer = getPitchMouseLogic().getAimingPlayer(); + + if(getPitchMouseLogic().getFieldAimingAt() != null && aimingPlayer != null) { + Color paint; + + if(getPitchMouseLogic().isAimingKickoff()) { + highlightField(g, + getPitchMouseLogic().getFieldAimingAt().getPos().x, + getPitchMouseLogic().getFieldAimingAt().getPos().y, + SBColor.YELLOW_80); + paint = SBColor.YELLOW; + } else { // only check if curve is drawn for normal passes + // curve + paint = SBColor.YELLOW; + if (aimingFieldDistanceClass == 1) paint = SBColor.ORANGE_BRIGHT; + if (aimingFieldDistanceClass == 2) paint = SBColor.ORANGE_DARK; + if (aimingFieldDistanceClass == 3) paint = SBColor.RED; + } + + Vector2d aimingPlayerPos = getPitchMouseLogic().getAimingPlayer().getPos(); + + if(aimingPlayerPos != null) { + drawCurveToMouse(g, aimingPlayerPos, paint); + } + + } + } + + public void drawGiveBall(Graphics2D g) { + if(getGameCanvas().canGiveBall()) { // only draw if can give ball + if(getPitchMouseLogic().getHoveringPlayer() != null) { + double x = getPitchMouseLogic().getHoveringPlayer().getPos().x, y = getPitchMouseLogic().getHoveringPlayer().getPos().y; + + g.setStroke(new BasicStroke((int) (getPW() / 36), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + // draw hovering field + g.setPaint(SBColor.YELLOW_80); + highlightField(g, x, y, SBColor.YELLOW_80); + // draw ball on hovering field + drawBall(g, x, y); + } + } + } + + public void drawFrameRate(Graphics2D g) { + g.setPaint(SBColor.WHITE); + g.setFont(new Font("Monospaced", Font.PLAIN, (int) (12 * 10* getT()))); + g.drawString(getGameCanvas().frameRate+"", (int) (18 * getT()), (int) (288 * getT())); + } + + public void drawPositionMarker(Graphics2D g) { + double[] mXY = getPitchMouseLogic().getMXY(); + int mX = (int) mXY[0], mY = (int) mXY[1]; + + String pos = mX + ", " + mY; + if(mX < 0) pos = "0, " + mY; + if(mY < 0) pos = mX + ", 0" ; + if(mX < 0 && mY < 0) pos = "0, 0"; + + if(getPitchMouseLogic().getHoveringPlayer() != null) { + pos += " – " + getPitchMouseLogic().getHoveringPlayer().getName() + " " + getPitchMouseLogic().getHoveringPlayer().getId(); + } + if(getGameCanvas().getMovingPlayer() != null) { + pos += " – " + "Moving " + getGameCanvas().getMovingPlayer().getName() + " " + getGameCanvas().getMovingPlayer().getId(); + } + if(getPitchMouseLogic().getAimingPlayer() != null && !getPitchMouseLogic().isAimingKickoff()) { + pos += " – aiming"; + } + g.setPaint(SBColor.WHITE); + g.setFont(new Font("Monospaced" , Font.PLAIN, (int) (12 * 10* getT()))); + g.drawString(pos, (int) (18 * getT()), (int) (138 * getT())); + } + + public void drawScore(Graphics2D g) { + try { + String score1 = getPitch().getTeam(0).getMatch().getScoreFromTeam(0) + "", score2 = getPitch().getTeam(1).getMatch().getScoreFromTeam(1) + "", + name1 = getPitch().getTeam(0).getName(), name2 = getPitch().getTeam(1).getName(), + divider = ":"; + + Font textFontBold = new Font("Monospaced", Font.BOLD, (int) (12*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFontBold); + int wN1 = metrics.stringWidth(name1); + int wS1 = metrics.stringWidth(score1); + int wS2 = metrics.stringWidth(score2); + int wD = metrics.stringWidth(divider); + + g.setPaint(SBColor.WHITE); + g.setFont(textFontBold); + g.drawString(divider, (int) (getPitchWidth() / 2 - wD / 2), (int) (138 * getT())); + g.drawString(score1, (int) (getPitchWidth() / 2 - wS1 - wD / 2), (int) (138 * getT())); + g.drawString(score2, (int) (getPitchWidth() / 2 + wD / 2), (int) (138 * getT())); + g.drawString(name1, (int) (getPitchWidth() / 2 - wN1 - wS1 - 2 * wD), (int) (138 * getT())); + g.drawString(name2, (int) (getPitchWidth() / 2 + wS2 + 2*wD), (int) (138 * getT())); + } catch(NullPointerException ignored) {} + } + + public void drawRoundCount(Graphics2D g) { + try { + String count = "Round " + ((getPitch().getTeam(0).getMatch().getRoundCount()+2)/2); + + Font textFontBold = new Font("Monospaced", Font.BOLD, (int) (12*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFontBold); + int wC = metrics.stringWidth(count); + + g.setPaint(SBColor.WHITE); + g.setFont(new Font("Monospaced", Font.PLAIN, (int) (12 * 10* getT()))); + g.drawString(count, (int) (getPitchWidth() - wC - 18 * getT()), (int) (138 * getT())); + } catch(NullPointerException ignored) {} + } + + public boolean preparedWeather() { + return weather != null; + } + + public void prepareWeather(Weather weather, double w, double h, double pitchH) { + this.weather = weather; + + switch(weather) { + case SWELTERING_HEAT: + + break; + + case VERY_SUNNY: + + break; + + case NICE: + + break; + + case POURING_RAIN: + weatherParticles = new int[1000][2]; + for(int i = 0; i < weatherParticles.length; i++) { + weatherParticles[i][0] = (int) (Math.random()*w); + weatherParticles[i][1] = (int) (Math.random()*h); + } + weatherParticleSizes = new int[]{-10, 20}; + weatherParticleVelocity = new int[]{-10, 20}; + break; + + case BLIZZARD: + + break; + } + } + + public void drawWeather(Graphics2D g) { + try { + + String weather = "Weather: " + this.weather.toNiceString(); + + Font textFontBold = new Font("Monospaced", Font.BOLD, (int) (12*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFontBold); + int wC = metrics.stringWidth(weather); + + g.setPaint(SBColor.WHITE); + g.setFont(new Font("Monospaced", Font.PLAIN, (int) (12 * 10* getT()))); + g.drawString(weather, (int) (getPitchWidth() - wC - 18 * getT()), (int) (288 * getT())); + + drawWeatherEffect(g); + + } catch(NullPointerException ignored) {} + } + + private void drawWeatherEffect(Graphics2D g) { + int x, y; + + switch(weather) { + case SWELTERING_HEAT: + + break; + + case VERY_SUNNY: + + break; + + case NICE: + + break; + + case POURING_RAIN: + g.setStroke(new BasicStroke((int) (getPW() / 20), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g.setPaint(SBColor.BLACK_20); +// for(int i = 0; i < weatherParticles.length; i++) { +// g.drawLine(weatherParticles[i][0], weatherParticles[i][1], weatherParticles[i][0]+weatherParticleSizes[0], weatherParticles[i][1]+weatherParticleSizes[1]); +// weatherParticles[i][0] += weatherParticleVelocity[0]; +// weatherParticles[i][1] += weatherParticleVelocity[1]; +// if(weatherParticles[i][0] < 0) { +// weatherParticles[i][0] = (int) getW(); +// weatherParticles[i][1] = (int) (Math.random()*getH() + i/getH()); +// } +// if(weatherParticles[i][1] > getH()) { +// weatherParticles[i][0] = (int) (Math.random()*getW() + i/getW()); +// weatherParticles[i][1] = 0; +// } +// } + for(int i = 0; i < 1000; i++) { + x = (int) (Math.random()*getW()); + y = (int) (Math.random()*getH()); + g.drawLine(x, y, x-10, y+20); + } + break; + + case BLIZZARD: + g.setPaint(SBColor.WHITE); + for(int i = 0; i < 500; i++) { + x = (int) (Math.random()*getW()); + y = (int) (Math.random()*getH()); + g.fillOval(x, y, 8, 5); + } + break; + + } + } + + public void drawGamePhaseInfo(Graphics2D g) { + String gamePhaseInfo = ""; + switch (getGameCanvas().getGamePhase()) { + case 0: // Team Choosing Phase + if(!getGameCanvas().choseTeam()) gamePhaseInfo = "Choose your team"; + else gamePhaseInfo = "Waiting for opponent to choose team"; + if(!getGameCanvas().choseTeamType()) drawChooseTeam(g); + else drawChoosePlayers(g); + break; + case 1: // Team Setup Phase + if(getGameCanvas().canSetUp() && !getGameCanvas().hasSetUpTeam()) gamePhaseInfo = "Setup your team"; + else gamePhaseInfo = "Waiting for opponent to set up"; + break; + case 2: // Kick Phase + if(getGameCanvas().canKickOff()) gamePhaseInfo = "Kick"; + else if(getGameCanvas().canGiveBall()) gamePhaseInfo = "Give ball to player"; + else gamePhaseInfo = "Kickoff: Waiting for opponent"; + break; + case 3: // Normal Playing Phase + if(getGameCanvas().isYourTurn()) gamePhaseInfo = "Your turn"; + else gamePhaseInfo = "Opponent turn"; + break; + case 4: // Finishing Phase + gamePhaseInfo = "Finishing"; + break; + case 5: // Waiting Phase + if(getGameCanvas().requestedDiceAPI()) gamePhaseInfo = "Choose Die"; + else if(getGameCanvas().requestedAimAPI()) gamePhaseInfo = "Aiming"; + else if(getGameCanvas().requestedChoiceAPI()) gamePhaseInfo = "Choose Player"; + else if(getGameCanvas().requestedFieldAPI()) gamePhaseInfo = "Choose Field"; + else gamePhaseInfo = "Waiting for decision"; + break; + } + if(gamePhaseInfo.length() > 0) { + Font textFontBig = new Font("SansSerif", Font.BOLD, (int) (28*10*getT())); + FontMetrics metricsBig = g.getFontMetrics(textFontBig); + int wGPI = metricsBig.stringWidth(gamePhaseInfo); + g.setFont(textFontBig); + g.setPaint(SBColor.WHITE); + if(getGameCanvas().getGamePhase() == 0) g.setPaint(SBColor.BLACK); + if(getGameCanvas().getGamePhase() > 0) + g.drawString(gamePhaseInfo, (int) (getPitchWidth()/2 - wGPI/2), (int) (getPitchHeight()/10)); + else { + g.drawString(gamePhaseInfo, (int) (getPitchWidth()/2 - wGPI/2), (int) (getPitchHeight() + 2*metricsBig.getHeight())); + if(getGameCanvas().choseTeamType()) { + String remainingMoneyMessage = "Remaining Money: " + getGameCanvas().getRemainingMoney() + CURRENCY_SYMBOL; + Font textFont = new Font("SansSerif", Font.PLAIN, (int) (12*10*getT())); + g.setFont(textFont); + g.drawString(remainingMoneyMessage, (int) (getPitchWidth() / 2 - wGPI / 2), (int) (getPitchHeight() + metricsBig.getHeight())); + } + } + } + } + + public void drawGameActionFields(Graphics2D g) { + double actionFieldSize = getPitchHeight()/4; + if(getGameCanvas().getGamePhase() == 0 && getGameCanvas().choseTeamType()) { + + // choose arrow + double x0 = getW() - actionFieldSize, y0 = getH() - actionFieldSize; + g.setPaint(SBColor.ORANGE_BRIGHT); + GeneralPath arrowChoose = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 7); + arrowChoose.moveTo(x0 + 5f/6f * actionFieldSize, y0 + 1f/2f * actionFieldSize); + arrowChoose.lineTo(x0 + 7f/12f * actionFieldSize, y0 + 1f/4f * actionFieldSize); + arrowChoose.lineTo(x0 + 7f/12f * actionFieldSize, y0 + 5f/12f * actionFieldSize); + arrowChoose.lineTo(x0 + 1f/6f * actionFieldSize, y0 + 5f/12f * actionFieldSize); + arrowChoose.lineTo(x0 + 1f/6f * actionFieldSize, y0 + 7f/12f * actionFieldSize); + arrowChoose.lineTo(x0 + 7f/12f * actionFieldSize, y0 + 7f/12f * actionFieldSize); + arrowChoose.lineTo(x0 + 7f/12f * actionFieldSize, y0 + 3f/4f * actionFieldSize); + arrowChoose.closePath(); + g.fill(arrowChoose); + + // back arrow + x0 = 0; + y0 = getH() - actionFieldSize; + g.setPaint(SBColor.ORANGE_DARK); + GeneralPath arrowBack = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 7); + arrowBack.moveTo(x0 + 1f/6f * actionFieldSize, y0 + 1f/2f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/12f * actionFieldSize, y0 + 1f/4f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/12f * actionFieldSize, y0 + 5f/12f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/6f * actionFieldSize, y0 + 5f/12f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/6f * actionFieldSize, y0 + 7f/12f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/12f * actionFieldSize, y0 + 7f/12f * actionFieldSize); + arrowBack.lineTo(x0 + 5f/12f * actionFieldSize, y0 + 3f/4f * actionFieldSize); + arrowBack.closePath(); + g.fill(arrowBack); + } + } + + public void drawChooseTeam(Graphics2D g) { + int numberOfTeams = getClient().getMatch().getNumberOfAvailableTeams(); + double teamWidth = getPitchWidth() / numberOfTeams; + Font textFont = new Font("SansSerif", Font.PLAIN, (int) (12*10*getT())); + g.setFont(textFont); + FontMetrics metrics = g.getFontMetrics(textFont); + int hN = metrics.getHeight(); + + for (int i = 0; i < numberOfTeams; i++) { + Team team = getClient().getMatch().getAvailableTeam(i); + int wN = metrics.stringWidth(team.getType()); + + g.setPaint(i % 2 == 0 ? SBColor.ORANGE_DARK : SBColor.ORANGE_BRIGHT); + g.fillRect((int) (i * teamWidth), 0, (int) teamWidth, (int) teamWidth); + g.setPaint(SBColor.BLACK); + g.drawString(team.getType(), (int) (i * teamWidth + teamWidth / 2 - wN / 2), (int) (teamWidth / 2 - hN / 2)); + } + } + + public void drawChoosePlayers(Graphics2D g) { + + // draw available players + int numberOfAvailablePlayers = getClient().getMatch().getAvailableTeam(getPitchMouseLogic().getTeamIndexHovering()).getAvailablePlayers().size(); + double calcScale = getPitchWidth()/numberOfAvailablePlayers / ResourceManager.IMAGE_WIDTH; + if(calcScale * ResourceManager.IMAGE_HEIGHT > getPitchHeight()/2) calcScale = getPitchHeight()/2 / ResourceManager.IMAGE_HEIGHT; + Font textFont = new Font("SansSerif", Font.PLAIN, (int) (20*10*getT())), + textFontSmall = new Font("Monospace", Font.PLAIN, (int) (16*10*getT())), + textFontBold = new Font("SansSerif", Font.BOLD, (int) (20*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFont), + metricsSmall = g.getFontMetrics(textFontSmall); + int hN = metrics.getHeight(), + hD = metricsSmall.getHeight(); + + for (int i = 0; i < getGameCanvas().getTeamChosen().getAvailablePlayers().size(); i++) { + Player p = getGameCanvas().getTeamChosen().getAvailablePlayer(i).getCloneForTeam(getGameCanvas().getTeamChosen()); + int wN = metrics.stringWidth(p.getName()); + int headcountLeft = p.getMaxHeadcount(); + for(Player player: getGameCanvas().getPlayersChosen()) if(player.getName().equals(p.getName())) headcountLeft--; + + AffineTransform transform = new AffineTransform(); + transform.scale(calcScale, calcScale); + transform.translate(i * ResourceManager.IMAGE_WIDTH, 0); + g.transform(transform); + + if(headcountLeft > 0 && getGameCanvas().getRemainingMoney() - p.getPrice() >= 0) g.drawImage(p.getSpriteL(), null, 0, 0); + else g.drawImage(p.getSpriteL(), new RescaleOp(new float[]{0.6f, 0.6f, 0.6f, 0.6f}, new float[4], null), 0, 0); + + g.setPaint(SBColor.BLACK); + g.setFont(textFontBold); + g.drawString(p.getName(), i == 0 ? hD : 0, ResourceManager.IMAGE_HEIGHT + 2 * hN); + g.setFont(textFontSmall); + String[] description = p.$GUIgetDescriptionLines(); + description = wrapLines(description, ResourceManager.IMAGE_WIDTH - hD, metricsSmall); + for(int j = 0; j < description.length; j++) { + g.drawString(description[j], i == 0 ? hD : 0, ResourceManager.IMAGE_HEIGHT + j * hD + 3 * hN); + } + g.setFont(textFont); + String[] strings = new String[]{ + p.getPrice()+CURRENCY_SYMBOL, + "BE: "+p.$GUIgetBe(), + "ST: "+p.$GUIgetSt(), + "GE: "+p.$GUIgetGe(), + "RS: "+p.$GUIgetRs(), + "Left: "+headcountLeft}; + for (int j = 0; j < strings.length; j++) { + g.drawString(strings[j], i == 0 ? hD : 0, ResourceManager.IMAGE_HEIGHT + (description.length+1)*hD + j*hN + 3*hN); + } + + try { g.transform(transform.createInverse()); } + catch (NoninvertibleTransformException e) { + getClient().log(Level.WARNING, "Tried to invert noninvertible transform."); + e.printStackTrace(); + } + } + + // draw chosen players + calcScale = getPitchWidth() / Team.MAX_TEAM_SIZE / ResourceManager.IMAGE_WIDTH; + + for (int i = 0; i < getGameCanvas().getPlayersChosen().size(); i++) { + Player p = getGameCanvas().getPlayersChosen().get(i); + + AffineTransform transform = new AffineTransform(); + transform.scale(calcScale, calcScale); + transform.translate(i * ResourceManager.IMAGE_WIDTH, getPitchHeight() / calcScale - ResourceManager.IMAGE_HEIGHT); + g.transform(transform); + + g.drawImage(p.getSpriteL(), null, 0, 0); + if(!getGameCanvas().hasSentChooseTeam()) g.drawImage(ResourceManager.PROP_CROSS, null, + ResourceManager.IMAGE_WIDTH - ResourceManager.PROP_CROSS.getWidth(), + -ResourceManager.PROP_CROSS.getHeight() / 3); + + try { g.transform(transform.createInverse()); } + catch (NoninvertibleTransformException e) { + getClient().log(Level.WARNING, "Tried to invert noninvertible transform."); + e.printStackTrace(); + } + } + } + + private String[] wrapLines(String[] lines, int maxWidth, FontMetrics metrics) { + int newSize = lines.length, offset = 0; + for(String line: lines) { + if(metrics.stringWidth(line) > maxWidth) + newSize++; + } + String[] newLines = new String[newSize]; + boolean testAgain = false; + for(int i = 0; i < lines.length; i++) { + String line = lines[i]; + if(metrics.stringWidth(line) > maxWidth) { + String[] splitted = splitInHalf(line); + newLines[i+offset] = splitted[0]; + newLines[i+1+offset] = splitted[1]; + offset++; + testAgain = true; + } else newLines[i+offset] = line; + } + if(testAgain) return wrapLines(newLines, maxWidth, metrics); + else return newLines; + } + + private String[] splitInHalf(String line) { + String[] words = line.split(" "); + String part1 = "", part2 = ""; + for(int k = 0; k < words.length / 2; k++) part1 += words[k] + " "; + for(int k = words.length / 2; k < words.length; k++) part2 += words[k] + " "; + return new String[]{part1, part2}; + } + + // API + + public void drawHighlightAPI(Graphics2D g, Vector2d[] positions, Color[] colors) { + if(positions.length == colors.length) { + for(int i = 0; i < positions.length; i++) { + highlightField(g, positions[i].x, positions[i].y, colors[i]); + } + } + } + + @SuppressWarnings("ConstantConditions") + public void drawShowMe(Graphics2D g, String player, String showMe, int alpha) { + Font textFontBig = new Font("SansSerif", Font.BOLD, (int) (28*10*getT())); + FontMetrics metrics = g.getFontMetrics(textFontBig); + String message = player + ": \"" + showMe + "\""; + int wSM = metrics.stringWidth(message), + hSM = metrics.getHeight(); + int alphaMax = GameCanvas.SHOW_ME_ALPHA; + if(alpha > 255) alpha = 255; + if(alphaMax > 255) alphaMax = 255; + g.setFont(textFontBig); + g.setPaint(new Color(0, 0, 0, (int) ((float) alphaMax/255 * alpha))); + g.drawString(message, (int) (getPitchWidth()/2 - wSM/2), (int) (getPitchHeight()/2 - hSM/2)); + } + + @SuppressWarnings("SuspiciousNameCombination") + public void drawDiceAPI(Graphics2D g, int[] sides) { + double actionFieldSize = getPitchHeight()/4, + dieSize = (actionFieldSize - 3*DIE_PADDING) / 2, + scale = dieSize / ResourceManager.DIE_IMAGE_WIDTH; + + for(int i = 0; i < sides.length; i++) { + int side = sides[i]; + BufferedImage sideImage = BlockDie.getImageFromSide(side); + + if(sideImage == null) { // create brown rect if image was not loaded + sideImage = new BufferedImage(ResourceManager.DIE_IMAGE_WIDTH, ResourceManager.DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB); + Graphics2D dieG = sideImage.createGraphics(); + dieG.setPaint(SBColor.BROWN); + dieG.drawRect(0, 0, sideImage.getWidth(), sideImage.getHeight()); + dieG.dispose(); + } + + sideImage = scaleImage(sideImage, sideImage.hashCode(), scale); + g.drawImage(sideImage, (int) (DIE_PADDING + (i % 2) * (DIE_PADDING + dieSize)), (int) (getH() - (i > 1 ? 1 : 2) * (DIE_PADDING + dieSize)), null); + + } + } + + public void drawAimAPI(Graphics2D g, int playerIndex, int distance) { + if(getGameCanvas().requestedAimAPI() && playerIndex >= 0 && playerIndex < Team.MAX_TEAM_SIZE && distance > 0) { + Team aimingTeam = isLeft() ? getClient().getMatch().getTeam(0) : getClient().getMatch().getTeam(1); + + if(aimingTeam != null) { + Player aimingPlayer = aimingTeam.getPlayers().get(playerIndex); + + if(aimingPlayer != null) { + Vector2d aimingPlayerPos = (Vector2d) aimingPlayer.getPos().clone(); + + if(aimingPlayerPos != null) { + Vector2d dist = new Vector2d(getGameCanvas().getPitchMouseLogic().getMXY()[0], getGameCanvas().getPitchMouseLogic().getMXY()[1]); + dist.sub(aimingPlayerPos); + + if(dist.length() <= distance) { + Color paint = SBColor.YELLOW; + if(aimingFieldDistanceClass == 1) paint = SBColor.ORANGE_BRIGHT; + if(aimingFieldDistanceClass == 2) paint = SBColor.ORANGE_DARK; + if(aimingFieldDistanceClass == 3) paint = SBColor.RED; + drawCurveToMouse(g, aimingPlayerPos, paint); + + paint = SBColor.YELLOW_80; + if(aimingFieldDistanceClass == 1) paint = SBColor.ORANGE_BRIGHT_80; + if(aimingFieldDistanceClass == 2) paint = SBColor.ORANGE_DARK_80; + if(aimingFieldDistanceClass == 3) paint = SBColor.RED_80; + if(getGameCanvas().getPitchMouseLogic() != null) + if(getGameCanvas().getPitchMouseLogic().getFieldAimingAt() != null) + highlightField(g, getGameCanvas().getPitchMouseLogic().getFieldAimingAt().getPos().x, getGameCanvas().getPitchMouseLogic().getFieldAimingAt().getPos().y, paint); + } + } + } + } + + } + } + + public void drawChoiceAPI(Graphics2D g, Player[] playerChoices) { + if(playerChoices != null) { + Player hoveringPlayer = getGameCanvas().getPitchMouseLogic().getHoveringPlayer(); + + for(Player player: playerChoices) { + Vector2d playerPos = player.getPos(); + if(playerPos != null) { + if(hoveringPlayer != null && playerPos.equals(hoveringPlayer.getPos())) { + highlightField(g, playerPos.x, playerPos.y, SBColor.BLUE_BRIGHT_80); + drawBeacon(g, playerPos.x, playerPos.y, SBColor.BLUE_BRIGHT); + } else { + highlightField(g, playerPos.x, playerPos.y, SBColor.YELLOW_80); + } + } + } + + } + } + + public void drawFieldAPI(Graphics2D g, Vector2d[] fieldChoices) { + if(fieldChoices != null) { + for(Vector2d field: fieldChoices) { + double[] hoveringFieldCoords = getGameCanvas().getPitchMouseLogic().getMXY(); + Vector2d hoveringField = new Vector2d(hoveringFieldCoords[0], hoveringFieldCoords[1]); + + if(field != null) { + if(field.equals(hoveringField)) { + highlightField(g, field.x, field.y, SBColor.BLUE_BRIGHT_80); + drawBeacon(g, field.x, field.y, SBColor.BLUE_BRIGHT); + } else { + highlightField(g, field.x, field.y, SBColor.YELLOW_80); + } + } + + } + } + } + + // HELPERS + + private void drawCurveToMouse(Graphics2D g, Vector2d aimingPlayerPos, Color color) { + aimingPlayerPos = (Vector2d) aimingPlayerPos.clone(); // clone incoming vector not to accidentally overwrite positions + + g.setStroke(new BasicStroke((int) (getPW() / 12), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g.setPaint(color); + + double mXCoord = getGameCanvas().getStoredMousePosition()[0], + mYCoord = getGameCanvas().getStoredMousePosition()[1]; + aimingPlayerPos.scale(getPW()); + aimingPlayerPos.add(new Vector2d(getPW() / 2, getPW() / 2)); + aimingPlayerPos = posToDisp(aimingPlayerPos); + double x1 = aimingPlayerPos.x, + y1 = aimingPlayerPos.y; + + if(mYCoord < getPitchHeight()) { + QuadCurve2D c = new QuadCurve2D.Double(); + double d = Math.sqrt((mXCoord - x1) * (mXCoord - x1) + (mYCoord - y1) * (mYCoord - y1)); + c.setCurve(x1, y1, (x1 + mXCoord) / 2, y1 > mYCoord ? mYCoord - d / 2 : y1 - d / 2, mXCoord, mYCoord); + + g.draw(c); + } + } + + private void highlightFieldEdges(Graphics2D g, double x, double y, Color color) { + highlightFieldEdges(g, (int) x, (int) y, color); + } + + private void highlightFieldEdges(Graphics2D g, int x, int y, Color color) { + g.setPaint(color); + drawRectangleOnPitch(g, x * getPW(), y * getPW(), getPW(), getPW()); + } + + private void highlightField(Graphics2D g, double x, double y, Color color) { + highlightField(g, (int) x, (int) y, color); + } + + private void highlightField(Graphics2D g, int x, int y, Color color) { + g.setPaint(color); + fillRectangleOnPitch(g, x * getPW(), y * getPW(), getPW(), getPW()); + } + + private void drawBeacon(Graphics2D g2D, double x, double y, Color color) { + drawBeacon(g2D, x, y, color, STANDARD_BEACON_ALPHA); + } + + private void drawBeacon(Graphics2D g2D, double x, double y, Color color, int a) { + g2D.setPaint( + new Color(color.getRed(), + color.getGreen(), + color.getBlue(), + (int) (a + 20 * Math.cos((double) getGameCanvas().timer * 3 /180*Math.PI))) + ); + Vector2d pXYleft = posToDisp(new Vector2d(x * getPW() + getPW()/4, y * getPW() + getPW()/2)), + pXYright = posToDisp(new Vector2d(x * getPW() + 3*getPW()/4, y * getPW() + getPW()/2)); + GeneralPath beacon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 4); + beacon.moveTo((pXYleft.x + pXYright.x) / 2, pXYleft.y - 1000); + beacon.lineTo(pXYright.x, pXYright.y); + beacon.lineTo(pXYleft.x, pXYleft.y); + beacon.closePath(); + g2D.fill(beacon); + } + + private void drawImageWithOp(Graphics2D g, BufferedImage sprite, RescaleOp op, double x, double y, double scale, double rotation, boolean transformPerspective) { + int hash = sprite.hashCode(); + AffineTransform transform = new AffineTransform(); + if(op != null) sprite = filterImage(sprite, hash, op); + + Vector2d v = new Vector2d(x*scale, y*scale); + if(transformPerspective) v = posToDisp(v); + double scaleDrag = 30, scaleDragFactor = 1.1; + double distanceScale = ((v.y/getPH() + scaleDrag*scaleDragFactor) / (Pitch.PITCH_WIDTH + scaleDrag)) * 0.99; + + v.y -= 11*ResourceManager.IMAGE_HEIGHT/20 * scale; + v.x += (v.x - getPitchWidth()/2) * 0.01; + + transform.translate(v.x, v.y); + if(scale != 1) transform.scale(scale, scale); + if(rotation != 0) transform.rotate(rotation); + g.transform(transform); + + sprite = scaleImage(sprite, hash, distanceScale); + g.drawImage(sprite, null, 0, 0); + + try { g.transform(transform.createInverse()); } + catch (NoninvertibleTransformException e) { + getClient().log(Level.WARNING, "Tried to invert noninvertible transform."); + e.printStackTrace(); + } + } + + private BufferedImage filterImage(BufferedImage original, int hash, RescaleOp op) { + HashMap imageMap = imageFilterBuffer.get(hash); + float[] scaleFactors = op.getScaleFactors(null); + int opHash = (int) (10*scaleFactors[0]+100*scaleFactors[1]+1000*scaleFactors[2]+10000*scaleFactors[3]); + if(imageMap != null) { + if(imageMap.size() > MAX_IMAGE_BUFFER_SIZE) emptyImageBuffer(); // empty image buffer of this image if it is too full. + else if(imageMap.containsKey(opHash)) return imageMap.get(opHash); // return the buffered resized image if it was resized before. + } + + BufferedImage filtered = gameCanvas.getGraphicsConfiguration().createCompatibleImage(original.getWidth(), original.getHeight(), Transparency.BITMASK); + + Graphics2D filteredG = filtered.createGraphics(); + filteredG.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + filteredG.drawImage(original, op, 0, 0); + filteredG.dispose(); + + if(imageMap != null) imageMap.put(opHash, filtered); + else { + imageMap = new HashMap(); + imageMap.put(opHash, filtered); + imageFilterBuffer.put(hash, imageMap); + } + + return filtered; + } + + private BufferedImage scaleImage(BufferedImage original, int hash, double scale) { + HashMap imageMap = imageBuffer.get(hash); + if(imageMap != null) { + if(imageMap.size() > MAX_IMAGE_BUFFER_SIZE) emptyImageBuffer(); // empty image buffer of this image if it is too full. + else if(imageMap.containsKey(scale)) return imageMap.get(scale); // return the buffered resized image if it was resized before. + } + + int scaledW = (int) (original.getWidth()*scale), scaledH = (int) (original.getHeight()*scale); +// BufferedImage scaled = (new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), AffineTransformOp.TYPE_BILINEAR)).filter(original, gameCanvas.getGraphicsConfiguration().createCompatibleImage(scaledW, scaledH, Transparency.BITMASK)); + + BufferedImage scaled = gameCanvas.getGraphicsConfiguration().createCompatibleImage(scaledW, scaledH, Transparency.BITMASK); + + Graphics2D scaledG = scaled.createGraphics(); + scaledG.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + scaledG.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + scaledG.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + scaledG.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + scaledG.drawImage(original, 0, 0, scaledW, scaledH, 0, 0, original.getWidth(), original.getHeight(), null); + scaledG.dispose(); + + if(imageMap != null) imageMap.put(scale, scaled); + else { + imageMap = new HashMap(); + imageMap.put(scale, scaled); + imageBuffer.put(hash, imageMap); + } + + return scaled; + } + + private void drawLineOnPitch(Graphics2D g, double x1, double y1, double x2, double y2) { + Vector2d v1 = posToDisp(new Vector2d(x1, y1)), v2 = posToDisp(new Vector2d(x2, y2)); + g.drawLine((int) v1.x, (int) v1.y, (int) v2.x, (int) v2.y); + } + + private void drawRectangleOnPitch(Graphics2D g, double x, double y, double w, double h) { + // v0, v1, v2, v3 clockwise with v0 is upper left. + Vector2d v0 = posToDisp(new Vector2d(x, y)), + v1 = posToDisp(new Vector2d(x + w, y)), + v2 = posToDisp(new Vector2d(x + w, y + h)), + v3 = posToDisp(new Vector2d(x, y + h)); + GeneralPath poly = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 4); + poly.moveTo(v0.x, v0.y); + poly.lineTo(v1.x, v1.y); + poly.lineTo(v2.x, v2.y); + poly.lineTo(v3.x, v3.y); + poly.closePath(); + g.setStroke(new BasicStroke((int) (getPW() / 18), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + g.draw(poly); + } + + private void fillRectangleOnPitch(Graphics2D g, double x, double y, double w, double h) { + // v0, v1, v2, v3 clockwise with v0 is upper left. + Vector2d v0 = posToDisp(new Vector2d(x, y)), + v1 = posToDisp(new Vector2d(x + w, y)), + v2 = posToDisp(new Vector2d(x + w, y + h)), + v3 = posToDisp(new Vector2d(x, y + h)); + GeneralPath poly = new GeneralPath(GeneralPath.WIND_EVEN_ODD, 4); + poly.moveTo(v0.x, v0.y); + poly.lineTo(v1.x, v1.y); + poly.lineTo(v2.x, v2.y); + poly.lineTo(v3.x, v3.y); + poly.closePath(); + g.fill(poly); + } + + /** + * Converts a position on the pitch to a position on the display. + * @param pos The position on the pitch. + * @return The position on the display. + */ + @SuppressWarnings("UnnecessaryLocalVariable") + public Vector2d posToDisp(Vector2d pos) { + // ATTEMPT 1 +// double dispX = vX - vZ * (vX - pos.x) / (vZ - pos.y), +// dispY = getPitchHeight() - (vY - vZ * vY / (vZ - pos.y)); + // ATTEMPT 2 +// double temp = (mZ - vZ) / (vZ - pos.y), +// dispX = vX + temp * (vX - pos.x), +// dispY = vY + temp * (vY - pY); + // ATTEMPT 3 +// HashMap row = null; +// +// if(storedCalculations.containsKey((int) pos.x)) { +// row = storedCalculations.get((int) pos.x); +// if(row.containsKey((int) pos.y)) { +// Vector2d stored = row.get((int) pos.y); +// return stored; +// } +// } +// + double t = (dY - vY) / (vY - pos.y), + dX = vX + t * (vX - pos.x), + dZ = getPitchHeight() - (vZ + t * (vZ - pZ)); + Vector2d calc = new Vector2d(dX, dZ); +// +// if(row != null) { +// row.put((int) pos.y, calc); +// } else { +// HashMap newRow = new HashMap(); +// newRow.put((int) pos.y, calc); +// storedCalculations.put((int) pos.x, newRow); +// } +// System.out.println(storedCalculations.size() + " – " + pos.x + ", " + pos.y + ": " + calc.x + ", " + calc.y); + + return calc; + } + + /** + * Converts a position on the display to a position on the pitch. + * @param disp The position on the display. + * @return The position on the pitch. + */ + public Vector2d dispToPos(Vector2d disp) { + // ATTEMPT 1 +// double posX = vX - vY * (vX - disp.x) / (vY - (getPitchHeight() - disp.y)), +// posY = vZ - vY * vZ / (vY - (getPitchHeight() - disp.y)); + // ATTEMPT 2 +// double posX = vX - (disp.x - vX) / (vY - pY) * (disp.y - vY), +// posY = vZ - (mZ - vZ) / (disp.y - vY) * (vY - pY); + // ATTEMPT 3 + double dZ = getPitchHeight() - disp.y, + t = (pZ - vZ) / (vZ - dZ), + pX = vX + t * (vX - disp.x), + pY = vY + t * (vY - dY); + return new Vector2d(pX, pY); + } + + public boolean isOwnPlayer(Player player) { + String hoveringPlayerCoach = player.getTeam().getCoach().getName(); + String username = getGameCanvas().getGameFrame().getClient().getUsername(); + return hoveringPlayerCoach.equals(username); + } + + public void emptyImageBuffer() { + imageBuffer = new HashMap>(); + } + + // GETTERS & SETTERS + + public GameCanvas getGameCanvas() { + return gameCanvas; + } + + public Client getClient() { + return getGameCanvas().getClient(); + } + + public PitchMouseLogic getPitchMouseLogic() { + return getGameCanvas().getPitchMouseLogic(); + } + + public Pitch getPitch() { + return getGameCanvas().getPitch(); + } + + public double getPW() { + return getGameCanvas().getPW(); + } + + public double getPH() { + return getGameCanvas().getPH(); + } + + public double getT() { + return getGameCanvas().getT(); + } + + public double getW() { + return getGameCanvas().getW(); + } + + public double getH() { + return getGameCanvas().getH(); + } + + public double getPitchWidth() { + return getGameCanvas().getPitchWidth(); + } + + public double getPitchHeight() { + return getGameCanvas().getPitchHeight(); + } + + public boolean isLeft() { + return getGameCanvas().isLeft(); + } +} diff --git a/src/client/logic/PitchMouseLogic.java b/src/client/logic/PitchMouseLogic.java new file mode 100644 index 0000000..465cb05 --- /dev/null +++ b/src/client/logic/PitchMouseLogic.java @@ -0,0 +1,952 @@ +package client.logic; + +import client.display.GameCanvas; +import gameLogic.*; +import gameLogic.rules.RuleThrow; +import util.ResourceManager; + +import javax.vecmath.Vector2d; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.ArrayList; +import java.util.Vector; +import java.util.logging.Level; + +/** + * A wrapper for all mouse related classes in SafariBowl. + */ +public class PitchMouseLogic { + + MouseMotionLogic mouseMotionLogic; + MouseActionLogic mouseActionLogic; + GameCanvas gameCanvas; + Player hoveringPlayer = null, hoveringPlayerOnBench = null, movingPlayer = null, aimingPlayer = null; + PitchField fieldBlitzing = null, fieldAimingAt = null; + DrawingPath drawingPath = null; + int mX = 0, mY = 0, mXOld = 0, mYOld = 0, mXCoord = 0, mYCoord = 0; + int teamIndexHovering = -1, playerChoiceHovering = -1, playerRemoveHovering = -1, dieChoiceHovering = -1, specialRuleChoiceHovering = -1; + boolean isAimingKickoff; + volatile boolean calculating = false; + + public PitchMouseLogic(GameCanvas gameCanvas) { + this.gameCanvas = gameCanvas; + } + + public MouseMotionLogic createMouseMotionLogic() { + this.mouseMotionLogic = new MouseMotionLogic(); + return mouseMotionLogic; + } + + public MouseActionLogic createMouseActionLogic() { + this.mouseActionLogic = new MouseActionLogic(); + return mouseActionLogic; + } + + public boolean isHoveringOwnPlayer() { + if(getHoveringPlayer() != null){ + String hoveringPlayerCoach = getHoveringPlayer().getTeam().getCoach().getName(); + String username = getGameCanvas().getGameFrame().getClient().getUsername(); + return hoveringPlayerCoach.equals(username); + }else{ + return false; + } + } + + public double[] getMXY() { + return new double[]{mX, mY}; + } + + public int[] getMXYCoord() { + return new int[]{mXCoord, mYCoord}; + } + + public Player getHoveringPlayer() { + return hoveringPlayer; + } + + public Player getHoveringPlayerOnBench() { + return hoveringPlayerOnBench; + } + + public Player getMovingPlayer() { + return movingPlayer; + } + + public Player getAimingPlayer() { + return aimingPlayer; + } + + public DrawingPath getDrawingPath() { + return drawingPath; + } + + public PitchField getFieldBlitzing() { + return fieldBlitzing; + } + + public PitchField getFieldAimingAt() { + return fieldAimingAt; + } + + public int getTeamIndexHovering() { + return teamIndexHovering; + } + + public int getDieChoiceHovering() { + return dieChoiceHovering; + } + + public int getSpecialRuleChoiceHovering() { + return specialRuleChoiceHovering; + } + + public boolean isAimingKickoff() { + return isAimingKickoff; + } + + Pitch getPitch() { + return getGameCanvas().getPitch(); + } + + int getGamePhase() { + return getGameCanvas().getGamePhase(); + } + + double getW() { + return getGameCanvas().getW(); + } + + double getH() { + return getGameCanvas().getH(); + } + + public double getPitchWidth() { + return getGameCanvas().getPitchWidth(); + } + + public double getPitchHeight() { + return getGameCanvas().getPitchHeight(); + } + + double getpW() { + return getGameCanvas().getPW(); + } + + double getpH() { + return getGameCanvas().getPH(); + } + + void setMXYCoord(MouseEvent e) { + mXCoord = e.getX(); + mYCoord = e.getY(); + } + + void resetMXY() { + mX = mY = mXOld = mYOld = -1; + } + + void setMXY(MouseEvent e) { + double xNew = e.getX(), yNew = e.getY(); + Vector2d mXYNew = getGameCanvas().getGameRenderer().dispToPos(new Vector2d(xNew, yNew)); + double mXNewTemp = mXYNew.x / getpW(), + mYNewTemp = mXYNew.y / getpH(); + if(mXNewTemp < 0) mXNewTemp -= 1; // fix negative coordinates rounding error + if(mYNewTemp < 0) mYNewTemp -= 1; // " + int mXNew = (int) mXNewTemp, + mYNew = (int) mYNewTemp; + mXOld = mX; + mYOld = mY; + if(mXNew >= 0 && mXNew < Pitch.PITCH_LENGTH && mYNew >= 0 && mYNew < Pitch.PITCH_WIDTH) { + mX = mXNew; + mY = mYNew; + } else { + resetMXY(); + } + } + + void setMXYEverywhere(MouseEvent e) { + double xNew = e.getX(), yNew = e.getY(); + Vector2d mXYNew = getGameCanvas().getGameRenderer().dispToPos(new Vector2d(xNew, yNew)); + double mXNewTemp = mXYNew.x / getpW(), + mYNewTemp = mXYNew.y / getpH(); + if(mXNewTemp < 0) mXNewTemp -= 1; // fix negative coordinates rounding error + if(mYNewTemp < 0) mYNewTemp -= 1; // " + int mXNew = (int) mXNewTemp, + mYNew = (int) mYNewTemp; + mXOld = mX; + mYOld = mY; + mX = mXNew; + mY = mYNew; + } + + void setTeamIndexHovering(int teamIndexHovering) { + this.teamIndexHovering = teamIndexHovering; + } + + void setPlayerChoiceHovering(int playerChoiceHovering) { + this.playerChoiceHovering = playerChoiceHovering; + } + + void setPlayerRemoveHovering(int playerRemoveHovering) { + this.playerRemoveHovering = playerRemoveHovering; + } + + void setDieChoiceHovering(int dieChoiceHovering) { + this.dieChoiceHovering = dieChoiceHovering; + } + + void setSpecialRuleChoiceHovering(int specialRuleChoiceHovering) { + this.specialRuleChoiceHovering = specialRuleChoiceHovering; + } + + void setMovingPlayer(Player movingPlayer) { + if(movingPlayer == null) + log(Level.WARNING, "Tried to set moving player to null via setMovingPlayer(). Use resetMovingPlayer() instead."); + else { + this.movingPlayer = movingPlayer; + this.drawingPath = new DrawingPath(getPitch(), movingPlayer.getPosition()); + } + } + + void setHoveringPlayer(Player hoveringPlayer) { + if(hoveringPlayer == null) + log(Level.WARNING, "Tried to set hovering player to null via setHoveringPlayer(). Use resetHoveringPlayer() instead."); + else + this.hoveringPlayer = hoveringPlayer; + } + + public void setHoveringPlayerOnBench(Player hoveringPlayerOnBench) { + if(hoveringPlayerOnBench == null) + log(Level.WARNING, "Tried to set hovering player on bench to null via setHoveringPlayerOnBench(). Use resetHoveringPlayerOnBench() instead."); + else + this.hoveringPlayerOnBench = hoveringPlayerOnBench; + } + + void setHoveringPlayerOffField(int index, Vector playersOnBench, boolean onBench) { + ArrayList injuredPlayers; + int i = 0; + do { + injuredPlayers = new ArrayList(); + for(; i <= index; i++) { + if(i < playersOnBench.size()) { + if(playersOnBench.get(i).$GUIisKOInjuredOrDead()) + injuredPlayers.add(i); + } + } + index += injuredPlayers.size(); + } while(injuredPlayers.size() > 0); + if(index < playersOnBench.size()) { + if(onBench) setHoveringPlayerOnBench(playersOnBench.get(index)); + else setHoveringPlayer(playersOnBench.get(index)); + } + } + + void setAimingPlayer(Player aimingPlayer) { + if(aimingPlayer == null) log(Level.WARNING, "Tried to set aiming player to null via setAimingPlayer(). Use resetAimingPlayer() instead."); + else { + this.aimingPlayer = aimingPlayer; + } + } + + void setIsAimingKickoff(boolean isAimingKickoff) { + this.isAimingKickoff = isAimingKickoff; + } + + void resetMovingPlayer() { + this.movingPlayer = null; + this.drawingPath = null; + getGameCanvas().getGameFrame().resetDrawingPath(); + resetFieldBlitzing(); + } + + void resetHoveringPlayer() { + this.hoveringPlayer = null; + } + + void resetHoveringPlayerOnBench() { + this.hoveringPlayerOnBench = null; + } + + void resetAimingPlayer() { + this.aimingPlayer = null; + this.fieldAimingAt = null; + } + + boolean isMovingPlayer() { + return movingPlayer != null; + } + + boolean isHoveringPlayer() { + return hoveringPlayer != null; + } + + boolean isHoveringPlayerOnBench() { + return hoveringPlayerOnBench != null; + } + + boolean isAiming() { + return aimingPlayer != null; + } + + void setFieldBlitzing(PitchField fieldBlitzing) { + if(fieldBlitzing == null) log(Level.WARNING, "Tried to set field blitzing to null via setFieldBlitzing(). Use resetFieldBlitzing() instead."); + else { + this.fieldBlitzing = fieldBlitzing; + } + } + + void setFieldAimingAt(PitchField fieldAimingAt) { + if(fieldAimingAt == null) log(Level.WARNING, "Tried to set field aiming at to null via setFieldAimingAt(). Use resetAimingPlayer() instead."); + else { + this.fieldAimingAt = fieldAimingAt; + } + } + + void resetFieldBlitzing() { + this.fieldBlitzing = null; + } + + void resetFieldAimingAt() { + this.fieldAimingAt = null; + } + + void addPathElement() { + if(getFieldBlitzing() == null && getMovingPlayer() != null) { // only move if moving player is not blitzing + boolean move = false; + if(getMovingPlayer().invokeGetPlayerCondition() == PlayerCondition.PRONE) { // player is prone and needs 3 BE to stand up + if (getDrawingPath().getPath().size() <= getMovingPlayer().invokeGetRemainingBe() - 3) // only add if prone moving player can move further + move = true; + } else if(getDrawingPath().getPath().size() <= getMovingPlayer().invokeGetRemainingBe()) // only add if moving player can move further + move = true; + if(move) { + resetFieldBlitzing(); + getDrawingPath().addPathElement(getPitch().getFields()[mX][mY]); + getGameCanvas().getGameFrame().setDrawingPath(getDrawingPath(), getMovingPlayer()); // set the drawing path on the game panel to send + } + } + } + + void removeLastPathElement() { + getDrawingPath().removeLastPathElement(); + } + + void log(Level level, String message) { + getGameCanvas().getGameFrame().getClient().log(level, message); + } + + public GameCanvas getGameCanvas() { + return gameCanvas; + } + + private class MouseMotionLogic implements MouseMotionListener { + + @Override + public void mouseDragged(MouseEvent event) { + if(!calculating && getPitch() != null) { // if no other calculating thread is running, calculate + final MouseEvent e = event; + calculating = true; + new Thread(new Runnable() { + @Override + public void run() { + calculate(e); + calculating = false; + } + }).start(); + } + } + + @Override + public void mouseMoved(MouseEvent event) { + if(!calculating && getPitch() != null) { // if no other calculating thread is running, calculate + final MouseEvent e = event; + calculating = true; + new Thread(new Runnable() { + @Override + public void run() { + calculate(e); + calculating = false; + } + }).start(); + } + } + + void calculate(MouseEvent e) { + try { + + setMXYCoord(e); + setSpecialRuleChoiceHovering(-1); + + if(getGameCanvas().isChoosingSpecialRule()) calculateChooseSpecialRule(e); + else { + // API + if(getGameCanvas().requestedDiceAPI()) calculateDiceAPI(e); + else if(getGameCanvas().requestedAimAPI()) calculateAimAPI(e); + else if(getGameCanvas().requestedChoiceAPI()) calculateChoiceAPI(e); + + else if(getGamePhase() == 0) calculateChooseTeam(); + else if(getGamePhase() == 2) calculateKickAndGiveBall(e); + + if(getGamePhase() >= 3 && e.getY() < getPitchHeight()) calculateOnPitch(e); + + if(getGamePhase() >= 1) calculateSetPlayers(e); + + if(getGameCanvas().requestedFieldAPI()) calculateFieldAPI(e); + } + + } catch(IndexOutOfBoundsException ex) { + ex.printStackTrace(); + } catch(NullPointerException ex) { + ex.printStackTrace(); + } + } + + void calculateChooseSpecialRule(MouseEvent e) { + int[] pos = getGameCanvas().getSpecialRuleMenuPosition(), + size = getGameCanvas().getSpecialRuleMenuSize(); + int hN = getGameCanvas().getSpecialRuleMenuNameHeight(), + hR = getGameCanvas().getSpecialRuleMenuItemHeight(); + + if(getMXYCoord()[0] > pos[0] && getMXYCoord()[0] < pos[0] + size[0] + && getMXYCoord()[1] > pos[1] + hN/2 && getMXYCoord()[1] < pos[1] + size[1]) { // is hovering item in special rules list + + setSpecialRuleChoiceHovering((getMXYCoord()[1] - pos[1] - hN) / hR); + + } + } + + void calculateDiceAPI(MouseEvent e) { + double actionFieldSize = getPitchHeight()/4, + actionFieldSizeHalved = actionFieldSize/2; + + if(mXCoord < actionFieldSize && mXCoord > 0 && mYCoord > getH() - actionFieldSize && mYCoord < getH()) { + + int col = mXCoord > actionFieldSizeHalved ? 1 : 0, + row = mYCoord > getH() - actionFieldSizeHalved ? 1 : 0; + setDieChoiceHovering(row*2 + col); + + } else setDieChoiceHovering(-1); + } + + void calculateAimAPI(MouseEvent e) { + setMXY(e); + resetFieldAimingAt(); + + Team aimingTeam = getGameCanvas().isLeft() ? getGameCanvas().getClient().getMatch().getTeam(0) : getGameCanvas().getClient().getMatch().getTeam(1); + + if(aimingTeam != null && getGameCanvas().getAimAPIIndex() >= 0) { + Player aimingPlayer = aimingTeam.getPlayers().get(getGameCanvas().getAimAPIIndex()); + + if(aimingPlayer != null) + if(mX >= 0 && mX < Pitch.PITCH_LENGTH && mY >= 0 && mY < Pitch.PITCH_WIDTH) + setFieldAimingAt(getPitch().getFields()[mX][mY]); + } + } + + void calculateChoiceAPI(MouseEvent e) { + setMXY(e); + + if(mX >= 0 && mX < Pitch.PITCH_LENGTH && mY >= 0 && mY < Pitch.PITCH_WIDTH) { + if (getPitch().getFields()[mX][mY].getPlayer() != null) { // there is a player on the hovered field + setHoveringPlayer(getPitch().getFields()[mX][mY].getPlayer()); + } else { + resetHoveringPlayer(); + } + } + } + + void calculateFieldAPI(MouseEvent e) { + setMXYEverywhere(e); + } + + void calculateChooseTeam() { + resetMXY(); + if(!getGameCanvas().choseTeamType()) { + int numberOfTeams = getGameCanvas().getClient().getMatch().getNumberOfAvailableTeams(); + double teamWidth = getPitchWidth() / numberOfTeams; + if (mXCoord > 0 && mXCoord < getPitchWidth() && mYCoord > 0 && mYCoord < teamWidth) { // mouse is over team choice + int mXTeam = (int) (mXCoord / teamWidth); + setTeamIndexHovering(mXTeam); + } else setTeamIndexHovering(-1); + } else { + calculateChoosePlayers(); + calculateRemovePlayers(); + } + } + + void calculateChoosePlayers() { + if(!getGameCanvas().hasSentChooseTeam()) { + int numberOfAvailablePlayers = getGameCanvas().getClient().getMatch().getAvailableTeam(teamIndexHovering).getAvailablePlayers().size(); + double playerWidth = getPitchWidth() / numberOfAvailablePlayers, + playerHeight = playerWidth / ResourceManager.IMAGE_RATIO; + if (playerHeight > getPitchHeight() / 2) { + playerHeight = getPitchHeight() / 2; + playerWidth = playerHeight * ResourceManager.IMAGE_RATIO; + } + if (mXCoord > 0 && mXCoord < getPitchWidth() && mYCoord > 0 && mYCoord < playerHeight) { // mouse is over player choice + int mXPlayer = (int) (mXCoord / playerWidth); + setPlayerChoiceHovering(mXPlayer); + } else setPlayerChoiceHovering(-1); + } + } + + void calculateRemovePlayers() { + int numberOfChosenPlayers = getGameCanvas().getPlayersChosen().size(); + double playerWidth = getPitchWidth() / Team.MAX_TEAM_SIZE, + playerHeight = playerWidth / ResourceManager.IMAGE_RATIO, + crossSize = playerWidth / ResourceManager.IMAGE_WIDTH * ResourceManager.PROP_CROSS.getWidth(); + boolean hoveringCross = false; + for (int i = 0; i < numberOfChosenPlayers; i++) { + if(mYCoord > getPitchHeight() - playerHeight - crossSize/3 && mYCoord < getPitchHeight() - playerHeight + 2*crossSize/3 + && mXCoord > (i+1) * playerWidth - crossSize && mXCoord < (i+1) * playerWidth) { + setPlayerRemoveHovering(i); + hoveringCross = true; + } + } + if(!hoveringCross) setPlayerRemoveHovering(-1); + } + + void calculateSetPlayers(MouseEvent e) { + resetHoveringPlayerOnBench(); + + if(mYCoord > getPitchHeight()) { // mouse is in control panel + resetHoveringPlayer(); + resetFieldAimingAt(); + + if(getMovingPlayer() == null) { + + boolean left = getGameCanvas().getClientIndex() == 0; + + if (left) { // left bench is players bench + + calculateHoveringPlayers(getGameCanvas().getPlayersOnBench(), true, false); + calculateHoveringPlayers(getGameCanvas().getPlayersOnOpponentBench(), false, true); + + } else { // right bench is players bench + + calculateHoveringPlayers(getGameCanvas().getPlayersOnBench(), false, false); + calculateHoveringPlayers(getGameCanvas().getPlayersOnOpponentBench(), true, true); + + } + + } else { // is moving player off the field + setFieldAimingAt(Pitch.THE_VOID); + } + + } else if(getGamePhase() == 1) { // mouse is on pitch and players can be set + resetHoveringPlayer(); + + setMXY(e); + boolean left = getGameCanvas().getClientIndex() == 0; + + if(mX >= 0 && mX < Pitch.PITCH_LENGTH && mY >= 0 && mY < Pitch.PITCH_WIDTH) { + + if(getMovingPlayer() == null) { // is not moving player + if(getPitch().getFields()[mX][mY].getPlayer() != null) { + setHoveringPlayer(getPitch().getFields()[mX][mY].getPlayer()); + } + } else { // is moving player + if(left && mX < Pitch.PITCH_LENGTH / 2 || !left && mX >= Pitch.PITCH_LENGTH / 2) + setFieldAimingAt(getPitch().getFields()[mX][mY]); + else resetFieldAimingAt(); + } + + } else { + setFieldAimingAt(Pitch.THE_VOID); + } + + } + } + + void calculateHoveringPlayers(Vector playersOnBench, boolean left, boolean onBench) { + int n = playersOnBench.size(); + double playersInRow = Team.MAX_TEAM_SIZE / 2, + seatsInRow = playersInRow + 2, + seatWidth = getPitchWidth() / 2 / seatsInRow, + seatHeight = seatWidth / ResourceManager.IMAGE_RATIO; + + if(playersOnBench.size() > 0) { + if(left) { + + if(mYCoord < getPitchHeight() + seatHeight) { // potentially hovering player on first row of bench + if(mXCoord > seatWidth && mXCoord < ((n > playersInRow ? playersInRow : n) + 1) * seatWidth) { + + setHoveringPlayerOffField((int) ((mXCoord - seatWidth) / seatWidth), playersOnBench, onBench); + + } + } else if(mYCoord < getPitchHeight() + 2 * seatHeight && n > playersInRow) { // potentially hovering player on second row of bench + if(mXCoord > seatWidth && mXCoord < (n - playersInRow + 1) * seatWidth) { + + setHoveringPlayerOffField((int) ((mXCoord - seatWidth) / seatWidth + playersInRow), playersOnBench, onBench); + + } + } + + } else { + + if(mYCoord < getPitchHeight() + seatHeight) { // potentially hovering player on first row of bench + if(mXCoord > seatWidth + getPitchWidth() / 2 && mXCoord < getPitchWidth() - ((n > playersInRow ? 0 : playersInRow - n) + 1) * seatWidth) { + + setHoveringPlayerOffField((int) ((mXCoord - seatWidth - getPitchWidth() / 2) / seatWidth), playersOnBench, onBench); + + } + } else if(mYCoord < getPitchHeight() + 2 * seatHeight && n > playersInRow) { // potentially hovering player on second row of bench + if(mXCoord > seatWidth + getPitchWidth() / 2 && mXCoord < getPitchWidth() - (2 * playersInRow - n + 1) * seatWidth) { + + setHoveringPlayerOffField((int) ((mXCoord - seatWidth - getPitchWidth() / 2) / seatWidth + playersInRow), playersOnBench, onBench); + + } + } + + } + } + } + + void calculateKickAndGiveBall(MouseEvent e) { + setMXY(e); + if(mX >= 0 && mX < Pitch.PITCH_LENGTH && mY >= 0 && mY < Pitch.PITCH_WIDTH) { + + if(getGameCanvas().canKickOff()) { // only kick of if allowed + + if(getGameCanvas().getClientIndex() == 0) { // team is on left side + + setAimingPlayer(new Kicker(Kicker.LEFT, getPitch())); + setIsAimingKickoff(true); + + if(mX >= 13 && mYCoord < getPitchHeight()) setFieldAimingAt(getPitch().getFields()[mX][mY]); + + } else if(getGameCanvas().getClientIndex() == 1) { // team is on right side + + setAimingPlayer(new Kicker(Kicker.RIGHT, getPitch())); + setIsAimingKickoff(true); + if(mX < 13 && mYCoord < getPitchHeight()) setFieldAimingAt(getPitch().getFields()[mX][mY]); + + } else getGameCanvas().getClient().log(Level.WARNING, "Illegal client index in MouseLogic::calculateKickAndGiveBall()"); + + } else setIsAimingKickoff(false); + if(getGameCanvas().canGiveBall()) { // only hover players potentially receiving ball if allowed + + Player playerPotentiallyReceivingBall = getPitch().getFields()[mX][mY].getPlayer(); + if (playerPotentiallyReceivingBall != null) { // there is a player on the hovered field + setHoveringPlayer(playerPotentiallyReceivingBall); + if (!isHoveringOwnPlayer()) + resetHoveringPlayer(); // don't set if hovering player is not own player + } + + } + + } + } + + void calculateOnPitch(MouseEvent e) { + if (!isMovingPlayer()) { // is not moving player + setMXY(e); + } else { // is moving player + // some magic numbers + double x0 = mX * getpW() + getpW() / 2, y0 = mY * getpH() + getpH() / 2, + a = 7*getpW()/8; + Vector2d mouse = getGameCanvas().getGameRenderer().dispToPos(new Vector2d(e.getX(), e.getY())); + double dx = mouse.x - x0, dy = mouse.y - y0; + if ((dx < -a || dx > a) || (dy < -a || dy > a)) { // moved outside of action zone (ask milan what the heck that means) + setMXY(e); + } + } + + if(mX >= 0 && mX < Pitch.PITCH_LENGTH && mY >= 0 && mY < Pitch.PITCH_WIDTH) { + + if (getPitch() != null && (mX != mXOld || mY != mYOld)) { // mouse is on different field and pitch exists + if (isAiming()) { // is aiming... + Vector2d dist = (Vector2d) getAimingPlayer().getPos().clone(); + if (!dist.equals(getPitch().getFields()[mX][mY].getPos())) { // ... but not on itself + dist.sub(new Vector2d(mX, mY)); + if (dist.length() <= RuleThrow.LONG_BOMB) // distance is shorter than max + setFieldAimingAt(getPitch().getFields()[mX][mY]); + } else { // ... but on itself + resetAimingPlayer(); + } + } else { // is not aiming + if (getPitch().getFields()[mX][mY].getPlayer() != null) { // there is a player on the hovered field + setHoveringPlayer(getPitch().getFields()[mX][mY].getPlayer()); + } else { + resetHoveringPlayer(); + } + + if (isMovingPlayer()) { // player was moved one field + if (!isHoveringPlayer() || getHoveringPlayer().equals(getMovingPlayer())) { // next field is empty or moved player stands on this field + if (getDrawingPath().getPath().get(getDrawingPath().getPath().size() - 1).getPos().equals(new Vector2d(mX, mY))) { // hovering last drawn field + resetFieldBlitzing(); + } + if (getDrawingPath().getPath().size() > 1) { // path is longer than one field + if (getDrawingPath().getPath().get(getDrawingPath().getPath().size() - 2).getPos().equals(new Vector2d(mX, mY))) { // moved one field back + removeLastPathElement(); + } else { + addPathElement(); + } + } else if (getDrawingPath().getPath().size() == 1) { + addPathElement(); + } + } else { // there is already a player on this field + PitchField potentialBlitzField = getPitch().getFields()[mX][mY]; + if (!isHoveringOwnPlayer() && potentialBlitzField != null) // cannot blitz own player + if (getMovingPlayer().invokeGetRemainingBe() >= getDrawingPath().getPath().size() // if player has another move.. + && getMovingPlayer().getTeam().getBlitz() // .. and team can still blitz, or .. + || getDrawingPath().getPath().size() <= 1 // .. player is blocking .. + && getMovingPlayer().invokeGetRemainingBe() == getMovingPlayer().invokeGetBe()) // .. and has not moved yet + if (getPitch().isAdjacent(potentialBlitzField, getDrawingPath().getPath().get(getDrawingPath().getPath().size() - 1))) + setFieldBlitzing(potentialBlitzField); + } + } + + } + } + + } else { + resetAimingPlayer(); + } + + if(mYCoord >= getPitchHeight()) { + resetHoveringPlayer(); + resetMovingPlayer(); + resetAimingPlayer(); + } + } + } + + private class MouseActionLogic implements MouseListener { + + @Override + public void mouseClicked(MouseEvent e) { + double actionFieldSize = getPitchHeight()/4; + if(mXCoord > getW() - actionFieldSize && mXCoord < getW() + && mYCoord > getH() - actionFieldSize && mYCoord < getH()) { // clicked on right action field + + if(getGamePhase() == 0 && !getGameCanvas().hasSentChooseTeam()) { // finished choosing team + getGameCanvas().chooseTeam(); + } else if(getGamePhase() == 1) { // finished setting up players + getGameCanvas().getGameFrame().finishedSettingPlayers(); + } else if(getGamePhase() >= 3) { // end turn + getGameCanvas().getGameFrame().endTurn(); + } + + } else if(mXCoord < actionFieldSize && mXCoord > 0 + && mYCoord > getH() - actionFieldSize && mYCoord < getH()) { // clicked on left action field + + if(getGamePhase() == 0 && !getGameCanvas().hasSentChooseTeam()) { // clicked back button + setTeamIndexHovering(-1); + getGameCanvas().setTeamChosen(null); + getGameCanvas().resetPlayersChosen(); + getGameCanvas().setChoseTeamType(false); + } + + } else { // potentially clicked surrender + + double seatsInRow = Team.MAX_TEAM_SIZE/2, + benchWidth = (seatsInRow + 1) / (seatsInRow + 2) * getW()/2, + surrenderHeight = (getW() - 2*benchWidth) / 2; + + if(mXCoord > benchWidth && mXCoord < getW() - benchWidth && mYCoord > getH() - surrenderHeight && mYCoord < getH()) { + getGameCanvas().getGameFrame().surrender(); + } + + } + } + + @Override + public void mousePressed(MouseEvent e) { + if(getGamePhase() == 0) { // choosing team and players + + if(!getGameCanvas().choseTeamType()) { // choosing team + if(teamIndexHovering >= 0) { + getGameCanvas().setTeamChosen(getGameCanvas().getClient().getMatch().getAvailableTeam(teamIndexHovering)); + getGameCanvas().setChoseTeamType(true); + } + } else { // choosing players + if(!getGameCanvas().hasSentChooseTeam()) { // only choose if hasn't chosen already + if (playerChoiceHovering >= 0) { + Player playerChosen = getGameCanvas().getTeamChosen().getAvailablePlayers().get(playerChoiceHovering).getCloneForTeam(getGameCanvas().getTeamChosen()); + if (playerChosen != null) { + int numberOfThisType = 0; + for (Player player : getGameCanvas().getPlayersChosen()) + if (player.getName().equals(playerChosen.getName())) + numberOfThisType++; + if (numberOfThisType < playerChosen.getMaxHeadcount()) + getGameCanvas().addPlayerChosen(playerChosen); + } + } else if(playerRemoveHovering >= 0) { + getGameCanvas().removePlayerChosen(playerRemoveHovering); + } + } + } + + } else if(getGamePhase() == 1) { // setting players + + if(getHoveringPlayer() != null + && isHoveringOwnPlayer() + && getGameCanvas().canSetUp() + && !getGameCanvas().hasSetUpTeam()) { // is hovering own player and can set up team + if((!getGameCanvas().getPlayersOnBench().contains(getHoveringPlayer()) || getGamePhase() == 1) + && getHoveringPlayer() != null && !getHoveringPlayer().getRedCard()) + setMovingPlayer(getHoveringPlayer()); + } + + } else if(getGamePhase() >= 3) { + + if(getGameCanvas().isYourTurn()) { + if (isHoveringPlayer() && !isMovingPlayer()) { // is hovering over player but not while moving player + // before moving player throwing ball, check if this user is coach of the player + if (isHoveringOwnPlayer()) { + if (e.getButton() == MouseEvent.BUTTON1 && !getGameCanvas().isShiftPressed()) { // left mouse button, move + if(!getGameCanvas().getPlayersOnBench().contains(getHoveringPlayer()) || getGamePhase() == 1) + if(!getGameCanvas().isChoosingSpecialRule()) + if(getHoveringPlayer().invokeGetRemainingBe() > 0) + setMovingPlayer(getHoveringPlayer()); + addPathElement(); + } else if (e.getButton() == MouseEvent.BUTTON3 || e.getButton() == MouseEvent.BUTTON2 || getGameCanvas().isShiftPressed()) { // right mouse button throw + if (getHoveringPlayer().isHoldingBall() + && !(getHoveringPlayer() != getPitch().getTeam(getGameCanvas().getClientIndex()).getMovingPlayer() + && getHoveringPlayer().invokeGetRemainingBe() == 0)) + setAimingPlayer(getHoveringPlayer()); + } + } + } + } + + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if(getGamePhase() == 1) { // setting players + + if(getFieldAimingAt() != null) { // is hovering field to set + if(getFieldAimingAt().equals(Pitch.THE_VOID)) { // is setting player back on bench + getGameCanvas().getGameFrame().setPlayer(getMovingPlayer(), getFieldAimingAt()); + } else if(getFieldAimingAt().getPlayer() == null) // there is no player on this field yet + getGameCanvas().getGameFrame().setPlayer(getMovingPlayer(), getFieldAimingAt()); + } + resetMovingPlayer(); + resetFieldAimingAt(); + + } else if(getGamePhase() >= 2) { + + // API + + if(getGameCanvas().requestedDiceAPI()) { // Dice API + if(getDieChoiceHovering() >= 0 && getDieChoiceHovering() < getGameCanvas().getDiceAPIDice().length) { + + getGameCanvas().getClient().getMatch().sendDiceAPI(getGameCanvas().getAPIMessage(), getDieChoiceHovering()); + getGameCanvas().setDiceAPI(false); + getGameCanvas().setDiceAPIDice(null); + setDieChoiceHovering(-1); + + } + } else if(getGameCanvas().requestedAimAPI() && getFieldAimingAt() != null) { // Aiming API + if(getGameCanvas().requestedAimAPI() + && getGameCanvas().getAimAPIIndex() >= 0 + && getGameCanvas().getAimAPIIndex() < Team.MAX_TEAM_SIZE + && getGameCanvas().getAimAPIDistance() > 0) { + Team aimingTeam = getGameCanvas().isLeft() ? getGameCanvas().getClient().getMatch().getTeam(0) : getGameCanvas().getClient().getMatch().getTeam(1); + + if(aimingTeam != null) { + Player aimingPlayer = aimingTeam.getPlayers().get(getGameCanvas().getAimAPIIndex()); + + if(aimingPlayer != null) { + Vector2d aimingPlayerPos = (Vector2d) aimingPlayer.getPos().clone(); + + if(aimingPlayerPos != null) { + Vector2d dist = new Vector2d(getMXY()[0], getMXY()[1]); + dist.sub(aimingPlayerPos); + + if(dist.length() <= getGameCanvas().getAimAPIDistance()) { + getGameCanvas().getClient().getMatch().sendAimAPI(getGameCanvas().getAPIMessage(), getFieldAimingAt().getPos()); + getGameCanvas().setAimAPI(false); + getGameCanvas().setAimAPIIndex(-1); + } + } + } + } + } + } else if(getGameCanvas().requestedChoiceAPI() && getHoveringPlayer() != null) { // Choice API + + Player[] playerChoices = getGameCanvas().getChoiceAPIPlayers(); + for(int i = 0; i < playerChoices.length; i++) { + if(playerChoices[i].equals(getHoveringPlayer())) { + getGameCanvas().getClient().getMatch().sendChoiceAPI(getGameCanvas().getAPIMessage(), i); + getGameCanvas().setChoiceAPI(false); + getGameCanvas().setChoiceAPIPlayers(null); + } + } + + } else if(getGameCanvas().requestedFieldAPI()) { // Field API + + for(Vector2d field: getGameCanvas().getFieldAPIFields()) { + if(field.x == getMXY()[0] && field.y == getMXY()[1]) { // send if clicked field is option + + getGameCanvas().getClient().getMatch().sendFieldAPI(getGameCanvas().getAPIMessage(), new Vector2d(getMXY()[0], getMXY()[1])); + getGameCanvas().setFieldAPI(false); + getGameCanvas().setFieldAPIFields(null); + break; + + } + } + + } + + // special rules + + else if(getGameCanvas().isChoosingSpecialRule()) { + + if(getSpecialRuleChoiceHovering() >= 0) { + getGameCanvas().getGameFrame().specialRule(getGameCanvas().getPlayerChoosingSpecialRuleFor(), getSpecialRuleChoiceHovering()); + } else { + getGameCanvas().setPlayerChoosingSpecialRuleFor(null); + } + + } + + // normal actions + + else if (isAiming()) { + if(mYCoord < getPitchHeight()) { + if (isAimingKickoff() && getFieldAimingAt() != null) { // is aiming + getGameCanvas().getGameFrame().kick(getFieldAimingAt()); + setIsAimingKickoff(false); + } else { // is aiming throw + getGameCanvas().getGameFrame().throwBall(getFieldAimingAt(), getAimingPlayer()); + } + } + } else if (!isHoveringPlayer() || getHoveringPlayer().equals(getMovingPlayer())) { // is not hovering over player or moved player stands on this field + if (isMovingPlayer()) { // was moving player + if (getDrawingPath().getPath().size() > 0) { + getGameCanvas().getGameFrame().sendPreparedGUIGameMessage(); + setHoveringPlayer(getMovingPlayer()); + } + } + } else { // is hovering over player + if (isMovingPlayer()) { // was moving player + if (getFieldBlitzing() != null) { // is blitzing + getGameCanvas().getGameFrame().blitzOrBlock(getDrawingPath(), getFieldBlitzing(), getMovingPlayer()); + } + } else if(getGameCanvas().canGiveBall()) { // wants to give ball to hovering player + getGameCanvas().getGameFrame().giveBall(getHoveringPlayer()); + } + } + + resetMovingPlayer(); + resetAimingPlayer(); + + } + + getGameCanvas().getGameFrame().getGamePanel().requestFocusInWindow(); + } + + @Override + public void mouseEntered(MouseEvent e) {} + + @Override + public void mouseExited(MouseEvent e) { + resetHoveringPlayer(); + resetMovingPlayer(); + } + } +} diff --git a/src/gameLogic/GameController.java b/src/gameLogic/GameController.java new file mode 100644 index 0000000..43f9a41 --- /dev/null +++ b/src/gameLogic/GameController.java @@ -0,0 +1,766 @@ +package gameLogic; + +import gameLogic.dice.BlockDie; +import gameLogic.dice.EightSidedDie; +import gameLogic.dice.SixSidedDie; +import gameLogic.dice.ThreeSidedDie; +import gameLogic.rules.RuleThrow; + +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; +import java.util.UUID; +import java.util.Vector; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; + +import javax.vecmath.Vector2d; + +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameter; +import network.SBProtocolParameterArray; +import server.logic.User; +import util.SBApplication; + +/** + * An abstract representation of a Game on the client or server side. + */ +public abstract class GameController extends Thread{ + + public static final int NUMBER_OF_ROUNDS_IN_GAME = 32; + public static final int MAX_MONEY_TO_SPEND = 1000000; + public static final File CLIENT_GAMES_FILE = new File("data/client_games"); + public static final File SERVER_GAMES_FILE = new File("data/server_games"); + + public ThreeSidedDie d3 = new ThreeSidedDie(); + public SixSidedDie d6 = new SixSidedDie(); + public EightSidedDie d8 = new EightSidedDie(); + public BlockDie db = new BlockDie(); + protected Vector availableTeams = new Vector(); + + private UUID matchID; + private boolean running; + protected Date finishedOn; + protected User winner; + protected int[] score = new int[]{0, 0}; + protected Vector casualties = new Vector(); + protected Pitch pitch = new Pitch(); + protected Weather weather; + protected User[] coaches = new User[2]; // the logged-in users coaching the opposing teams. + protected Team[] teams = new Team[2]; // the teams playing against each other. + protected int roundCount = 0; + protected int gamePhase = 0; //0: Team Choosing Phase, 1: Team Setup Phase, 2: Kick Phase, 3: Normal Playing Phase, 4: Finishing Phase, 5: WaitingPhase + protected int firstPlayer; // the Player that gets the first Playing round (the opponent gets the first kickoff) + protected int actingPlayer; + private volatile LinkedBlockingQueue incomingMessages = new LinkedBlockingQueue(); + private volatile LinkedBlockingQueue incomingAnswers = new LinkedBlockingQueue(); + private SBApplication parent; + + /** + * The game must be initialized with a reference to its game so it can send messages to clients and exactly two users. + * @param parent The game client or server (SBApplication) to create this game for. + * @param coach1 The coach of the first team. + * @param coach2 The coach of the second team. + */ + public GameController(SBApplication parent, User coach1, User coach2) { + this.coaches = new User[]{coach1, coach2}; + this.teams = new Team[]{new Team(this, coach1), new Team(this, coach2)}; + this.parent = parent; + this.matchID = UUID.randomUUID(); + } + + /** + * A game constructor to construct from a text string from a file. (To represent logged games in the application)
+ * Only used in util.FinishedGame. + * @param dataString the string with the game data. + */ + public GameController(String dataString) { + setRunning(false); + } + + public abstract void run(); + + // MESSAGE PROCESSING + + /** + * The thread that listens for new incoming messages and notifies the game thread if new messages arrive. + */ + protected abstract class MessageListener extends Thread { + private GameController game; + + public MessageListener(GameController parent) { + this.game = parent; + } + + @Override + public void run(){ + while(true) { + try { + int incomingMessagesSize = incomingMessages.size(), incomingAnswersSize = incomingAnswers.size(); + if(incomingMessagesSize > 0) { // process the next message. + SBProtocolMessage receivedMessage = incomingMessages.poll(); + try { + if(receivedMessage.getParameterContent(0).equals(SBProtocolMessage.EVENT_SHOW_ME)) { + getParent().log(Level.INFO, receivedMessage.getParameterContent(1) + ": \"" + receivedMessage.getParameterContent(2) + "\""); + } else if(receivedMessage.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_WEATHER)) { + getParent().log(Level.INFO, "Weather: "+Weather.valueOf(receivedMessage.getParameterContent(1))); + } else { + getParent().log(Level.FINER, " Received game message " + + receivedMessage.getMID().toString().substring(0, 5) + + ": " + receivedMessage.getParameters().toStringUnescaped()); + } + } catch(Exception e) { + getParent().log(Level.INFO, "Received game message: " + receivedMessage.toStringShortenUUID()); + } + processMessage(receivedMessage); + } + if(incomingAnswersSize > 0) { // process the next answer. + SBProtocolMessage receivedAnswer = incomingAnswers.poll(); + try { + if(receivedAnswer.getCommand() == SBProtocolCommand.WORKD) + getParent().log(Level.FINER, "Received success answer " + + receivedAnswer.getMID().toString().substring(0, 5) + + ": " + receivedAnswer.getParameters().toStringUnescaped()); + else + getParent().log(Level.FINER, "Received failure answer " + + receivedAnswer.getMID().toString().substring(0, 5) + + ": " + receivedAnswer.getParameters().toStringUnescaped()); + } catch(Exception e) { + getParent().log(Level.INFO, " Received game answer: " + receivedAnswer.toStringShortenUUID()); + } + processAnswer(receivedAnswer); + } + if(incomingMessagesSize <= 0 && incomingAnswersSize <= 0){ // wait for a bit and check again + Thread.sleep(100); + } + } catch (InterruptedException e) { + getParent().log(Level.SEVERE, "Interrupted while waiting for new message or answer in queue. Trying again in 1s."); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + // I hope the following two lines will one day cause us headaches: + getParent().log(Level.SEVERE, "Interrupted-ception! Giving up. No hope."); + System.exit(-314159265); // for all who don't know: this error code is π! + } + } + } + } + + /** + * Is called when new messages arrive. Will be overwritten by client or server match class. + * @param message The message that was received. + */ + public abstract void processMessage(SBProtocolMessage message); + + /** + * Is called when new answers arrive. Will be overwritten by client or server match class. + * @param answer The answer that was received. + */ + public abstract void processAnswer(SBProtocolMessage answer); + + public GameController getGame() { + return game; + } + } + + // HELPERS + + /** + * Return a failure answer with parameters to sender of message. + * @param message The message to return a failure answer for. + * @param params The params to send with the message. + */ + public void returnFailureMessage(SBProtocolMessage message, String... params) { + if(message.getSocket() != null) message.getSocket().sendMessage(SBProtocolMessage.createFailureMessage(getParent().UID, message.getMID(), new SBProtocolParameterArray(params))); + } + + /** + * Return a success answer with parameters to sender of message. + * @param message The message to return a success answer for. + * @param params The params to send with the message. + */ + public void returnSuccessMessage(SBProtocolMessage message, String... params) { + if(message.getSocket() != null) message.getSocket().sendMessage(SBProtocolMessage.createSuccessMessage(getParent().UID, message.getMID(), new SBProtocolParameterArray(params))); + } + + /** + * Return an empty failure answer to sender of message. + * @param message The message to return a failure answer for. + */ + public void returnFailureMessage(SBProtocolMessage message) { + if(message.getSocket() != null) message.getSocket().sendMessage(SBProtocolMessage.createFailureMessage(getParent().UID, message.getMID(), new SBProtocolParameterArray())); + } + + /** + * Return an empty success answer to sender of message. + * @param message The message to return a success answer for. + */ + public void returnSuccessMessage(SBProtocolMessage message) { + if(message.getSocket() != null) message.getSocket().sendMessage(SBProtocolMessage.createSuccessMessage(getParent().UID, message.getMID(), new SBProtocolParameterArray())); + } + + /** + * Send a message to a recipient. + * @param destinationUID the UID of the authenticated (logged-in) user to send the message to. + * @param message The message to be sent. + */ + public void sendMessage(UUID destinationUID, SBProtocolMessage message) { + if(message.getCommand() == SBProtocolCommand.EVENT){ + if(getTeam(findUserIndex(destinationUID)) != null)getTeam(findUserIndex(destinationUID)).invokeEventHappenedForAllPlayers(message.getParameterContent(0)); + } + if(getParent().getSocketManager() != null) { + boolean log = true; + if(message.getParameters().size() >= 1) + if(message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_PLAYER) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_BALL_POS) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_BLITZ_PASS_FOUL) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_GAME_PHASE) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_MOVING_PLAYER) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_ROUND_COUNT) || + message.getParameterContent(0).equals(SBProtocolMessage.EVENT_SEND_SCORE)) + log = false; + if(log) + getParent().log(Level.FINER, " Sending game message " + + message.getMID().toString().substring(0, 5) + + ": " + message.getParameters().toStringUnescaped()); + getParent().getSocketManager().sendMessage(destinationUID, message); + } + } + + public void sendMessage(SBProtocolMessage message, SBProtocolCommand command, String... parameters){ + sendMessage(message.getUID(), new SBProtocolMessage(new SBProtocolMessage(getParent().UID, command, parameters))); + } + + public void sendMessageShowMe(User user, String a, String b){ + sendMessage(user.getUID(), new SBProtocolMessage(new SBProtocolMessage(getParent().UID, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, a, b))); + } + + public void sendMessageShowMe(String a, String b){ + sendMessageShowMe(getUser(0), a, b); + sendMessageShowMe(getUser(1), a, b); + } + + public abstract void sendMessage(User destinationUser, SBProtocolCommand command, String... parameters); + + /** + * chooses a direction where the ball scatters + * @return a Vector2d which gives the direction + */ + public Vector2d scatter(){ + Vector2d scatterDirection = new Vector2d(0, 0); + int direction = d8.throwDie(); + switch(direction){ + case 0: scatterDirection = new Vector2d(-1, -1); break; + case 1: scatterDirection = new Vector2d(0, -1); break; + case 2: scatterDirection = new Vector2d(1, -1); break; + case 3: scatterDirection = new Vector2d(-1, 0); break; + case 4: scatterDirection = new Vector2d(1, 0); break; + case 5: scatterDirection = new Vector2d(-1, 1); break; + case 6: scatterDirection = new Vector2d(0, 1); break; + case 7: scatterDirection = new Vector2d(1, 1); break; + } + return scatterDirection; + } + + /** + * Create a string of this game that can be written to file and read again. + * @return The string that represents this game. + */ + public String toLogString() { + String beginParam = "\", \"", beginArray = "\", [\"", endArray = "\"], \"", beginArrayFromArray = "\"], [\""; + String team1Coach="", team1Name="", team1Type="", team1Score=escape(score[0]+""), + team2Coach="", team2Name="", team2Type="", team2Score=escape(score[1]+""), + winnerName="", finishedOnString=""; + try{ + if(coaches[0] != null) team1Coach = escape(coaches[0].getName()); + if(teams[0] != null) team1Name = escape(teams[0].getName()); + if(teams[0] != null) team1Type = escape(teams[0].getType()); + if(coaches[1] != null) team2Coach = escape(coaches[1].getName()); + if(teams[1] != null) team2Name = escape(teams[1].getName()); + if(teams[1] != null) team2Type = escape(teams[1].getType()); + if(winner != null) winnerName = escape(winner.getName()); + if(finishedOn != null) finishedOnString = escape((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(finishedOn)); + + String r = "[[\"" + team1Coach + beginArray + team1Name + beginParam + team1Type + endArray + team1Score + beginArrayFromArray + + team2Coach + beginArray + team2Name + beginParam + team2Type + endArray + team2Score + endArray + + winnerName + beginParam + + finishedOnString; + if(casualties.size() > 0) { + r += beginArray; // add the beginning of the casualties array + for (Player casualty: casualties) r += escape(casualty.toString()) + beginParam; // add all casualties + r = r.replaceAll(beginParam+"$", "\"]]"); // replace the last beginParam with the end of the string + } else r += "\", []]"; // add an empty casualties array + return r; + }catch(NullPointerException e){ + return ""; + } + } + + /** + * Escape and return the passed string. + * @param toEscape The string to escape. + * @return The escaped passed string. + */ + public String escape(String toEscape) { + // replace the set [&, ", space, [, ], \t, \n] with the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] and return + return toEscape.replace("&", "&").replace("\"", """).replace(" ", "&space;").replace("[", "&brackl;").replace("]", "&brackr;").replace("\t", "&tab;").replace("\n", "&newl;"); + } + + /** + * Unescape and return the passed string. + * @param toUnescape The string to unescape. + * @return The unescaped passed string. + */ + public String unescape(String toUnescape) { + // replace the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] with the set [&, ", space, [, ], \t, \n] and return + return toUnescape.replace("&newl;", "\n").replace("&tab;", "\t").replace("&brackr;", "]").replace("&brackl;", "[").replace("&space;", " ").replace(""", "\"").replace("&", "&"); + } + + /** + * Write this game to the file specified in the specified games file. + * @param gamesFile The file to log this game to. + */ + protected void logGame(File gamesFile) { + // backup file before writing to prevent data loss (if games file exists) + File backup = null; + if(gamesFile.exists()) { + backup = new File(gamesFile.getAbsolutePath() + ".lock"); + try { + InputStream backupReader = new FileInputStream(gamesFile); + OutputStream backupWriter = new FileOutputStream(backup); + byte[] backupBuffer = new byte[1024]; + int length; + while ((length = backupReader.read(backupBuffer)) > 0) + backupWriter.write(backupBuffer, 0, length); + backupWriter.close(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Could not backup file. Not writing to disk."); + return; + } + } + // write file + PrintWriter writer; + try { + writer = new PrintWriter(new FileWriter(gamesFile, true)); + boolean createdFile = gamesFile.createNewFile(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Could not write file."); + return; + } + // write game + writer.println(toLogString()); + if(writer.checkError()) { + getParent().log(Level.SEVERE, "Could not write file."); + } else { + writer.close(); + // remove backup file again + if(backup != null) { boolean deletedBackup = backup.delete(); } + } + } + + // GETTERS & SETTERS + + /** + * Get the opponent of the passed user. + * @param user The user whose opponent is searched. + * @return The user in this game that is not the passed user. Null if the passed user is not in this game. + */ + public User getOpponent(User user) { + if(coaches[0].equals(user)) return coaches[1]; + if(coaches[1].equals(user)) return coaches[0]; + else return null; + } + + /** + * Get the first or the second coach (indices 0 or 1). + * @param index The index of the coach to get (0 or 1). + * @return The User at the index in coaches. Null if index is not 0 or 1. + */ + public User getUser(int index){ + if(index == 0 || index == 1) return coaches[index]; + else return null; + } + + /** + * Get the Opponent of the first or the second coach (indices 0 or 1). + * @param index The index of the coach you want the Opponent from (0 or 1). + * @return The Opponent of the User at the index in coaches. Null if index is not 0 or 1. + */ + public User getOpponent(int index) { + if(index == 0 || index == 1) return getOpponent(coaches[index]); + else return null; + } + + /** + * Get the opponent of the passed team. + * @param team The team whose opponent is searched. + * @return The team in this game that is not the passed team. Void if the passed team is not in this game. + */ + public Team getOpposingTeam(Team team) { + if(teams[0].equals(team)) return teams[1]; + if(teams[1].equals(team)) return teams[0]; + else return null; + } + + public User getWinner() { + return winner; + } + + public void setWinner(User winner) { + this.winner = winner; + } + + /** + * Get the game application (client or server) of this game. + * @return The game application (client or server) of this game. + */ + public SBApplication getParent(){ + return parent; + } + + public void addIncomingMessage(SBProtocolMessage message) { + putInQueue(incomingMessages, message); + getParent().log(Level.FINE, "Added message to incoming messages queue: " + message.toStringShortenUUID()); + } + + public void addIncomingAnswer(SBProtocolMessage answer) { + putInQueue(incomingAnswers, answer); + getParent().log(Level.FINE, "Added answer to incoming answers queue: " + answer.toStringShortenUUID()); + } + + /** + * Try to add a message to a queue until it worked (wait 100ms after each retry). + * @param queue The queue to put the message in. + * @param message The message to put into the queue. + */ + void putInQueue(LinkedBlockingQueue queue, SBProtocolMessage message) { + boolean putInQueue = false; + while(!putInQueue) { + try { + queue.put(message); + putInQueue = true; + } catch (InterruptedException e) { + getParent().log(Level.WARNING, "Interrupted while adding the message to the queue. Retrying in 100ms..."); + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + getParent().log(Level.SEVERE, "Interrupted while waiting to retry adding the message to the queue. Continuing anyway (because I'm badass)."); + } + } + } + } + + public boolean isRunning() {return running;} + public void setRunning(boolean running) {this.running = running;} + public Pitch getPitch(){return pitch;} + public int getRoundCount(){return roundCount;} + public void countUpRound(){roundCount++;} + + public Vector getCasualties() { + return casualties; + } + + /** + * Get the score of team 0 or team 1. + * @param index The index (0 or 1) of the team to get the score for. + * @return The score of team with specified index (0 or 1). + * @throws IndexOutOfBoundsException If the index is not 0 or 1. + */ + public int getScoreFromTeam(int index) { + if(index == 0 || index == 1) return score[index]; + else { + getParent().log(Level.SEVERE, "Tried to get score from team with illegal index "+index+". (use 0 or 1)"); + throw new IndexOutOfBoundsException(); + } + } + + /** + * Get team 0 or team 1. + * @param index The index (0 or 1) of the team to get. + * @return The team with specified index (0 or 1). + * @throws IndexOutOfBoundsException If the index is not 0 or 1. + */ + public Team getTeam(int index) { + if(index == 0 || index == 1) return teams[index]; + else { + getParent().log(Level.SEVERE, "Tried to get team with illegal index "+index+". (use 0 or 1)"); + throw new IndexOutOfBoundsException(); + } + } + + /** + * Set team 0 or team 1. + * @param index The index (0 or 1) of the team to set. + * @param team The team to set at index (0 or 1). + * @throws IndexOutOfBoundsException If the index is not 0 or 1. + */ + protected void setTeam(int index, Team team) { + if(index == 0 || index == 1) teams[index] = team; + else { + getParent().log(Level.SEVERE, "Tried to set team with illegal index "+index+". (use 0 or 1)"); + throw new IndexOutOfBoundsException(); + } + } + + protected void addAvailableTeam(Team team) { + availableTeams.add(team); + } + + public abstract void touchdown(Team t); + + + public void addCasualty(Player p){ + casualties.addElement(p); + } + + protected void countUpScore(Team t){ + if(t == teams[0]){ + countUpScore(0); + }else if(t == teams[1]){ + countUpScore(1); + } + } + + protected void countUpScore(int teamIndex){ + score[teamIndex]++; + } + + protected void sendGame(){ + sendTeam(0); + sendTeam(1); + sendGamePhase(); + sendScore(); + sendRoundCount(); + sendBallPos(); + + } + + protected void sendGamePhase() { + sendMessage(coaches[0], SBProtocolCommand.EVENT, new SBProtocolParameterArray(SBProtocolMessage.EVENT_SEND_GAME_PHASE, gamePhase+"")); + sendMessage(coaches[1], SBProtocolCommand.EVENT, new SBProtocolParameterArray(SBProtocolMessage.EVENT_SEND_GAME_PHASE, gamePhase+"")); + } + + protected void sendScore(){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_SCORE)); + parameters.addParameter(new SBProtocolParameter(score[0] + "")); + parameters.addParameter(new SBProtocolParameter(score[1] + "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + public void sendPlayer(Player p){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + int actingUserIndex; + if(p.getTeam().equals(teams[0])){ + actingUserIndex = 0; + }else if(p.getTeam().equals(teams[1])){ + actingUserIndex = 1; + }else{ + return; + } + int playerIndex = -1; + for(int i = 0; i < teams[actingUserIndex].getPlayers().size(); i++){ + if(teams[actingUserIndex].getPlayers().get(i).equals(p)){ + playerIndex = i; + break; + } + } + if(playerIndex == -1){return;} + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_PLAYER)); + parameters.addParameter(new SBProtocolParameter(actingUserIndex+"")); + parameters.addParameter(new SBProtocolParameter(playerIndex+"")); + parameters.addParameter(new SBProtocolParameter(p.getName())); + parameters.addParameter(new SBProtocolParameter(p.invokeGetBe()+"")); + parameters.addParameter(new SBProtocolParameter(p.invokeGetSt()+"")); + parameters.addParameter(new SBProtocolParameter(p.invokeGetGe()+"")); + parameters.addParameter(new SBProtocolParameter(p.invokeGetRs()+"")); + parameters.addParameter(new SBProtocolParameter(p.invokeGetRemainingBe()+"")); + parameters.addParameter(new SBProtocolParameter(p.invokeGetPlayerCondition().name())); + parameters.addParameter(new SBProtocolParameter((int)p.getPos().x +"")); + parameters.addParameter(new SBProtocolParameter((int)p.getPos().y +"")); + parameters.addParameter(new SBProtocolParameter(p.isHoldingBall() +"")); + parameters.addParameter(new SBProtocolParameter(p.getRedCard()+ "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + protected void sendWeather(){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_WEATHER)); + parameters.addParameter(new SBProtocolParameter(getWeather().name())); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + protected void sendNewTeam(int teamIndex){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_NEW_TEAM)); + parameters.addParameter(new SBProtocolParameter(teamIndex + "")); + parameters.addParameter(new SBProtocolParameter(getTeam(teamIndex).getName())); + parameters.addParameter(new SBProtocolParameter(getTeam(teamIndex).getType())); + for(int i = 0; i < teams[teamIndex].getPlayers().size(); i++){ + parameters.addParameter(new SBProtocolParameter(teams[teamIndex].getPlayers().get(i).getName())); + } + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + void sendTeam(int teamIndex){ + for(int i = 0; i < teams[teamIndex].getPlayers().size(); i++){ + sendPlayer(teams[teamIndex].getPlayers().get(i)); + } + sendMovingPlayer(teamIndex); + sendBlitzPassFoul(teamIndex); + } + + void sendBlitzPassFoul(int teamIndex){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_BLITZ_PASS_FOUL)); + parameters.addParameter(new SBProtocolParameter(teamIndex + "")); + int blitz = 0; + int pass = 0; + int foul = 0; + if(getTeam(teamIndex).getBlitz()) blitz = 1; + if(getTeam(teamIndex).getPass()) pass = 1; + if(getTeam(teamIndex).getFoul()) foul = 1; + parameters.addParameter(new SBProtocolParameter(blitz + "")); + parameters.addParameter(new SBProtocolParameter(pass + "")); + parameters.addParameter(new SBProtocolParameter(foul + "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + void sendMovingPlayer(int teamIndex){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_MOVING_PLAYER)); + parameters.addParameter(new SBProtocolParameter(teamIndex + "")); + int movingPlayerIndex = -1; + for(int i = 0; i < teams[teamIndex].getPlayers().size(); i++){ + if(teams[teamIndex].getMovingPlayer() == teams[teamIndex].getPlayers().get(i)){ + movingPlayerIndex = i; + } + } + parameters.addParameter(new SBProtocolParameter(movingPlayerIndex + "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + void sendRoundCount(){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_ROUND_COUNT)); + parameters.addParameter(new SBProtocolParameter(roundCount + "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + public void sendBallPos(){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_SEND_BALL_POS)); + parameters.addParameter(new SBProtocolParameter((int)getPitch().getBallPos().x + "")); + parameters.addParameter(new SBProtocolParameter((int)getPitch().getBallPos().y + "")); + sendMessage(coaches[0], SBProtocolCommand.EVENT, parameters); + sendMessage(coaches[1], SBProtocolCommand.EVENT, parameters); + } + + public void sendMessage(User destinationUser, SBProtocolCommand command, SBProtocolParameterArray parameters) { + SBProtocolMessage message = new SBProtocolMessage(this.getParent().UID, command, parameters); + sendMessage(destinationUser.getUID(), message); + } + + public Team getAvailableTeam(int index){ + return availableTeams.get(index); + } + + public int getNumberOfAvailableTeams() { + return availableTeams.size(); + } + + protected Team setTeamType(SBProtocolMessage message, int typeIndex){//If the message is sent to the client, the typeIndex must be set to 3, if sent to server, then it must be 2 + int teamTypeIndex = -1; + for(int i = 0; i < availableTeams.size(); i++){ + if(availableTeams.get(i).getType().equalsIgnoreCase(message.getParameterContent(typeIndex))){ + teamTypeIndex = i; + } + } + Team newTeam = null; + if(teamTypeIndex < availableTeams.size() && teamTypeIndex >= 0) newTeam = getAvailableTeam(teamTypeIndex); + return newTeam; + } + + /** + * finds the internal User-index in this Game that sent a message + * @param message the message you want to find out the user + * @return 0 if its the first User, 1 if its the second, -1 if its nether and sends a failure message + */ + public int findUserIndex(SBProtocolMessage message){ + if(message.getUID().equals(coaches[0].getUID()) || message.getUID().equals(coaches[1].getUID())){ + return findUserIndex(message.getUID()); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_DONT_BELONG_HERE); + return -1; + } + } + + public int findUserIndex(UUID UID){ + if(UID.equals(coaches[0].getUID())){ + return 0; + }else if(UID.equals(coaches[1].getUID())){ + return 1; + }else{ + return -1; + } + } + + protected void clearAllPlayerPos(){ + for(Player p:getTeam(0).getPlayers()){ + p.invokeClearPosition(); + p.invokeSetIsHoldingBall(false); + } + for(Player p:getTeam(1).getPlayers()){ + p.invokeClearPosition(); + p.invokeSetIsHoldingBall(false); + } + } + + public void setGamePhase(int i){ + if(i >= 0 && i < 6){ + this.gamePhase = i; + } + } + + public int getGamePhase() { + return gamePhase; + } + + public UUID getMatchID() { + return matchID; + } + + public Weather getWeather(){ + return weather; + } + + protected void setWeather(Weather weather){ + this.weather = weather; + } + + protected void checkForBlizzard(){ + if(weather == Weather.BLIZZARD){ + RuleThrow.LONG_PASS = 8; + RuleThrow.LONG_BOMB = 8; + } + } + + protected void checkForSwelteringHeat(Vector players){ + if(getWeather().equals(Weather.SWELTERING_HEAT)){ + for(Player p: players){ + if(d6.throwDie() == 1){ + p.invokeSetPlayerCondition(PlayerCondition.KO); + } + } + } + } +} \ No newline at end of file diff --git a/src/gameLogic/GameTest.java b/src/gameLogic/GameTest.java new file mode 100644 index 0000000..59b0a8e --- /dev/null +++ b/src/gameLogic/GameTest.java @@ -0,0 +1,293 @@ +package gameLogic; + +import static org.junit.Assert.*; + +import network.SBProtocolCommand; +import network.SBProtocolMessage; + +import org.junit.Test; + +import server.Server; +import client.Client; + +public class GameTest { + + private static Server server; + private static Client client1, client2; + + static { + server = new Server(); + server.runServer(); + server.start(9989); + server.logmatches = false; + client1 = new Client(); + client1.autologin = false; + client1.automatchstart = false; + client2 = new Client(); + client2.autologin = false; + client2.automatchstart = false; + client1.runClient(); + client2.runClient(); + client1.connect("localhost", 9989); + client2.connect("localhost", 9989); + String password = "1234"; + client1.login("Client1", password); + try { Thread.sleep(5000); } catch (InterruptedException ignored) {} + if(!client1.isLoggedIn()){ + client1.login("Client1", password); + } + client2.login("Client2", password); + try { Thread.sleep(5000); } catch (InterruptedException ignored) {} + if(!client2.isLoggedIn()){ + client2.login("Client2", password); + } + client1.startGame(); + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + client2.invitePlayer("Client1"); + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + + SBProtocolMessage message1 = new SBProtocolMessage(client1.getUID(), SBProtocolCommand.ACTIO, "SET TEAM","Team1", "SAVANNA", "Giraffe", "Elephant", "Rhino"); + SBProtocolMessage message2 = new SBProtocolMessage(client2.getUID(), SBProtocolCommand.ACTIO, "SET TEAM","Team2", "POLARREGION", "Puffin", "Seacow", "Penguin"); + + client1.sendGameMessage(message1); + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + client2.sendGameMessage(message2); + try { Thread.sleep(1000); } catch (InterruptedException ignored) {} + + + } + + @Test + public void testGetOpponentUser() { + assertEquals(server.getRunningMatches().get(0).getUser(0), server.getRunningMatches().get(0).getOpponent(1)); + assertEquals(server.getRunningMatches().get(0).getUser(1), server.getRunningMatches().get(0).getOpponent(0)); + assertEquals(null, server.getRunningMatches().get(0).getOpponent(2)); + assertNotEquals(server.getRunningMatches().get(0).getUser(0), server.getRunningMatches().get(0).getOpponent(0)); + assertNotEquals(server.getRunningMatches().get(0).getUser(1), server.getRunningMatches().get(0).getOpponent(1)); + } + + @Test + public void testGetOpponentInt() { + assertEquals(server.getRunningMatches().get(0).getUser(1),server.getRunningMatches().get(0).getOpponent(0)); + assertEquals(server.getRunningMatches().get(0).getUser(0),server.getRunningMatches().get(0).getOpponent(1)); + assertNotEquals(null,server.getRunningMatches().get(0).getOpponent(0)); + assertNotEquals(null,server.getRunningMatches().get(0).getOpponent(1)); + assertNotEquals(server.getRunningMatches().get(0).getUser(0),server.getRunningMatches().get(0).getOpponent(0)); + assertNotEquals(server.getRunningMatches().get(0).getUser(1),server.getRunningMatches().get(0).getOpponent(1)); + assertEquals(null, server.getRunningMatches().get(0).getOpponent(2)); + + } + + @Test + public void testGetOpposingTeam() { + assertEquals(server.getRunningMatches().get(0).getTeam(0), server.getRunningMatches().get(0).getOpposingTeam(server.getRunningMatches().get(0).getTeam(1))); + assertEquals(server.getRunningMatches().get(0).getTeam(1), server.getRunningMatches().get(0).getOpposingTeam(server.getRunningMatches().get(0).getTeam(0))); + assertNotEquals(null, server.getRunningMatches().get(0).getOpponent(1)); + assertNotEquals(null, server.getRunningMatches().get(0).getOpponent(0)); + assertNotEquals(server.getRunningMatches().get(0).getTeam(0), server.getRunningMatches().get(0).getOpposingTeam(server.getRunningMatches().get(0).getTeam(0))); + assertNotEquals(server.getRunningMatches().get(0).getTeam(1), server.getRunningMatches().get(0).getOpposingTeam(server.getRunningMatches().get(0).getTeam(1))); + } + + @Test + public void testGetWinner() { + if(server.getRunningMatches().get(0).getScoreFromTeam(0)>server.getRunningMatches().get(0).getScoreFromTeam(1)){ + assertEquals(server.getRunningMatches().get(0).getWinner(),server.getRunningMatches().get(0).getUser(0)); + assertNotEquals(server.getRunningMatches().get(0).getWinner(),server.getRunningMatches().get(0).getUser(1)); + } else if(server.getRunningMatches().get(0).getScoreFromTeam(0)server.getRunningMatches().get(0).getScoreFromTeam(1)){ + server.getRunningMatches().get(0).setWinner(server.getRunningMatches().get(0).getUser(0)); + + assertEquals(server.getRunningMatches().get(0).getUser(0), server.getRunningMatches().get(0).getWinner()); + assertNotEquals(server.getRunningMatches().get(0).getUser(1), server.getRunningMatches().get(0).getWinner()); + + } else if(server.getRunningMatches().get(0).getScoreFromTeam(0)Kicker.LEFT or Kicker.RIGHT) + * @param side The side from which this kicker kicks. (Kicker.LEFT or Kicker.RIGHT) + * @param pitch The pitch this kicker kicks onto. + */ + @SuppressWarnings("SuspiciousNameCombination") + public Kicker(String side, Pitch pitch) { + super("Kicker", null); + position = new PitchField(-1, Pitch.PITCH_WIDTH); + if(side.equals(RIGHT)) position = new PitchField(Pitch.PITCH_LENGTH, Pitch.PITCH_WIDTH); + } + +} diff --git a/src/gameLogic/Pitch.java b/src/gameLogic/Pitch.java new file mode 100644 index 0000000..c43aa15 --- /dev/null +++ b/src/gameLogic/Pitch.java @@ -0,0 +1,381 @@ +package gameLogic; + +import javax.vecmath.Vector2d; + +import gameLogic.rules.RuleCatch; +import network.SBProtocolCommand; +import network.SBProtocolMessage; + +/** + * The pitch in which a match takes place. + */ +public class Pitch { + public static final int PITCH_LENGTH = 26; + public static final int PITCH_WIDTH = 15; + public static final PitchField THE_VOID = new PitchField(-1, -1); + + private PitchField[][] fields = new PitchField[PITCH_LENGTH][PITCH_WIDTH]; + private Vector2d ball = new Vector2d(-1, -1);// Position of the Ball in Coordinates x,y + private Team[] teams = new Team[2]; + + /** + * Initiates all the Pitch Fields and sets the ball outside the Pitch + */ + Pitch(){ + for(int i = 0; i < PITCH_LENGTH; i++){ + for(int j = 0; j < PITCH_WIDTH; j++){ + fields[i][j] = new PitchField(new Vector2d(i, j)); + } + } + } + + public Vector2d getBallPos(){return ball;} + public Team getTeam(int i){return teams[i];} + public void setBallPos(int x, int y){setBallPos(new Vector2d(x, y));} + public void setBallPos(Vector2d pos){ + setBallPos(pos, false); + } + /** + * sets the ball position + * @param pos to this position + * @param b true, if it was a successful throw, that set the ball there. + */ + public void setBallPos(Vector2d pos, boolean b) { + this.ball = pos; + getTeam(0).getMatch().sendBallPos(); + if(!(isOnField(pos))){ + throwIn(pos); + } + if(fields[(int)ball.x][(int)ball.y].getPlayer() != null){ + if(!(fields[(int)ball.x][(int)ball.y].getPlayer().isHoldingBall())){ + ((RuleCatch)(fields[(int)ball.x][(int)ball.y].getPlayer().getRule(4))).apply(b); + } + } + } + public void adjustBallPos(Vector2d pos){this.ball = pos;} + + private Vector2d getLastBallPos(Vector2d pos){ + Vector2d lastPos = new Vector2d(pos); + if(pos.x < 0){ + if(pos.y < 0){ + lastPos = new Vector2d(0, 0); + }else if(pos.y > PITCH_WIDTH-1){ + lastPos = new Vector2d(0, PITCH_WIDTH-1); + }else{ + lastPos = new Vector2d(0, pos.y); + } + }else if(pos.x > PITCH_LENGTH-1){ + if(pos.y < 0){ + lastPos = new Vector2d(PITCH_LENGTH-1, 0); + }else if(pos.y > PITCH_WIDTH-1){ + lastPos = new Vector2d(PITCH_LENGTH-1, PITCH_WIDTH-1); + }else{ + lastPos = new Vector2d(PITCH_LENGTH-1, pos.y); + } + }else if(pos.y < 0){ + lastPos = new Vector2d(pos.x, 0); + }else if(pos.y > PITCH_WIDTH-1){ + lastPos = new Vector2d(pos.x, PITCH_WIDTH-1); + } + return lastPos; + } + + /** + * makes a throw in by the Crowd + * @param pos the position where the ball would have landed + */ + public void throwIn(Vector2d pos){ + getTeam(0).getMatch().sendMessageShowMe("Referee", "The ball was thrown in by the crowd!"); + Vector2d lastPos = getLastBallPos(pos); + int directionThrow = getTeam(0).getMatch().d3.throwDie(); + Vector2d direction = new Vector2d(0, 0); + if((int)lastPos.x == 0){ + direction = new Vector2d(1, directionThrow-2); + }else if((int)lastPos.y == 0){ + direction = new Vector2d(directionThrow-2, 1); + }else if((int)lastPos.x == PITCH_LENGTH-1){ + direction = new Vector2d(-1, directionThrow-2); + }else if((int)lastPos.y == PITCH_WIDTH-1){ + direction = new Vector2d(directionThrow-2, -1); + } + int distance = getTeam(0).getMatch().d6.throwDie() + getTeam(0).getMatch().d6.throwDie(); + setBallPos((int)(lastPos.x + direction.x*distance), (int)(lastPos.y + direction.y*distance)); + getTeam(0).getMatch().sendMessage(getTeam(0).getMatch().getUser(0), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_BALL_THROWN_IN_BY_THE_CROWD); + getTeam(0).getMatch().sendMessage(getTeam(1).getMatch().getUser(1), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_BALL_THROWN_IN_BY_THE_CROWD); + } + + public boolean isOnField(int d, int e){ + return isOnField(new Vector2d(d, e)); + } + + /** + * Checks whether a given Position v is on the Pitch + * @param v Position as a Vector2d + * @return Boolean which is true if v is on the Pitch and false if not + */ + public boolean isOnField(Vector2d v){ + return !((int) v.x < 0 || (int) v.x > PITCH_LENGTH - 1 || (int) v.y < 0 || (int) v.y > PITCH_WIDTH - 1); + } + + /** + * Checks whether a given Position is on the left half of the pitch + * @param x x position + * @param y y position + * @return true if its on the left half, false if not + */ + public boolean isOnLeftHalf(int x, int y){ + return isOnLeftHalf(new Vector2d(x, y)); + } + + /** + * Checks whether a given Position is on the left half of the pitch + * @param v the position to be checked + * @return true if its on the left half, false if not + */ + public boolean isOnLeftHalf(Vector2d v){ + return !((int) v.x < 0 || (int) v.x > (PITCH_LENGTH / 2) - 1 || (int) v.y < 0 || (int) v.y > PITCH_WIDTH - 1); + } + + /** + * Checks whether a given Position is on the right half of the pitch + * @param x x position + * @param y y position + * @return true if its on the right half, false if not + */ + public boolean isOnRightHalf(int x, int y){ + return isOnRightHalf(new Vector2d(x, y)); + } + + /** + * Checks whether a given Position is on the right half of the pitch + * @param v the position to be checked + * @return true if its on the right half, false if not + */ + public boolean isOnRightHalf(Vector2d v){ + return !((int) v.x < (PITCH_LENGTH / 2) || (int) v.x > PITCH_LENGTH - 1 || (int) v.y < 0 || (int) v.y > PITCH_WIDTH - 1); + } + + private boolean isOnUpperLeftWideZone(Vector2d v){ + return !((int) v.x < 0 || (int) v.x > (PITCH_LENGTH / 2) - 1 || (int) v.y < 0 || (int) v.y > 3); + } + + private boolean isOnUpperRightWideZone(Vector2d v){ + return !((int) v.x < (PITCH_LENGTH / 2) || (int) v.x > PITCH_LENGTH - 1 || (int) v.y < 0 || (int) v.y > 3); + } + + private boolean isOnLowerLeftWideZone(Vector2d v){ + return !((int) v.x < 0 || (int) v.x > (PITCH_LENGTH / 2) - 1 || (int) v.y < 11 || (int) v.y > PITCH_WIDTH - 1); + } + + private boolean isOnLowerRightWideZone(Vector2d v){ + return !((int) v.x < (PITCH_LENGTH / 2) || (int) v.x > PITCH_LENGTH - 1 || (int) v.y < 11 || (int) v.y > PITCH_WIDTH - 1); + } + + private boolean isOnLeftLineOfScrimmage(Vector2d v){ + return (int) v.x == 12 && (int) v.y > 3 && (int) v.y < 11; + } + + private boolean isOnRightLineOfScrimmage(Vector2d v){ + return (int) v.x == 13 && (int) v.y > 3 && (int) v.y < 11; + } + + public boolean teamSetCorrect(int userIndex){ + int playersInUpperWideZone = 0; + int playersInLowerWideZone = 0; + int playersInLineOfScrimmage = 0; + int playersOnField = 0; + if(userIndex == 0){ + for(Player p: teams[userIndex].getPlayers()){ + if(isOnUpperLeftWideZone(p.getPos())){ + playersInUpperWideZone++; + }else if(isOnLowerLeftWideZone(p.getPos())){ + playersInLowerWideZone++; + }else if(isOnLeftLineOfScrimmage(p.getPos())){ + playersInLineOfScrimmage++; + } + if(isOnField(p.getPos())){ + playersOnField++; + } + } + }else if(userIndex == 1){ + for(Player p: teams[userIndex].getPlayers()){ + if(isOnUpperRightWideZone(p.getPos())){ + playersInUpperWideZone++; + }else if(isOnLowerRightWideZone(p.getPos())){ + playersInLowerWideZone++; + }else if(isOnRightLineOfScrimmage(p.getPos())){ + playersInLineOfScrimmage++; + } + if(isOnField(p.getPos())){ + playersOnField++; + } + } + } + return playersInLineOfScrimmage > 2 && playersInUpperWideZone < 3 && playersInLowerWideZone < 3 && playersOnField < 12; + } + + /** + * checks whether this position is int the touchdownzone + * @param t the touchdownzone from the team t + * @param pos the position + * @return true if its in the touchdownzone, false if not + */ + public boolean isInTouchdownZone(Team t, Vector2d pos){ + int side; + if(t.getMatch().getTeam(0).equals(t)){ + side = 25; + }else if(t.getMatch().getTeam(1).equals(t)){ + side = 0; + }else{ + return false; + } + return (int) pos.x == side; + } + + public boolean isAdjacent(int x1, int y1, int x2, int y2){ + return isAdjacent(new Vector2d(x1, y1), new Vector2d(x2, y2)); + } + + /** + * Checks whether two Fields on the Pitch are adjacent + * @param f1 The first Field + * @param f2 The second Field + * @return Boolean which is true if the two fields are adjacent and false if not + */ + public boolean isAdjacent(PitchField f1, PitchField f2){ + return isAdjacent(f1.getPos(),f2.getPos()); + } + + /** + * Checks whether two field-positions are adjacent + * @param pos1 Position of the first Field (as a Vector 2d) + * @param pos2 Position of the second Field (as a Vector 2d) + * @return Boolean which is true if the two fields are adjacent and false if not + */ + public boolean isAdjacent(Vector2d pos1, Vector2d pos2){ + try{ + if(!(isOnField(pos1)) || !(isOnField(pos2))){ + throw new SBOutOfFieldException("given position is not on the pitch"); + }else{ + for(PitchField f: getNeighbours(pos2)){ + if(fields[(int)pos1.x][(int)pos1.y] == f){ + return true; + } + } + return false; + } + }catch(SBOutOfFieldException e){ + e.printStackTrace(); + } + return false; + } + + /** + * Returns all the neighbors of a given Field + * @param f The field from whom you want to know the neighbors + * @return An array with the PitchFields that are neighbors to pos + */ + public PitchField[] getNeighbours(PitchField f){ + return getNeighbours(f.getPos()); + } + /** + * Returns all the neighbors of a given Position + * @param x The x position from whom you want to know the neighbors + * @param y The y position from whom you want to know the neighbors + * @return An array with the PitchFields that are neighbors to pos + */ + public PitchField[] getNeighbours(int x, int y){ + return getNeighbours(new Vector2d(x, y)); + } + /** + * Returns all the neighbors of a given Position + * @param pos The position from whom you want to know the neighbors (as a Vector 2d) + * @return An array with the PitchFields that are neighbors to pos + */ + public PitchField[] getNeighbours(Vector2d pos){ + int x = (int)pos.x; + int y = (int)pos.y; + try{ + if(!isOnField(pos)){ + throw new SBOutOfFieldException("given position is not on the pitch"); + } + PitchField[] returnFields; + if(x == 0){ + if(y == 0){ + returnFields = new PitchField[3]; + returnFields[0] = fields[x+1][y]; + returnFields[1] = fields[x][y+1]; + returnFields[2] = fields[x+1][y+1]; + }else if(y == 14){ + returnFields = new PitchField[3]; + returnFields[0] = fields[x][y-1]; + returnFields[1] = fields[x+1][y-1]; + returnFields[2] = fields[x+1][y]; + }else{ + returnFields = new PitchField[5]; + returnFields[0] = fields[x][y-1]; + returnFields[1] = fields[x+1][y-1]; + returnFields[2] = fields[x+1][y]; + returnFields[3] = fields[x][y+1]; + returnFields[4] = fields[x+1][y+1]; + + } + }else if(x == 25){ + if(y == 0){ + returnFields = new PitchField[3]; + returnFields[0] = fields[x-1][y]; + returnFields[1] = fields[x-1][y+1]; + returnFields[2] = fields[x][y+1]; + }else if(y == 14){ + returnFields = new PitchField[3]; + returnFields[0] = fields[x-1][y-1]; + returnFields[1] = fields[x][y-1]; + returnFields[2] = fields[x-1][y]; + }else{ + returnFields = new PitchField[5]; + returnFields[0] = fields[x-1][y-1]; + returnFields[1] = fields[x][y-1]; + returnFields[2] = fields[x-1][y]; + returnFields[3] = fields[x-1][y+1]; + returnFields[4] = fields[x][y+1]; + } + }else if(y == 0){ + returnFields = new PitchField[5]; + returnFields[0] = fields[x-1][y]; + returnFields[1] = fields[x+1][y]; + returnFields[2] = fields[x-1][y+1]; + returnFields[3] = fields[x][y+1]; + returnFields[4] = fields[x+1][y+1]; + }else if(y == 14){ + returnFields = new PitchField[5]; + returnFields[0] = fields[x-1][y-1]; + returnFields[1] = fields[x][y-1]; + returnFields[2] = fields[x+1][y-1]; + returnFields[3] = fields[x-1][y]; + returnFields[4] = fields[x+1][y]; + }else{ + returnFields = new PitchField[8]; + for(int i = 0; i < 3; i++){ + returnFields[i] = fields[x-1+i][y-1]; + } + returnFields[3] = fields[x-1][y]; + returnFields[4] = fields[x+1][y]; + for(int i = 0; i < 3; i++){ + returnFields[i+5] = fields[x-1+i][y+1]; + } + } + return returnFields; + }catch(SBOutOfFieldException e){ + e.printStackTrace(); + } + return null; + } + + public PitchField[][] getFields(){ + return fields; + } + + public void setTeam(int i, Team t){ + teams[i] = t; + } +} diff --git a/src/gameLogic/PitchField.java b/src/gameLogic/PitchField.java new file mode 100644 index 0000000..035cd03 --- /dev/null +++ b/src/gameLogic/PitchField.java @@ -0,0 +1,41 @@ +package gameLogic; + +import javax.vecmath.*; + +/** + * One field on the pitch + */ +public class PitchField { + private Player player = null;//at first there are no Players on the Pitch + private Vector2d pos; + + public PitchField(int x, int y){ + pos = new Vector2d(x, y); + } + /** + * Constructor of a PitchField + * @param pos Position where the PitchField is placed. + */ + public PitchField(Vector2d pos){ + this.pos = pos; + } + + /** + * checks whether two PitchFields have the same position + * @param f The second PitchField + * @return Boolean which is true if they have the same position and false if not + */ + public boolean isEqual(PitchField f){ + return (int) f.pos.x == (int) pos.x && (int) f.pos.y == (int) pos.y; + } + + /** + * method only to adjust the Position if someone changes the Player on a Pitchfield + * @param p the new Player + */ + public void adjustPlayer(Player p){player = p;} + + //getters and setters + public Vector2d getPos(){return pos;} + public Player getPlayer(){return player;} +} diff --git a/src/gameLogic/Player.java b/src/gameLogic/Player.java new file mode 100644 index 0000000..87eaf13 --- /dev/null +++ b/src/gameLogic/Player.java @@ -0,0 +1,550 @@ +package gameLogic; + +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Vector; +import java.util.logging.Level; + +import javax.vecmath.*; + +import gameLogic.rules.*; + +/** + * The abstract representation of a player. + */ +public class Player { + + /** + * The stats that are assigned to the player if they aren't overwritten. + */ + private static final int DEFAULT_GE = 3, + DEFAULT_RS = 7, + DEFAULT_ST = 3, + DEFAULT_BE = 5, + DEFAULT_PRICE = 50000, + DEFAULT_MIN_HEADCOUNT = 0, + DEFAULT_MAX_HEADCOUNT = 16; + + private int ge, rs, st, be, remainingBe; + private Rule[] rules = new Rule[5]; //0: MOVE, 1: BLOCK, 2: PUSH, 3: THROW, 4:CATCH + private SpecialRule[] specialRules; + private Vector tackleZone; + private Vector activeTackleZone = new Vector(); + private PlayerCondition playerCondition = PlayerCondition.FINE; + protected PitchField position; // needs to be directly accessible from Kicker + private boolean isHoldingBall = false; + private boolean redCard = false; + + protected String name; + private String[] descriptionLines; + private int id, price, minHeadcount, maxHeadcount; + private Team team; + /** + * The images of the player looking left (spriteL) and looking right (spriteR). + */ + private BufferedImage spriteR, spriteL; + /** + * A hash map for scripts to store data in. Is not used by standard players. + */ + private HashMap specialStats = new HashMap(); + /** + * The team manager that manages the script for this player. + */ + private TeamManager manager; + + /** + * Constructs a new player with the given name and a team manager that manages its script. + * @param name The name of the player. + * @param manager The manager that manages the script of this player. + */ + public Player(String name, TeamManager manager) { + this.manager = manager; + setName(name); + } + + /** + * Private initializer to clone a player. + * @param p The player blueprint to clone. + * @param team The team this clone is made for. + */ + private Player(Player p, Team team) { + this.manager = p.manager; + setName(p.getName()); + setSpriteR(p.getSpriteR()); + setSpriteL(p.getSpriteL()); + invokeAdjustTeam(team); + invokeSetGe(DEFAULT_GE); + invokeSetRs(DEFAULT_RS); + invokeSetSt(DEFAULT_ST); + invokeSetBe(DEFAULT_BE); + invokeAdjustRemainingBe(invokeGetBe()); + invokeSetPrice(DEFAULT_PRICE); + invokeAdjustMinHeadcount(DEFAULT_MIN_HEADCOUNT); + invokeAdjustMaxHeadcount(DEFAULT_MAX_HEADCOUNT); + invokeSetPosition(Pitch.THE_VOID); + setStandardRules(); + invokeSetSpecialRules(); + setStandardTackleZone(); + invokeSetPlayerCondition(PlayerCondition.FINE); + invokeSetDescriptionLines(new String[0]); + } + + public Player getCloneForTeam(Team team) { + return new Player(this, team); + } + + // METHODS SETTING STANDARDS + + /** + * setts the standard rules for all players + */ + public void setStandardRules() { + Rule[] rules = new Rule[]{new RuleMove(this), new RuleBlock(this), new RulePush(this), new RuleThrow(this), new RuleCatch(this)}; + invokeSetRules(rules); + } + + /** + * sets the standard Tacklezone, witch is exactly one field around the Player + */ + public void setStandardTackleZone() { + Vector tackleZone = new Vector(); + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + if(!(x == 0 && y == 0)){ + tackleZone.add(new int[]{x,y}); + } + } + } + invokeSetTackleZone(tackleZone); + } + + /** + * Makes an update of the Players Tacklezone, depending on his Condition + */ + public void updateActiveTackleZone() { + Vector activeTackleZone = new Vector(); + if(playerCondition == PlayerCondition.FINE && getPosition() != Pitch.THE_VOID){ + for (int[] tackleZoneField : tackleZone) { + //setzte tacklezonen, falls das betreffende feld auf dem platz liegt + if (getTeam().getMatch().getPitch().isOnField(new Vector2d(tackleZoneField[0] + (int) getPos().x, tackleZoneField[1] + (int) getPos().y))) { + activeTackleZone.addElement(team.getMatch().getPitch().getFields()[tackleZoneField[0] + (int) getPos().x][tackleZoneField[1] + (int) getPos().y]); + } + } + } + invokeSetActiveTackleZone(activeTackleZone); + } + + // INVOKING SETTERS + + public void invokeSetDescriptionLines(String[] descriptionLines) { + this.descriptionLines = descriptionLines; + + Object descriptionLinesFromScript = manager.invokeFunction("setDescriptionLines", this, descriptionLines); + if(descriptionLinesFromScript != null) this.descriptionLines = (String[]) descriptionLinesFromScript; + } + + public void invokeSetGe(int ge) { + this.ge = ge; + + Object geFromScript = manager.invokeFunction("setGe", this, ge); + if(geFromScript != null) this.ge = (Integer) geFromScript; + } + + public void invokeSetRs(int rs) { + this.rs = rs; + + Object rsFromScript = manager.invokeFunction("setRs", this, rs); + if(rsFromScript != null) this.rs = (Integer) rsFromScript; + } + + public void invokeSetSt(int st) { + this.st = st; + + Object stFromScript = manager.invokeFunction("setSt", this, st); + if(stFromScript != null) this.st = (Integer) stFromScript; + } + + public void invokeSetBe(int be) { + this.be = be; + + Object beFromScript = manager.invokeFunction("setBe", this, be); + if(beFromScript != null) this.be = (Integer) beFromScript; + } + + public void invokeSetRemainingBe(int remainingBe) { + this.remainingBe = remainingBe; + + Object remainingBeFromScript = manager.invokeFunction("setRemainingBe", this, remainingBe); + if(remainingBeFromScript != null) this.remainingBe = (Integer) remainingBeFromScript; + } + + public void invokeSetRules(Rule[] rules) { + this.rules = rules; + + Object rulesFromScript = manager.invokeFunction("setRules", this, (Object[]) rules); + if(rulesFromScript != null) this.rules = (Rule[]) rulesFromScript; + } + + public void invokeSetRedCard(boolean redCard){ + this.redCard = redCard; + + Object redCardFromScript = manager.invokeFunction("setRedCard", this, redCard); + if(redCardFromScript != null) this.redCard = (Boolean) redCardFromScript; + } + + @SuppressWarnings("unchecked") + public void invokeSetSpecialRules() { + this.specialRules = new SpecialRule[0]; + + Object specialRulesFromScript = manager.invokeFunction("setSpecialRules", this); + if(specialRulesFromScript != null) this.specialRules = (SpecialRule[]) specialRulesFromScript; + } + + @SuppressWarnings("unchecked") + public void invokeSetTackleZone(Vector tackleZone) { + this.tackleZone = tackleZone; + + Object tackleZoneFromScript = manager.invokeFunction("setTackleZone", this, tackleZone); + if(tackleZoneFromScript != null) this.tackleZone = (Vector) tackleZoneFromScript; + } + + @SuppressWarnings("unchecked") + public void invokeSetActiveTackleZone(Vector activeTackleZone) { + this.activeTackleZone = activeTackleZone; + + Object activeTackleZoneFromScript = manager.invokeFunction("setActiveTackleZone", this, activeTackleZone); + if(activeTackleZoneFromScript != null) this.activeTackleZone = (Vector) activeTackleZoneFromScript; + } + + public void invokeSetPlayerCondition(PlayerCondition playerCondition) { + this.playerCondition = playerCondition; + + Object playerConditionFromScript = manager.invokeFunction("setPlayerCondition", this, playerCondition); + if(playerConditionFromScript != null) this.playerCondition = (PlayerCondition) playerConditionFromScript; + + if(isHoldingBall()){ + if(invokeGetPlayerCondition() != PlayerCondition.FINE){ + invokeSetIsHoldingBall(false); + Vector2d newBallPos = new Vector2d(position.getPos()); + newBallPos.add(getTeam().getMatch().scatter()); + getTeam().getMatch().getPitch().setBallPos(newBallPos); + getTeam().getMatch().sendBallPos(); + } + } + if(team != null) updateActiveTackleZone(); // only update active tackle zone if this player is used in a team + } + + public void invokeSetPosition(int x, int y) { + invokeSetPosition(new Vector2d(x, y)); + } + + public void invokeSetPosition(Vector2d position) { + invokeSetPosition(getTeam().getMatch().getPitch().getFields()[(int) position.x][(int) position.y]); + } + + public void invokeSetPosition(PitchField position) { + if(this.position != null) this.position.adjustPlayer(null); // remove the player from the old field + this.position = position; + + Object positionFromScript = manager.invokeFunction("setPosition", this, position); + if(positionFromScript != null) this.position = (PitchField) positionFromScript; + + position.adjustPlayer(this); + if(isHoldingBall()) { + getTeam().getMatch().getPitch().adjustBallPos(this.getPos()); + if(getTeam().getMatch().getPitch().isInTouchdownZone(getTeam(), getPos())) + getTeam().getMatch().touchdown(getTeam()); + getTeam().getMatch().sendBallPos(); + } + if(team != null) updateActiveTackleZone(); // only update active tackle zone if this player is used in a team + if(!this.position.equals(Pitch.THE_VOID)) getTeam().getMatch().sendPlayer(this); + } + + public void invokeSetIsHoldingBall(boolean isHoldingBall) { + this.isHoldingBall = isHoldingBall; + + Object isHoldingBallFromScript = manager.invokeFunction("setIsHoldingBall", this, isHoldingBall); + if(isHoldingBallFromScript != null) this.isHoldingBall = (Boolean) isHoldingBallFromScript; + + if(isHoldingBall() && getTeam().getMatch().getPitch().isInTouchdownZone(getTeam(), getPos())){ + getTeam().getMatch().touchdown(getTeam()); + } + } + + public void invokeSetPrice(int price) { + this.price = price; + + Object priceFromScript = manager.invokeFunction("setPrice", this, price); + if(priceFromScript != null) this.price = (Integer) priceFromScript; + } + + // INVOKING ADJUSTERS + + public void invokeAdjustPosition(PitchField position) { + this.position = position; + + Object positionFromScript = manager.invokeFunction("adjustPosition", this, position); + if(positionFromScript != null) this.position = (PitchField) positionFromScript; + } + + void invokeAdjustTeam(Team team) { + this.team = team; + + Object teamFromScript = manager.invokeFunction("adjustTeam", this, team); + if(teamFromScript != null) this.team = (Team) teamFromScript; + } + + void invokeAdjustRemainingBe(int remainingBe) { + this.remainingBe = remainingBe; + + Object remainingBeFromScript = manager.invokeFunction("adjustRemainingBe", this, remainingBe); + if(remainingBeFromScript != null) this.remainingBe = (Integer) remainingBeFromScript; + } + + void invokeAdjustMinHeadcount(int minHeadcount) { + this.minHeadcount = minHeadcount; + + Object minHeadcountFromScript = manager.invokeFunction("adjustMinHeadcount", this, minHeadcount); + if(minHeadcountFromScript != null) this.minHeadcount = (Integer) minHeadcountFromScript; + } + + void invokeAdjustMaxHeadcount(int maxHeadcount) { + this.maxHeadcount = maxHeadcount; + + Object maxHeadcountFromScript = manager.invokeFunction("adjustMaxHeadcount", this, maxHeadcount); + if(maxHeadcountFromScript != null) this.maxHeadcount = (Integer) maxHeadcountFromScript; + } + + // INVOKING HELPERS + + public void invokeCountDownRemainingBe(int i) { + invokeSetRemainingBe(invokeGetRemainingBe() - i); + } + + public void invokeClearPosition() { + invokeSetPosition(Pitch.THE_VOID); + } + + public void invokeResetRemainingBe() { + invokeAdjustRemainingBe(invokeGetBe()); + } + + // INVOKING GETTERS + + public int invokeGetGe() { + int geToReturn = ge; + Object geFromScript = manager.invokeFunction("getGe", this); + if(geFromScript != null) geToReturn = (Integer) geFromScript; + return geToReturn; + } + + public int invokeGetRs() { + int rsToReturn = rs; + Object rsFromScript = manager.invokeFunction("getRs", this); + if(rsFromScript != null) rsToReturn = (Integer) rsFromScript; + return rsToReturn; + } + + public int invokeGetSt() { + int stToReturn = st; + Object stFromScript = manager.invokeFunction("getSt", this); + if(stFromScript != null) stToReturn = (Integer) stFromScript; + return stToReturn; + } + + public int invokeGetBe() { + int beToReturn = be; + Object beFromScript = manager.invokeFunction("getBe", this); + if(beFromScript != null) beToReturn = (Integer) beFromScript; + return beToReturn; + } + + public int invokeGetRemainingBe() { + int remainingBeToReturn = remainingBe; + Object remainingBeFromScript = manager.invokeFunction("getRemainingBe", this); + if(remainingBeFromScript != null) remainingBeToReturn = (Integer) remainingBeFromScript; + return remainingBeToReturn; + } + + public PlayerCondition invokeGetPlayerCondition() { + PlayerCondition playerConditionToReturn = playerCondition; + Object playerConditionFromScript = manager.invokeFunction("getPlayerCondition", this); + if(playerConditionFromScript != null) playerConditionToReturn = (PlayerCondition) playerConditionFromScript; + return playerConditionToReturn; + } + + public void invokeEventHappened(String eventString){ + manager.invokeFunction("eventHappened", this, eventString); + } + + public Object invokeFunctionByName(String functionName, Object... params){ + return manager.invokeFunction(functionName, this, params); + } + + public String toString() { + return getName() + " No. " + getId(); + } + + public int findPlayerIndex(){ + for(int i = 0; i < this.getTeam().getPlayers().size(); i++){ + if(this.getTeam().getPlayers().get(i) == this){ + return i; + } + } + return -1; + } + + // DIRECT GETTERS FOR GUI + + public boolean $GUIisKOInjuredOrDead() { + return playerCondition == PlayerCondition.INJURED + || playerCondition == PlayerCondition.KO + || playerCondition == PlayerCondition.DEAD; + } + + public String[] $GUIgetDescriptionLines() { + return descriptionLines; + } + + public PlayerCondition $GUIgetPlayerCondition() { + return playerCondition; + } + + public int $GUIgetGe() { + return ge; + } + + public int $GUIgetRs() { + return rs; + } + + public int $GUIgetSt() { + return st; + } + + public int $GUIgetBe() { + return be; + } + + public int $GUIgetRemainingBe() { + return remainingBe; + } + + // NORMAL GETTERS AND SETTERS + + public GameController getMatch() { + if(team == null) { + System.out.println("WARNING: Tried to getMatch() on a player without team. Create usable player with getCloneForTeam()."); + return null; + } else return getTeam().getMatch(); + } + + public Team getTeam() { + if(team == null) { + System.out.println("WARNING: Tried to getTeam() on a player without team. Create usable player with getCloneForTeam()."); + return null; + } else return team; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BufferedImage getSpriteR() { + return spriteR; + } + + public void setSpriteR(BufferedImage spriteR) { + this.spriteR = spriteR; + } + + public BufferedImage getSpriteL() { + return spriteL; + } + + public void setSpriteL(BufferedImage spriteL) { + this.spriteL = spriteL; + } + + public void setId() { + this.id = getTeam().countUpIdCounter(); + } + + public int getId() { + return id; + } + + public int getPrice() { + return price; + } + + public int getMinHeadcount() { + return minHeadcount; + } + + public int getMaxHeadcount() { + return maxHeadcount; + } + + public boolean getRedCard(){ + return redCard; + } + + public Rule getRule(int i) { + if(i < 0 || i > 4) { + getTeam().getMatch().getParent().log(Level.WARNING, "Tried to get a standard rule that doesn't exist. Returning null."); + return null; + } + else return rules[i]; + } + + public SpecialRule[] getSpecialRules() { + return specialRules; + } + + public SpecialRule getSpecialRule(int i){ + return specialRules[i]; + } + + public Vector getTackleZone() { + return tackleZone; + } + + public Vector getActiveTackleZone() { + return activeTackleZone; + } + + public PitchField getPosition() { + return position; + } + + public Vector2d getPos() { + return position.getPos(); + } + + public boolean isHoldingBall() { + return isHoldingBall; + } + + public String getSpecialStat(String key) { + return specialStats.get(key); + } + + public void addSpecialStat(String key, String value) { + specialStats.put(key, value); + } + + public void removeSpecialStat(String key) { + specialStats.remove(key); + } + + public void setSpecialStat(String key, String value){ + removeSpecialStat(key); + addSpecialStat(key, value); + } + +} diff --git a/src/gameLogic/PlayerCondition.java b/src/gameLogic/PlayerCondition.java new file mode 100644 index 0000000..464a01e --- /dev/null +++ b/src/gameLogic/PlayerCondition.java @@ -0,0 +1,135 @@ +package gameLogic; + +import java.util.Random; + +/** + * The conditions a player can be in. + */ +public enum PlayerCondition { + FINE, PRONE, STUNNED, KO, INJURED, DEAD; + + public String randomConditionDescription(Player player) { + String[] strings; + switch(player.invokeGetPlayerCondition()) { + case FINE: + Weather weather = player.getMatch().getWeather(); + strings = new String[]{ + "Ready to rumble!", + "Ready to go!", + "Let's go!", + "Let's win this game!", + "WE MUST WIN", + "Let's go "+player.getName()+"s!", + "Come on "+player.getTeam().getName()+"!", + "Let's go "+player.getTeam().getName()+"!", + "So close!", + "Heh, we're gonna win anyway.", + "Please no touchdowns on our side!", + "God, am I hungry!", + "Can I eat this Puffin?", + player.getName()+"s totally rock!", + player.getName()+"s are the best!", + player.getName()+"s will blow you away!", + "We'll blow them away!", + player.getTeam().getName().length() > 0 ? "GIVE ME A "+player.getTeam().getName().substring(0, 1) : "Why does our team have no name?!", + player.getTeam().getName()+" will blow you away!", + player.getTeam().getName()+" always win!", + player.getTeam().getName()+" are epic!", + player.getTeam().getName()+" for the win!", + player.getTeam().getMatch().getOpposingTeam(player.getTeam()).getName()+" can suck it!", + player.getTeam().getMatch().getOpposingTeam(player.getTeam()).getName()+" are just morons!", + player.getTeam().getMatch().getOpposingTeam(player.getTeam()).getName()+" can't even pee straight!", + "We'll crush "+player.getTeam().getMatch().getOpposingTeam(player.getTeam()).getName()+"!", + "We'll totally murder "+player.getTeam().getMatch().getOpposingTeam(player.getTeam()).getName()+"!", + "I'M HUNGRY!", + "I urgently need to clobber someone!", + "I urgently need to batter someone!", + "Those greenhorns gonna eat mud.", + "I like fish.", + "I don't like fish", + "Ey, "+player.getTeam().getCoach().getName()+", fuck off!", + "Ey, "+player.getTeam().getCoach().getName()+", get on your seat!", + "Ey, "+player.getTeam().getCoach().getName()+", take a walk!", + weather == Weather.BLIZZARD ? "I'm gonna freeze in this cold!" : "", + weather == Weather.BLIZZARD ? "It's so cold!" : "", + weather == Weather.BLIZZARD ? "What ugly weather!" : "", + weather == Weather.BLIZZARD ? "Oh look! Snow!" : "", + weather == Weather.BLIZZARD ? "SNOWBALL FIGHT!" : "", + weather == Weather.BLIZZARD ? "Snowball fight is on!" : "", + weather == Weather.BLIZZARD ? "I wanna make a snowman!" : "", + weather == Weather.BLIZZARD ? "I wanna make a snowgirl!" : "", + weather == Weather.POURING_RAIN ? "I don't like rain!" : "", + weather == Weather.POURING_RAIN ? "Ah, I love rain!" : "", + weather == Weather.POURING_RAIN ? "Rain!" : "", + weather == Weather.POURING_RAIN ? "It's raining man!" : "", + weather == Weather.POURING_RAIN ? "Crap rain!" : "", + weather == Weather.POURING_RAIN ? "Stop raining already!" : "", + weather == Weather.POURING_RAIN ? "Petrus fuck off!" : "", + weather == Weather.POURING_RAIN ? "I'm soaking wet!" : "", + weather == Weather.VERY_SUNNY ? "It's so hot!" : "", + weather == Weather.VERY_SUNNY ? "Look at this weather!" : "", + weather == Weather.VERY_SUNNY ? "Boy is it hot!" : "", + weather == Weather.VERY_SUNNY ? "HOT HOT HOT!" : "", + weather == Weather.VERY_SUNNY ? "The sun is shining." : "", + weather == Weather.VERY_SUNNY ? "What a beautiful sun!" : "", + weather == Weather.VERY_SUNNY ? "The sun is yellow!" : "", + weather == Weather.VERY_SUNNY ? "I have a sunburn!" : "", + weather == Weather.SWELTERING_HEAT ? "This sun really is burning me!" : "", + weather == Weather.SWELTERING_HEAT ? "This heat, I think I'm fainting." : "", + weather == Weather.SWELTERING_HEAT ? "Yeah! Sun!" : "", + weather == Weather.SWELTERING_HEAT ? "I hope I won't faint in this heat" : "", + weather == Weather.SWELTERING_HEAT ? "Who invented this weather system?! I'm dying in this heat!" : "", + weather == Weather.SWELTERING_HEAT ? "Matias I hate you for this heat!" : "", + weather == Weather.SWELTERING_HEAT ? "Whoever made it so hot is an arse!" : "", + weather == Weather.SWELTERING_HEAT ? "Hhhhhhhhhhhh" : "", + }; + return randomStringFromArray(strings, player.hashCode()); + case PRONE: + strings = new String[]{ + "Prone", + "Prone as a pear", + "Prone already", + "Bananaprone", + "Prone as always" + }; + return randomStringFromArray(strings, player.hashCode()); + case STUNNED: + strings = new String[]{ + "Ugh", + "Grks", + "Phhk", + "Grgrgll", + "Bhhh" + }; + return randomStringFromArray(strings, player.hashCode()); + case KO: + return "K.O."; + case INJURED: + strings = new String[]{ + "It hurst!", + "Ouch!", + "Ooh, my knee!" + }; + return randomStringFromArray(strings, player.hashCode()); + case DEAD: + strings = new String[]{ + "R.I.P.", + "Dead!", + "D E A D" + }; + return randomStringFromArray(strings, player.hashCode()); + default: + return "In outer space!"; + } + } + + private String randomStringFromArray(String[] strings, int seed) { + String returnString; + int add = 0; + do { + returnString = strings[(new Random(seed+add)).nextInt(strings.length-1)]; + add++; + } while(returnString.length() == 0); + return returnString; + } +} \ No newline at end of file diff --git a/src/gameLogic/SBOutOfFieldException.java b/src/gameLogic/SBOutOfFieldException.java new file mode 100644 index 0000000..c13153d --- /dev/null +++ b/src/gameLogic/SBOutOfFieldException.java @@ -0,0 +1,14 @@ +package gameLogic; + + +import util.SBException; + +/** + * Thrown when fields outside the pitch are accessed. + * Created by matias on 25.3.15. + */ +public class SBOutOfFieldException extends SBException { + public SBOutOfFieldException(String s){ + super(s); + } +} diff --git a/src/gameLogic/Team.java b/src/gameLogic/Team.java new file mode 100644 index 0000000..865b7f8 --- /dev/null +++ b/src/gameLogic/Team.java @@ -0,0 +1,252 @@ +package gameLogic; + +import java.util.Vector; +import java.util.logging.Level; + +import javax.vecmath.Vector2d; + +import network.SBProtocolParameterArray; +import server.logic.User; +import util.SBLogger; + +/** + * A team with players. + */ +public class Team { + static final SBLogger L = new SBLogger(Team.class.getName(), util.SBLogger.LOG_LEVEL); + + public static final int MAX_TEAM_SIZE = 16; + public static final int MIN_TEAM_SIZE = 3; + + private String type; + protected String name; + protected Vector availablePlayers = new Vector(); + private Vector players = new Vector(); + private GameController match; + private User coach; + private int idCounter = 0; + private Player movingPlayer = null; + private boolean blitz = true; //Determines whether the team can still perform a Blitz during the current turn + private boolean pass = true; //Determines whether the team can still perform a Pass during the current turn + private boolean foul = true; //Determines whether the team can still perform a foul during the current turn + /** + * This constructor is used to create a team from a team blueprint. + * @param t The team blueprint to use. + * @param name The name of the team. + * @param match The match in which the team participates. + * @param coach The coach user of the team. + */ + public Team(Team t, String name, GameController match, User coach){ + this.type = t.type; + this.availablePlayers = t.availablePlayers; + this.name = name; + this.coach = coach; + adjustMatch(match); + } + + /** + * This constructor is used by the team manager to create available team blueprints. + * @param type The type of the team. + */ + public Team(String type){ + this.type = type; + } + + /** + * This constructor is used to create empty and unusable teams. + * @param match The match for this team. + * @param coach The coach of this team. + */ + public Team(GameController match, User coach) { + this.name = coolTeamName(); + this.type = ""; + this.coach = coach; + adjustMatch(match); + } + + public int getTacklezones(int x, int y){ + return getTacklezones(new Vector2d(x, y)); + } + + public int getTacklezones(Vector2d pos){ + Vector tacklezones = new Vector(getTacklezones()); + int n = 0; + for(PitchField f: tacklezones){ + if(f.getPos().equals(pos)){ + n++; + } + } + return n; + } + + /** + * Finds all the Tacklezones of the team (fields that are in multiple tacklezones are multiple times in the return Vector) + * @return The Vector<PitchField> that contains all the Tacklezones of the team t. + */ + public Vector getTacklezones(){ + Vector returnFields = new Vector(); + for (Player player : players) { + for (PitchField f: player.getActiveTackleZone()) { + returnFields.add(f); + } + } + return returnFields; + } + + /** + * The team manager uses this method to add players from the team dir to the available players of this team. + * @param p The player to add to the available players of this team. + */ + public void addAvailablePlayer(Player p) { + availablePlayers.addElement(p); + } + + /** + * Adds a Player (from the available Players) to the Team and throws an exception, if the team is already full + * @param p The Player that should be added + */ + public void addPlayer(Player p){//TODO check whether a Player is allowed to put this player in his team + if(players.size() < MAX_TEAM_SIZE){ + players.addElement(p.getCloneForTeam(this)); + players.get(players.size()-1).invokeAdjustTeam(this); + players.get(players.size()-1).setId(); + }else{ + L.log(Level.WARNING, "Too many players, cannot add more Players to this team."); + } + } + + /** + * removes all the Players in one team, but keeps the available Players + */ + public void clearPlayers(){ + players.removeAllElements(); + } + + /** + * counts up the idCounter of the team + * @return the new Value of the idCounter + */ + public int countUpIdCounter(){ + idCounter += 1; + return idCounter; + } + + /** + * Return a cool name. + * @return A cool name. + */ + public static String coolTeamName(){ + String[] names = {"Funky Town Monkey Pimps", + "Hugh Jass Construction", + "Super Optimistic Noodle Squad", + "The Untouchables", + "Space Monkey Mafia", + "Viscious and Delicious", + "Cranium Krusherz", + "Spinal Tappers", + "Axis of Ignorance", + "The Monstars", + "FUCKIN BEASTS", + "End Zone Chasers", + "Touchdown Razors", + "Stunned Punts", + "Swift Kick in the Grass", + "The Mighty Mushroom Bruisers", + "Superbowl Brawlers", + "Cigar Smoking Icebears", + "Blue Balls of Destiny", + "Ball Busters", + "Artful Dodgers", + "Speedy Penguins And Co.", + "Spikey Horns", + "The Nullpointerexceptions", + "Stay Away From Us", + "The Loosers", + "Laughing Hyenas", + "Blizzard Blockers", + "Maleficient Marilyns", + "Crazy Clowns", + "The Thousand Mountain Crusher", + "Roaring Grizzlies", + "Dancing Umbrellas", + "Shrimp Army", + "Dark Commando Squad", + "Night Tigers", + "Bottle Lightning", + "Sorrowful Seamonsters", + "Hot Heroes", + "Wind Lightning", + "Lost Lions", + "Skull Bandits", + "Demolition Lunatics", + "Flash Pirates", + "Hot Falcons", + "Scorpion Crushers", + "Wind Kickers", + "Cyborg Mavericks", + "Lightning Bulls", + "Pink Heroes", + "Seagrazers", + "Banana Dancers", + "Pizza Lovers", + "Beach Buddies", + "Hippie Flowers", + "Hanging Trees", + "Chocolate flips", + "Mourning Monkeys"}; + return names[(int)(Math.random()*names.length)]; + } + + public void invokeEventHappenedForAllPlayers(String eventString){ + for(Player p: getPlayers()){ + p.invokeEventHappened(eventString); + } + } + + public int findPlayerIndex(Player player){ + for(int i = 0; i < getPlayers().size(); i++){ + if(player == getPlayers().get(i)){ + return i; + } + } + return -1; + } + + public void clearMovingPlayer(){ + movingPlayer = null; + } + + public String toString(){ + return "Team: " + getName(); + } + + //methods only to adjust values, that shouldnt be changed directly! + public void adjustMatch(GameController match) {this.match = match;} + + //getters + public Player getAvailablePlayer(int index) { + if(index >= 0 && index < availablePlayers.size()) return availablePlayers.get(index); + else return null; + } + public Vector getAvailablePlayers(){return availablePlayers;} + public String getType(){return type;} + public String getName(){return name;} + public Vector getPlayers(){return players;} + public boolean getBlitz(){return blitz;} + public boolean getPass(){return pass;} + public boolean getFoul(){return foul;} + public GameController getMatch(){return match;} + public User getCoach(){return coach;} + public Player getMovingPlayer(){return movingPlayer;} + + //setters + public void setBlitz(boolean b){blitz = b;} + public void setPass(boolean p){pass = p;} + public void setFoul(boolean f){foul=f;} + public void setType(String s){type = s;} + public void setMatch(GameController m){match = m;} + public void setName(String n){name = n;} + public void setCoach(User u){coach = u;} + public void setMovingPlayer(Player p){movingPlayer = p;} +} + diff --git a/src/gameLogic/TeamManager.java b/src/gameLogic/TeamManager.java new file mode 100644 index 0000000..77af6ab --- /dev/null +++ b/src/gameLogic/TeamManager.java @@ -0,0 +1,253 @@ +package gameLogic; + +import server.Server; +import util.ResourceManager; +import util.SBApplication; + +import javax.imageio.ImageIO; +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.logging.Level; + +/** + * The manager that reads, processes and creates teams from external files. + */ +public class TeamManager { + + private SBApplication parent; + private static final ScriptEngineManager manager = new ScriptEngineManager(); + private HashMap invocables = new HashMap(); + private Vector teamBlueprints = new Vector(); + private String loadingMessage = "Loading teams"; + + /** + * Create a team manager that reads teams from a remote zip file. + * @param parent The parent of the team manager. + * @param teamsURL The URL to read the teams zip from. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public TeamManager(SBApplication parent, URL teamsURL) { + + this.parent = parent; + + // load team files + File tempFile = new File("data/"+(int)(Math.random()*1000)+"teams.zip"), destinationFile; + if(this.parent instanceof Server) destinationFile = new File("data/server"); + else destinationFile = new File("data/client"); + + if(teamsURL != null) { // url exists + DataInputStream teamsStream = null; + try { + teamsStream = new DataInputStream(teamsURL.openStream()); + } catch (IOException e) { + e.printStackTrace(); + } + + if(teamsStream != null) { // stream was successfully opened + try { + tempFile.delete(); // delete old temp file + Files.copy(teamsStream, Paths.get(tempFile.toURI())); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + destinationFile.delete(); // delete old teams + ResourceManager.extractZipFile(tempFile, destinationFile); + tempFile.delete(); // delete temp file + + setUp(new File(destinationFile.getPath()+"/teams")); + + } + + /** + * Create a team manager that reads teams from a local directory. + * @param parent The parent of the team manager. + * @param teamsDir The directory to read the teams from. + */ + public TeamManager(SBApplication parent, File teamsDir) { + + this.parent = parent; + setUp(teamsDir); + + } + + /** + * Constructor to create team manager without loading teams if teams folder was not found. + * @param parent The parent of the team manager. + */ + public TeamManager(SBApplication parent) { + + this.parent = parent; + + } + + private void setUp(File teamsDir) { + + String filename = "NOTAFILE"; + + try { + + HashMap teamImages = new HashMap(); + + // handle team files + if(teamsDir != null) { + //noinspection ConstantConditions + for (File teamDir: teamsDir.listFiles()) { + if (teamDir.isDirectory()) { + String teamType = teamDir.getName(); + Team createdTeam = createTeam(teamType); + + //noinspection ConstantConditions + for (File teamFile : teamDir.listFiles()) { + filename = addSpacesToName(teamFile.getName()); + + if (filename.toLowerCase().endsWith(".js")) { // is player js file + + // create new engine for script + ScriptEngine engine = manager.getEngineByName("JS"); + engine.eval(new FileReader(teamFile.getAbsolutePath())); + // add invocable for script and create player + String name = addSpacesToName(teamFile.getName().replaceAll("\\.[^\\.]+$", "")); + invocables.put(name, (Invocable) engine); + createPlayer(name, createdTeam); + + } else if (filename.toLowerCase().endsWith(".png")) { // is image + + try { + String imageName = filename.substring(0, filename.length() - ".png".length()); + teamImages.put(imageName, ImageIO.read(teamFile)); + } catch (IOException e) { + getParent().log(Level.WARNING, "IOException while reading team image " + teamFile.getName() + "."); + } + + } + } + + teamBlueprints.add(createdTeam); + } + } + + // handle player images + int w = ResourceManager.IMAGE_WIDTH, h = ResourceManager.IMAGE_HEIGHT; + for (Team team : teamBlueprints) { + for (Player player : team.availablePlayers) { + + // get sprites + BufferedImage imageR = teamImages.get(player.getName() + " R"); + BufferedImage imageL = teamImages.get(player.getName() + " L"); + + // resize if needed and add as sprites + if (imageR != null) { + if (imageR.getWidth() != w || imageR.getHeight() != h) imageR = resize(imageR, w, h); + player.setSpriteR(imageR); + } else player.setSpriteR(ResourceManager.DEFAULT_PLAYER_R); + if (imageL != null) { + if (imageL.getWidth() != w || imageL.getHeight() != h) imageL = resize(imageL, w, h); + player.setSpriteL(imageL); + } else player.setSpriteL(ResourceManager.DEFAULT_PLAYER_L); + + } + } + } else { + getParent().log(Level.SEVERE, "Could not load available teams. Make sure you are connected to the Internet."); + } + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + e.printStackTrace(); + } catch (ScriptException e) { + getParent().log(Level.SEVERE, "Exception in script " + filename + "."); + e.printStackTrace(); + System.exit(-1); + } + + } + + private Team createTeam(String type) { + getParent().log(Level.INFO, (loadingMessage += "..")); + try { Thread.sleep(100); } catch (InterruptedException ignored) {} + return new Team(type); + } + + private void createPlayer(String name, Team teamCreatedFor) { + getParent().log(Level.INFO, (loadingMessage += "..")); + try { Thread.sleep(100); } catch (InterruptedException ignored) {} + teamCreatedFor.addAvailablePlayer(new Player(name, this)); + } + + public Object invokeFunction(String functionName, Player actor, Object... params) { + Invocable invocable = invocables.get(actor.getName()); + try { + Object[] paramsWithActor = null; + if(params != null) { + paramsWithActor = new Object[params.length + 1]; + paramsWithActor[0] = actor; + System.arraycopy(params, 0, paramsWithActor, 1, params.length); + } + return invocable.invokeFunction(functionName, paramsWithActor); + } catch (ScriptException e) { // script caused exception + getParent().log(Level.SEVERE, "Exception in script of " + actor.getName() + "."); + if(params != null) { + getParent().log(Level.SEVERE, "Params:"); + for(Object param : params) + getParent().log(Level.SEVERE, param.toString()); + } + e.printStackTrace(); + System.exit(-1); + } catch (NoSuchMethodException ignored) { // script didn't contain method + // Removed warnings to prevent overflowing the logs + // getParent().log(Level.WARNING, "Script of " + actor.getName() + " didn't contain method " + functionName + "."); + // e.printStackTrace(); + } catch (NullPointerException e) { // invocable didn't exist + e.printStackTrace(); + } + return null; + } + + // HELPERS + + /** + * Adds a space before every capital letter in names. + * @param name The name to "spaceify". + * @return The "spaceified" name. + */ + private String addSpacesToName(String name) { + // insert spaces between + String[] nameSplitted = name.split("(?<=.)(?=[A-Z])"); + name = nameSplitted[0]; + for(int i = 1; i < nameSplitted.length; i++) + name += " " + nameSplitted[i]; + return name; + } + + private BufferedImage resize(BufferedImage image, int w, int h) { + BufferedImage resizedImage = new BufferedImage(w, h, image.getType()); + Graphics2D g = resizedImage.createGraphics(); + g.drawImage(image, 0, 0, w, h, null); + g.dispose(); + return resizedImage; + } + + // GETTERS & SETTERS + + public SBApplication getParent() { + return parent; + } + + public Vector getTeamBlueprints() { + return teamBlueprints; + } + +} diff --git a/src/gameLogic/Weather.java b/src/gameLogic/Weather.java new file mode 100644 index 0000000..d68623d --- /dev/null +++ b/src/gameLogic/Weather.java @@ -0,0 +1,18 @@ +package gameLogic; + +public enum Weather { + SWELTERING_HEAT("Sweltering Heat"), + VERY_SUNNY("Very Sunny"), + NICE("Nice"), + POURING_RAIN("Pouring Rain"), + BLIZZARD("Blizzard"); + String niceString; + + Weather(String niceString) { + this.niceString = niceString; + } + + public String toNiceString() { + return niceString; + } +} diff --git a/src/gameLogic/dice/BlockDie.java b/src/gameLogic/dice/BlockDie.java new file mode 100644 index 0000000..81b871c --- /dev/null +++ b/src/gameLogic/dice/BlockDie.java @@ -0,0 +1,64 @@ +package gameLogic.dice; + +import util.ResourceManager; + +import java.awt.image.BufferedImage; + +/** + * The die thrown while blocking + */ +public class BlockDie implements Die { + private int throwCounter = 0; + /** + * @return Returns a int from 1 to 5 where the numbers mean 1: "Attacker Down", 2: "Both Down", 3: "Pushed", 4: "Defender Stumbles", 5: "Defender Down" + */ + public int throwDie() { + /*int[] throwsForPresentation = {5, 3, 3, 1, 3, 5, 1, 3, 5, 4, 3, 2, 3, 4, 1, 1, 3, 3, 3, 5, 3, 4, 5, 2, 4, 4}; + if(throwCounter < throwsForPresentation.length){ + int i = throwsForPresentation[throwCounter]; + throwCounter++; + System.out.println("DB: " + i); + return i; + }*/ + int i = rand.nextInt(6) + 1; + if(i == 6){ + i = 3; + } +// System.out.println("DB: " + i); + return i; + } + + public static String toName(int side) { + switch (side) { + case 1: + return "Attacker Down"; + case 2: + return "Both Down"; + case 3: + return "Pushed"; + case 4: + return "Defender Stumbles"; + case 5: + return "Defender Down"; + default: + return "Strange, I don't know this die."; + } + } + + public static BufferedImage getImageFromSide(int side) { + switch (side) { + case 1: + return ResourceManager.DIE_ATTACKER_DOWN; + case 2: + return ResourceManager.DIE_BOTH_DOWN; + case 3: + return ResourceManager.DIE_PUSHED; + case 4: + return ResourceManager.DIE_DEFENDER_STUMBLES; + case 5: + return ResourceManager.DIE_DEFENDER_DOWN; + default: + return null; + } + } +} diff --git a/src/gameLogic/dice/Die.java b/src/gameLogic/dice/Die.java new file mode 100644 index 0000000..768bd22 --- /dev/null +++ b/src/gameLogic/dice/Die.java @@ -0,0 +1,11 @@ +package gameLogic.dice; + +import java.util.Random; + +/** + * Interface for all dice + */ +public interface Die { + Random rand = new Random(); + int throwDie(); +} diff --git a/src/gameLogic/dice/EightSidedDie.java b/src/gameLogic/dice/EightSidedDie.java new file mode 100644 index 0000000..63d9b30 --- /dev/null +++ b/src/gameLogic/dice/EightSidedDie.java @@ -0,0 +1,23 @@ +package gameLogic.dice; + +/** + * A die with eight sides + */ +public class EightSidedDie implements Die { + private int throwCounter = 0; + /** + * @return returns a int from 1 to 8 + */ + public int throwDie(){ + /*int[] throwsForPresentation = {3, 3, 6}; + if(throwCounter < throwsForPresentation.length){ + int i = throwsForPresentation[throwCounter]; + throwCounter++; + System.out.println("D8: " + i); + return i; + }*/ + int i = rand.nextInt(8) + 1; +// System.out.println("D8: " + i); + return i; + } +} diff --git a/src/gameLogic/dice/SixSidedDie.java b/src/gameLogic/dice/SixSidedDie.java new file mode 100644 index 0000000..cb0c14d --- /dev/null +++ b/src/gameLogic/dice/SixSidedDie.java @@ -0,0 +1,24 @@ +package gameLogic.dice; + +/** + * A die with six sides + */ +public class SixSidedDie implements Die { + int throwCounter = 0; + /** + * @return returns a int from 1 to 6 + */ + public int throwDie(){ + /*int[] throwsForPresentation = {5,5,4,4,5,2,2,5,6,2,6,1,1,4,3,2,4,2,2,2,3,5,4,1,3,6,1,2,2,3,1,1,5,4,2,2,4,5,4,6,2,1,3,4,3,5,2,1,3, 3, 3, 5, 1, 2, 3, 6, 4, 5, 6, 6, 6, 1, 4, 6, 1, 1, 2, 1, 2, 3, 4, 5, 1, 1, 5, 1, 2, 4, }; + if(throwCounter < throwsForPresentation.length){ + int i = throwsForPresentation[throwCounter]; + throwCounter++; + System.out.println("D6: " + i); + return i; + }*/ + int i = rand.nextInt(6) + 1; +// System.out.println("D6: " + i); + return i; + + } +} diff --git a/src/gameLogic/dice/ThreeSidedDie.java b/src/gameLogic/dice/ThreeSidedDie.java new file mode 100644 index 0000000..ed6a06b --- /dev/null +++ b/src/gameLogic/dice/ThreeSidedDie.java @@ -0,0 +1,23 @@ +package gameLogic.dice; + +/** + * A die with three sides + */ +public class ThreeSidedDie implements Die { + private int throwCounter = 0; + /** + * @return returns a int from 1 to 3 + */ + public int throwDie(){ + /*int[] throwsForPresentation = {1, 2, 3, 2, 3, 1, 2}; + if(throwCounter < throwsForPresentation.length){ + int i = throwsForPresentation[throwCounter]; + throwCounter++; + System.out.println("D3: " + i); + return i; + }*/ + int i = rand.nextInt(3) + 1; +// System.out.println("D3: " + i); + return i; + } +} diff --git a/src/gameLogic/rules/Rule.java b/src/gameLogic/rules/Rule.java new file mode 100644 index 0000000..606d85a --- /dev/null +++ b/src/gameLogic/rules/Rule.java @@ -0,0 +1,71 @@ +package gameLogic.rules; + +import java.util.UUID; + +import server.logic.User; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameterArray; +import gameLogic.*; + +/** + * An abstract game rule. + */ +public abstract class Rule { + protected Player actor; + + public boolean geTest(int mod) { + int die = actor.getMatch().d6.throwDie(); + return die == 6 || die != 1 && mod + die + actor.invokeGetGe() >= 7; + } + + public Rule(Player actor) { + setActor(actor); + } + public void returnFailureMessage(SBProtocolMessage message, String... parameters){ + message.returnFailureMessage(getMatch().getParent().UID, new SBProtocolParameterArray(parameters)); + } + public void returnSuccessMessage(SBProtocolMessage message, String... parameters){ + message.returnSuccessMessage(getMatch().getParent().UID, new SBProtocolParameterArray(parameters)); + } + public void sendMessage(SBProtocolMessage message, SBProtocolCommand command, String... parameters){ + sendMessage(message.getUID(), command, parameters); + } + public void sendMessage(UUID UID, SBProtocolCommand command, String... parameters){ + if(UID.equals(getMatch().getUser(0).getUID())){ + sendMessage(getMatch().getUser(0), command, parameters); + }else if(UID.equals(getMatch().getUser(1).getUID())){ + sendMessage(getMatch().getUser(1), command, parameters); + } + } + public void sendMessage(User destinationUser, SBProtocolCommand command, SBProtocolParameterArray parameters){ + getMatch().sendMessage(destinationUser, command, parameters); + } + public void sendMessage(User destinationUser, SBProtocolCommand command, String... parameters){ + actor.getMatch().sendMessage(destinationUser, command, parameters); + } + public void sendMessageShowMe(String a, String b){ + sendMessage(getMatch().getUser(0), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, a, b); + sendMessage(getMatch().getUser(1), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, a, b); + } + public void sendMessageShowMe(User user, String a, String b){ + sendMessage(user, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SHOW_ME, a, b); + } + + public void checkForMovingPlayer(Player actor){ + if(actor.getTeam().getMovingPlayer() != null){ + if(actor.getTeam().getMovingPlayer() != actor){ + actor.getTeam().getMovingPlayer().invokeSetRemainingBe(0); + actor.getTeam().setMovingPlayer(actor); + } + }else{ + actor.getTeam().setMovingPlayer(actor); + } + } + + // GETTERS & SETTERS + + public void setActor(Player actor){this.actor = actor;} + public Player getActor(){return actor;} + public GameController getMatch(){return actor.getMatch();} +} diff --git a/src/gameLogic/rules/RuleBlock.java b/src/gameLogic/rules/RuleBlock.java new file mode 100644 index 0000000..be05519 --- /dev/null +++ b/src/gameLogic/rules/RuleBlock.java @@ -0,0 +1,356 @@ +package gameLogic.rules; + +import java.util.Vector; + +import javax.vecmath.Vector2d; + +import GUI.SBColor; + +import java.awt.Color; + +import gameLogic.*; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameter; +import network.SBProtocolParameterArray; +import server.logic.ServerMatch; + +/** + * The game rule for blocking stuff. + */ +public class RuleBlock extends Rule { + public static final String ENEMY_DOWN = "YOUR ENEMY IS "; + public static final String YOU_SUCK = "YOU ARE "; + public static final String ENEMY_FOULED = "YOU HAVE BEATEN UP YOUR ENEMY. YOUR ENEMY IS "; + + SBProtocolMessage message; + //Player defender; + + public RuleBlock(Player actor) { + super(actor); + } + /** + * switch-case: which method gets executed + * @param result which method is chosen + * @param defender defending Player + * @param message SBProtocolMessage + */ + public boolean reaction(int result, Player defender, SBProtocolMessage message){ + switch(result){ + case 1: + sendMessageShowMe("Block result", "Attacker down!"); + return attackerDown(message); + case 2: + sendMessageShowMe("Block result", "Both down!"); + return bothDown(message, defender); + case 3: + sendMessageShowMe("Block result", "Pushed!"); + return pushed(message, defender); + case 4: + sendMessageShowMe("Block result", "Defender Stumbles!"); + return defenderStumbles(message, defender); + case 5: + sendMessageShowMe("Block result", "Defender Down!"); + return defenderDown(message, defender); + default: return false; + } + } + + /** + * the main blocking method + * @param message the message that initiated the block + * @param defender the blocked player + */ + public void apply(SBProtocolMessage message, Player defender) { + checkForMovingPlayer(actor); + boolean playerIsFine = (actor.getMatch().getPitch().isOnField(actor.getPos()) && actor.invokeGetPlayerCondition() == PlayerCondition.FINE); + boolean playerHasRemainingBe = (actor.invokeGetRemainingBe() > 0); + boolean playerWantsToBlitz = (actor.invokeGetRemainingBe() != actor.invokeGetBe()); + boolean teamCanBlitz = actor.getTeam().getBlitz(); + if(playerIsFine && playerHasRemainingBe){ + if((!playerWantsToBlitz || teamCanBlitz) || defender.invokeGetPlayerCondition() != PlayerCondition.FINE){ + // Set fields for defender and message so they are available in attackerDown() and bothDown() + this.message = message; + if (getMatch().getPitch().isAdjacent(actor.getPosition(), defender.getPosition()) + && actor.invokeGetPlayerCondition() == PlayerCondition.FINE){ + if(playerWantsToBlitz){ + actor.getTeam().setBlitz(false); + actor.invokeCountDownRemainingBe(1); + } + if(defender.invokeGetPlayerCondition()== PlayerCondition.FINE){ + throwDice(message, defender); + if(!playerWantsToBlitz){actor.invokeSetRemainingBe(0);} + }else if(defender.invokeGetPlayerCondition()== PlayerCondition.PRONE || defender.invokeGetPlayerCondition()== PlayerCondition.STUNNED){ + beatHim(defender, message, playerWantsToBlitz); + } + } else returnFailureMessage(message, SBProtocolMessage.FAILD_BLOCKING_NOT_POSSIBLE); + } else returnFailureMessage(message, SBProtocolMessage.FAILD_NO_BLITZ_LEFT); + } else returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_CANNOT_TAKE_ACTION); + + } +/** + * The defender moves one field back and falls down even if he has the specialrule block + * @param defender defending Player + * @param message SBProtocolMessage + */ + protected boolean defenderDown(SBProtocolMessage message, Player defender) { + ((RuleBlock)defender.getRule(1)).beingBlockedDefenderDown(message, actor, defender, 0, 0); + return true; + } +/** + * The defender moves one field back and falls down. the actor moves on the defenders startpoint. when the defender has de specialrule block, the defender is only pushed. + * @param defender defending Player + * @param message SBProtocolMessage + */ + protected boolean defenderStumbles(SBProtocolMessage message, Player defender) { + ((RuleBlock)defender.getRule(1)).beingBlockedDefenderStumbles(message, actor, defender, 0, 0); + return true; + } +/** + * The defender moves one field back and the actor moves on the defenders startpoint. + * @param defender defending Player + * @param message SBProtocolMessage + */ + protected boolean pushed(SBProtocolMessage message, Player defender) { + ((RuleBlock)defender.getRule(1)).beingBlockedPushed(message, actor, defender.getPosition()); + return true; + } +/** + * The actor falls down. + * @param message SBProtocolMessage + */ + protected boolean attackerDown(SBProtocolMessage message) { + playerDown(message, actor, YOU_SUCK); + clearHighlightFields(); + return true; + } +/** + * Both fall down except they have the specialrule block. + * @param defender defending Player + * @param message SBProtocolMessage + */ + protected boolean bothDown(SBProtocolMessage message, Player defender) { + ((RuleBlock)defender.getRule(1)).beingBlockedBothDown(message, 0, 0); + playerDown(message, actor, YOU_SUCK); + clearHighlightFields(); + return true; + } + + public void playerDown(SBProtocolMessage message, Player p, String s){ + playerDown(message, p, s, 0, 0); + } + + public void playerDown(SBProtocolMessage message, Player p, String s, int armorRollModifier, int injuryRollModifier){ + PlayerCondition defenderCondition = p.invokeGetPlayerCondition(); + int armorRoll = p.getMatch().d6.throwDie() + p.getMatch().d6.throwDie() + armorRollModifier; + if(armorRoll > p.invokeGetRs()){ + s = ((RuleBlock)p.getRule(1)).injuryRoll(injuryRollModifier); + }else{ + p.invokeSetPlayerCondition(PlayerCondition.PRONE); + s = "prone"; + } + if(defenderCondition == PlayerCondition.STUNNED && p.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + sendMessageShowMe(p.toString(), "Next time you have to hit harder!"); + }else{ + sendMessageShowMe(p.toString(), "I am " + s + "!"); + } + returnSuccessMessage(message, s); + int teamIndex; + if(p.getTeam().equals(p.getMatch().getTeam(0))){ + teamIndex = 0; + }else if(p.getTeam().equals(p.getMatch().getTeam(1))){ + teamIndex = 1; + }else{ + return; + } + if(((ServerMatch)p.getMatch()).checkUserTurn(teamIndex)){ + ((ServerMatch)p.getMatch()).endTurn(teamIndex); + } + } + + public String injuryRoll(int modifier){ + String s = ""; + int injuryRoll = actor.getMatch().d6.throwDie() + actor.getMatch().d6.throwDie(); + if(injuryRoll + modifier < 8){ + actor.invokeSetPlayerCondition(PlayerCondition.STUNNED); + s += "stunned"; + }else{ + if(injuryRoll + modifier < 10){ + actor.invokeSetPlayerCondition(PlayerCondition.KO); + s += "KO"; + }else{ + int casultyRoll = actor.getMatch().d6.throwDie() * 10 + actor.getMatch().d8.throwDie(); + if(casultyRoll < 61){ + actor.invokeSetPlayerCondition(PlayerCondition.INJURED); + s += "injured"; + }else{ + actor.invokeSetPlayerCondition(PlayerCondition.DEAD); + s += "dead"; + } + actor.getMatch().addCasualty(actor); + } + actor.invokeClearPosition(); + } + return s; + } + + public boolean blockReaction(SBProtocolMessage message, SBProtocolMessage answer, Player defender){ + actor.getMatch().setGamePhase(3); + int dice1, dice2, dice3; + try{ + dice1 = Integer.parseInt(message.getParameterContent(1)); + dice2 = Integer.parseInt(message.getParameterContent(2)); + try { + dice3 = Integer.parseInt(message.getParameterContent(3)); + } catch(ArrayIndexOutOfBoundsException e) { // only two dice were given + dice3 = dice1; + } + }catch(NumberFormatException e){ + return false; + } + switch(Integer.parseInt(answer.getParameterContent(2))){ // if answer was dice index 1 or 2, set dice1 to chosen dice index + case 1: dice1 = dice2; break; + case 2: dice1 = dice3; break; + } + return reaction(dice1, defender, answer); + } + + public void beingBlockedDefenderDown(SBProtocolMessage message, Player attacker, Player defender, int firstThrowModifier, int injuryRollModifier){ + Vector2d posToBackup = new Vector2d(defender.getPos()); + playerDown(message, actor, ENEMY_DOWN, firstThrowModifier, injuryRollModifier); + ((ServerMatch)actor.getMatch()).clearCurrentPlayerPositionsBeingPushed(); + ((ServerMatch)actor.getMatch()).clearCurrentPlayersBeingPushed(); + ((RulePush)attacker.getRule(2)).apply(message, defender, defender.getPosition(), attacker.getTeam().getCoach().getUID(), attacker, posToBackup); + } + + public void beingBlockedDefenderStumbles(SBProtocolMessage message, Player attacker, Player defender, int firstThrowModifier, int injuryRollModifier){ + Vector2d posToBackup = new Vector2d(defender.getPos()); + playerDown(message, actor, ENEMY_DOWN, firstThrowModifier, injuryRollModifier); + ((ServerMatch)actor.getMatch()).clearCurrentPlayerPositionsBeingPushed(); + ((ServerMatch)actor.getMatch()).clearCurrentPlayersBeingPushed(); + ((RulePush)attacker.getRule(2)).apply(message, defender, defender.getPosition(), attacker.getTeam().getCoach().getUID(), attacker, posToBackup); + } + + public void beingBlockedPushed(SBProtocolMessage message, Player attacker, PitchField defenderField){ + Vector2d posToBackup = new Vector2d(defenderField.getPos()); + ((ServerMatch)actor.getMatch()).clearCurrentPlayerPositionsBeingPushed(); + ((ServerMatch)actor.getMatch()).clearCurrentPlayersBeingPushed(); + ((RulePush) attacker.getRule(2)).apply(message, actor, defenderField, attacker.getTeam().getCoach().getUID(), attacker, posToBackup); + } + + public void beingBlockedBothDown(SBProtocolMessage message, int firstThrowModifier, int injuryRollModifier){ + playerDown(message, actor, ENEMY_DOWN, firstThrowModifier, injuryRollModifier); + } + public void beatHim(Player defender, SBProtocolMessage message, boolean playerWantsToBlitz){ + if(actor.getTeam().getFoul()){ + int armorRollModifier = actor.getTeam().getTacklezones(defender.getPos()) -1; + armorRollModifier += defender.getTeam().getTacklezones(actor.getPos()); + PlayerCondition defenderCondition = defender.invokeGetPlayerCondition(); + ((RuleBlock)defender.getRule(1)).playerDown(message, defender, ENEMY_FOULED, armorRollModifier, 0); + if(defenderCondition == PlayerCondition.STUNNED && defender.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + defender.invokeSetPlayerCondition(PlayerCondition.STUNNED); + } + if(!playerWantsToBlitz){actor.invokeSetRemainingBe(0);} + actor.getTeam().setFoul(false); + refereeTriesToKeepSurvey(message); + } + else{ + sendMessageShowMe(actor.getTeam().getCoach(), actor.toString(), "Referee is watching me, I can't foul!"); + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_FOUL_LEFT); + } + } + public void refereeTriesToKeepSurvey(SBProtocolMessage message){ + int refereeThrow1 = actor.getMatch().d6.throwDie(); + int refereeThrow2 = actor.getMatch().d6.throwDie(); + if(refereeThrow1 == refereeThrow2){ + if(actor.isHoldingBall()){ + actor.invokeSetIsHoldingBall(false); + Vector2d newBallPos = new Vector2d(actor.getPos()); + newBallPos.add(actor.getMatch().scatter()); + actor.getMatch().getPitch().setBallPos(newBallPos); + } + actor.invokeClearPosition(); + actor.invokeSetRedCard(true); + int actingUserIndex = -1; + if(actor.getMatch().getTeam(0) == actor.getTeam()){ + actingUserIndex = 0; + }else if(actor.getMatch().getTeam(1) == actor.getTeam()){ + actingUserIndex = 1; + } + ((ServerMatch)actor.getMatch()).endTurn(actingUserIndex); + sendMessageShowMe(actor.toString(), "This stupid referee sent me of the pitch!"); + returnSuccessMessage(message, SBProtocolMessage.WORKD_PLAYER_HAS_BEEN_SENT_OFF_THE_PITCH_BY_REFEREE); + } + } + + public void throwDice(SBProtocolMessage message, Player defender){ + ((ServerMatch)getMatch()).addCurrentHighlitedFields(actor.getPos()); + ((ServerMatch)getMatch()).addCurrentHighlitedFields(defender.getPos()); + sendHighlightFields(((ServerMatch)getMatch()).getCurrentHighlitedFields()); + int dice1 ,dice2, dice3; + int forceActor = actor.invokeGetSt() + actor.getTeam().getTacklezones(defender.getPos()) - 1; + int forceDefender = defender.invokeGetSt() + defender.getTeam().getTacklezones(actor.getPos()) - 1; + if (forceActor == forceDefender){ + //same force + reaction(actor.getMatch().db.throwDie(), defender, message); + }else{ + // set the actor and the defender in the match so it knows who was waiting for an answer. + ((ServerMatch) getMatch()).setCurrentActorWaitingForAnswer(actor); + ((ServerMatch) getMatch()).setCurrentDefenderWaitingForAnswer(defender); + // send question + if(forceActor < forceDefender && forceDefender <= 2* forceActor){ + dice1 = actor.getMatch().db.throwDie(); + dice2 = actor.getMatch().db.throwDie(); + getMatch().sendMessage(defender.getTeam().getCoach(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_WHAT_DIE, dice1+"", dice2+""); + }else if(forceDefender < forceActor && forceActor <= 2* forceDefender){ + dice1 = actor.getMatch().db.throwDie(); + dice2 = actor.getMatch().db.throwDie(); + getMatch().sendMessage(actor.getTeam().getCoach(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_WHAT_DIE, dice1+"", dice2+""); + }else if(forceActor < forceDefender && forceDefender > 2* forceActor){ + dice1 = actor.getMatch().db.throwDie(); + dice2 = actor.getMatch().db.throwDie(); + dice3 = actor.getMatch().db.throwDie(); + getMatch().sendMessage(defender.getTeam().getCoach(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_WHAT_DIE, dice1+"", dice2+"", dice3+""); + }else if (forceDefender < forceActor && forceActor > 2*forceDefender){ + dice1 = actor.getMatch().db.throwDie(); + dice2 = actor.getMatch().db.throwDie(); + dice3 = actor.getMatch().db.throwDie(); + getMatch().sendMessage(actor.getTeam().getCoach(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_WHAT_DIE, dice1+"", dice2+"", dice3+""); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + return; + } + actor.getMatch().setGamePhase(5); + } + } + + public void sendHighlightFields(Vector fields){ + Vector colors = new Vector(); + colors.add(SBColor.BLACK_80); + for(int i = 0; i < fields.size()-1; i++){ + colors.add(SBColor.RED_80); + } + sendHighlightFields(fields, colors); + } + + public void sendHighlightFields(Vector fields, Vector colors){ + SBProtocolParameterArray parameters = new SBProtocolParameterArray(); + parameters.addParameter(new SBProtocolParameter(SBProtocolMessage.EVENT_API_HIGHLIGHT)); + for(int i = 0; i < fields.size(); i++){ + parameters.addParameter(new SBProtocolParameter((int)fields.get(i).x + "")); + parameters.addParameter(new SBProtocolParameter((int)fields.get(i).y + "")); + parameters.addParameter(new SBProtocolParameter(colors.get(i).getRed() + "")); + parameters.addParameter(new SBProtocolParameter(colors.get(i).getGreen() + "")); + parameters.addParameter(new SBProtocolParameter(colors.get(i).getBlue() + "")); + parameters.addParameter(new SBProtocolParameter(colors.get(i).getAlpha() + "")); + } + sendMessage(getMatch().getUser(0), SBProtocolCommand.EVENT, parameters); + sendMessage(getMatch().getUser(1), SBProtocolCommand.EVENT, parameters); + } + + public void clearHighlightFields(){ + ((ServerMatch)getMatch()).clearCurrentHighlitedFields(); + sendMessage(getMatch().getUser(0), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_API_HIGHLIGHT); + sendMessage(getMatch().getUser(1), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_API_HIGHLIGHT); + } +} \ No newline at end of file diff --git a/src/gameLogic/rules/RuleCatch.java b/src/gameLogic/rules/RuleCatch.java new file mode 100644 index 0000000..a031032 --- /dev/null +++ b/src/gameLogic/rules/RuleCatch.java @@ -0,0 +1,71 @@ +package gameLogic.rules; + +import javax.vecmath.Vector2d; + +import server.logic.ServerMatch; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import gameLogic.Player; +import gameLogic.PlayerCondition; +import gameLogic.Weather; + +public class RuleCatch extends Rule{ + + public RuleCatch(Player actor) { + super(actor); + } + + public void apply(boolean successfulThrow){ + int actingUserIndex; + if(actor.getTeam() == actor.getMatch().getTeam(0)){ + actingUserIndex = 0; + }else if(actor.getTeam() == actor.getMatch().getTeam(1)){ + actingUserIndex = 1; + }else{ + return; + } + if(actor.invokeGetPlayerCondition() == PlayerCondition.FINE){ + catchBall(successfulThrow, actingUserIndex); + }else{ + scatterBallAround(actingUserIndex); + } + } + + protected void catchBall(boolean successfulThrow, int actingUserIndex){ + int mod = -(actor.getMatch().getOpposingTeam(actor.getTeam()).getTacklezones(actor.getPos())); + if(successfulThrow){ + mod++; + } + if(getMatch().getWeather() == Weather.POURING_RAIN){ + mod--; + } + if(geTest(mod)){ + ballCatched(actingUserIndex); + }else{ + sendMessageShowMe(actor.toString(), "Dammit, I didn't catch the ball!"); + scatterBallAround(actingUserIndex); + } + } + + protected void scatterBallAround(int actingUserIndex){ + Vector2d newBallPos = new Vector2d(actor.getMatch().getPitch().getBallPos()); + newBallPos.add(actor.getMatch().scatter()); + actor.getMatch().getPitch().setBallPos(newBallPos); + sendMessage(actor.getMatch().getUser(actingUserIndex), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_BALL_NOT_CATCHED); + } + + protected void ballCatched(int actingUserIndex){ + actor.invokeSetIsHoldingBall(true); + sendMessageShowMe(actor.toString(), "I catched the ball!"); + sendMessage(actor.getMatch().getUser(actingUserIndex), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_BALL_CATCHED); + actor.getMatch().sendPlayer(actor); + } + + public boolean giveBall(SBProtocolMessage message){ + actor.getMatch().getPitch().adjustBallPos(actor.getPos()); + actor.invokeSetIsHoldingBall(true); + sendMessage(message, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_YOUR_TURN); + actor.getMatch().setGamePhase(3); + return true; + } +} diff --git a/src/gameLogic/rules/RuleMove.java b/src/gameLogic/rules/RuleMove.java new file mode 100644 index 0000000..13f62ef --- /dev/null +++ b/src/gameLogic/rules/RuleMove.java @@ -0,0 +1,223 @@ +package gameLogic.rules; + +import javax.vecmath.Vector2d; + +import gameLogic.*; +import network.SBProtocolMessage; +import network.SBProtocolParameterArray; +import server.logic.ServerMatch; + +/** + * rule for moving one player + */ +public class RuleMove extends Rule { + + public static final String I_AM_EXHAUSTED = "I am exhausted!"; + + public RuleMove(Player actor) { + super(actor); + } + + /** + * can the player attain his goal? + * @param mod number of difficulties + * @return boolean true if he is successful and false if he don't + */ + protected boolean stayFine(int mod){ + return geTest(mod + 1); + } + /** + * an enemy is on your target field. you try to block him. + * @param message SBProtocolMessage + * @param i every step of the path + * @param path The way the actor wants to move + */ + protected void tryToBlock(SBProtocolMessage message, int i, PitchField... path){ + //enemy is on your path -> try to block him + if(actor.getTeam().getBlitz()){ + if (actor.invokeGetRemainingBe() > 0){ + //blocking + ((RuleBlock) actor.getRule(1)).apply(message, getMatch().getPitch().getFields()[(int)path[i].getPos().x][(int)path[i].getPos().y].getPlayer()); + } + else { + //can't block + sendMessageShowMe(actor.getTeam().getCoach(), actor.toString(), I_AM_EXHAUSTED); + returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_BLITZ_LEFT); + } + } + + /** + * test whether there is a teammate or an enemy on your target field, and if he can block the enemy + * @param message SBProtocolMessage + * @param i Every step of the path + * @param path The way the actor wants to move + */ + protected void isItEnemy(SBProtocolMessage message, int i, PitchField... path ){ + if (getMatch().getPitch().getFields()[(int)path[i].getPos().x][(int)path[i].getPos().y].getPlayer().getTeam() == actor.getTeam()){ + //own player stands on your path + returnFailureMessage(message, SBProtocolMessage.FAILD_PATH_IS_BLOCKED); + }else if(actor.getTeam().getBlitz()){ + tryToBlock(message, i, path); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_BLITZ_LEFT); + } + } + /** + * test whether there is the ball on your field and in case of that whether you pick him up or not + * @param message SBProtocolMessage + * @param i Every step of the path + * @param path The way the actor wants to move + */ + protected boolean isBall(SBProtocolMessage message, int i, PitchField... path) { + return actor.isHoldingBall() || !path[i].getPos().equals(actor.getMatch().getPitch().getBallPos()) || tryToPickUpBall(message, i, path); + } + + protected boolean tryToPickUpBall(SBProtocolMessage message, int i, PitchField... path){ + int mod = 1-(actor.getMatch().getOpposingTeam(actor.getTeam()).getTacklezones(path[i].getPos())); + if(getMatch().getWeather() == Weather.POURING_RAIN){ + mod--; + } + if(geTest(mod) && actor.invokeGetPlayerCondition()==PlayerCondition.FINE){ + return pickedUpBall(message); + }else{ + return faildToPickUpBall(message); + } + } + + protected boolean pickedUpBall(SBProtocolMessage message){ + actor.invokeSetIsHoldingBall(true); + returnSuccessMessage(message, SBProtocolMessage.WORKD_PICKED_UP_BALL); + if(actor.getMatch().getPitch().isInTouchdownZone(actor.getTeam(), actor.getPos())){ + actor.getMatch().touchdown(actor.getTeam()); + } + return true; + } + + protected boolean faildToPickUpBall(SBProtocolMessage message){ + returnSuccessMessage(message, SBProtocolMessage.WORKD_FAILED_PICK_UP_BALL); + Vector2d newBallPos = new Vector2d(actor.getMatch().getPitch().getBallPos()); + newBallPos.add(actor.getMatch().scatter()); + actor.getMatch().getPitch().setBallPos(newBallPos); + ((ServerMatch)actor.getMatch()).endTurn(message); + return false; + } + + + /** + * trys to move one Field of the Path + * @param message SBProtocolMessage + * @param i Every step of the path + * @param path The way the actor wants to move + * @return true if he can move further (or block), false if not for any reason + */ + protected boolean moveOneField(SBProtocolMessage message, int i, PitchField... path){ + if(i != 1){ + try{ + Thread.sleep(300); + }catch(InterruptedException e){ + e.printStackTrace(); + } + } + //is the field next to the player? + boolean isNeighbour = getMatch().getPitch().isAdjacent(actor.getPosition(), path[i]); + boolean isBlocked; + //test: is there someone standing in your way? can you block him? + //noinspection SimplifiableIfStatement + if(getMatch().getPitch().getFields()[(int)path[i].getPos().x][(int)path[i].getPos().y].getPlayer() == null){ + isBlocked=false; + }else{ + isItEnemy(message, i, path); + isBlocked=true; + } + if(isNeighbour && !isBlocked) { + int problems = -(actor.getMatch().getOpposingTeam(actor.getTeam()).getTacklezones(path[i - 1].getPos())); + //difficulties: how many tacklezones is the player standing in? + actor.invokeSetPosition(path[i]); + actor.invokeCountDownRemainingBe(1); + if (problems != 0) { + if(!(tackleTest(message, problems, i, path))){ + return false; + } + } + return isBall(message, i, path); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PATH_NOT_REACHABLE); + return false; + } + } + + /** + * main method, moving + * @param message SBProtocolMessage + * @param path The way he wants to move + */ + public void apply(SBProtocolMessage message, PitchField... path) { + if(path.length > 1){ + checkForMovingPlayer(actor); + } + if(actor.invokeGetRemainingBe()==0){ + sendMessageShowMe(actor.getTeam().getCoach(), actor.toString(), I_AM_EXHAUSTED); + returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + }else{ + if(actor.invokeGetPlayerCondition().equals(PlayerCondition.PRONE)){ + checkForMovingPlayer(actor); + if(actor.invokeGetRemainingBe() > 2){ + actor.invokeSetPlayerCondition(PlayerCondition.FINE); + actor.invokeCountDownRemainingBe(3); + actor.getTeam().setMovingPlayer(actor); + returnSuccessMessage(message, SBProtocolMessage.WORKD_PLAYER_IS_NOW_FINE); + }else{ + if(actor.getMatch().d6.throwDie() > 3){ + actor.invokeSetPlayerCondition(PlayerCondition.FINE); + returnSuccessMessage(message, SBProtocolMessage.WORKD_PLAYER_IS_NOW_FINE); + actor.invokeSetRemainingBe(0); + }else{ + sendMessageShowMe(actor.getTeam().getCoach(), actor.toString(), "I can't get up, it hurts to much"); + actor.invokeSetRemainingBe(0); + } + } + } + actor.getMatch().sendPlayer(actor); + if(actor.getMatch().getPitch().isOnField(actor.getPos()) && actor.invokeGetPlayerCondition() == PlayerCondition.FINE){ + //test: is the path too long? in case of that, send a failure message + if (path.length-1 <= actor.invokeGetRemainingBe()){ + for(int i=1;i pushPossibilities = pushDirections(actor.getPosition(), defenderField); + for(Vector2d direction: pushPossibilities){ + Vector2d destinationField = new Vector2d(direction); + destinationField.add(defenderField.getPos()); + parameters.addParameter(new SBProtocolParameter((int)destinationField.x + "")); + parameters.addParameter(new SBProtocolParameter((int)destinationField.y + "")); + } + SBProtocolMessage messageToSend = new SBProtocolMessage(actor.getMatch().getParent().getUID(), SBProtocolCommand.EVENT, parameters); + getMatch().sendMessage(pushChooser, messageToSend); + actor.getMatch().setGamePhase(5); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PUSHING_NOT_POSSIBLE); + } + } + + protected Vector pushDirections(PitchField attackerField, PitchField defenderField){ + Vector2d mainDirection = new Vector2d(defenderField.getPos()); + mainDirection.sub(attackerField.getPos()); + Vector2d[] potentialDirections = new Vector2d[3]; + potentialDirections[0] = new Vector2d(mainDirection); + if((int)mainDirection.x == 0){ + potentialDirections[1] = new Vector2d(-1, mainDirection.y); + potentialDirections[2] = new Vector2d(1, mainDirection.y); + }else if((int)mainDirection.y == 0){ + potentialDirections[1] = new Vector2d(mainDirection.x, -1); + potentialDirections[2] = new Vector2d(mainDirection.x, 1); + }else{ + potentialDirections[1] = new Vector2d(mainDirection.x, 0); + potentialDirections[2] = new Vector2d(0, mainDirection.y); + } + Vector directions = new Vector(); + int emptyFields = 0; + int outOfPitchFields = 0; + for(Vector2d pd: potentialDirections){ + Vector2d newPosition = new Vector2d(defenderField.getPos()); + newPosition.add(pd); + if(!(attackerField.getPlayer().getMatch().getPitch().isOnField(newPosition))){ + outOfPitchFields++; + }else if(attackerField.getPlayer().getMatch().getPitch().getFields()[(int)newPosition.x][(int)newPosition.y].getPlayer() == null){ + emptyFields++; + } + } + if(emptyFields > 0){ + for(Vector2d pd: potentialDirections){ + Vector2d newPosition = new Vector2d(defenderField.getPos()); + newPosition.add(pd); + if(actor.getMatch().getPitch().isOnField(newPosition)){ + if(actor.getMatch().getPitch().getFields()[(int)newPosition.x][(int)newPosition.y].getPlayer() == null){ + directions.add(pd); + } + } + } + }else if(outOfPitchFields > 0){ + for(Vector2d pd: potentialDirections){ + Vector2d newPosition = new Vector2d(defenderField.getPos()); + newPosition.add(pd); + if(!(actor.getMatch().getPitch().isOnField(newPosition))){ + directions.add(pd); + } + } + }else{ + for(Vector2d pd: potentialDirections){ + directions.add(pd); + } + } + return directions; + } + + protected boolean pushPossible(PitchField attackerField, PitchField defenderField) { + return pushDirections(attackerField, defenderField).size() > 0; + } + + protected boolean pushPossible(PitchField attackerField, PitchField defenderField, Vector2d direction){ + Vector possibilities = pushDirections(attackerField, defenderField); + for(Vector2d d: possibilities){ + if(d.equals(direction)){ + return true; + } + } + return false; + } + + /** + * tests weather the chosen direction to push your enemy is legal and then moves (or not) + * @param defender the pushed person + * @param message SBProtocolMessage + * @param answer SBProtocolMessage + */ + public boolean chosenDirection(Player defender, SBProtocolMessage message, SBProtocolMessage answer, Player playerBackingUp, Vector2d posToBackUp){ + Vector2d actorField = new Vector2d(actor.getPos()); + Vector2d defenderField = new Vector2d(defender.getPos()); + Vector2d direction = new Vector2d(defenderField); + direction.sub(actorField); + Vector2d newDefenderField = new Vector2d(defenderField); + + int x; + int y; + try{ + x = Integer.parseInt(answer.getParameterContent(2)); + y = Integer.parseInt(answer.getParameterContent(3)); + } + catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + return false; + } + Vector2d walk = new Vector2d(x,y); + walk.sub(defenderField); + if(pushPossible(actor.getPosition(), defender.getPosition(), walk)){ + newDefenderField.add(walk); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_DIRECTION); + getMatch().sendMessage(actor.getTeam().getCoach().getUID(), message); + return true; + } + if(!(actor.getMatch().getPitch().isOnField(newDefenderField))){ + crowdBeatsUpPlayer(defender); + returnSuccessMessage(message, SBProtocolMessage.WORKD_DEFENDER_PUSHED); + actor.getMatch().setGamePhase(3); + pushAllWaitingPlayers(message); + ((RulePush)playerBackingUp.getRule(2)).backUp(posToBackUp, message, playerBackingUp); + }else if(actor.getMatch().getPitch().getFields()[(int)newDefenderField.x][(int)newDefenderField.y].getPlayer() == null){ + //liegt der ball auf dem feld? -> scatter + if (getMatch().getPitch().getBallPos().x == (int)newDefenderField.x && getMatch().getPitch().getBallPos().y == (int)newDefenderField.y){ + Vector2d newBallPos = new Vector2d (actor.getMatch().getPitch().getBallPos()); + newBallPos.add(actor.getMatch().scatter()); + actor.getMatch().getPitch().setBallPos(newBallPos); + } + ((ServerMatch)actor.getMatch()).addCurrentPlayersBeingPushed(defender); + ((ServerMatch)actor.getMatch()).addCurrentPlayerPositionsBeingPushed(newDefenderField); + pushAllWaitingPlayers(message); + actor.getMatch().setGamePhase(3); + ((RulePush)playerBackingUp.getRule(2)).backUp(posToBackUp, message, playerBackingUp); + }else{ + Player newDefender = actor.getMatch().getPitch().getFields()[(int)newDefenderField.x][(int)newDefenderField.y].getPlayer(); + ((ServerMatch)getMatch()).addCurrentHighlitedFields(newDefender.getPos()); + ((RuleBlock)actor.getRule(1)).sendHighlightFields(((ServerMatch)getMatch()).getCurrentHighlitedFields()); + ((ServerMatch)actor.getMatch()).addCurrentPlayersBeingPushed(defender); + ((ServerMatch)actor.getMatch()).addCurrentPlayerPositionsBeingPushed(newDefenderField); + ((RulePush)defender.getRule(2)).apply(message, newDefender, newDefender.getPosition(), answer.getUID(), playerBackingUp, posToBackUp); + returnSuccessMessage(message, SBProtocolMessage.WORKD_DEFENDER_PUSHED, "NEXT PUSH?"); + return true; + } + return true; + } + public void crowdBeatsUpPlayer(Player p) { + if(p.isHoldingBall()){ + p.invokeSetIsHoldingBall(false); + p.getMatch().getPitch().throwIn(p.getPos()); + } + p.invokeClearPosition(); + if(p.invokeGetPlayerCondition() == PlayerCondition.FINE || p.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + ((RuleBlock)p.getRule(1)).injuryRoll(0); + } + sendMessageShowMe("Crowd", "Let's beat up this " + p.getName()); + sendMessage(p.getTeam().getCoach(), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_CROWD_BEATS_UP_YOUR_PLAYER); + } + + public void pushAllWaitingPlayers(SBProtocolMessage message){ + if(((ServerMatch)actor.getMatch()).getCurrentPlayerPositionsBeingPushed().size() == ((ServerMatch)actor.getMatch()).getCurrentPlayersBeingPushed().size()){ + int numberOfPlayersBeingPushed = ((ServerMatch)actor.getMatch()).getCurrentPlayersBeingPushed().size(); + for(int i = 0; i < numberOfPlayersBeingPushed; i++){ + ((ServerMatch)actor.getMatch()).getCurrentPlayersBeingPushed().get(numberOfPlayersBeingPushed-i-1).invokeSetPosition(((ServerMatch)actor.getMatch()).getCurrentPlayerPositionsBeingPushed().get(numberOfPlayersBeingPushed-i-1)); + returnSuccessMessage(message, SBProtocolMessage.WORKD_DEFENDER_PUSHED); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PUSHING_NOT_POSSIBLE); + } + } +} + diff --git a/src/gameLogic/rules/RuleThrow.java b/src/gameLogic/rules/RuleThrow.java new file mode 100644 index 0000000..0d5fa4f --- /dev/null +++ b/src/gameLogic/rules/RuleThrow.java @@ -0,0 +1,228 @@ +package gameLogic.rules; + +import java.util.Vector; + +import gameLogic.*; +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import server.logic.ServerMatch; + +import javax.vecmath.Vector2d; +/** + * The game rule for throwing stuff. + */ +public class RuleThrow extends Rule { + + public static int QUICK_PASS=4; + public static int SHORT_PASS=8; + public static int LONG_PASS=12; + public static int LONG_BOMB=16; + public static final int INTERCEPT_DISTANCE = 1; + + + public RuleThrow(Player actor) { + super(actor); + } + /** + * where lands the ball if he doesnt pass the target + * @param targetField the target field + */ + protected void notWellThrown(Vector2d targetField, SBProtocolMessage message){ + Vector2d failField=new Vector2d(targetField); + failField.add(actor.getMatch().scatter()); + failField.add(actor.getMatch().scatter()); + failField.add(actor.getMatch().scatter()); + actor.getMatch().getPitch().setBallPos(failField); + returnSuccessMessage(message, SBProtocolMessage.WORKD_NOT_WELL_THROWN); + } + + protected void successfulThrow(SBProtocolMessage message, Vector2d destinationField){ + actor.getMatch().getPitch().setBallPos(destinationField, true); + returnSuccessMessage(message, SBProtocolMessage.WORKD_SUCCESSFUL_THROW); + } + + /** + * the actual throw + * @param message the message that initiated the throw + * @param destinationField where to throw + * @param problems how difficult it is to throw + */ + protected void throwBall(SBProtocolMessage message, Vector2d destinationField, int problems){ + if(geTest(problems)){ + successfulThrow(message, destinationField); + }else{ + notWellThrown(destinationField, message); + } + actor.invokeSetIsHoldingBall(false); + actor.invokeSetRemainingBe(0); + actor.getTeam().setPass(false); + actor.getMatch().sendBallPos(); + if(((ServerMatch)actor.getMatch()).findTeamHoldingTheBall() != actor.getMatch().findUserIndex(message)){ + ((ServerMatch)actor.getMatch()).endTurn(message); + } + actor.getMatch().sendPlayer(actor); + } + + /** + * the main method + * @param message The message that caused this rule to be applied. + * @param destination The pitch field to throw the ball to. + */ + public void apply(SBProtocolMessage message, PitchField destination) { + if(actor.invokeGetRemainingBe() == 0 && !(actor.getTeam().getMovingPlayer() == actor)){ + sendMessageShowMe(actor.getTeam().getCoach(), actor.toString(), RuleMove.I_AM_EXHAUSTED); + returnFailureMessage(message, SBProtocolMessage.FAILD_YOU_ARE_EXHAUSTED); + return; + } + checkForMovingPlayer(actor); + if(actor.getMatch().getPitch().isOnField(actor.getPos()) && actor.invokeGetPlayerCondition() == PlayerCondition.FINE){ + if(actor.isHoldingBall()){ + if(actor.getTeam().getPass()){ + Vector2d destinationField = new Vector2d(destination.getPos()); + Vector2d actorField = new Vector2d(actor.getPos()); + Vector2d difference = new Vector2d(destinationField); + difference.sub(actorField); + double distance = Math.sqrt((difference.x)*(difference.x)+(difference.y)*(difference.y)); + int problems = -(actor.getMatch().getOpposingTeam(actor.getTeam()).getTacklezones(actor.getPos())); + if(findThrowModificator(problems, distance) < 1000){//if 1000 or higher, then its to far away. + askForInterception(message, destinationField, findThrowModificator(problems, distance)); + }else{returnFailureMessage(message, SBProtocolMessage.FAILD_TOO_FAR_AWAY);} + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_PASS_LEFT); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_HOLD_THE_BALL); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_CANNOT_TAKE_ACTION); + } + } + + protected int findThrowModificator(int problems, double distance){ + int temp = problems; + if(getMatch().getWeather() == Weather.VERY_SUNNY){ + temp++; + } + if(distance<=QUICK_PASS){ + return temp+1; + }else if(distance<=SHORT_PASS){ + return temp; + }else if(distance<=LONG_PASS){ + return temp-1; + }else if(distance<=LONG_BOMB){ + return temp-2; + }else{ + return 2000; + } + } + + public int getLONG_BOMB(){ + return LONG_BOMB; + } + + protected double getDistanceFromPassLine(Vector2d a, Vector2d b, Vector2d p){ + double quotient = Math.abs((b.y-a.y)*(p.x-a.x)-(b.x-a.x)*(p.y-a.y)); + double divisor = Math.sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y)); + return (quotient/divisor); + } + + private double getDistance(Vector2d a, Vector2d b){ + return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); + } + + protected Vector findInterceptPositions(Vector2d a, Vector2d b){ + Vector possiblePositions = new Vector(); + Vector2d iterationA = new Vector2d(a); + Vector2d iterationB = new Vector2d(b); + if(a.x > b.x){ + double temp = iterationA.x; + iterationA = new Vector2d(iterationB.x, iterationA.y); + iterationB = new Vector2d(temp, iterationB.y); + } + if(a.y > b.y){ + double temp = iterationA.y; + iterationA = new Vector2d(iterationA.x, iterationB.y); + iterationB = new Vector2d(iterationB.x, temp); + } + for(int j = (int)iterationA.y-1; j < (int)iterationB.y+2; j++){ + for(int i = (int)iterationA.x-1; i < (int)iterationB.x+2; i++){ + if(getMatch().getPitch().isOnField(i, j)){ + if(getDistanceFromPassLine(a, b, getMatch().getPitch().getFields()[i][j].getPos()) <= INTERCEPT_DISTANCE + && getDistance(getMatch().getPitch().getFields()[i][j].getPos(), a) < getDistance(a, b) + && getDistance(getMatch().getPitch().getFields()[i][j].getPos(), b) < getDistance(a, b)){ + possiblePositions.add(getMatch().getPitch().getFields()[i][j]); + } + } + } + } + return possiblePositions; + } + + protected Vector findPossibleInterceptors(Player thrower, Vector2d b){ + int teamIndex = -1; + if(thrower.getTeam() == getMatch().getTeam(0)){ + teamIndex = 0; + }else if(thrower.getTeam() == getMatch().getTeam(1)){ + teamIndex = 1; + }else{ + return null; + } + Vector possibleInterceptors = new Vector(); + for(PitchField field: findInterceptPositions(thrower.getPos(), b)){ + if(field.getPlayer() != null){ + if(field.getPlayer().getTeam() != getMatch().getTeam(teamIndex) && field.getPlayer().invokeGetPlayerCondition() == PlayerCondition.FINE){ + possibleInterceptors.add(field.getPlayer()); + } + } + } + return possibleInterceptors; + } + + protected void askForInterception(SBProtocolMessage message, Vector2d destinationField, int throwModificator) { + Vector possibleInterceptors = findPossibleInterceptors(actor, destinationField); + if(possibleInterceptors.size() == 0){ + throwBall(message, destinationField, throwModificator); + }else{ + Vector parameters = new Vector(); + parameters.add(SBProtocolMessage.EVENT_API_CHOICE); + parameters.add(SBProtocolMessage.EVENT_ASK_FOR_INTERCEPTOR); + for(Player player:possibleInterceptors){ + if(player.getTeam() == getMatch().getTeam(0)){ + parameters.add("0"); + parameters.add((player.getId()-1) + ""); + }else if(player.getTeam() == getMatch().getTeam(1)){ + parameters.add("1"); + parameters.add((player.getId()-1) + ""); + } + } + String[] parameterArray = parameters.toArray(new String[parameters.size()]); + ((ServerMatch)getMatch()).setCurrentModificatorWaitingForAnser(throwModificator); + ((ServerMatch)getMatch()).setCurrentDefenderFieldWaitingForAnswer(destinationField); + ((ServerMatch)getMatch()).setCurrentMessageWaitingForAnswer(message); + ((ServerMatch)getMatch()).setCurrentActorWaitingForAnswer(actor); + getMatch().setGamePhase(5); + sendMessage(getMatch().getOpponent(actor.getTeam().getCoach()), SBProtocolCommand.EVENT, parameterArray); + } + } + + public void intercept(Player player, int teamIndex, int playerIndex, int userIndex){ + getMatch().setGamePhase(3); + int mod = -2; + mod -= getMatch().getOpposingTeam(getMatch().getTeam(teamIndex)).getTacklezones(getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + if(getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getRule(3).geTest(mod)){ + player.invokeSetIsHoldingBall(false); + getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).invokeSetIsHoldingBall(true); + getMatch().getPitch().adjustBallPos(getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).getPos()); + int actingUserIndex = -1; + if(userIndex == 0){ + actingUserIndex = 1; + }else if(userIndex == 1){ + actingUserIndex = 0; + } + sendMessageShowMe(getMatch().getTeam(teamIndex).getPlayers().get(playerIndex).toString(), "Successfully intercepted"); + ((ServerMatch)getMatch()).endTurn(actingUserIndex); + }else{ + ((RuleThrow)player.getRule(3)).throwBall(((ServerMatch)getMatch()).getCurrentMessageWaitingForAnswer(), ((ServerMatch)getMatch()).getCurrentDefenderFieldWaitingForAnser(), ((ServerMatch)getMatch()).getCurrentModificatorWaitingForAnser()); + } + } +} diff --git a/src/gameLogic/rules/SpecialRule.java b/src/gameLogic/rules/SpecialRule.java new file mode 100644 index 0000000..c84c987 --- /dev/null +++ b/src/gameLogic/rules/SpecialRule.java @@ -0,0 +1,24 @@ +package gameLogic.rules; + +import network.SBProtocolMessage; +import gameLogic.Player; + +public abstract class SpecialRule extends Rule { + + private String name; + + public SpecialRule(Player actor, String name) { + super(actor); + this.name = name; + } + + public abstract void apply(SBProtocolMessage message); + + public void setName(String name){ + this.name = name; + } + + public String getName(){ + return name; + } +} diff --git a/src/network/SBNetworkException.java b/src/network/SBNetworkException.java new file mode 100644 index 0000000..3dabd1f --- /dev/null +++ b/src/network/SBNetworkException.java @@ -0,0 +1,9 @@ +package network; + +import util.SBException; + +/** + * Exception thrown for network problems + */ +public class SBNetworkException extends SBException { +} diff --git a/src/network/SBProtocolCommand.java b/src/network/SBProtocolCommand.java new file mode 100644 index 0000000..59f8d6a --- /dev/null +++ b/src/network/SBProtocolCommand.java @@ -0,0 +1,465 @@ +package network; + + +import java.util.ArrayList; + +/** + * Enumeration of protocol commands and modules. + * Created by milan on 23.3.15. + */ +public enum SBProtocolCommand { + // module CHT: + /** + * Chatnachrichten senden.
+ * Parameter: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$recipient $message] + * + * Die erste Form von SENDM wird von Clients gesendet und hat als ersten Parameter den Namen des Empfängers. + *
+ * [$sender $message] + * + * Die zweite Form von SENDM wird von Servern gesendet und hat als ersten Parameter den Namen des Senders. + *
+ */ + SENDM(SBProtocolModule.CHT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$sender $message] + * + * $message an alle Benutzer senden. + *
+ */ + BDCST(SBProtocolModule.CHT), + + + // module AUT: + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Ausloggen. + *
+ */ + LOGUT(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$user $password_hash] + * + * $user mit $password_hash einloggen. + *
+ */ + LOGIN(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$user] + * + * Existiert bereits ein Benutzer mit diesem Namen? + *
+ */ + EXIST(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Liste mit allen angemeldeten Benutzern anfordern. + *
+ */ + LSUSR(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [["name"], ["name", "punkte_spieler", "punkte_gegner"], ["name", ""], ...] + * + * Server sendet Liste mit allen angemeldeten Spielern. Erster Parameterarray für Spieler in Lobby, zweiter für Spieler in Spiel und dritter für Spieler, die auf Spiel warten (mit leerem zweitem Parameter). + *
+ */ + UPUSR(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Liste mit allen Spielen anfordern. + *
+ */ + LSGAM(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [["spieler1", "spieler2", "punkte1", "punkte2"], ["spieler_auf_spiel_wartend"], ...] + * + * Server sendet Liste mit allen laufenden Spielen. + *
+ */ + UPGAM(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [["spieler1", "wins", "losses", "ratio", "scored", "received", "ratio", "casualties"], ...] + * + * Server sendet Liste mit den Highscores. + *
+ */ + SCORE(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$user $new_user] + * + * Name von $user durch $new_user ersetzen. Kann nur bei eigenem $user (d.h. UID stimmt mit UID von $user überein) oder von user mit OP-Rechten ausgeführt werden. + *
+ */ + CHNGE(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$user] + * + * $user löschen (erfordert OP-Rechte). + *
+ */ + RMUSR(SBProtocolModule.AUT), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$user] + * + * $user OP-Rechte erteilen. + *
+ */ + OPUSR(SBProtocolModule.AUT), + + + // module GAM: + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$action $params[]] + * + * $action mit $params[] im aktuellen Spiel ausführen. + *
+ */ + ACTIO(SBProtocolModule.GAM), + + /** + * Spiel starten.
+ * Parameter: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$opponent_name] + * + * Bestätigung/Anfrage vom Server zum Starten eines Spiels gegen Gegner mit Name $opponent_name. (Wird von Server gesendet, entweder bei Einladung durch anderen Benutzer oder bei gefundenem zufälligen Match.) + *
+ * [$invited_user_name] + * + * Gegen Benutzer mit Name $invited_user_name ein neues Match starten. Von Client gesendet. + *
+ * [] + * + * Auf zufälligen Gegner für ein neues Match warten. Von Client gesendet. + *
+ */ + START(SBProtocolModule.GAM), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Den aktuellen Match aufgeben. + *
+ */ + SRNDR(SBProtocolModule.GAM), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$event $params[]] + * + * $event mit $params[] an Empfänger senden (z.B. "$params[:user] gewinnt Match"). + *
+ */ + EVENT(SBProtocolModule.GAM), + + + // other modules: + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Ping an Gegenüber, um Verbindung zu prüfen. (Von Server gesendet) + *
+ */ + PIING(SBProtocolModule.PNG), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [] + * + * Pong an Gegenüber, um bestehende Verbindung zu bestätigen. (Von Client gesendet) + *
+ */ + POONG(SBProtocolModule.PNG), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$MID $reply] + * + * Positive Antwort auf empfangene Nachricht $MID mit $reply. + *
+ */ + WORKD(SBProtocolModule.SUC), + + /** + * + * + * + * + * + * + * + * + * + *
ParameterBeschreibung
+ * [$MID $reply] + * + * Negative Antwort auf empfangene Nachricht $MID mit $reply. + *
+ */ + FAILD(SBProtocolModule.FAI); + + private SBProtocolModule module; + + /** + * Assign the module of this command on creation. + * @param module The module of this command. + */ + SBProtocolCommand(SBProtocolModule module) { + this.module = module; + } + + public SBProtocolCommand[] getAllCommandsFromModule(SBProtocolModule module) { + ArrayList commandsFound = new ArrayList(); + for(SBProtocolCommand command: values()) + if(command.getModule() == module) + commandsFound.add(command); + return commandsFound.toArray(new SBProtocolCommand[commandsFound.size()]); + } + + /** + * Get the corresponding module for this command. + * @return The corresponding module. + */ + public SBProtocolModule getModule() { + return module; + } + + /** + * Get a string representation of a command and a corresponding moodule. + * Example: SBProtocolCommand.LOGIN.toString() - "AUT LOGIN" + * @return The string representation of the command and a corresponding moodule. + */ + public String toMessageString() { + return module.toString()+" "+toString(); + } + + /** + * The protocol modules + */ + public enum SBProtocolModule { + CHT, AUT, GAM, PNG, SUC, FAI + } +} diff --git a/src/network/SBProtocolManager.java b/src/network/SBProtocolManager.java new file mode 100644 index 0000000..1b34962 --- /dev/null +++ b/src/network/SBProtocolManager.java @@ -0,0 +1,108 @@ +package network; + +import util.SBApplication; + +import java.util.Vector; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; + +/** + * The abstract protocol manager to handle protocols on a client or server. + * Created by milan on 23.3.15. + */ +public abstract class SBProtocolManager { + + private volatile Vector unansweredMessages = new Vector(); // table with sent messages + private volatile LinkedBlockingQueue processedMessages = new LinkedBlockingQueue(); // a list containing all messages that have already been processed. + private volatile LinkedBlockingQueue messagesToProcess = new LinkedBlockingQueue(); // a list containing all messages that need to be processed by the server/client protocol manager thread. + private volatile LinkedBlockingQueue answersToProcess = new LinkedBlockingQueue(); // a list containing all answers that need to be processed by the server/client protocol manager thread. + private volatile LinkedBlockingQueue pingAnswersToProcess = new LinkedBlockingQueue(); // a list containing all ping answers that need to be processed by the socket manager. + SBApplication parent; + + /** + * Create a new protocol manager. + * @param parent The parent application of the protocol manager. + */ + public SBProtocolManager(SBApplication parent) { + this.parent = parent; + // create and start new thread to start processing messagesToProcess and answersToProcess queues + (new Thread(new Runnable() { + @Override + public void run() { + while(true) { + getParent().processAnswers(); + getParent().processMessages(); + try { + Thread.sleep(100); + } catch (InterruptedException e) { break; } + } + } + })).start(); + } + + /** + * Try to add a message to a queue until it worked (wait 100ms after each retry). + * @param queue The queue to put the message in. + * @param message The message to put into the queue. + */ + void putInQueue(LinkedBlockingQueue queue, SBProtocolMessage message) { + boolean putInQueue = false; + while(!putInQueue) { + try { + queue.put(message); + putInQueue = true; + } catch (InterruptedException e) { + getParent().log(Level.WARNING, "Interrupted while adding the message to the queue. Retrying in 100ms..."); + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + getParent().log(Level.SEVERE, "Interrupted while waiting to retry adding the message to the queue. Continuing anyway (because I'm badass)."); + } + } + } + } + + // getters and setters + + public SBApplication getParent() { + return parent; + } + + public Vector getUnansweredMessages() { + return unansweredMessages; + } + + public void removeUnansweredMessage(SBProtocolMessage message) { + if(message != null) getUnansweredMessages().remove(message); + } + + public void addUnansweredMessage(SBProtocolMessage message) { + if(message != null) getUnansweredMessages().add(message); + } + + public LinkedBlockingQueue getProcessedMessages() { + return processedMessages; + } + + public void addProcessedMessage(SBProtocolMessage message) { + if(message != null) getProcessedMessages().add(message); + } + + public LinkedBlockingQueue getMessagesToProcess() { + return messagesToProcess; + } + + public LinkedBlockingQueue getAnswersToProcess() { + return answersToProcess; + } + + public LinkedBlockingQueue getPingAnswersToProcess() { + return pingAnswersToProcess; + } + + public SBProtocolMessage getNextMessageToProcessAndStoreIt() { + SBProtocolMessage message = getMessagesToProcess().poll(); + addProcessedMessage(message); + return message; + } +} diff --git a/src/network/SBProtocolMessage.java b/src/network/SBProtocolMessage.java new file mode 100644 index 0000000..1ce00a5 --- /dev/null +++ b/src/network/SBProtocolMessage.java @@ -0,0 +1,437 @@ +package network; + +import util.SBLogger; + +import java.util.Date; +import java.util.UUID; // A UUID has the hexadecimal form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, e.g. 550e8400-e29b-11d4-a716-446655440000. +import java.util.logging.Level; + +/** + * Object representation of a protocol message that is being sent from socket to socket. + * Created by milan on 24.3.15. + */ +public class SBProtocolMessage { + + private static final SBLogger L = new SBLogger(SBProtocolMessage.class.getName(), util.SBLogger.LOG_LEVEL); + // GAME ACTIO STRINGS + public static final String ACTIO_SET_TEAM = "SET TEAM"; + public static final String ACTIO_SET_PLAYER = "SET PLAYER"; + public static final String ACTIO_MOVE = "MOVE"; + public static final String ACTIO_THRW = "THRW"; + public static final String ACTIO_BLCK = "BLCK"; + public static final String ACTIO_SPCL = "SPCL"; + // GAME EVENT STRINGS + public static final String EVENT_END_TURN = "END TURN"; + public static final String EVENT_INITIATE_KICK = "INITIATE KICK"; + public static final String EVENT_GIVE_THE_BALL_TO_SOMEONE = "GIVE THE BALL TO SOMEONE"; + public static final String EVENT_SETUP_YOUR_TEAM = "SETUP YOUR TEAM"; + public static final String EVENT_OPPONENT_SURRENDERED = "OPPONENT SURRENDERED"; + public static final String EVENT_WON_GAME = "WON GAME"; + public static final String EVENT_LOST_GAME = "LOST GAME"; + public static final String EVENT_DRAW = "THE GAME ENDED IN A DRAW"; + public static final String EVENT_SEND_NEW_TEAM = "SEND NEW TEAM"; + public static final String EVENT_SEND_PLAYER = "SEND PLAYER"; + public static final String EVENT_SEND_ROUND_COUNT = "SEND ROUND COUNT"; + public static final String EVENT_SEND_BALL_POS = "SEND BALL POS"; + public static final String EVENT_SEND_SCORE = "SEND SCORE"; + public static final String EVENT_SEND_GAME_PHASE = "SEND GAME PHASE"; + public static final String EVENT_SEND_MOVING_PLAYER = "SEND MOVING PLAYER"; + public static final String EVENT_MAKE_PLAYER_TURN = "MAKE PLAYER TURN"; + public static final String EVENT_WHAT_DIE = "WHAT DIE?"; + public static final String EVENT_BALL_CATCHED = "BALL CATCHED"; + public static final String EVENT_BALL_NOT_CATCHED = "BALL NOT CATCHED"; + public static final String EVENT_FOLLOW = "FOLLOW?"; + public static final String EVENT_ALL_PLAYERS_SET = "ALL PLAYERS SET"; + public static final String EVENT_YOUR_ENEMY_IS_TACKLED = "YOUR ENEMY IS TACKLED"; + public static final String EVENT_WHICH_DIRECTION = "WHICH DIRECTION?"; + public static final String EVENT_BALL_THROWN_IN_BY_THE_CROWD = "BALL THROWN IN BY THE CROWD"; + public static final String EVENT_ENDED_TURN = "ENDED TURN"; + public static final String EVENT_YOUR_TURN = "YOUR TURN"; + public static final String EVENT_CROWD_BEATS_UP_YOUR_PLAYER = "CROWD BEATS UP YOUR PLAYER"; + public static final String EVENT_SHOW_ME = "SHOW ME"; + public static final String EVENT_API_AIM = "API AIM"; + public static final String EVENT_API_CHOICE = "API CHOICE"; + public static final String EVENT_API_FIELD = "API FIELD"; + public static final String EVENT_SEND_BLITZ_PASS_FOUL = "SEND BLITZ PASS FOUL"; + public static final String EVENT_SEND_WEATHER = "SEND WEATHER"; + public static final String EVENT_ASK_FOR_INTERCEPTOR = "$intercept"; + public static final String EVENT_API_HIGHLIGHT = "API HIGHLIGHT"; + // SUCCESS ANSWER STRINGS + public static final String WORKD_DIE_CHOSEN = "DIECHOSEN"; + public static final String WORKD_DECIDED = "DECIDED"; + public static final String WORKD_PLAYER_SET = "PLAYER SET"; + public static final String WORKD_TEAMSETUP_SUCCESSFUL = "TEAMSETUP SUCCESSFUL"; + public static final String WORKD_PICKED_UP_BALL = "PICKED UP BALL"; + public static final String WORKD_FAILED_PICK_UP_BALL = "FAILED PICK UP BALL"; + public static final String WORKD_YOU_HAVE_LOST_YOUR_BALLS = "YOU HAVE LOST YOUR BALLS"; + public static final String WORKD_YOU_ARE_TACKLED = "YOUR ARE TACKLED"; + public static final String WORKD_PLAYER_IS_NOW_FINE = "PLAYER IS NOW FINE"; + public static final String WORKD_PLAYER_IS_NOW_PRONE = "PLAYER IS NOW PRONE"; + public static final String WORKD_DEFENDER_PUSHED = "DEFENDER PUSHED"; + public static final String WORKD_NOT_WELL_THROWN = "NOT WELL THROWN"; + public static final String WORKD_SUCCESSFUL_THROW = "SUCCESSFUL THROW"; + public static final String WORKD_GIVE_BALL = "GIVEBALL"; + public static final String WORKD_KICK = "KICK"; + public static final String WORKD_DIRECTION = "DIRECTION"; + public static final String WORKD_FOLLOWED = "YOU FOLLOWED HIM"; + public static final String WORKD_NOTFOLLOWED = "YOU STAYED AT YOUR PLACE"; + public static final String WORKD_PLAYER_HAS_BEEN_SENT_OFF_THE_PITCH_BY_REFEREE = "PLAYER HAS BEEN SENT OFF THE PITCH BY REFEREE"; + // FAILURE ANSWER STRINGS + public static final String FAILD_PARAMANIA_HAS_TAKEN_OVER = "PARAMANIA HAS TAKEN OVER"; + public static final String FAILD_WHERE_IS_THE_GURU = "WHERE IS THE GURU"; + public static final String FAILD_PLAYER_DOESNT_EXIST = "PLAYER DOESNT EXIST"; + public static final String FAILD_PATH_NOT_REACHABLE = "PATH NOT REACHABLE"; + public static final String FAILD_GIMME_AN_INT = "GIMME_AN_INT"; + public static final String FAILD_THIS_IS_NO_VALID_COORDINATE = "THIS IS NO VALID COORINATE"; + public static final String FAILD_WRONG_SIDE = "WRONG SIDE"; + public static final String FAILD_NOT_ON_THE_PITCH = "NOT ON THE PITCH"; + public static final String FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION = "PLAYER IS NOT IN A GOOD CONDITION"; + public static final String FAILD_FIELD_ALREADY_TAKEN = "FIELD ALREADY TAKEN"; + public static final String FAILD_SOME_PLAYERS_DONT_EXIST = "SOME PLAYERS DONT EXIST"; + public static final String FAILD_NO_SUCH_TEAM_TYPE = "NO SUCH TEAM TYPE"; + public static final String FAILD_YOUR_TEAM_IS_ALREADY_SET = "YOUR TEAM IS ALREADY SET"; + public static final String FAILD_GIVE_A_VALID_PLAYER_INDEX = "GIVE A VALID PLAYER INDEX"; + public static final String FAILD_YOU_DONT_BELONG_HERE = "YOU DONT BELONG HERE"; + public static final String FAILD_BLOCKING_NOT_POSSIBLE = "BLOCKING NOT POSSIBLE"; + public static final String FAILD_NO_BLITZ_LEFT = "NO BLITZ LEFT"; + public static final String FAILD_PLAYER_CANNOT_TAKE_ACTION = "PLAYER CANNOT TAKE ACTION"; + public static final String FAILD_YOU_ARE_EXHAUSTED = "YOU ARE EXHAUSTED"; + public static final String FAILD_PATH_IS_BLOCKED = "PATH IS BLOCKED"; + public static final String FAILD_PATH_TOO_LONG = "PATH TOO LONG"; + public static final String FAILD_CANT_BACKUP_FOR_SOME_ODD_REASON = "CANT BACKUP FOR SOME ODD REASON"; + public static final String FAILD_PUSHING_NOT_POSSIBLE = "PUSHING NOT POSSIBLE"; + public static final String FAILD_TOO_FAR_AWAY = "TOO FAR AWAY"; + public static final String FAILD_NO_PASS_LEFT = "NO PASS LEFT"; + public static final String FAILD_PLAYER_DOESNT_HOLD_THE_BALL = "PLAYER DOESNT HOLD THE BALL"; + public static final String FAILD_PLAYER_IS_NOT_ON_THE_PITCH = "PLAYER IS NOT ON THE PITCH"; + public static final String FAILD_RECEIVED_WRONG_GAME_DATA = "RECEIVED WRONG GAME DATA"; + public static final String FAILD_NOT_YOUR_TURN = "NOT YOUR TURN"; + public static final String FAILD_WRONG_GAME_PHASE = "WRONG GAME PHASE"; + public static final String FAILD_WRONG_DIRECTION = "WRONG DIRECTION"; + public static final String FAILD_INVALID_TEAMSETUP = "INVALID TEAMSETUP"; + public static final String FAILD_NOT_ENOUGH_PLAYERS = "NOT ENOUGH PLAYERS"; + public static final String FAILD_NO_FOUL_LEFT = "NO FOUL LEFT"; + public static final String FAILD_NOT_ENOUGH_MONEY = "NOT ENOUGH MONEY"; + public static final String FAILD_NO_SUCH_SPECIAL_RULE = "NO SUCH SPECIAL RULE"; + public static final String FAILD_PLAYER_IS_BANNED_FROM_THE_GAME = "PLAYER IS BANNED FROM THE GAME"; + + private UUID UID; + private UUID MID; + private SBProtocolCommand.SBProtocolModule module; + private SBProtocolCommand command; + private SBProtocolParameterArray parameters; + private Date sentDate = new Date(0); + private SBSocket socket; // the socket to return answers to. Is being set after message has been received by a socket. + + /** + * Create a new protocol Message as user with the ID UID. + * @param UID The UID of the user sending this message. + * @param command The command for the protocol message. + * @param parameters An string array with the parameters for the protocol message. + */ + public SBProtocolMessage(UUID UID, SBProtocolCommand command, String... parameters) { + this.UID = UID; + this.MID = UUID.randomUUID(); + this.module = command.getModule(); + this.command = command; + this.parameters = new SBProtocolParameterArray(parameters); + } + + /** + * Create a new protocol Message as user with the ID UID. + * @param UID The UID of the user sending this message. + * @param command The command for the protocol message. + * @param parameters An protocol array with the parameters for the protocol message. + */ + public SBProtocolMessage(UUID UID, SBProtocolCommand command, SBProtocolParameterArray parameters) { + this.UID = UID; + this.MID = UUID.randomUUID(); + this.module = command.getModule(); + this.command = command; + this.parameters = parameters; + } + + /** + * Create a new protocol Message as user with the ID UID and a MID. + * @param UID The UID of the user sending this message. + * @param MID The message ID of this message. + * @param command The command for the protocol message. + * @param parameters An protocol array with the parameters for the protocol message. + */ + public SBProtocolMessage(UUID UID, UUID MID, SBProtocolCommand command, SBProtocolParameterArray parameters) { + this.UID = UID; + this.MID = MID; + this.module = command.getModule(); + this.command = command; + this.parameters = parameters; + } + + public SBProtocolMessage(SBProtocolMessage answer) { + this.UID = answer.UID; + this.MID = answer.MID; + this.module = answer.module; + this.command = answer.command; + this.parameters = answer.parameters; + this.socket = answer.socket; + } + + /** + * Parse a message from a string. + * @param message The message string to parse. Null if the message if malformed + * @return The parsed protocol message. + */ + public static SBProtocolMessage fromString(String message) { + // modules: CHT, AUT, GAM, PNG, SUC, FAI + // commands: + // module CHT: SENDM/CHT, BDCST/CHT + // module AUT: LOGUT/AUT, LOGIN/AUT, SGNUP/AUT, CHNGE/AUT, RMUSR/AUT, OPUSR/AUT + // module GAM: ACTIO/GAM, START/GAM, SRNDR/GAM, EVENT/GAM + // other modules: HELLO/PNG, WORKD/SUC, FAILD/FAI, + + // e.g. XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX AUT LOGIN ["peter", "6d8aa0c02bb914a4c47d27f563fa3dca"] + // |0 |36 |73 |77 |83 + + try { + // first of all check if message is not null + if (message != null) { + // parse UID + UUID UID; + try { + UID = UUID.fromString(message.substring(0, 36)); + } catch (IllegalArgumentException e) { + // UID is malformed + L.log(Level.SEVERE, "Tried to parse malformed message. Ignoring it."); + return null; + } + + // parse MID + UUID MID; + try { + MID = UUID.fromString(message.substring(37, 73)); + } catch (IllegalArgumentException e) { + // MID is malformed + L.log(Level.SEVERE, "Tried to parse malformed message. Ignoring it."); + return null; + } + + // parse module and command + SBProtocolCommand command; + String moduleString = message.substring(74, 77); + String commandString = message.substring(78, 83); + try { + command = SBProtocolCommand.valueOf(commandString); + } catch (IllegalArgumentException e) { + L.log(Level.SEVERE, "Tried to parse malformed message. Ignoring it."); + return null; + } + + // parse parameters if there are any + SBProtocolParameterArray parameters; + if (!message.substring(83).equals("")) { + parameters = SBProtocolParameterArray.paraception(message.substring(84)); + if (parameters == null) { // paraception returned null + L.log(Level.SEVERE, "Tried to parse malformed message. Ignoring it."); + return null; + } + } else { + // save empty parameter array + parameters = new SBProtocolParameterArray(); + } + + // return parsed message + return new SBProtocolMessage(UID, MID, command, parameters); + } else return null; + } catch(StringIndexOutOfBoundsException e) { + L.log(Level.SEVERE, "Some strange client tried to connect. No chance!"); + return null; + } + } + + /** + * Create a string in SBProcotol-standard format to send. + * @return The message as a string. + */ + public String toString() { + return UID+" "+MID+" "+command.toMessageString()+" "+parameters; + } + + /** + * Create a string in SBProcotol-standard format but with shortened UUIDs for better readability. + * @return The message as a string with shortened UUIDs. + */ + public String toStringShortenUUID() { + return UID.toString().substring(0, 8)+" "+MID.toString().substring(0, 8)+" "+command.toMessageString()+" "+parameters.toStringUnescaped(); + } + + /** + * Get the parameter at index i. + * @param i The index of the parameter to return. + * @return The parameter at index i. + */ + public SBProtocolParameter getParameter(int i) { + return parameters.getParameter(i); + } + + /** + * Get the content of the parameter at index i. + * @param i The index of the parameter whose content to return. + * @return The content of the parameter at index i. + */ + public String getParameterContent(int i) { + return parameters.getParameter(i).getContent(); + } + + /** + * Get all parameters as a parameter array. + * @return the parameter array with all parameters in it + */ + public SBProtocolParameterArray getParameters() { + return parameters; + } + + /** + * Create a new ping message. + * @param UID The UID of the sender. + * @return The ping message. + */ + public static SBProtocolMessage createPingMessage(UUID UID) { + return new SBProtocolMessage(UID, SBProtocolCommand.PIING, new SBProtocolParameterArray()); + } + + /** + * Create a new ping answer for an existing ping MID + * @param UID The UID of the sender. + * @param MID The MID of the original ping message. + * @return The ping answer for the ping message with the given MID. + */ + public static SBProtocolMessage createPingAnswer(UUID UID, UUID MID) { + return new SBProtocolMessage(UID, MID, SBProtocolCommand.POONG, new SBProtocolParameterArray()); + } + + /** + * Create a new success message to answer a certain message with MID. + * @param UID The UID of the user sending the message. + * @param MID The MID of the message that is being answered. + * @param reply The success reply for the message with MID. + * @return The created success message + */ + public static SBProtocolMessage createSuccessMessage(UUID UID, UUID MID, SBProtocolParameterArray reply) { + // add the MID of the message that is being answered to the start of the reply parameters + reply.addParameter(0, new SBProtocolParameter(MID.toString())); + // return the message + return new SBProtocolMessage(UID, SBProtocolCommand.WORKD, reply); + } + + /** + * Create a new failure message to answer a certain message with MID. + * @param UID The UID of the user sending the message. + * @param MID The MID of the message that is being answered. + * @param reply The failure reply for the message with MID. + * @return the created failure message + */ + public static SBProtocolMessage createFailureMessage(UUID UID, UUID MID, SBProtocolParameterArray reply) { + // add the MID of the message that is being answered to the start of the reply parameters + reply.addParameter(0, new SBProtocolParameter(MID.toString())); + // return the message + return new SBProtocolMessage(UID, SBProtocolCommand.FAILD, reply); + } + + /** + * Return a new failure message sent from a given UID with parameters. + * @param UID The UID of the sender. + * @param params The params to send with the answer. + */ + public void returnFailureMessage(UUID UID, SBProtocolParameterArray params) { + if(getSocket() != null) getSocket().sendMessage(createFailureMessage(UID, getMID(), params)); + } + + /** + * Return a new success message sent from a given UID with parameters. + * @param UID The UID of the sender. + * @param params The params to send with the answer. + */ + public void returnSuccessMessage(UUID UID, SBProtocolParameterArray params) { + if(getSocket() != null) getSocket().sendMessage(createSuccessMessage(UID, getMID(), params)); + } + + /** + * Return a new success message sent from a given UID with parameters. + * @param UID The UID of the sender. + * @param params The params to send with the answer. + */ + public void returnSuccessMessage(UUID UID, String... params) { + if(getSocket() != null) getSocket().sendMessage(createSuccessMessage(UID, getMID(), new SBProtocolParameterArray(params))); + } + + /** + * Return a new failure message sent from a given UID. + * @param UID The UID of the sender. + */ + public void returnFailureMessage(UUID UID) { + if(getSocket() != null) getSocket().sendMessage(createFailureMessage(UID, getMID(), new SBProtocolParameterArray())); + } + + /** + * Return a new success message sent from a given UID. + * @param UID The UID of the sender. + */ + public void returnSuccessMessage(UUID UID) { + if(getSocket() != null) getSocket().sendMessage(createSuccessMessage(UID, getMID(), new SBProtocolParameterArray())); + } + + /** + * Return an answer to this message. + * @param message The answer to return. + */ + public void returnMessage(SBProtocolMessage message) { + if(getSocket() != null) getSocket().sendMessage(message); + } + + // getters and setters + + + public SBSocket getSocket() { + return socket; + } + + public void setSocket(SBSocket socket) { + this.socket = socket; + } + + public UUID getUID() { + return UID; + } + + public void setUID(UUID UID) { + this.UID = UID; + } + + public UUID getMID() { + return MID; + } + + public SBProtocolCommand getCommand() { + return command; + } + + /** + * Set the command and its corresponding module. + * @param command The command to set. + */ + public void setCommand(SBProtocolCommand command) { + this.command = command; + this.module = command.getModule(); + } + + public SBProtocolCommand.SBProtocolModule getModule() { + return module; + } + + public Date getSentDate() { + return sentDate; + } + + public void setSentDate(Date sentDate) { + this.sentDate = sentDate; + } +} diff --git a/src/network/SBProtocolParameter.java b/src/network/SBProtocolParameter.java new file mode 100644 index 0000000..88fa124 --- /dev/null +++ b/src/network/SBProtocolParameter.java @@ -0,0 +1,96 @@ +package network; + +/** + * One parameter in the SafariBowl protocol. + */ +public class SBProtocolParameter { + + private String content; + + /** + * Create parameter from string. + * @param parameter The string to create the parameter from. + */ + public SBProtocolParameter(String parameter) { + this.content = parameter; + } + + /** + * Empty constructor. + */ + public SBProtocolParameter() { + this.content = ""; + } + + /** + * Escape and return the content of this parameter. + * @return The escaped content of this parameter. + */ + public String escape() { + // replace the set [&, ", space, [, ], \t, \n] with the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] and return + return content.replace("&", "&").replace("\"", """).replace(" ", "&space;").replace("[", "&brackl;").replace("]", "&brackr;").replace("\t", "&tab;").replace("\n", "&newl;"); + } + + /** + * Unescape and return the content of this parameter. + * @return The unescaped content of this parameter. + */ + public String unescape() { + // replace the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] with the set [&, ", space, [, ], \t, \n] and return + return content.replace("&newl;", "\n").replace("&tab;", "\t").replace("&brackr;", "]").replace("&brackl;", "[").replace("&space;", " ").replace(""", "\"").replace("&", "&"); + } + + /** + * Escape and return the given content. + * @param content The content to escape. + * @return The escaped content. + */ + public static String escape(String content) { + // replace the set [&, ", space, [, ], \t, \n] with the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] and return + return content.replace("&", "&").replace("\"", """).replace(" ", "&space;").replace("[", "&brackl;").replace("]", "&brackr;").replace("\t", "&tab;").replace("\n", "&newl;"); + } + + /** + * Unescape and return the given content. + * @param content The content to unescape. + * @return The unescaped content. + */ + public static String unescape(String content) { + // replace the set [&, ", &space;, &brackl;, &brackr;, &tab;, &newl;] with the set [&, ", space, [, ], \t, \n] and return + return content.replace("&newl;", "\n").replace("&tab;", "\t").replace("&brackr;", "]").replace("&brackl;", "[").replace("&space;", " ").replace(""", "\"").replace("&", "&"); + } + + /** + * Get the string representation of this parameter. + * @return The string representation of this parameter. + */ + public String toString() { + return "\""+escape()+"\""; + } + + /** + * Find out if a SBProtocolParameter is a SBProtocolParameterArray + * @return False because this is not an array. + */ + public boolean isArray() { + return false; + } + + /** + * Create a new SBProtocolParameterArray with the content of this parameter. + * @return The created parameter array. + */ + public SBProtocolParameterArray toArray() { + return new SBProtocolParameterArray(this); + } + + /** + * Get the content of this message. + * @return The content of this message. + */ + public String getContent() { + return content; + } + +} + diff --git a/src/network/SBProtocolParameterArray.java b/src/network/SBProtocolParameterArray.java new file mode 100644 index 0000000..fd7f2de --- /dev/null +++ b/src/network/SBProtocolParameterArray.java @@ -0,0 +1,218 @@ +package network; + +import util.SBLogger; + +import java.util.Arrays; +import java.util.Vector; +import java.util.logging.Level; + +/** + * Representation of a parameter array in a sb protocol message + * Created by milan on 27.3.15. + */ +public class SBProtocolParameterArray extends SBProtocolParameter { + + private static final SBLogger L = new SBLogger(SBProtocolParameterArray.class.getName(), util.SBLogger.LOG_LEVEL); + + private Vector parameters = new Vector(); + + /** + * Create an empty parameter array. + */ + public SBProtocolParameterArray() { + super(); + this.parameters = new Vector(); + } + + /** + * Create a new parameter array with one or more parameters. + * @param parameters The parameters to put into the parameter array. + */ + public SBProtocolParameterArray(SBProtocolParameter... parameters) { + super(); + this.parameters.addAll(Arrays.asList(parameters)); + } + /** + * Create a new parameter array with one or more parameters from strings. + * @param parameters The strings to put into the parameter array. + */ + public SBProtocolParameterArray(String... parameters) { + super(); + for(String parameter: parameters) this.parameters.add(new SBProtocolParameter(parameter)); + } + + /** + * Get the parameter at index i. + * @param i The index of the parameter to return. + * @return The parameter at index i. + */ + public SBProtocolParameter getParameter(int i) { + return parameters.get(i); + } + + /** + * Get the size of the parameter array. + * @return The size of the parameter array. + */ + public int size() { + return parameters.size(); + } + + /** + * Append parameter to parameter array. + * @param parameter The parameter to add. + */ + public void addParameter(SBProtocolParameter parameter) { + if(parameter != null) parameters.add(parameter); + } + + /** + * Insert parameter in parameter array at specified index. + * @param index The index to insert the parameter at. + * @param parameter The parameter to add. + */ + public void addParameter(int index, SBProtocolParameter parameter) { + parameters.add(index, parameter); + } + + /** + * Overwrite the escape method from SBProtocolParameter + * @return The escaped parameter array. + */ + public String escape() { + return toString(); + } + + /** + * Find out if a SBProtocolParameter is a SBProtocolParameterArray + * @return True because this is an array. + */ + public boolean isArray() { + return true; + } + + public SBProtocolParameterArray toArray() { + return this; + } + + /** + * Get the string representation of this parameter array (with escaped parameters). + * @return The string representation of this parameter array. E.g. "["param1", "param2", ["param3", "param4"]]" + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for(SBProtocolParameter parameter: parameters) { + sb.append(parameter).append(", "); + } + if(sb.length() > 1) { // there was at least one parameter added + sb.replace(sb.length() - 2, sb.length(), "]"); + } else { // array is empty, only add closing bracket + sb.append("]"); + } + return sb.toString(); + } + + /** + * Get the string representation of this parameter array. + * @return The string representation of this parameter array. E.g. "["param1", "param2", ["param3", "param4"]]" + */ + public String toStringUnescaped() { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for(SBProtocolParameter parameter: parameters) { + sb.append(unescape(parameter.toString())).append(", "); + } + if(sb.length() > 1) { // there was at least one parameter added + sb.replace(sb.length() - 2, sb.length(), "]"); + } else { // array is empty, only add closing bracket + sb.append("]"); + } + return sb.toString(); + } + + /** + * Create a new SBProtocolParameterArray from an ESCAPED string. (see SBProtocolParameter.escape()) + * NOTE: This method will fail if parameter strings contain unescaped spaces, ", [, or ]. + * @param p The ESCAPED string to create a parameter array from. + * @return The created parameter array. Null if there was an error creating the parameter array. + */ + public static SBProtocolParameterArray paraception(String p) { + // prepare return array + SBProtocolParameterArray returnArray = new SBProtocolParameterArray(); + // remove whitespace in this string + p = p.replaceAll("\\s+",""); + if(p.startsWith("[") && p.endsWith("]") || p.startsWith("\"") && p.endsWith("\"")) { // p starts and ends correctly + // strip [ and ] from string if there + if(p.startsWith("[") && p.endsWith("]")) p = p.substring(1, p.length() - 1); + // return empty array if string is empty after stripping + if(p.equals("")) return new SBProtocolParameterArray(); + // prepare counter + int c = 0; + // loop the whole parameter + while(c < p.length()-1) { + // prepare subparameter string builder + StringBuilder subparameter = new StringBuilder(); + if (p.charAt(c) == '"') { // subparameter is simple parameter + // skip first quote + c++; + // get simple subparameter + while (p.charAt(c) != '"') { // loop until a closing quote appears + if(c >= p.length()-1) { + L.log(Level.WARNING, "Tried to parse malformed parameter into parameter array. Definetly not doing that!"); + return null; + } + // add character to subparameter stringbuilder and count c up + subparameter.append(p.charAt(c)); + c++; + } + // skip closing quote + c++; + // add collected parameter string as parameter + returnArray.addParameter(new SBProtocolParameter(SBProtocolParameter.unescape(subparameter.toString()))); + } else if(p.charAt(c) == '[') { // subparameter is array + // skip first bracket + c++; + // get subparameter array + int level = 0; // how deep the loop is in the array (to find the correct closing bracket) + while (p.charAt(c) != ']' && level == 0) { // loop until a closing quote at level 0 appears + if(c >= p.length()-1) { + L.log(Level.WARNING, "Tried to parse malformed parameter into parameter array. Definetly not doing that!"); + return null; + } + // increase level if an opening bracket appears and decrease level if a closing bracket appears + if(p.charAt(c) == '[') level++; + if(p.charAt(c) == ']') level--; + // add character to subparameter stringbuilder and count c up + subparameter.append(p.charAt(c)); + c++; + } + // skip last closing bracket + c++; + // recursively get the parameter array of this subparameter array string + SBProtocolParameterArray subArray = paraception(subparameter.toString()); + // add collected parameter string as parameter + returnArray.addParameter(subArray); + } else { // character at beginning of next subparameter is not [ nor " + L.log(Level.WARNING, "Tried to parse malformed parameter into parameter array. Definetly not doing that!"); + return null; + } + // check after last parameter + if(c >= p.length()-1) { // reached end of array + break; + } else if(p.charAt(c) != ',') { // the next character after a parameter must be the end of the array or a comma + L.log(Level.WARNING, "Tried to parse malformed parameter into parameter array. Definetly not doing that!"); + return null; + } else c++; // skip comma + } + } else { + if(p.equals("")) return new SBProtocolParameterArray(); // p was empty, which is allowed + else { + L.log(Level.WARNING, "Tried to parse malformed parameter into parameter array. Definetly not doing that!"); + return null; + } + } + return returnArray; + } + +} diff --git a/src/network/SBSocket.java b/src/network/SBSocket.java new file mode 100644 index 0000000..e7e49cd --- /dev/null +++ b/src/network/SBSocket.java @@ -0,0 +1,249 @@ +package network; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.*; +import java.util.Date; +import java.util.UUID; +import java.util.logging.Level; + +/** + * A subclass of socket with an attached UID, listener and reader. And methods to send SBProtocolMessages. + * Created by milan on 23.3.15. + */ +public class SBSocket { + + private UUID UID; + private BufferedReader listener; + private PrintWriter writer; + private SBSocketManager socketManager; + private Socket socket; + private SBProtocolReceiver receiver; + + /** + * Create new SBSocket with an existing socket and a UUID. + * @param socket The socket to create this socket with. + * @param UID The UID of this SBSocket. + * @param socketManager The socket manager of this SBSocket. + * @throws SBNetworkException If there was an exception while creating the socket. + */ + public SBSocket(Socket socket, UUID UID, SBSocketManager socketManager) throws SBNetworkException { + this.socket = socket; + this.UID = UID; + this.socketManager = socketManager; + createListenerAndWriter(); + createThreads(); + } + + /** + * Create new SBSocket with a UUID, a listener and a writer. + * @param address The address to connect to. + * @param port The port to connect to. + * @param UID The UID of this SBSocket. + * @param socketManager The socket manager of this SBSocket. + * @throws SBNetworkException If there was an exception while creating the socket. + */ + public SBSocket(InetAddress address, int port, UUID UID, SBSocketManager socketManager) throws SBNetworkException { + try { + this.socket = new Socket(address, port); + } catch (IOException e) { + socketManager.getParent().log(Level.SEVERE, "Exception while creating socket for SBSocket. " + e.toString()); + throw new SBNetworkException(); + } + this.UID = UID; + this.socketManager = socketManager; + createListenerAndWriter(); + createThreads(); + } + + /** + * Create new SBSocket with an empty UUID, a listener and a writer. + * @param address The address to connect to. + * @param port The port to connect to. + * @param socketManager The socket manager of this SBSocket. + * @throws SBNetworkException If there was an exception while creating the socket. + */ + public SBSocket(InetAddress address, int port, SBSocketManager socketManager) throws SBNetworkException { + try { + this.socket = new Socket(address, port); + } catch (IOException e) { + getSocketManager().getParent().log(Level.SEVERE, "Exception while creating socket for SBSocket. " + e.toString()); + throw new SBNetworkException(); + } + this.UID = new UUID(0,0); + this.socketManager = socketManager; + createListenerAndWriter(); + createThreads(); + } + + /** + * Create the receiver thread for this socket. + */ + private void createThreads() { + // prepare thread for the receiver + receiver = new SBProtocolReceiver(getSocketManager().getProtocolManager()); + // start receiver thread + (new Thread(receiver)).start(); + } + + /** + * Create the listener and the writer for this socket. + * @throws SBNetworkException If the creation failed. + */ + public void createListenerAndWriter() throws SBNetworkException { + try { + listener = new BufferedReader(new InputStreamReader(socket.getInputStream())); + getSocketManager().getParent().log(Level.FINE, "Successfully created listener."); + } catch (IOException e) { + getSocketManager().getParent().log(Level.SEVERE, "Exception while trying to create listener. " + e.toString()); + throw new SBNetworkException(); + } + try { + writer = new PrintWriter(socket.getOutputStream(), true); + getSocketManager().getParent().log(Level.FINE, "Successfully created writer."); + } catch (IOException e) { + getSocketManager().getParent().log(Level.SEVERE, "Exception while trying to create writer. " + e.toString()); + throw new SBNetworkException(); + } + } + + /** + * Send a message to the connected socket. + * @param message The message to send. + */ + public void sendMessage(SBProtocolMessage message) { + if (message != null) { + // add to unanswered messages only if the message is no answer to another message and no ping + if (message.getModule() != SBProtocolCommand.SBProtocolModule.SUC + && message.getModule() != SBProtocolCommand.SBProtocolModule.FAI + && message.getModule() != SBProtocolCommand.SBProtocolModule.PNG) + socketManager.protocolManager.addUnansweredMessage(message); + // log + if(message.getModule() != SBProtocolCommand.SBProtocolModule.PNG) // don't log ping messages + getSocketManager().getParent().log(Level.FINE, "Sending message " + message.toStringShortenUUID()); + // set sent date + message.setSentDate(new Date(System.currentTimeMillis())); + // sign message with this socket for returning answers + message.setSocket(getThis()); + // send it + writer.println(message.toString()); + if(writer.checkError()) getSocketManager().getParent().log(Level.FINE, "Exception while sending message. Socket probably disconnected."); + } else { + getSocketManager().getParent().log(Level.SEVERE, "Tried to send NULL message. Obviously not sending this."); + } + } + + /** + * Read a line from the input stream. + * @return The line read from the listener. Null if an error occurred. + * @throws SBNetworkException if the socket is unable to read the next line. + */ + public String readLine() throws SBNetworkException { + try { + if(listener == null) { + getSocketManager().getParent().log(Level.SEVERE, "Exception while trying to read line. Listener is null."); + throw new SBNetworkException(); + } + return listener.readLine(); + } catch (IOException e) { + getSocketManager().getParent().log(Level.WARNING, "Unable to read next line."); + throw new SBNetworkException(); + } + } + + public boolean isConnected() { + return socket != null && socket.isConnected(); + } + + /** + * The thread processing received messages. + */ + private class SBProtocolReceiver extends Thread { + SBProtocolManager manager; + + SBProtocolReceiver(SBProtocolManager manager) { + this.manager = manager; + } + + public void run() { + while (true) { + if(socket != null) { + String readLine; + try { + readLine = readLine(); + } catch (SBNetworkException e) { + readLine = null; + socket = null; + } + SBProtocolMessage receivedMessage = SBProtocolMessage.fromString(readLine); + if (receivedMessage != null) { + if(receivedMessage.getModule() != SBProtocolCommand.SBProtocolModule.PNG) // don't log ping messages + getSocketManager().getParent().log(Level.FINE, "Received message " + receivedMessage.toStringShortenUUID()); + // sign message with this socket for returning answers + receivedMessage.setSocket(getThis()); + if (receivedMessage.getCommand() == SBProtocolCommand.PIING) { + // return a success message (will only ever be executed by a client) + getSocketManager().getParent().log(Level.FINEST, "Answered ping message " + receivedMessage.getMID().toString().substring(0, 8)); + sendMessage(SBProtocolMessage.createPingAnswer(getSocketManager().getParent().UID, receivedMessage.getMID())); + getSocketManager().setLastPingAnswered(new Date(System.currentTimeMillis())); + } else if (receivedMessage.getCommand() == SBProtocolCommand.POONG) { + // put in pingAnswersToProcess (will only ever be executed by a server) + getSocketManager().getProtocolManager().putInQueue(getSocketManager().getProtocolManager().getPingAnswersToProcess(), receivedMessage); + getSocketManager().getParent().log(Level.FINEST, "Received ping answer " + receivedMessage.getMID().toString().substring(0, 8)); + } else if(receivedMessage.getModule() != SBProtocolCommand.SBProtocolModule.SUC && receivedMessage.getModule() != SBProtocolCommand.SBProtocolModule.FAI) { + // forward all messages that are no answers to the specific (client/server) protocol manager + getSocketManager().getProtocolManager().putInQueue(getSocketManager().getProtocolManager().getMessagesToProcess(), receivedMessage); + getSocketManager().getParent().log(Level.FINE, "Put the message " + receivedMessage.toStringShortenUUID() + " in handle queue."); + } else { + // remove the original message from the unanswered messages if this is an answer to that original message + getSocketManager().getProtocolManager().putInQueue(getSocketManager().getProtocolManager().getAnswersToProcess(), receivedMessage); + getSocketManager().getParent().log(Level.FINE, "Put the answer " + receivedMessage.toStringShortenUUID() + " in handle queue"); + } + } + try { // sleep for a bit after each received message (prevents null from flooding the logs) + Thread.sleep(10); + } catch (InterruptedException e) { e.printStackTrace(); } + } else { + break; + } + } + } + } + + /** + * Close this socket. + * @throws IOException If there was an error closing this socket. + */ + public void close() throws IOException { + if(socket != null) socket.close(); + socket = null; + } + + // getters and setters + + public UUID getUID() { + return UID; + } + + public void setUID(UUID UID) { + this.UID = UID; + } + + public BufferedReader getListener() { + return listener; + } + + public PrintWriter getWriter() { + return writer; + } + + public SBSocketManager getSocketManager() { + return socketManager; + } + + private SBSocket getThis() { + return this; + } +} \ No newline at end of file diff --git a/src/network/SBSocketManager.java b/src/network/SBSocketManager.java new file mode 100644 index 0000000..7f8e7a6 --- /dev/null +++ b/src/network/SBSocketManager.java @@ -0,0 +1,83 @@ +package network; + +import util.SBApplication; + +import java.util.Date; +import java.util.UUID; +import java.util.Vector; + +/** + * An interface for both the server and the client socket managers. + * Created by milan on 28.3.15. + */ +public abstract class SBSocketManager { + + public static final int PING_SENDER_INTERVAL = 3*1000; // in milliseconds + public static final int PING_MESSAGE_TIMEOUT = 8*1000; // in milliseconds + protected int port = 0; + protected SBApplication parent; + SBProtocolManager protocolManager; + private Date lastPingAnswered; + + /** + * Create a new socket manager for application parent. + * @param parent The application to create this socket manager for. + * @param protocolManager The protocol parameter for this socket manager. + */ + public SBSocketManager(SBApplication parent, SBProtocolManager protocolManager) { + this.parent = parent; + this.protocolManager = protocolManager; + this.lastPingAnswered = new Date(System.currentTimeMillis()); + } + + /** + * Get the parent application of this socket manager. + * @return The parent application of this socket manager. + */ + public SBApplication getParent() { + return parent; + } + + /** + * Get the procotol manager of this socket manager. + * @return The protocol manager of this socket manager. + */ + public SBProtocolManager getProtocolManager() { + return protocolManager; + } + + public SBSocketManager getThis() { + return this; + } + + public abstract void removeSocket(SBSocket socket); + + public abstract SBSocket getSocket(UUID UID); + + /** + * Send a message to a connected socket. Server searches for UID in his connected Sockets and Client always sends the message to the connected server if it is connected. + * @param UID The UID of the socket to send the message to. (The client socket manager ignores this) + * @param message The protocol message to send. + */ + public abstract void sendMessage(UUID UID, SBProtocolMessage message); + + public abstract Vector getSockets(); + + public abstract void startConnectionListener(); + + public Date getLastPingAnswered() { + return lastPingAnswered; + } + + public void setLastPingAnswered(Date lastPingAnswered) { + this.lastPingAnswered = lastPingAnswered; + } + + /** + * Get the port to which the socket is currently connected to. + * @return The port. 0 if the socket is not connected. + */ + public int getPort() { + return port; + } +} diff --git a/src/server/Server.java b/src/server/Server.java new file mode 100644 index 0000000..40179b5 --- /dev/null +++ b/src/server/Server.java @@ -0,0 +1,571 @@ +package server; + +import gameLogic.GameController; +import server.logic.*; +import network.*; +import server.display.*; +import util.*; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.net.InetAddress; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +/** + * The main server application + * Created by milan on 18.3.15. + */ +public class Server extends SBApplication { + + private static final SBLogger L = new SBLogger(Server.class.getName(), util.SBLogger.LOG_LEVEL); + + public static final int DEFAULT_PORT = 9989, TIME_AFTER_CONNECTION_LOST_BEFORE_AUTOSURRENDER = 40*1000; + public static final Level LOG_OUTPUT_LEVEL = Level.INFO; + + private Vector usersWaitingForRandomGame = new Vector(); + private Vector runningMatches = new Vector(); + private ServerSocketManager socketManager; + private ServerProtocolManager protocolManager; + private ServerFrame frame; + private UserManager userManager; + private ServerMessageProcessor messageProcessor; + + /** + * The main method of the server. + * @param args Command line arguments. + */ + public static void main(String[] args) { + + Server server = new Server(); + server.runServer(); + + } + + /** + * Start the server application + */ + public void runServer() { + // prepare and show server frame + frame = new ServerFrame(this); + frame.showServerPanel(); + frame.setVisible(true); + frame.getServerPanel().setControlsEnabled(false); + + // prepare team manager + loadAvailableTeamsLocally(); + frame.getServerPanel().setControlsEnabled(true); + + // prepare the message processor + messageProcessor = new ServerMessageProcessor(this); + + // prepare the protocol and socket managers + protocolManager = new ServerProtocolManager(this); + socketManager = new ServerSocketManager(this, protocolManager); + + // prepare the user manager + File userDatabase = new File("data/users"); + boolean madeDir = userDatabase.getParentFile().mkdirs(); + try { + if (!userDatabase.exists()) + if(!userDatabase.createNewFile()) throw new SBException(); + userManager = new UserManager(this, userDatabase); + } catch (Exception e) { + log(Level.SEVERE, "Couldn't create user manager. Quitting."); + e.printStackTrace(); + for (int i = 0; i < e.getStackTrace().length; i++) { + L.log(Level.SEVERE, e.getStackTrace()[i].toString()); + } + System.exit(-1); + } + + // read old games from file + getLoggedGames(GameController.SERVER_GAMES_FILE.getPath()); + + // prepare games and users updater + (new Timer(5000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + broadcastUpdatedGamesList(); + } + })).start(); + } + + // MESSAGE & ANSWER PROCESSING + + /** + * Tell the message processor to process any new incoming messages. + */ + public void processMessages() { + while (getProtocolManager() == null) // sometimes accessing getProtocolManager() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } + while (getProtocolManager().getMessagesToProcess() == null) // sometimes accessing getMessagesToProcess() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } + + while (getProtocolManager().getMessagesToProcess().size() > 0) { + SBProtocolMessage message = getProtocolManager().getNextMessageToProcessAndStoreIt(); + if (message.getSocket() != null) getMessageProcessor().processMessage(message); + else log(Level.WARNING, "Message " + message.toStringShortenUUID() + " has no socket to return answers to. Dropping."); + } + } + + /** + * Tell the answer processor to process any new incoming answers. + */ + public void processAnswers() { + while(getProtocolManager() == null) { // sometimes accessing getProtocolManager() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + while(getProtocolManager().getAnswersToProcess() == null) { // sometimes accessing getAnswersToProcess() throws a mysterious null pointer exception. Wait until it can't anymore + try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } + + while(getProtocolManager().getAnswersToProcess().size() > 0) { + SBProtocolMessage answer = getProtocolManager().getAnswersToProcess().poll(); // get message + if(answer.getSocket() != null) getMessageProcessor().processAnswer(answer); // process + else log(Level.WARNING, "Message "+answer.toStringShortenUUID()+" has no socket to return answers to. Dropping."); + } + } + + // ACTIONS + + public void broadcastUpdatedGamesList() { + SBProtocolParameterArray gamesParameters = new SBProtocolParameterArray(); + + // add running matches + for(ServerMatch match: getRunningMatches()) + if(match.isRunning()) + gamesParameters.addParameter(new SBProtocolParameterArray(match.getUser(0).getName(), match.getUser(1).getName(), match.getScoreFromTeam(0)+"", match.getScoreFromTeam(1)+"")); + // add not yet running matches + for(User user: getUsersWaitingForRandomGame()) + gamesParameters.addParameter(new SBProtocolParameterArray(user.getName())); + + sendBroadcastMessage(SBProtocolCommand.UPGAM, gamesParameters); + broadcastUpdatedUsersList(); + broadcastUpdatedHighscoresList(); + } + + public void broadcastUpdatedUsersList() { + SBProtocolParameterArray usersParameters = new SBProtocolParameterArray(); + + for(User user: getUserManager().getUsers()) { + if(user.isLoggedIn()) { + SBProtocolParameterArray userParameters = new SBProtocolParameterArray(); + userParameters.addParameter(new SBProtocolParameter(user.getName())); + if(getUsersWaitingForRandomGame().contains(user)) + userParameters.addParameter(new SBProtocolParameter("")); + else if(user.isInGame()) { + ServerMatch match = null; + int userIndex = 0; + for(ServerMatch potentialMatchWithUserInIt: getRunningMatches()) { + if(potentialMatchWithUserInIt.getUser(0).equals(user)) { + match = potentialMatchWithUserInIt; + userIndex = 0; + break; + } else if(potentialMatchWithUserInIt.getUser(1).equals(user)) { + match = potentialMatchWithUserInIt; + userIndex = 1; + break; + } + } + if(match != null) { + userParameters.addParameter(new SBProtocolParameter(match.getScoreFromTeam(userIndex) + "")); + userParameters.addParameter(new SBProtocolParameter(match.getScoreFromTeam(userIndex == 0 ? 1 : 0) + "")); + } + } + usersParameters.addParameter(userParameters); + } + } + + sendBroadcastMessage(SBProtocolCommand.UPUSR, usersParameters); + } + + public void broadcastUpdatedHighscoresList() { + SBProtocolParameterArray scoresParameters = new SBProtocolParameterArray(), + scoreParameters; + //noinspection unchecked + Vector usersTemp = (Vector) getUserManager().getUsers().clone(); + DecimalFormat format = new DecimalFormat("0.###"); + double max; + int maxIndex; + + while(scoresParameters.size() < getUserManager().getUsers().size()) { + max = maxIndex = -1; + + for(int i = 0; i < usersTemp.size(); i++) { + User user = usersTemp.get(i); + double ratio = (double)user.getWins()/(user.getLosses() == 0 ? 1 : user.getLosses()); + if(ratio > max) { + maxIndex = i; + max = ratio; + } + } + + User user = usersTemp.get(maxIndex); + usersTemp.remove(maxIndex); + scoreParameters = new SBProtocolParameterArray(); + scoreParameters.addParameter(new SBProtocolParameter(user.getName())); + scoreParameters.addParameter(new SBProtocolParameter(user.getWins()+"")); + scoreParameters.addParameter(new SBProtocolParameter(user.getLosses()+"")); + scoreParameters.addParameter(new SBProtocolParameter(format.format((double)user.getWins()/(user.getLosses() == 0 ? 1 : user.getLosses()))+"")); + scoreParameters.addParameter(new SBProtocolParameter(user.getTouchdownsScored()+"")); + scoreParameters.addParameter(new SBProtocolParameter(user.getTouchdownsReceived()+"")); + scoreParameters.addParameter(new SBProtocolParameter(format.format((double)user.getTouchdownsScored()/(user.getTouchdownsReceived() == 0 ? 1 : user.getTouchdownsReceived()))+"")); + scoreParameters.addParameter(new SBProtocolParameter(user.getCasualties()+"")); + scoresParameters.addParameter(scoreParameters); + } + + sendBroadcastMessage(SBProtocolCommand.SCORE, scoresParameters); + } + + /** + * Start listening on port. + * @param port The port to set the server socket to listen to. + */ + public void start(int port) { + try { + log(Level.INFO, "Starting server on port " + port + "."); + // assign a UID to this server + UID = UUID.randomUUID(); + log(Level.INFO, "My UID is " + UID); + // start server + socketManager.startServer(port); + frame.getServerPanel().setServerRunning(); + frame.getServerPanel().focusPortField(); + } catch (SBNetworkException e) { + log(Level.SEVERE, "Exception while starting server on port " + port + ". " + e.toString()); + frame.getServerPanel().focusPortField(); + } + } + + /** + * Stop the server socket + */ + public void stop() { + try { + log(Level.INFO, "Stopping server."); + socketManager.stopServer(); + logOutAllUsers(); + finishAllMatches(); + frame.getServerPanel().setServerStopped(); + frame.getServerPanel().focusPortField(); + } catch (SBNetworkException e) { + log(Level.SEVERE, "Could not stop server. " + e.toString()); + } + } + + /** + * Create a new game with two opposing users. + * @param opponent1 The first user in the game. + * @param opponent2 The second user in the game. + */ + public void createGame(User opponent1, User opponent2) { + opponent1.setInGame(true); + opponent2.setInGame(true); + ServerMatch match = new ServerMatch(this, opponent1, opponent2); + addRunningMatch(match); + broadcastUpdatedGamesList(); + } + + public void finishMatch(ServerMatch match) { + if(logmatches)addFinishedGame(match); + removeRunningMatch(match); + broadcastUpdatedGamesList(); + match.getUser(0).setInGame(false); + match.getUser(1).setInGame(false); + } + + // HELPERS + + /** + * Logs a message with a level to the logger and to the server window. + * @param level The level of the log message. + * @param message The message to log. + */ + public void log(Level level, String message) { + if(getFrame() != null) + if(getFrame().getServerPanel() != null) + if(level.intValue() >= LOG_OUTPUT_LEVEL.intValue()) getFrame().getServerPanel().writeMessage(message); + if(!message.endsWith("....")) L.log(level, message); + } + + /** + * Return a success answer for a received message. + * @param returnTo The message to return an answer to. + * @param parameters The parameters to send with the answer. + */ + public void returnSuccessMessage(SBProtocolMessage returnTo, String... parameters) { + returnTo.returnSuccessMessage(UID, new SBProtocolParameterArray(parameters)); + } + + /** + * Return a failure answer for a received message. + * @param returnTo The message to return an answer to. + * @param parameters The parameters to send with the answer. + */ + public void returnFailureMessage(SBProtocolMessage returnTo, String... parameters) { + returnTo.returnFailureMessage(UID, new SBProtocolParameterArray(parameters)); + } + + /** + * Sends a message to all users. + * @param command The command of the message to send. + * @param parameters The parameters of the message to send. + */ + public void sendBroadcastMessage(SBProtocolCommand command, String... parameters) { + socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, command, new SBProtocolParameterArray(parameters))); + } + + /** + * Sends a message to all users. + * @param command The command of the message to send. + * @param parameters The parameters of the message to send. + */ + public void sendBroadcastMessage(SBProtocolCommand command, SBProtocolParameterArray parameters) { + socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, command, parameters)); + } + + /** + * Gets a socket by its assigned UUID. + * @param UID The UUID to get the socket for. + * @return The socket found for this UUID or null if no socket exists with this UUID. + */ + public SBSocket getSocketByUID(UUID UID) { + for(SBSocket socket: getSocketManager().getSockets()) { + if(socket.getUID().equals(UID)) return socket; + } + return null; + } + + /** + * Checks whether a user exists. + * @param name The name to check. + * @return Whether the user with the given name exists. + */ + public boolean checkIfUserExists(String name) { + return getUserManager().existsName(name); + } + + /** + * Log out a user with a specific UID. + * @param UID The UID of the user to log out. + */ + public void logOutUserWithUID(final UUID UID) { + final User userToLogOut = getUserManager().getAuthenticatedUser(UID); + if(userToLogOut != null) { + getUsersWaitingForRandomGame().remove(userToLogOut); + for(final ServerMatch match: runningMatches) { + if(match.getUser(0).getUID().equals(UID) || match.getUser(1).getUID().equals(UID)) { + // if the user was in a running match, end the match if the user doesn't log in again after some time. + (new Thread(new Runnable() { + @Override + public void run() { + UUID uidWhenLostConnection = getUID(); + try { Thread.sleep(Server.TIME_AFTER_CONNECTION_LOST_BEFORE_AUTOSURRENDER); } catch (InterruptedException ignored) {} + // if user has not logged back in, auto-surrender for him. + // also check that the server UID has not changed (which would mean it was restarted) + if(getUserManager().getAuthenticatedUser(UID) == null && uidWhenLostConnection.equals(getUID())) { + for(ServerMatch matchCheck: runningMatches) { // check if match is still running + if(matchCheck.getUser(0).getUID().equals(UID) || matchCheck.getUser(1).getUID().equals(UID) + || matchCheck.getUser(0).getLastUID().equals(UID) || matchCheck.getUser(1).getLastUID().equals(UID)) { // users are in this match + if(match.isRunning()) match.finishGame(userToLogOut); + break; + } + } + } else { + userToLogOut.setInGame(true); + } + } + })).start(); + } + } + + String name = userToLogOut.getName(); + if (name != null) socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, SBProtocolCommand.BDCST, new SBProtocolParameterArray(MODERATOR_NAME, name + " fell down a cliff!"))); + else socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, SBProtocolCommand.BDCST, new SBProtocolParameterArray(MODERATOR_NAME, "Some unknown user fell down a cliff!"))); + + if (getUserManager().getAuthenticatedUser(UID) != null) getUserManager().getAuthenticatedUser(UID).setLoggedOut(); + } else socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, SBProtocolCommand.BDCST, new SBProtocolParameterArray(MODERATOR_NAME, "Some unknown user fell down a cliff!"))); + } + + /** + * Log out all users on this server. + */ + public void logOutAllUsers() { + getUserManager().logoutAllUsers(); + } + + /** + * Finishes all matches. + */ + public void finishAllMatches() { + for(int i = 0; i < getRunningMatches().size(); i++) + getRunningMatches().get(i).finishGame(); + } + + /** + * Checks whether a name is allowed. (doesn't exist yet, contains no spaces, longer than 0, not 'all'). + * @param name The name to be checked. + * @return Whether the name is allowed. + */ + public boolean validateName(String name) { + return !getUserManager().existsName(name) && !name.contains(" ") && name.length() > 0 && !name.toLowerCase().equals("all"); + } + + /** + * Check if there are any users waiting for a game against a random opponent. + * @return Whether there are any users waiting for a game against a random opponent. + */ + public boolean anyUsersWaitingForRandomGame() { + return !getUsersWaitingForRandomGame().isEmpty(); + } + + /** + * Check if a specific user is waiting for a game against a random opponent. + * @param user The user to check if they are waiting for a game against a random opponent. + * @return Whether the user is waiting for a game against a random opponent. + */ + public boolean userWaitingForRandomGame(User user) { + return getUsersWaitingForRandomGame().contains(user); + } + + /** + * Get the number of users waiting for a game against a random opponent. + * @return The number of users waiting for a game against a random opponent. + */ + public int getNumberOfUsersWaitingForRandomGame() { + return getUsersWaitingForRandomGame().size(); + } + + /** + * Request a list with all logged-in users from the server. + */ + public void getUsersList() { + String usersList = ""; + + for(User user: getUserManager().getUsers()) { + if(user.isLoggedIn()) { + usersList += user.getName(); + if(user.isInGame()) usersList += " – in game"; + usersList += "\n"; + } + } + usersList = usersList.replaceAll("\\n$", ""); // remove last newline + + if(usersList.length() > 0) getFrame().getServerPanel().writeMessage(usersList); + else getFrame().getServerPanel().writeMessage("No users online right now."); + } + + /** + * Request a list with all running and not running games from the server. + */ + public void getGamesList() { + String gamesList = ""; + + // add finished matches + for(String finishedGameString: getFinishedGames()) { + FinishedGame finishedGame = new FinishedGame(finishedGameString); + gamesList += finishedGame.getWinnerString() + " won against " + finishedGame.getLooserString() + ". " + finishedGame.getTeam1Score() + ":" + finishedGame.getTeam2Score() + "\n"; + } + // add running matches + for(ServerMatch match: getRunningMatches()) + if(match.isRunning()) + gamesList += match.getOpponent(0).getName() + " is playing against " + match.getOpponent(1).getName() + ". " + match.getScoreFromTeam(0) + ":" + match.getScoreFromTeam(1) + "\n"; + // add not yet running matches + for(User user: getUsersWaitingForRandomGame()) + gamesList += user.getName() + " is waiting for someone to join his game.\n"; + gamesList = gamesList.replaceAll("\\n$", ""); // remove last newline + + if(gamesList.length()> 0) getFrame().getServerPanel().writeMessage(gamesList); + else getFrame().getServerPanel().writeMessage("No games on the server right now."); + } + + /** + * Send a chat message to a user or to all users. + * @param recipientName The name of the user to send the message to. If this is 'all', the message is sent as a broadcast message to everyone. + * @param message The message to send. + */ + public void chat(String recipientName, String message) { + if(recipientName.equals("all")) { + socketManager.sendBroadcastMessage(new SBProtocolMessage(UID, SBProtocolCommand.BDCST, new SBProtocolParameterArray(MODERATOR_NAME, message))); + log(Level.INFO, "Sent broadcast message by " + MODERATOR_NAME + ": " + message); + } + else { + User recipient = getUserManager().getUser(recipientName); + if(recipient != null) { // user with name exists + SBSocket returnSocket = getSocketByUID(recipient.getUID()); + if(returnSocket != null) { // socket for recipient is connected + returnSocket.sendMessage(new SBProtocolMessage(UID, SBProtocolCommand.SENDM, new SBProtocolParameterArray(MODERATOR_NAME, message))); + log(Level.INFO, "Sent message by " + MODERATOR_NAME + " to " + recipientName + ": " + message); + } else log(Level.WARNING, recipientName + " is offline."); + } else log(Level.WARNING, recipientName + " does not exist"); + } + } + + // GETTERS & SETTERS + + public void addUserWaitingForRandomGame(User user) { + getUsersWaitingForRandomGame().add(user); + } + + public void removeUserWaitingForRandomGame(User user) { + getUsersWaitingForRandomGame().remove(user); + } + + public ServerFrame getFrame() { + return frame; + } + + public Vector getUsersWaitingForRandomGame() { + return usersWaitingForRandomGame; + } + + public ServerMessageProcessor getMessageProcessor() { + return messageProcessor; + } + + public SBSocketManager getSocketManager() { + return socketManager; + } + + public ServerProtocolManager getProtocolManager() { + return protocolManager; + } + + public UserManager getUserManager() { + return userManager; + } + + /** + * Returns a copy of all running matches (to iterate on, etc.). + * @return A copy of all running matches. + */ + @SuppressWarnings("unchecked") + public Vector getRunningMatches() { + return (Vector) runningMatches.clone(); + } + + /** + * Adds a match to the vector of all running matches. + * @param match The match to add. + */ + public void addRunningMatch(ServerMatch match) { + this.runningMatches.add(match); + } + + public void removeRunningMatch(ServerMatch match) { + this.runningMatches.remove(match); + } + + // UNUSED + + public void lostConnection() {} + + public void isConnecting() {} + + public void hasConnected(InetAddress address, int port, boolean connected) {} + +} diff --git a/src/server/display/ServerFrame.java b/src/server/display/ServerFrame.java new file mode 100644 index 0000000..5c8a957 --- /dev/null +++ b/src/server/display/ServerFrame.java @@ -0,0 +1,82 @@ +package server.display; + +import GUI.SBFrame; +import server.Server; + +import javax.swing.*; +import java.awt.*; + +/** + * The main frame for the main server application. + * Created by milan on 23.3.15. + */ +public class ServerFrame extends SBFrame { + + private static final String MAIN_TITLE = "SafariBowl", SERVER_TITLE = "SafariBowl Server"; + private JPanel mainPanel; + private CardLayout cardLayout; + private ServerPanel serverPanel; + private Server parent; + + /** + * Create a new server frame with a parent server. + * @param parent The server that is parent of this frame. + */ + public ServerFrame(Server parent) { + super(MAIN_TITLE); + setUp(parent); + } + + /** + * Create a new server frame with a parent server and a frame title. + * @param parent The server that is parent of this frame. + * @param title The title for the frame. + */ + public ServerFrame(Server parent, String title) { + super(title); + setUp(parent); + } + + /** + * Set the frame up. + * @param parent The server that is parent of this frame. + */ + private void setUp(Server parent) { + this.parent = parent; + + mainPanel = new JPanel(); + cardLayout = new CardLayout(); + mainPanel.setLayout(cardLayout); + // create panels + serverPanel = new ServerPanel(this); + // add panels + mainPanel.add(SERVER_TITLE, serverPanel); + cardLayout.addLayoutComponent(serverPanel, SERVER_TITLE); + add(mainPanel); + } + + /** + * Show the server configuration panel. + */ + public void showServerPanel() { + setSize(new Dimension(600, 420)); + setResizable(false); + cardLayout.show(mainPanel, SERVER_TITLE); + } + + /** + * Get the parent server. + * @return The server that is parent of this frame. + */ + public Server getServer() { + return parent; + } + + /** + * Get the server configuration panel. + * @return The server configuration panel of this frame. + */ + public ServerPanel getServerPanel() { + return serverPanel; + } +} diff --git a/src/server/display/ServerPanel.java b/src/server/display/ServerPanel.java new file mode 100644 index 0000000..6abd563 --- /dev/null +++ b/src/server/display/ServerPanel.java @@ -0,0 +1,294 @@ +package server.display; + +import GUI.SBFrame; +import GUI.SBGUIPanel; +import server.Server; +import util.SBLogger; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.logging.Level; + +/** + * The client configuration panel + * Created by milan on 23.3.15. + */ +public class ServerPanel extends SBGUIPanel { + + private JScrollPane messagesAreaPane; + private JTextArea messagesArea; + private JTextField portField, messageField; + private JLabel portLabel; + private JButton stopButton, startButton, sendMessageButton; + private ServerFrame parent; + private boolean autoFilledChatRecipient; + + /** + * Create a panel with server configuration to be displayed in a server frame. + * @param parent The server frame to display this panel in. + */ + public ServerPanel(ServerFrame parent) { + super(new GridBagLayout()); + this.parent = parent; + + // prepare components + messageField = new JTextField(20); + portLabel = new JLabel("Port"); + portField = new JTextField(Server.DEFAULT_PORT+"", 4); + messagesArea = new JTextArea(1, 20); + messagesAreaPane = new JScrollPane(messagesArea); + messagesAreaPane.setPreferredSize(new Dimension(messagesArea.getWidth(), 200)); + messagesArea.setEditable(false); + startButton = new JButton("Start Server"); + stopButton = new JButton("Stop Server"); + stopButton.setEnabled(false); + sendMessageButton = new JButton("Send"); + + addEventListeners(); + setLayout(); + + // add components + add(messagesAreaPane); + + add(messageField); + add(sendMessageButton); + + add(portLabel); + add(portField); + + add(startButton); + add(stopButton); + + // DOSTUFF™ + SBFrame.addScroller(messagesArea, messagesAreaPane); + } + + /** + * Set focus to the port field. + */ + public void focusPortField() { + portField.requestFocusInWindow(); + } + + /** + * Set the GUI to running state. + */ + public void setServerRunning() { + startButton.setText("Restart Server"); + stopButton.setEnabled(true); + } + + /** + * Set the GUI to stopped state. + */ + public void setServerStopped() { + startButton.setText("Start Server"); + stopButton.setEnabled(false); + } + + /** + * Enable or disable the buttons and fields. + * @param enable Whether the buttons should be enabled or disabled. + */ + public void setControlsEnabled(boolean enable) { + startButton.setEnabled(enable); + portField.setEnabled(enable); + messageField.setEnabled(enable); + sendMessageButton.setEnabled(enable); + } + + /** + * Tell parent to stop and start the server. + */ + private void restartServer() { + stopServer(); + startServer(); + } + + /** + * Tell parent to start the server. + */ + private void startServer() { + // get port from field + int port = Integer.parseInt(portField.getText()); + // start server if port is valid + if(port < 0 || port > 65535) portField.requestFocusInWindow(); + else parent.getServer().start(port); + } + + /** + * Tell parent to stop the server. + */ + private void stopServer() { + parent.getServer().stop(); + } + + /** + * Append a message without a sender to the message panel. + * @param message The message to append. + */ + public void writeMessage(String message) { + if(messagesArea.getText().length() <= 0) messagesArea.append(message); + else { + if(message.startsWith("Loading")) messagesArea.setText(message); + else messagesArea.append("\n" + message); + } + } + + /** + * Append a chat message to the message panel. + * @param sender The name of the sender of this message. + * @param message The message itself. + */ + public void addChatMessage(String sender, String message) { + if(messagesArea.getText().length() <= 0) messagesArea.append(sender + ": " + message); + else messagesArea.append("\n" + sender + ": " + message); + } + + /** + * Send a chat message to some- or everybody. + */ + private void sendMessage() { + String message, recipient; + String text = messageField.getText(); + if(messageField.getText().startsWith("@")) { + // "@milan hello there!" -> message = "hello there!" recipient = "milan" + message = text.replaceAll("^@\\S+", ""); + if(message.length() > 1) { + message = message.substring(1, message.length()); + recipient = text.substring(1, text.length() - message.length() - 1); + } + else return; // don't send if message was empty + } else { + // check if it is a command + if(text.toLowerCase().equals("/games") || text.toLowerCase().equals("/g")) { // get games list command + parent.getServer().log(Level.INFO, "Getting games list."); + parent.getServer().getGamesList(); + messageField.setText(""); + return; + } else if(text.toLowerCase().equals("/users") || text.toLowerCase().equals("/u")) { // get users list command + parent.getServer().log(Level.INFO, "Getting list of users online."); + parent.getServer().getUsersList(); + messageField.setText(""); + return; + } else { + message = text; + recipient = "all"; + } + } + parent.getServer().chat(recipient.toLowerCase().trim(), message); + if(!recipient.equals("all")) { + messageField.setText("@"+recipient+" "); + autoFilledChatRecipient = true; + } else messageField.setText(""); + } + + /** + * Outsourced method for setting any handlers to avoid monolith constructor. + */ + private void addEventListeners() { + portField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + startButton.doClick(); + } + }); + startButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if(startButton.getText().equals("Start Server")) startServer(); + else restartServer(); + } + }); + stopButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stopServer(); + } + }); + messagesAreaPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + messagesArea.select(messagesArea.getHeight() + 1000000000, 0); + } + }); + messageField.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + sendMessage(); + } + }); + messageField.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE && autoFilledChatRecipient) messageField.setText(""); + if (e.getKeyCode() != KeyEvent.VK_ENTER) autoFilledChatRecipient = false; + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + sendMessageButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + sendMessage(); + } + }); + sendMessageButton.addKeyListener(new KeyListener() { + public void keyTyped(KeyEvent e) { + } + + public void keyPressed(KeyEvent e) { + } + + public void keyReleased(KeyEvent e) { + // submit form if enter was typed on logoutButton + if (e.getKeyCode() == KeyEvent.VK_ENTER) sendMessage(); + } + }); + } + + /** + * Outsourced method for setting the layout to avoid monolith constructor. + */ + private void setLayout() { + // prepare layout + GridBagLayout layout = new GridBagLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + setLayout(layout); + + // set constraints + constraints.fill = GridBagConstraints.HORIZONTAL; + + constraints.insets = new Insets(0, DEFAULT_PADDING, DEFAULT_PADDING/2, 0); + constraints.gridwidth = 1; + layout.setConstraints(portLabel, constraints); + + constraints.insets = new Insets(0, 0, DEFAULT_PADDING/2, 0); + constraints.gridwidth = 1; + layout.setConstraints(portField, constraints); + + constraints.insets = new Insets(0, DEFAULT_PADDING, DEFAULT_PADDING/4, 0); + constraints.gridwidth = 4; + layout.setConstraints(messageField, constraints); + + constraints.insets = new Insets(0, 0, DEFAULT_PADDING/2, 0); + constraints.gridwidth = 3; + layout.setConstraints(startButton, constraints); + + constraints.gridwidth = GridBagConstraints.REMAINDER; + + constraints.insets = new Insets(0, 0, DEFAULT_PADDING/4, DEFAULT_PADDING); + layout.setConstraints(sendMessageButton, constraints); + + constraints.insets = new Insets(DEFAULT_PADDING/2, DEFAULT_PADDING, DEFAULT_PADDING/4, DEFAULT_PADDING); + layout.setConstraints(messagesAreaPane, constraints); + + constraints.insets = new Insets(0, 0, DEFAULT_PADDING/2, DEFAULT_PADDING); + layout.setConstraints(stopButton, constraints); + } +} diff --git a/src/server/logic/ServerMatch.java b/src/server/logic/ServerMatch.java new file mode 100644 index 0000000..da94e05 --- /dev/null +++ b/src/server/logic/ServerMatch.java @@ -0,0 +1,1060 @@ +package server.logic; + +import java.util.ConcurrentModificationException; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +import javax.vecmath.Vector2d; + +import network.SBProtocolCommand; +import network.SBProtocolMessage; +import network.SBProtocolParameterArray; +import server.Server; +import util.SBApplication; +import gameLogic.GameController; +import gameLogic.Pitch; +import gameLogic.PitchField; +import gameLogic.Player; +import gameLogic.PlayerCondition; +import gameLogic.Team; +import gameLogic.Weather; +import gameLogic.rules.*; + +/** + * A game on a server. + */ +public class ServerMatch extends GameController { + + private boolean[] teamsChosen = new boolean[]{false, false}; //Determines whether the Teams are chosen + private boolean[] teamsSet = new boolean[]{false, false}; //Determines whether the Teams are set on the Pitch + private volatile Player currentActorWaitingForAnswer, currentDefenderWaitingForAnswer, currentPusherWaitingForAnswer; + private volatile Vector2d currentDefenderFieldWaitingForAnswer, currentBackUpPosWaitingForAnswer; + private volatile Vector currentPlayersBeingPushed = new Vector(); + private volatile Vector currentPlayerPositionsBeingPushed = new Vector(), currentHighlitedFields = new Vector(); + private volatile int currentModificaorWaitingForAnswer; + private volatile SBProtocolMessage currentMessageWaitingForAnswer; + + /** + * Initializes the Match on the Server with the super Constructor from the Class Game, and then starts the Match tread itself. + * @param parent The parent client or server (SBApplication) to create this game for. + * @param coach1 The coach of the first team. + * @param coach2 The coach of the second team. + */ + public ServerMatch(SBApplication parent, User coach1, User coach2) { + super(parent, coach1, coach2); + for(Team team: parent.getTeamManager().getTeamBlueprints()) addAvailableTeam(team); + setFirstPlayer(); + setRunning(true); + // start message listener + start(); + } + + @Override + public void run() { + MessageListener messageListener = new MessageListener(this) { + @Override + public void processMessage(SBProtocolMessage message) { + try{ + boolean allowed = checkUserTurn(message); + if(allowed){ + switch(message.getCommand()){ + case ACTIO: + processMessageACTIO(message); + sendGame(); + break; + case EVENT: + processMessageEVENT(message); + sendGame(); + break; + case SRNDR: + processMessageSRNDR(message); + break; + default: + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + break; + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NOT_YOUR_TURN); + } + }catch(IndexOutOfBoundsException e){ + e.printStackTrace(); + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + } + } + + @Override + public void processAnswer(SBProtocolMessage answer) { + try { + SBProtocolMessage message = null; + try { + for(SBProtocolMessage messageThatIsPotentiallyAnswered: ((Server) getParent()).getProtocolManager().getUnansweredMessages()) { // for all unanswered messages + message = messageThatIsPotentiallyAnswered; + if (messageThatIsPotentiallyAnswered.getMID().equals(UUID.fromString(answer.getParameterContent(0)))) { // get the message whose MID equals the MID in the answer + message = messageThatIsPotentiallyAnswered; + break; + } else message = null; + } + } catch (ConcurrentModificationException ignored) {} // ignoring strange ConcurrentModificationException that does not affect any arse + + if(message != null) { + + boolean delete; + + switch (answer.getModule()) { + case SUC: + delete = processAnswerSUC(answer, message); + break; + case FAI: + delete = processAnswerFAI(answer, message); + break; + default: + delete = false; + break; + } + + if(delete) ((Server) getParent()).getProtocolManager().removeUnansweredMessage(message); + + } else getParent().log(Level.WARNING, "Received answer but found no message it belonged to: " + answer.toStringShortenUUID() + " Ignoring it."); + + } catch (IndexOutOfBoundsException e) { // Don't return failure message because answers don't expect (e.g. ignore) answers anyway + getParent().log(Level.WARNING, "Index out of bounds at process answer."); + } + } + + private void processMessageEVENT(SBProtocolMessage message) { + String eventString = message.getParameterContent(0); + if(teamsSet[0] && teamsSet[1] && gamePhase == 3){ + if(eventString.equals(SBProtocolMessage.EVENT_END_TURN)){ + endTurn(message); + + } + }else{ + if(eventString.equals(SBProtocolMessage.EVENT_ALL_PLAYERS_SET)){ + allPlayersSet(message); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_GAME_PHASE); + } + } + } + + private void processMessageACTIO(SBProtocolMessage message) { + if(teamsSet[0] && teamsSet[1] && gamePhase == 3){ + if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_MOVE)){ + move(message); + }else if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_THRW)){ + pass(message); + }else if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_BLCK)){ + block(message); + }else if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_SPCL)){ + invokeSpecialRule(message); + } + }else{ + if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_SET_TEAM)){ + setTeam(message); + }else if(message.getParameterContent(0).equals(SBProtocolMessage.ACTIO_SET_PLAYER) && gamePhase == 1){ + setPlayer(message); + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_GAME_PHASE); + } + } + } + + private void processMessageSRNDR(SBProtocolMessage message) { + User surrenderingUser = ((Server) getParent()).getUserManager().getAuthenticatedUser(message.getUID()); + if(surrenderingUser != null) { // surrendering user is logged in + if(surrenderingUser.isInGame()) { // surrendering user is currently in a game + finishGame(surrenderingUser); + returnSuccessMessage(message); + } else returnFailureMessage(message); + } else returnFailureMessage(message); + } + + private boolean processAnswerSUC(SBProtocolMessage answer, SBProtocolMessage message) { + try{ + switch (message.getCommand()) { + case EVENT: + return processSuccessAnswerEVENT(answer, message); + case ACTIO: + return processSuccessAnswerACTIO(answer, message); + default: + return false; + } + }catch(IndexOutOfBoundsException e){ + e.printStackTrace(); + returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + return false; + } + } + + private boolean processSuccessAnswerACTIO(SBProtocolMessage answer, SBProtocolMessage message) { + // TODO Auto-generated method stub + return false; + } + private boolean processSuccessAnswerEVENT(SBProtocolMessage answer, SBProtocolMessage message) { + if(answer.getParameters().size() > 1) { + if (answer.getParameterContent(1).equals(SBProtocolMessage.WORKD_DIE_CHOSEN)) { + try { + getParent().log(Level.INFO, "Client chose die with index " + answer.getParameterContent(2) + "."); + boolean removeMessage = ((RuleBlock)currentActorWaitingForAnswer.getRule(1)).blockReaction(message, answer, currentDefenderWaitingForAnswer); + sendGame(); + return removeMessage; + } catch (IndexOutOfBoundsException e) { + getParent().log(Level.WARNING, "Client returned DIE CHOSEN but without a chosen die. What a weirdo."); + return false; + } + + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.WORKD_GIVE_BALL) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE)){ + boolean removeMessage = giveBall(answer); + sendGame(); + return removeMessage; + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.WORKD_KICK) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_INITIATE_KICK)){ + boolean removeMessage = kick(answer); + sendGame(); + return removeMessage; + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.WORKD_DIRECTION) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_WHICH_DIRECTION)){ + boolean removeMessage = ((RulePush)currentActorWaitingForAnswer.getRule(2)).chosenDirection(currentDefenderWaitingForAnswer, message, answer, currentPusherWaitingForAnswer, currentBackUpPosWaitingForAnswer); + sendGame(); + return removeMessage; + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.WORKD_DECIDED) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_FOLLOW)){ + boolean removeMessage = ((RulePush)currentActorWaitingForAnswer.getRule(2)).follow(currentDefenderFieldWaitingForAnswer, message, answer); + sendGame(); + return removeMessage; + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.EVENT_API_AIM) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_API_AIM)){ + try{ + int xPos = Integer.parseInt(answer.getParameterContent(2)); + int yPos = Integer.parseInt(answer.getParameterContent(3)); + currentActorWaitingForAnswer.invokeFunctionByName(message.getParameterContent(1), xPos, yPos, findUserIndex(answer)); + sendGame(); + return true; + }catch(NumberFormatException e){ + returnFailureMessage(answer, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.EVENT_API_CHOICE) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_API_CHOICE)){ + try{ + int messageIndex = Integer.parseInt(answer.getParameterContent(2)); + int teamIndex = Integer.parseInt(message.getParameterContent(messageIndex*2+2)); + int playerIndex = Integer.parseInt(message.getParameterContent(messageIndex*2+3)); + if(message.getParameterContent(1).equals("$intercept")){ + Player defender = getTeam(teamIndex).getPlayers().get(playerIndex); + ((RuleThrow)defender.getRule(3)).intercept(currentActorWaitingForAnswer, teamIndex, playerIndex, findUserIndex(answer)); + }else{ + currentActorWaitingForAnswer.invokeFunctionByName(message.getParameterContent(1), teamIndex, playerIndex, findUserIndex(answer)); + } + sendGame(); + return true; + }catch(NumberFormatException e){ + returnFailureMessage(answer, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } else if(answer.getParameterContent(1).equals(SBProtocolMessage.EVENT_API_FIELD) && message.getParameterContent(0).equals(SBProtocolMessage.EVENT_API_FIELD)){ + try{ + if(message.getParameterContent(1).equals("$push")){ + boolean removeMessage = ((RulePush)currentActorWaitingForAnswer.getRule(2)).chosenDirection(currentDefenderWaitingForAnswer, message, answer, currentPusherWaitingForAnswer, currentBackUpPosWaitingForAnswer); + sendGame(); + return removeMessage; + }else{ + int xPos = Integer.parseInt(answer.getParameterContent(2)); + int yPos = Integer.parseInt(answer.getParameterContent(3)); + currentActorWaitingForAnswer.invokeFunctionByName(message.getParameterContent(1), xPos, yPos, findUserIndex(answer)); + sendGame(); + return true; + } + }catch(NumberFormatException e){ + returnFailureMessage(answer, SBProtocolMessage.FAILD_RECEIVED_WRONG_GAME_DATA); + } + } + } + return false; + } + + private boolean processAnswerFAI(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case EVENT: + return true; + case ACTIO: + return true; + default: + return false; + } + } + }; + messageListener.start(); + + while(isRunning()) { + //TODO: GAME LOOP + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + // HELPERS + + /** + * sends a Message from this Match to a User + * @param destinationUser the User to whom the message is sent + * @param command the Command of the Message + * @param parameters the Parameters of the Message + */ + public void sendMessage(User destinationUser, SBProtocolCommand command, String... parameters){ + sendMessage(destinationUser.getUID(), new SBProtocolMessage(new SBProtocolMessage(getParent().UID, command, parameters))); + } + + public void sendMessage(UUID destinationUID, SBProtocolCommand command, String... parameters) { + sendMessage(destinationUID, new SBProtocolMessage(new SBProtocolMessage(getParent().UID, command, parameters))); + } + + public boolean checkUserTurn(int actingUserIndex){ + if(gamePhase == 0){ + return true; + }else if(gamePhase == 1){ + if(actingPlayer == 0){ + if(actingUserIndex == 0){ + return true; + }else if(actingUserIndex == 1){ + return false; + } + }else if(actingPlayer == 1){ + if(actingUserIndex == 0){ + return false; + }else if(actingUserIndex == 1){ + return true; + } + } + return false; + }else if(gamePhase == 5){ + return false; + }else{ + if(firstPlayer == 0) { + if(actingUserIndex == 0) + return (roundCount < NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 0) || (roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 1); + else if(actingUserIndex == 1) + return (roundCount < NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 1) || (roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2&& roundCount%2 == 0); + } else if(firstPlayer == 1) { + if(actingUserIndex == 0) + return (roundCount < NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 1) || (roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 0); + else if(actingUserIndex == 1) + return (roundCount < NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 0) || (roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount%2 == 1); + } + return false; + } + } + + public boolean checkUserTurn(SBProtocolMessage message) { + return message.getCommand().equals(SBProtocolCommand.SRNDR) || checkUserTurn(findUserIndex(message)); + } + + public void endTurn(SBProtocolMessage message){ + endTurn(findUserIndex(message)); + } + + public void endTurn(int actingUserIndex){ + resetRemainingBe(); + resetFoul(); + teams[actingUserIndex].setBlitz(true); + teams[actingUserIndex].setPass(true); + countUpRound(); + unstunPlayers(getOpposingTeam(teams[actingUserIndex])); + getOpposingTeam(teams[actingUserIndex]).clearMovingPlayer(); + sendMessage(coaches[actingUserIndex], SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_ENDED_TURN); + sendMessage(getOpponent(actingUserIndex), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_YOUR_TURN); + if(roundCount == NUMBER_OF_ROUNDS_IN_GAME/2){ + actingPlayer = firstPlayer; + sendMessageShowMe("Referee", "Half time!"); + halfTime(); + }else if(roundCount == NUMBER_OF_ROUNDS_IN_GAME){ + sendMessageShowMe("Referee", "The game has ended!"); + finishGame(); + }else{ + sendMessageShowMe(coaches[actingUserIndex], "Referee", "Turnover!"); + sendMessageShowMe(coaches[actingUserIndex == 1 ? 0 : 1], "Referee", "Your turn!"); + } + sendGame(); + } + + private void resetRemainingBe(){ + resetRemainingBe(0); + resetRemainingBe(1); + } + + private void resetFoul(){ + getTeam(0).setFoul(true); + getTeam(1).setFoul(true); + } + private void resetRemainingBe(int teamIndex){ + for(Player p: teams[teamIndex].getPlayers()){ + p.invokeResetRemainingBe(); + } + } + + private void halfTime() { + Vector playersOnThePitch = findPlayersOnThePitch(); + clearAllPlayerPos(); + getPitch().adjustBallPos(new Vector2d(-1, -1)); + teamsSet[0] = false; + teamsSet[1] = false; + gamePhase = 1; + resetPlayerConditions(); + checkForSwelteringHeat(playersOnThePitch); + sendGame(); + checkForTeamCondition(); + cleanPlayersAfterTouchdownOrHalfTime(); + if(roundCount >= NUMBER_OF_ROUNDS_IN_GAME){ + finishGame(); + }else{ + initiateTeamSetup(coaches[actingPlayer]); + } + } + + private Vector findPlayersOnThePitch() { + Vector players = new Vector(); + for(Player p:getTeam(0).getPlayers()){ + if(p.getPosition() != Pitch.THE_VOID){ + players.add(p); + } + } + for(Player p:getTeam(1).getPlayers()){ + if(p.getPosition() != Pitch.THE_VOID){ + players.add(p); + } + } + return players; + } + + private void checkForTeamCondition() { + int roundsBeforeCheckTeamCondition = roundCount; + int finePlayers0 = 0; + for(Player p: teams[0].getPlayers()){ + if(p.invokeGetPlayerCondition()==PlayerCondition.FINE){ + finePlayers0++; + } + } + int finePlayers1 = 0; + for(Player p: teams[1].getPlayers()){ + if(p.invokeGetPlayerCondition()==PlayerCondition.FINE){ + finePlayers1++; + } + } + if(finePlayers0 < 3 || finePlayers1 < 3){ + if(finePlayers0 < 3 && !(finePlayers1 < 3)){ + countUpScore(1); + }else if(finePlayers1 < 3 && !(finePlayers0 < 3)){ + countUpScore(0); + } + for(int i = 0; i < 4; i++){ + countUpRound(); + } + resetPlayerConditions(); + if(roundsBeforeCheckTeamCondition < NUMBER_OF_ROUNDS_IN_GAME/2 && roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2){ + roundCount = NUMBER_OF_ROUNDS_IN_GAME/2; + actingPlayer = firstPlayer; + halfTime(); + }else if(roundCount >= NUMBER_OF_ROUNDS_IN_GAME){ + finishGame(); + }else{ + checkForTeamCondition(); + } + } + } + + private void resetPlayerConditions(){ + resetPlayerConditions(0); + resetPlayerConditions(1); + } + + private void resetPlayerConditions(int teamIndex){ + for(Player p: teams[teamIndex].getPlayers()){ + if(p.invokeGetPlayerCondition() == PlayerCondition.STUNNED || p.invokeGetPlayerCondition() == PlayerCondition.PRONE){ + p.invokeSetPlayerCondition(PlayerCondition.FINE); + }else if(p.invokeGetPlayerCondition() == PlayerCondition.KO){ + if(d6.throwDie() > 3){ + p.invokeSetPlayerCondition(PlayerCondition.FINE); + } + } + } + } + + private void unstunPlayers(Team t) { + for(Player p: t.getPlayers()){ + if(p.invokeGetPlayerCondition() == PlayerCondition.STUNNED){ + p.invokeSetPlayerCondition(PlayerCondition.PRONE); + p.invokeSetRemainingBe(0); + } + } + } + + private void allPlayersSet(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + if(getPitch().teamSetCorrect(actingUserIndex)){ + teamsSet[actingUserIndex] = true; + if(actingPlayer == 0){ + actingPlayer = 1; + }else if(actingPlayer == 1){ + actingPlayer = 0; + } + if(teamsSet[0] && teamsSet[1]){ + gamePhase = 2; + initiateKick(coaches[actingPlayer]); + }else{ + initiateTeamSetup(coaches[actingPlayer]); + } + }else{ + sendMessageShowMe(getUser(actingUserIndex), "Referee", "Sry, but this Teamsetup is not Valid."); + returnFailureMessage(message, SBProtocolMessage.FAILD_INVALID_TEAMSETUP); + } + } + + private void block(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + int playerIndex, defenderIndex; + try{ + playerIndex = Integer.parseInt(message.getParameterContent(1)); + defenderIndex = Integer.parseInt(message.getParameterContent(2)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_GIMME_AN_INT); + return; + } + ((RuleBlock)getTeam(actingUserIndex).getPlayers().get(playerIndex).getRule(1)).apply(message, getOpposingTeam(getTeam(actingUserIndex)).getPlayers().get(defenderIndex)); + } + + private void pass(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + int x, y, playerIndex; + try{ + playerIndex = Integer.parseInt(message.getParameterContent(1)); + x = Integer.parseInt(message.getParameterContent(2)); + y = Integer.parseInt(message.getParameterContent(3)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_GIMME_AN_INT); + return; + } + ((RuleThrow)getTeam(actingUserIndex).getPlayers().get(playerIndex).getRule(3)).apply(message, getPitch().getFields()[x][y]); + } + + private void move(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + int playerIndex; + int[] x, y; + int messageLength = message.getParameters().size(); + x = new int[(messageLength-2)/2]; + y = new int[(messageLength-2)/2]; + try{ + playerIndex = Integer.parseInt(message.getParameterContent(1)); + for(int i = 0; i < (messageLength-2)/2; i++){ + x[i] = Integer.parseInt(message.getParameterContent(2*i+2)); + y[i] = Integer.parseInt(message.getParameterContent(2*i+3)); + } + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_GIMME_AN_INT); + return; + } catch(ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + return; + } + PitchField[] path = new PitchField[x.length]; + for(int i = 0; i < path.length; i++){ + path[i] = getPitch().getFields()[x[i]][y[i]]; + } + if(getTeam(actingUserIndex).getPlayers().size() <= playerIndex) + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_EXIST); + else + ((RuleMove)getTeam(actingUserIndex).getPlayers().get(playerIndex).getRule(0)).apply(message, path); + } + + private void invokeSpecialRule(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + int playerIndex; + try{ + playerIndex = Integer.parseInt(message.getParameterContent(1)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_GIMME_AN_INT); + return; + } + Player p = teams[actingUserIndex].getPlayers().get(playerIndex); + if(findSpecialRule(p, message.getParameterContent(2)) == -1){ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_SUCH_SPECIAL_RULE); + }else{ + p.getSpecialRules()[findSpecialRule(p, message.getParameterContent(2))].apply(message); + } + } + + private int findSpecialRule(Player player, String name){ + for(int i = 0; i < player.getSpecialRules().length; i++){ + if(name.equals(player.getSpecialRules()[i].getName())){ + return i; + } + } + return -1; + } + + /** + * Chooses randomly which Player starts the game + */ + private void setFirstPlayer(){ + int firstPlayerThrow = d6.throwDie(); + if(firstPlayerThrow < 4){ + firstPlayer = 0; + actingPlayer = 1; + }else{ + firstPlayer = 1; + actingPlayer = 0; + } + weatherRoll(); + } + + /** + * makes a kick + * @param message the Message which initiated this Kick + */ + private boolean kick(SBProtocolMessage message){ + int x, y; + try{ + x = Integer.parseInt(message.getParameterContent(2)); + y = Integer.parseInt(message.getParameterContent(3)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_THIS_IS_NO_VALID_COORDINATE); + return false; + } + if(getPitch().isOnField(x, y)){ + if(!(actingPlayer == 0 || actingPlayer == 1)) return false; + boolean isOnCorrectHalf; + //überprüft ob der Ball die richtige Spielhälfte gekickt wird + if(actingPlayer == 0){ + isOnCorrectHalf = getPitch().isOnRightHalf(x, y); + }else{ + isOnCorrectHalf = getPitch().isOnLeftHalf(x, y); + } + if(isOnCorrectHalf){ + Vector2d direction = scatter(); + int distance = d6.throwDie(); + boolean isOnCorrectHalfAfterScatter; + //überprüft ob der Ball auch nach der Abweichung auf der richtigen Spielhälfte ist + if(actingPlayer == 0){ + isOnCorrectHalfAfterScatter = getPitch().isOnRightHalf(x + (int)(distance*direction.x), y + (int)(distance*direction.y)); + }else{ + isOnCorrectHalfAfterScatter = getPitch().isOnLeftHalf(x + (int)(distance*direction.x), y + (int)(distance*direction.y)); + } + //kickt den ball dahin, oder initiert das übergeben des Balls an einen Spieler + if(isOnCorrectHalfAfterScatter){ + getPitch().setBallPos(new Vector2d(x + distance*direction.x, y + distance*direction.y)); + boolean isOnCorrectHalfAfterSecondScatter; + if(actingPlayer == 0){ + isOnCorrectHalfAfterSecondScatter = getPitch().isOnRightHalf((int)getPitch().getBallPos().x, (int)getPitch().getBallPos().y); + }else{ + isOnCorrectHalfAfterSecondScatter = getPitch().isOnLeftHalf((int)getPitch().getBallPos().x, (int)getPitch().getBallPos().y); + } + if(isOnCorrectHalfAfterSecondScatter){ + sendMessageShowMe(getOpponent(actingPlayer), "Referee", "Your turn!"); + sendMessage(getOpponent(actingPlayer), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_YOUR_TURN); + gamePhase = 3; + }else{ + if(getPitch().isOnField(getPitch().getBallPos())){ + if(getPitch().getFields()[(int)getPitch().getBallPos().x][(int)getPitch().getBallPos().y].getPlayer() != null){ + getPitch().getFields()[(int)getPitch().getBallPos().x][(int)getPitch().getBallPos().y].getPlayer().invokeSetIsHoldingBall(false); + } + } + sendMessage(getOpponent(actingPlayer), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE); + } + }else{ + sendMessage(getOpponent(actingPlayer), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_GIVE_THE_BALL_TO_SOMEONE); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_SIDE); + return false; + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NOT_ON_THE_PITCH); + return false; + } + return true; + } + + /** + * Gives the Permission to make a Kickoff + * @param u the user that makes the Kickoff + */ + private void initiateKick(User u){ + sendMessageShowMe(u, "Referee", "Kick the Ball!"); + sendMessage(u, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_INITIATE_KICK); + } + + private void weatherRoll(){ + int weatherThrow = d6.throwDie() + d6.throwDie(); + if(weatherThrow == 2 || weatherThrow == 3){ + setWeather(Weather.SWELTERING_HEAT); + }else if(weatherThrow == 4 || weatherThrow == 5){ + setWeather(Weather.VERY_SUNNY); + }else if(weatherThrow < 9){ + setWeather(Weather.NICE); + }else if(weatherThrow == 9 || weatherThrow == 10){ + setWeather(Weather.POURING_RAIN); + }else if(weatherThrow == 11 || weatherThrow == 12){ + setWeather(Weather.BLIZZARD); + checkForBlizzard(); + } + sendWeather(); + sendMessageShowMe("Petrus", "Weather: " + getWeather().toNiceString()); + } + + /** + * Gives the Permission to set up the team + * @param u the user that gets the permission + */ + private void initiateTeamSetup(User u){ + sendMessageShowMe(u, "Referee", "Setup your Team!"); + sendMessage(u, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_SETUP_YOUR_TEAM); + } + + /** + * sets a Player on a given Position + * @param message the message that tells which player to place where + */ + private void setPlayer(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + if(!(actingUserIndex == 0 || actingUserIndex == 1)){return;} + int x, y, playerIndex; + try{ + playerIndex = Integer.parseInt(message.getParameterContent(1)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_EXIST); + return; + } + // check if player exists in team + if(playerIndex >= getTeam(actingUserIndex).getPlayers().size()) { + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_EXIST); + return; + } + if(getTeam(actingUserIndex).getPlayers().get(playerIndex).invokeGetPlayerCondition().equals(PlayerCondition.DEAD) + || getTeam(actingUserIndex).getPlayers().get(playerIndex).invokeGetPlayerCondition().equals(PlayerCondition.INJURED) + || getTeam(actingUserIndex).getPlayers().get(playerIndex).invokeGetPlayerCondition().equals(PlayerCondition.KO)){ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_IS_NOT_IN_A_GOOD_CONDITION); + return; + }else if(getTeam(actingUserIndex).getPlayers().get(playerIndex).getRedCard()){ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_IS_BANNED_FROM_THE_GAME); + return; + } + try { + x = Integer.parseInt(message.getParameterContent(2)); + y = Integer.parseInt(message.getParameterContent(3)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_THIS_IS_NO_VALID_COORDINATE); + return; + } + Vector2d pos = new Vector2d(x, y); + if(getPitch().isOnField(pos)){ + if (getPitch().getFields()[(int) pos.x][(int) pos.y].getPlayer() == null) { + if (actingUserIndex == 0) { + if (getPitch().isOnRightHalf(pos)) { + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_SIDE); + return; + } + } else { + if (getPitch().isOnLeftHalf(pos)) { + returnFailureMessage(message, SBProtocolMessage.FAILD_WRONG_SIDE); + return; + } + } + if(getTeam(actingUserIndex).getPlayers().size() > playerIndex) + getTeam(actingUserIndex).getPlayers().get(playerIndex).invokeSetPosition(getPitch().getFields()[(int) pos.x][(int) pos.y]); + else { + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_EXIST); + return; + } + returnSuccessMessage(message, SBProtocolMessage.WORKD_PLAYER_SET); + } else returnFailureMessage(message, SBProtocolMessage.FAILD_FIELD_ALREADY_TAKEN); + + } else if(x == -1 && y == -1){ + getTeam(actingUserIndex).getPlayers().get(playerIndex).invokeClearPosition(); + } else { returnFailureMessage(message, SBProtocolMessage.FAILD_NOT_ON_THE_PITCH); } + } + + /** + * sets all the Players in a Team + * @param message the message, where all the Infos are set + */ + private void setTeam(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + if(!(actingUserIndex == 0 || actingUserIndex == 1)){return;} + if(!(teamsChosen[actingUserIndex])){ + //find the actual team type + if(message.getParameters().size() >= 3+Team.MIN_TEAM_SIZE){ + Team newTeam = setTeamType(message, 2); + if(newTeam!=null){ + newTeam = new Team(newTeam, message.getParameterContent(1), this, getUser(actingUserIndex)); + int existingPlayers = 0; + // TODO: handle team setup messages without any players (["name", "type"]) + for(int i = 3; i < message.getParameters().size(); i++){ + for(int j = 0; j < newTeam.getAvailablePlayers().size(); j++){ + if(message.getParameterContent(i).equalsIgnoreCase(newTeam.getAvailablePlayers().get(j).getName())){ + newTeam.addPlayer(newTeam.getAvailablePlayers().get(j)); + existingPlayers++; + break; + } + } + } + setTeam(actingUserIndex, newTeam); + if(existingPlayers == message.getParameters().size()-3){ + int teamPrice=0; + for (Player p:teams[actingUserIndex].getPlayers()){ + teamPrice +=p.getPrice(); + } + if(teamPrice <= GameController.MAX_MONEY_TO_SPEND){ + teamsChosen[actingUserIndex] = true; + getPitch().setTeam(actingUserIndex, newTeam); + returnSuccessMessage(message, SBProtocolMessage.WORKD_TEAMSETUP_SUCCESSFUL); + if(teamsChosen[0] && teamsChosen[1]){ + gamePhase = 1; + initiateTeamSetup(coaches[actingPlayer]); + } + } + else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NOT_ENOUGH_MONEY); + } + }else{ + newTeam.clearPlayers(); + returnFailureMessage(message, SBProtocolMessage.FAILD_SOME_PLAYERS_DONT_EXIST); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NO_SUCH_TEAM_TYPE); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_NOT_ENOUGH_PLAYERS); + } + }else{ + returnFailureMessage(message, SBProtocolMessage.FAILD_YOUR_TEAM_IS_ALREADY_SET); + } + sendNewTeam(actingUserIndex); + } + + /** + * gives the ball to a specific player + * @param message the message which contains the player to give the ball to + */ + private boolean giveBall(SBProtocolMessage message){ + int actingUserIndex = findUserIndex(message); + int playerIndex; + sendMessageShowMe(coaches[actingUserIndex], "Referee", "Give the Ball to someone!"); + try{ + playerIndex = Integer.parseInt(message.getParameterContent(2)); + }catch(NumberFormatException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_GIVE_A_VALID_PLAYER_INDEX); + return false; + } + try{ + getPitch().adjustBallPos(new Vector2d(-1, -1)); + return ((RuleCatch)getTeam(actingUserIndex).getPlayers().get(playerIndex).getRule(4)).giveBall(message); + }catch(IndexOutOfBoundsException e){ + returnFailureMessage(message, SBProtocolMessage.FAILD_PLAYER_DOESNT_EXIST); + return false; + } + } + + /** + * Write this server match to file. + */ + public void logServerMatch() { + logGame(SERVER_GAMES_FILE); + } + + /** + * initiates everything that is necessary at the end of a game + * @param surrenderingUser The user that surrendered. + */ + public void finishGame(User surrenderingUser) { + gamePhase = 4; + setWinner(getOpponent(surrenderingUser)); + sendMessage(getOpponent(surrenderingUser), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_OPPONENT_SURRENDERED); + setRunning(false); + updateHighscore(); + logServerMatch(); + ((Server) getParent()).finishMatch(this); + } + + /** + * initiates everything that is necessary at the end of a game + */ + public void finishGame() { + gamePhase = 4; + determineWinner(); + setRunning(false); + updateHighscore(); + if(winner == null){ + sendMessage(getUser(0), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_DRAW); + sendMessage(getUser(1), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_DRAW); + }else{ + sendMessage(winner, SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_WON_GAME); + sendMessage(getOpponent(winner), SBProtocolCommand.EVENT, SBProtocolMessage.EVENT_LOST_GAME); + } + logServerMatch(); + ((Server) getParent()).finishMatch(this); + } + + /** + * Sets winner to who would have won the game if it ended now. + */ + private void determineWinner() { + // TODO: coach 1 wins on tie?! + if(score[0] > score[1]){ + setWinner(coaches[0]); + }else if(score[1] > score[0]){ + setWinner(coaches[1]); + }else{ + setWinner(null); + } + } + + /** + * sets the new Player Highscore + */ + private void updateHighscore(){ + if(winner != null) { + winner.wonGame(this); + getOpponent(winner).lostGame(this); + } + } + + @Override + public void touchdown(Team t) { + sendMessageShowMe("Referee", "TOUCHDOWN!"); + countUpScore(t); + Vector playersOnThePitch = findPlayersOnThePitch(); + clearAllPlayerPos(); + getPitch().adjustBallPos(new Vector2d(-1, -1)); + teamsSet[0] = false; + teamsSet[1] = false; + int actingUserIndex = -1; + if(t.equals(teams[0])){ + actingUserIndex = 0; + }else if(t.equals(teams[1])){ + actingUserIndex = 1; + } + countUpRound(); + if(checkUserTurn(actingUserIndex)){ + countUpRound(); + } + resetPlayerConditions(); + checkForSwelteringHeat(playersOnThePitch); + int roundsBeforeCheckTeamCondition = roundCount; + checkForTeamCondition(); + cleanPlayersAfterTouchdownOrHalfTime(); + sendGame(); + if(roundCount >= NUMBER_OF_ROUNDS_IN_GAME){ + finishGame(); + }else if(roundCount >= NUMBER_OF_ROUNDS_IN_GAME/2 && roundsBeforeCheckTeamCondition < NUMBER_OF_ROUNDS_IN_GAME/2){ + roundCount = NUMBER_OF_ROUNDS_IN_GAME/2; + actingPlayer = firstPlayer; + halfTime(); + }else{ + gamePhase = 1; + actingPlayer = actingUserIndex; + initiateTeamSetup(coaches[actingUserIndex]); + } + } + + private void cleanPlayersAfterTouchdownOrHalfTime(){ + getTeam(0).clearMovingPlayer(); + getTeam(1).clearMovingPlayer(); + resetFoul(); + getTeam(0).setBlitz(true); + getTeam(1).setBlitz(true); + getTeam(0).setPass(true); + getTeam(1).setPass(true); + } + + // GETTERS & SETTERS + + public void setCurrentActorWaitingForAnswer(Player currentActorWaitingForAnswer) { + this.currentActorWaitingForAnswer = currentActorWaitingForAnswer; + } + + public void setCurrentDefenderWaitingForAnswer(Player currentDefenderWaitingForAnswer) { + this.currentDefenderWaitingForAnswer = currentDefenderWaitingForAnswer; + } + + public int findTeamHoldingTheBall(){ + for(int i = 0; i < teams[0].getPlayers().size(); i++){ + if(teams[0].getPlayers().get(i).isHoldingBall()){ + return 0; + } + }for(int i = 0; i < teams[1].getPlayers().size(); i++){ + if(teams[1].getPlayers().get(i).isHoldingBall()){ + return 1; + } + } + return -1; + } + + public void setCurrentDefenderFieldWaitingForAnswer(Vector2d defenderField) { + this.currentDefenderFieldWaitingForAnswer = defenderField; + } + + public void setCurrentPusherWaitingForAnswer(Player pusherWaitingForAnswer) { + this.currentPusherWaitingForAnswer = pusherWaitingForAnswer; + } + + public void setCurrentBackUpPosWaitingForAnswer(Vector2d posToBackup) { + this.currentBackUpPosWaitingForAnswer = posToBackup; + } + + public void clearCurrentPlayersBeingPushed(){ + this.currentPlayersBeingPushed.removeAllElements(); + } + + public void addCurrentPlayersBeingPushed(Player p){ + this.currentPlayersBeingPushed.add(p); + } + + public void setCurrentModificatorWaitingForAnser(int mod){ + this.currentModificaorWaitingForAnswer = mod; + } + + public Vector getCurrentPlayersBeingPushed(){ + return this.currentPlayersBeingPushed; + } + + public void clearCurrentPlayerPositionsBeingPushed(){ + this.currentPlayerPositionsBeingPushed.removeAllElements(); + } + + public void addCurrentPlayerPositionsBeingPushed(Vector2d pos){ + this.currentPlayerPositionsBeingPushed.add(pos); + } + + public void setCurrentMessageWaitingForAnswer(SBProtocolMessage message){ + this.currentMessageWaitingForAnswer = message; + } + + public void addCurrentHighlitedFields(Vector2d pos){ + this.currentHighlitedFields.add(pos); + } + + public void clearCurrentHighlitedFields(){ + this.currentHighlitedFields.removeAllElements(); + } + + public Vector getCurrentPlayerPositionsBeingPushed(){ + return this.currentPlayerPositionsBeingPushed; + } + + public Player getCurrentDefenderWaitingForAnswer(){ + return this.currentDefenderWaitingForAnswer; + } + + public int getCurrentModificatorWaitingForAnser(){ + return this.currentModificaorWaitingForAnswer; + } + + public SBProtocolMessage getCurrentMessageWaitingForAnswer(){ + return this.currentMessageWaitingForAnswer; + } + + public Vector2d getCurrentDefenderFieldWaitingForAnser(){ + return this.currentDefenderFieldWaitingForAnswer; + } + + public Vector getCurrentHighlitedFields(){ + return this.currentHighlitedFields; + } +} diff --git a/src/server/logic/ServerMessageProcessor.java b/src/server/logic/ServerMessageProcessor.java new file mode 100644 index 0000000..65aadd1 --- /dev/null +++ b/src/server/logic/ServerMessageProcessor.java @@ -0,0 +1,560 @@ +package server.logic; + +import network.*; +import server.Server; +import util.FinishedGame; +import util.MessageProcessor; + +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.UUID; +import java.util.logging.Level; + +/** + * The message processor on the server side. + * Created by milan on 1.4.15. + */ +public class ServerMessageProcessor implements MessageProcessor { + + private final Server server; + private HashMap unansweredStartGameMessages = new HashMap(); + + public ServerMessageProcessor(Server server) { + this.server = server; + } + + /** + * Sorts messages to process by module and continues in processMessageMODULE(message). + * @param message The message to process + */ + @Override + public void processMessage(SBProtocolMessage message) { + try { + + boolean processMessage; + switch (message.getModule()) { + case CHT: + processMessage = processMessageCHT(message); + break; + case AUT: + processMessage = processMessageAUT(message); + break; + case GAM: + processMessage = processMessageGAM(message); + break; + default: + processMessage = false; + break; + } + if(!processMessage) returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); + + } catch (IndexOutOfBoundsException e) { returnFailureMessage(message, SBProtocolMessage.FAILD_PARAMANIA_HAS_TAKEN_OVER); } + } + + /** + * Sorts answers to process by module and continues in processAnswerMODULE(answer). + * @param answer The answer to process + */ + @Override + public void processAnswer(SBProtocolMessage answer) { + try { + SBProtocolMessage message = null; + while(message == null){ + try { + for (SBProtocolMessage messageThatIsPotentiallyAnswered : getProtocolManager().getUnansweredMessages()) // for all unanswered messages + if (messageThatIsPotentiallyAnswered.getMID().equals(UUID.fromString(answer.getParameterContent(0)))) { // get the message whose MID equals the MID in the answer + message = messageThatIsPotentiallyAnswered; + break; + } + } catch(ConcurrentModificationException e) { + getServer().log(Level.SEVERE, "Catched concurrent modification exception! But continuing anyway."); + e.printStackTrace(); + } + } + getProtocolManager().removeUnansweredMessage(message); + + if(message != null) { + + switch (answer.getModule()) { + case SUC: + processAnswerSUC(answer, message); + break; + case FAI: + processAnswerFAI(answer, message); + break; + } + + } else getServer().log(Level.WARNING, "Received answer but found no message it belonged to: " + answer.toStringShortenUUID() + " Ignoring it."); + + } catch (IndexOutOfBoundsException e) { /* Don't return failure message because answers don't expect (e.g. ignore) answers anyway */ } + } + + + + // MESSAGE PROCESSORS + + /** + * Processes messages in the module CHT. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageCHT(SBProtocolMessage message) { + switch (message.getCommand()) { + case SENDM: + User recipient = getUserManager().getUser(message.getParameterContent(0)); + if(recipient != null) { // user with name exists + SBSocket returnSocket = getServer().getSocketByUID(recipient.getUID()); + if(returnSocket != null) { // socket for recipient is connected + User sender = getUserManager().getAuthenticatedUser(message.getUID()); + if (sender != null) { // sender is logged in + returnSocket.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.SENDM, new SBProtocolParameterArray(sender.getName(), message.getParameterContent(1)))); + getServer().log(Level.INFO, "Sent message by " + sender.getName() + " to " + recipient.getName() + "."); + } else { // sender is not logged in. send anonymously + returnSocket.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.SENDM, new SBProtocolParameterArray("", message.getParameterContent(1)))); + getServer().log(Level.INFO, "Sent message by anonymous user to " + recipient.getName() + "."); + } + returnSuccessMessage(message, "SENT"); + } else returnFailureMessage(message, "USER OFFLINE"); + } else returnFailureMessage(message, "USER NONEXISTENT"); + return true; + + case BDCST: + sendBroadcastMessage(SBProtocolCommand.BDCST, message.getParameterContent(0), message.getParameterContent(1)); + getServer().log(Level.INFO, "Sent broadcast message by "+message.getParameterContent(0)+"."); + return true; + + default: + return false; + } + } + + /** + * Processes messages in the module AUT. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageAUT(SBProtocolMessage message) { + switch (message.getCommand()) { + case LOGUT: + User userToLogOut = getUserManager().getAuthenticatedUser(message.getUID()); + if (userToLogOut != null) { // client is logged in + userToLogOut.setLoggedOut(); // log out + userToLogOut.setUID(new UUID(0, 0)); // reset UID + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, userToLogOut.getName() + " ran away into the bushes!"); // send broadcast to tell user has logged out + getServer().log(Level.INFO, "Logged out " + userToLogOut.getName() + "."); + returnSuccessMessage(message); // user was logged out + } else returnFailureMessage(message); // client was not logged in + + getServer().broadcastUpdatedUsersList(); + return true; + + case LOGIN: + String name = message.getParameterContent(0), passwordEncrypted = message.getParameterContent(1); + + if(name.length() > 0 && !passwordEncrypted.equals("d41d8cd98f00b204e9800998ecf8427e")) { // only continue if no field was empty. (password: empty string digested by md5 = d41d8cd98f00b204e9800998ecf8427e) + + if (getUserManager().existsName(name)) { // user with name exists + User userToLogIn = getUserManager().getUser(name); + if (userToLogIn.getPasswordEncrypted().equals(passwordEncrypted)) { // password matched + if (!userToLogIn.isLoggedIn()) { // not logged in yet, log in + userToLogIn.setLoggedIn(); // log in + message.getSocket().setUID(message.getUID()); // set UID of socket to UID of received message + userToLogIn.setUID(message.getUID()); // set UID of user to UID of received message + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, "A wild " + name + " appeared!"); // send broadcast to tell user has logged in + returnSuccessMessage(message, "LOGGED IN"); + getServer().log(Level.INFO, "Logged in " + name + "."); + } else { // user was already logged in + returnFailureMessage(message, "ALREADY LOGGED IN"); + getServer().log(Level.INFO, "Refused login for " + name + ". Already logged in."); + } + } else { // wrong password + returnFailureMessage(message, "WRONG PASS"); + getServer().log(Level.INFO, "Refused login for " + name + ". Wrong password."); + } + + } else { // create user + if(getServer().validateName(name)) { // name is valid (no spaces, not yet registered, not 'all') + getUserManager().writeUser(new User(getUserManager(), name, passwordEncrypted, message.getUID())); + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, name + " was born."); // send broadcast to tell user has been created + returnSuccessMessage(message, "CREATED USER"); + getServer().log(Level.INFO, "Created new user with name " + name + "."); + } else { + returnFailureMessage(message, "ILLEGAL NAME"); + getServer().log(Level.INFO, "Refused creation of " + name + ". Illegal name."); + } + } + } else { // name and/or password was empty + returnFailureMessage(message, "EMPTY FIELD"); + } + + getServer().broadcastUpdatedGamesList(); + return true; + + case EXIST: + if (getUserManager().existsName(message.getParameterContent(0))) + returnSuccessMessage(message, "EXISTS"); + else returnSuccessMessage(message, "EXISTS NOT"); + return true; + + case CHNGE: + User userToChangeName = getUserManager().getUser(message.getParameterContent(0)); + + if (userToChangeName != null) { // user exists + if (userToChangeName.getName().equals(message.getParameterContent(0)) // current name equals name in message + && userToChangeName.getName().equals(getUserManager().getAuthenticatedUser(message.getUID()).getName()) // current name equals name of sender (if logged in) + && getServer().validateName(message.getParameterContent(1))) // new name is valid (no spaces, not yet registered, not 'all') + userToChangeName.setName(message.getParameterContent(1)); // change name + + else if (getUserManager().getAuthenticatedUser(message.getUID()).isOp() // client is authenticated as operator (which allows him to change any names he desires) + && getServer().validateName(message.getParameterContent(1))) // new name is valid (no spaces, not yet registered, not 'all') + userToChangeName.setName(message.getParameterContent(1)); // change name + + else { // old name doesn't match and client is no authenticated operator + returnFailureMessage(message); + return true; // return to suppress sending the messages below + } + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, message.getParameterContent(0) + " evolves to " + userToChangeName.getName()); // send broadcast to tell user has changed their name + returnSuccessMessage(message); + getServer().log(Level.INFO, "Changed user name of " + message.getParameterContent(0) + " to " + userToChangeName.getName() + "."); + } else returnFailureMessage(message); // user doesn't exist + + getServer().broadcastUpdatedUsersList(); + return true; + + case RMUSR: + User userToRemove = getUserManager().getUser(message.getParameterContent(0)); + + if (userToRemove != null) { // user exists + if (userToRemove.getName().equals(message.getParameterContent(0)) + && userToRemove.getName().equals(getUserManager().getAuthenticatedUser(message.getUID()).getName())) // name matches name in params and name of authenticated client + getUserManager().removeUser(userToRemove.getName()); // remove user + + else if (getUserManager().getAuthenticatedUser(message.getUID()).isOp()) // client is authenticated as operator (which allows him to remove any users he desires) + getUserManager().removeUser(userToRemove.getName()); // remove user + + else { + returnFailureMessage(message); + return true; // return to suppress sending the messages below + } + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, userToRemove.getName() + " went neverland and never came back."); // send broadcast to tell user has been deleted + returnSuccessMessage(message); + getServer().log(Level.INFO, "Removed user "+userToRemove.getName()+"."); + } else returnFailureMessage(message); // user doesn't exist + + getServer().broadcastUpdatedUsersList(); + return true; + + case OPUSR: + User userToOp = getUserManager().getUser(message.getParameterContent(0)); + + if (userToOp != null) { // user exists + if (userToOp.getName().equals(message.getParameterContent(0)) + && userToOp.getName().equals(getUserManager().getAuthenticatedUser(message.getUID()).getName())) // name matches name in params and name of authenticated client + userToOp.setIsOp(true); + + else if (getUserManager().getAuthenticatedUser(message.getUID()).isOp()) // client is authenticated as operator (which allows him to op any users he desires) + userToOp.setIsOp(true); + + else { // old name doesn't match and client is no authenticated operator + returnFailureMessage(message); + return true; // return to suppress sending the messages below + } + } else { // user doesn't exist + returnFailureMessage(message); + return true; // return to suppress sending the messages below + } + + sendBroadcastMessage(SBProtocolCommand.BDCST, Server.MODERATOR_NAME, "That puny " + userToOp.getName() + " is now suddenly VERY important."); // send broadcast to tell user has been granted op rights + returnSuccessMessage(message); + getServer().log(Level.INFO, "Granted user "+userToOp.getName()+" operator rights."); + return true; + + case LSUSR: + SBProtocolParameterArray usersParameters = new SBProtocolParameterArray(); + + for(User user: getUserManager().getUsers()) { + if(user.isLoggedIn()) { + String inGame = user.isInGame() ? "true" : "false"; + usersParameters.addParameter(new SBProtocolParameterArray(user.getName(), inGame)); + } + } + + message.returnSuccessMessage(getServer().UID, usersParameters); + return true; + + case LSGAM: + SBProtocolParameterArray gamesParameters = new SBProtocolParameterArray(); + + // add finished matches + for(String finishedGameString: getServer().getFinishedGames()) { + FinishedGame g = new FinishedGame(finishedGameString); + gamesParameters.addParameter(new SBProtocolParameterArray(g.getCoach1Name(), g.getCoach2Name(), g.getWinnerString(), g.getTeam1Score(), g.getTeam2Score())); + } + // add running matches + for(ServerMatch match: getServer().getRunningMatches()) + if(match.isRunning()) + gamesParameters.addParameter(new SBProtocolParameterArray(match.getUser(0).getName(), match.getUser(1).getName(), match.getScoreFromTeam(0)+"", match.getScoreFromTeam(1)+"")); + // add not yet running matches + for(User user: getServer().getUsersWaitingForRandomGame()) + gamesParameters.addParameter(new SBProtocolParameterArray(user.getName())); + + message.returnSuccessMessage(getServer().UID, gamesParameters); + return true; + + default: + return false; + } + } + + /** + * Processes messages in the module GAM. + * @param message The message to process. + * @return Whether the message was processed successfully. + */ + private boolean processMessageGAM(SBProtocolMessage message) { + switch (message.getCommand()) { + case START: + if(getUserManager().getAuthenticatedUser(message.getUID()) != null) { // user is logged in + User userStartingGame = getUserManager().getAuthenticatedUser(message.getUID()); + + if(message.getParameters().size() > 0) { // Start game against name in parameter 0 + User invitedUser = getUserManager().getUser(message.getParameterContent(0)); // set the invited user + + if(invitedUser != null) { // invited user exists + if(invitedUser.isLoggedIn()) { // invited user is logged in + if(getServer().userWaitingForRandomGame(invitedUser)) { // invited user is waiting for a random game + + if(userStartingGame != null && !userStartingGame.equals(invitedUser)) { // if both users still are logged in and actually are two different users + SBProtocolMessage originalStartGameMessage = getUnansweredStartGameMessage(invitedUser.getName()); + + if(originalStartGameMessage != null) { // the original unanswered message still exists + removeUnansweredStartGameMessage(invitedUser.getName()); + returnSuccessMessage(originalStartGameMessage, userStartingGame.getName()); // tell waiting user that game has been created + returnSuccessMessage(message, invitedUser.getName()); // tell inviting user that game has been created + getServer().createGame(userStartingGame, invitedUser); + getServer().removeUserWaitingForRandomGame(invitedUser); // remove invited user from list of users waiting for random game + getServer().log(Level.INFO, "Starting match. "+invitedUser.getName()+" versus "+userStartingGame.getName()+"!"); + } else { + getServer().log(Level.SEVERE, "Original start-game-message not found. Ignoring request:\n"+message.toStringShortenUUID()); + returnFailureMessage(message); + } + } else { + getServer().log(Level.SEVERE, "Error while checking if both users wanting to start a game are still online. Ignoring request:\n"+message.toStringShortenUUID()); + returnFailureMessage(message); + } + + } else { // ask invited user to accept invitation + + if (addUnansweredStartGameMessage(message)) { // add message to unanswered start game messages to be able to reply later + SBSocket inviteSocket = getServer().getSocketByUID(invitedUser.getUID()); // prepare socket to invite + if (inviteSocket != null) { // if socket exists, invite client to play against user starting game + inviteSocket.sendMessage(new SBProtocolMessage(getUID(), SBProtocolCommand.START, new SBProtocolParameterArray(userStartingGame.getName()))); + getServer().log(Level.INFO, "Inviting " + invitedUser.getName() + " to play against " + userStartingGame.getName() + "."); + } + } else returnFailureMessage(message); + + } + } else returnFailureMessage(message); + } else returnFailureMessage(message); + + } else // Add or remove user to or from waiting list for a random game + if(!getServer().userWaitingForRandomGame(userStartingGame)) { // authenticated user is not waiting for a random game yet + if(addUnansweredStartGameMessage(message)) { + getServer().addUserWaitingForRandomGame(userStartingGame); + getServer().log(Level.INFO, userStartingGame.getName() + " is now waiting for a game."); + getServer().broadcastUpdatedGamesList(); + } else returnFailureMessage(message); + } else { + getServer().removeUserWaitingForRandomGame(userStartingGame); + getServer().log(Level.INFO, userStartingGame.getName() + " is not waiting for a game anymore."); + getServer().broadcastUpdatedGamesList(); + } + } + return true; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(message); + return true; + + default: + return false; + } + } + + // ANSWER PROCESSORS + + /** + * Processes success answers. + * @param answer The answer to process. + * @param message The original message that was sent by this server. + */ + private void processAnswerSUC(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case START: // client has confirmed starting a game against suggested opponent + User confirmingUser = getUserManager().getAuthenticatedUser(answer.getUID()); // the user that confirmed starting the match + User otherUser = getUserManager().getUser(message.getParameterContent(0)); // the opponent of the first user + + if(confirmingUser != null && otherUser != null && !confirmingUser.equals(otherUser)) { // if both users still are logged in and actually are two different users + SBProtocolMessage originalStartGameMessage = getUnansweredStartGameMessage(otherUser.getName()); + + if(originalStartGameMessage != null) { // the original unanswered message still exists + removeUnansweredStartGameMessage(otherUser.getName()); + getServer().removeUserWaitingForRandomGame(otherUser); + returnSuccessMessage(originalStartGameMessage, confirmingUser.getName()); + getServer().createGame(otherUser, confirmingUser); + getServer().log(Level.INFO, "Starting match. "+otherUser.getName()+" versus "+confirmingUser.getName()+"!"); + } else getServer().log(Level.SEVERE, "Original start-game-message not found. Ignoring answer:\n"+answer.toStringShortenUUID()); + } else getServer().log(Level.SEVERE, "Error while checking if both users wanting to start a game are still online. Ignoring answer:\n"+answer.toStringShortenUUID()); + break; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(answer); + getProtocolManager().addUnansweredMessage(message); // add answered message back to unanswered messages so it isn't lost + break; + } + } + + /** + * Processes failure answers. + * @param answer The answer to process. + * @param message The original message that was sent by this server. + */ + private void processAnswerFAI(SBProtocolMessage answer, SBProtocolMessage message) { + switch (message.getCommand()) { + case START: // client has declined starting a game against suggested opponent + User otherUser = getUserManager().getUser(message.getParameterContent(0)); // the opponent of the first user + SBProtocolMessage originalStartGameMessage = getUnansweredStartGameMessage(otherUser.getName()); + + if(originalStartGameMessage != null) { // only return failure answers if the original message is still unanswered + removeUnansweredStartGameMessage(otherUser.getName()); + returnFailureMessage(originalStartGameMessage, otherUser.getName()); + } + break; + + case SRNDR: + case EVENT: + case ACTIO: + addMessageOrAnswerToMatch(answer); + getProtocolManager().addUnansweredMessage(message); // add answered message back to unanswered messages so it isn't lost + break; + } + } + + // HELPERS + + /** + * Returns a success message for the given message with parameters. + * @param returnTo The message to answer. + * @param parameters The parameters to send back with the answer. + */ + @Override + public void returnSuccessMessage(SBProtocolMessage returnTo, String... parameters) { + getServer().returnSuccessMessage(returnTo, parameters); + } + + /** + * Returns a failure message for the given message with parameters. + * @param returnTo The message to answer. + * @param parameters The parameters to send back with the answer. + */ + @Override + public void returnFailureMessage(SBProtocolMessage returnTo, String... parameters) { + getServer().returnFailureMessage(returnTo, parameters); + } + + /** + * Sends a new message to all connected clients. + * @param command The command of the message to send. + * @param parameters The parameters for the message to send. + */ + private void sendBroadcastMessage(SBProtocolCommand command, String... parameters) { + getServer().sendBroadcastMessage(command, parameters); + } + + /** + * Add an incoming game message or answer to the match incoming messages or answers queue. + * @param messageOrAnswer The message or answer to forward. + */ + private void addMessageOrAnswerToMatch(SBProtocolMessage messageOrAnswer) { + if(getUserManager().getAuthenticatedUser(messageOrAnswer.getUID()) != null) { // user is logged in + User userSendingMessage = getUserManager().getAuthenticatedUser(messageOrAnswer.getUID()); + + if(userSendingMessage.isInGame()) { // user is in game + for(ServerMatch match: getServer().getRunningMatches()) { + if(match.getOpponent(userSendingMessage) != null) { // user is in this match + if(messageOrAnswer.getModule() == SBProtocolCommand.SBProtocolModule.GAM) // is message + match.addIncomingMessage(messageOrAnswer); // add message to incoming messages in match + else // is answer + match.addIncomingAnswer(messageOrAnswer); // add answer to incoming answers in match + break; + } + } + } else returnFailureMessage(messageOrAnswer); + } else returnFailureMessage(messageOrAnswer); + } + + // SETTERS & GETTERS + + /** + * Adds a message and the name of its sender to the unansweredStartGameMessages queue. + * @param message The message to add. + * @return False if the sender of the message was not logged in (no user was logged in with the UID of the message). + */ + private boolean addUnansweredStartGameMessage(SBProtocolMessage message) { + String username = getUserManager().getAuthenticatedUser(message.getUID()).getName(); + if(username != null) { + unansweredStartGameMessages.put(username, message); + return true; + } + else return false; + } + + /** + * Remove a message from the unansweredStartGameMessages queue by the name of its sender. + * @param senderName The name of the sender that sent the message. + */ + private void removeUnansweredStartGameMessage(String senderName) { + unansweredStartGameMessages.remove(senderName); + } + + /** + * Get a message from the unansweredStartGameMessages queue by the name of its sender. + * @param senderName The name of the sender that sent the message. + */ + private SBProtocolMessage getUnansweredStartGameMessage(String senderName) { + return unansweredStartGameMessages.get(senderName); + } + + /** + * Get the server this message processor works for. + * @return The server this message processor works for. + */ + public Server getServer() { + return server; + } + + private UserManager getUserManager() { + return getServer().getUserManager(); + } + + private SBSocketManager getSocketManager() { + return getServer().getSocketManager(); + } + + private SBProtocolManager getProtocolManager() { + return getServer().getProtocolManager(); + } + + private UUID getUID() { + return getServer().UID; + } +} diff --git a/src/server/logic/ServerProtocolManager.java b/src/server/logic/ServerProtocolManager.java new file mode 100644 index 0000000..a017438 --- /dev/null +++ b/src/server/logic/ServerProtocolManager.java @@ -0,0 +1,20 @@ +package server.logic; + +import network.SBProtocolManager; +import util.SBApplication; + +/** + * A protocol manager that will communicate with a client socket manager and handle messages for a server. + * Created by milan on 28.3.15. + */ +public class ServerProtocolManager extends SBProtocolManager { + + /** + * Create new server protocol manager. + * @param parent The server that is parent of this protocol manager. + */ + public ServerProtocolManager(SBApplication parent) { + super(parent); + } + +} diff --git a/src/server/logic/ServerSocketManager.java b/src/server/logic/ServerSocketManager.java new file mode 100644 index 0000000..2959095 --- /dev/null +++ b/src/server/logic/ServerSocketManager.java @@ -0,0 +1,261 @@ +package server.logic; + +import network.SBNetworkException; +import network.SBProtocolMessage; +import network.SBSocket; +import network.SBSocketManager; +import server.Server; +import util.SBApplication; + +import java.io.IOException; +import java.net.CookieHandler; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.Date; +import java.util.Hashtable; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +/** + * A protocol manager that will handle the sending and recieving of messages for a server. + * Created by milan on 25.3.15. + */ +public class ServerSocketManager extends SBSocketManager { + + private ServerSocket serverSocket; + private Vector sockets; + private int socketsCount = -1; + private ConnectionListener connenctionListener = new ConnectionListener(); + + /** + * Create a new server socket manager for server. + * @param server The server to create this socket manager for. + * @param protocolManager The protocol manager for this server socket manager. + */ + public ServerSocketManager(SBApplication server, ServerProtocolManager protocolManager) { + super(server, protocolManager); + } + + /** + * Start the server on port. + * @param port The port for the server to listen to. + * @throws SBNetworkException If there occurred an exception while creating the socket. + */ + public void startServer(int port) throws SBNetworkException { + this.port = port; + this.sockets = new Vector(); + // create sockets + try { + serverSocket = new ServerSocket(port); + // create and start socket listener to accept incoming connections + (new Thread(new Runnable() { + @Override + public void run() { + while(serverSocket != null) { + Socket socket = null; + // wait for connection + try { + socket = serverSocket.accept(); + } catch (SocketException e) { + if(!e.getMessage().equals("Socket closed")) + getParent().log(Level.SEVERE, "Exception while listening for connections. " + e.toString()); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Exception while listening for connections. " + e.toString()); + } + if(socket != null) { + try { + // create SBSocket from socket and create listener and writer for it + SBSocket sbsocket = new SBSocket(socket, UUID.randomUUID(), getThis()); + sbsocket.createListenerAndWriter(); + sockets.add(sbsocket); + } catch (SBNetworkException e) { + // close socket if creating listener or writer for sbsocket fails + try { + getParent().log(Level.SEVERE, "Error while creating SBSocket from socket. Closing socket."); + socket.close(); + } catch (IOException e1) { getParent().log(Level.SEVERE, "Error while closing socket. Ignoring."); } + } + } + } + } + })).start(); + startConnectionListener(); + getParent().log(Level.INFO, "Successfully started server on port " + port + "."); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Exception while creating socket on port "+port+". "+e.toString()); + throw new SBNetworkException(); + } + } + + /** + * Stop the server. + * @throws SBNetworkException If there was an exception while stopping the server. + */ + public void stopServer() throws SBNetworkException { + try { + if(sockets != null) + if(sockets.size() > 0) + for(SBSocket socket: sockets) + socket.close(); + sockets = null; + serverSocket.close(); + serverSocket = null; + setSocketsCount(-1); + port = 0; + } catch (IOException e) { + getParent().log(Level.SEVERE, "Exception while stopping server. "+e.toString()); + throw new SBNetworkException(); + } + } + + /** + * Restart the server on previously specified port. + * @throws SBNetworkException If there was an exception while starting or stopping the server. + */ + public void restartServer() throws SBNetworkException { + stopServer(); + startServer(port); + } + + /** + * Restart the server on a new port. + * @param port The port for the server to listen to. + * @throws SBNetworkException If there was an exception while starting the server. + */ + public void restartServer(int port) throws SBNetworkException { + stopServer(); + startServer(port); + } + + /** + * Get the port on which the server is currently listening to. + * @return The port. 0 if the server is not running. + */ + public int getPort() { + return port; + } + + /** + * Close a socket and remove it from the sockets. + * @param socket The socket to close and remove. + */ + public void removeSocket(SBSocket socket) { + try { + parent.logOutUserWithUID(socket.getUID()); + ((Server) parent).broadcastUpdatedUsersList(); + socket.close(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Error while closing socket " + socket.getUID().toString().substring(0, 8) + ". Leaving open."); + } + if(sockets != null) sockets.remove(socket); + } + + public Vector getSockets() { + return sockets; + } + + /** + * Get a socket by its UID. + * @param UID The UID of the socket to get. + * @return The socket if there exists a socket with the given UID or else null. + */ + public SBSocket getSocket(UUID UID) { + if(sockets != null) for(SBSocket socket: sockets) if(socket.getUID().equals(UID)) return socket; + return null; + } + + public void sendMessage(UUID UID, SBProtocolMessage message) { + if(sockets != null) for(SBSocket socket: sockets) if(socket.getUID().equals(UID)) socket.sendMessage(message); + } + + public void sendBroadcastMessage(SBProtocolMessage message) { + if(sockets != null) + for(SBSocket socket: sockets) + socket.sendMessage(message); + } + + public int getSocketsCount() { + return socketsCount; + } + + public void setSocketsCount(int socketsCount) { + this.socketsCount = socketsCount; + } + + + /** + * Start a connection listener that sends pings and handles ping timeouts. + */ + public void startConnectionListener() { + // create and start connection listener to ping connected clients + connenctionListener.stopListening(); + connenctionListener = new ConnectionListener(); + connenctionListener.start(); + } + + private class ConnectionListener extends Thread { + private boolean listening = true; + + @Override + public void run() { + // set up map with sent ping messages MIDs and corresponding sockets + Hashtable sentPings = new Hashtable(); + // send ping to every socket in regular interval + while(sockets != null && serverSocket != null && listening) { + if(sockets.size() != getSocketsCount()) { + getParent().log(Level.INFO, "Sockets connected: "+sockets.size()); + setSocketsCount(sockets.size()); + } + // send new pings + for(SBSocket socket: sockets) { + SBProtocolMessage pingMessage = SBProtocolMessage.createPingMessage(parent.UID); + getParent().log(Level.FINEST, "Sent ping to socket " + socket.getUID().toString().substring(0, 8)); + sentPings.put(pingMessage, socket); + socket.sendMessage(pingMessage); + } + // process new ping answers + while(getProtocolManager().getPingAnswersToProcess().size() > 0) { + // prepare answer and ping to remove + SBProtocolMessage pong = getProtocolManager().getPingAnswersToProcess().poll(); + SBProtocolMessage pingToRemove = null; // ping messages that have been answered and will be removed + for(SBProtocolMessage ping: sentPings.keySet()) { // check all sentPings + if(ping.getMID().equals(pong.getMID())) pingToRemove = ping; // remove ping from sentPings if ping MID equals pong MID + } + if(pingToRemove != null) sentPings.remove(pingToRemove); // remove ping to remove + } + // check unanswered pings for timeouts + Vector socketsToRemove = new Vector(); // vector with sockets for which all sent messages should be removed from sent pings because the following loop cannot do so while iterating + for (SBProtocolMessage ping : sentPings.keySet()) { + if (!ping.getSentDate().equals(new Date(0))) { + if (ping.getSentDate().before((new Date(System.currentTimeMillis() - PING_MESSAGE_TIMEOUT)))) { + getParent().log(Level.SEVERE, "Timeout while waiting for a ping answer to ping " + ping.getMID().toString().substring(0, 8) + + ".\nClosing socket " + sentPings.get(ping).getUID().toString().substring(0, 8)+"."); + // remove socket + socketsToRemove.add(sentPings.get(ping)); + removeSocket(sentPings.get(ping)); + } + } + } + // remove all messages that have been sent to the socket of each message queued for removal in messagesToRemove + Vector messagesToRemove = new Vector(); + for(SBSocket socket: socketsToRemove) // for every socket in removal list + for(SBProtocolMessage ping: sentPings.keySet()) // for every message in unanswered pings + if(sentPings.get(ping).equals(socket)) messagesToRemove.add(ping); // queue message for removal if it was sent to socket + for(SBProtocolMessage ping: messagesToRemove) sentPings.remove(ping); // remove all messages queued for removal + // wait for the next iteration + try { + Thread.sleep(PING_SENDER_INTERVAL); + } catch (InterruptedException e) { + getParent().log(Level.WARNING, "Interrupted while waiting until sending next ping. Sending next ping now instead."); + } + } + getParent().log(Level.SEVERE, "Connection listener stopped."); + } + + public void stopListening() { + this.listening = false; + } + } +} diff --git a/src/server/logic/User.java b/src/server/logic/User.java new file mode 100644 index 0000000..ce84b5b --- /dev/null +++ b/src/server/logic/User.java @@ -0,0 +1,440 @@ +package server.logic; + +import gameLogic.GameController; +import gameLogic.Player; +import util.SBLogger; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; +import java.util.logging.Level; + +/** + * A representation of a User. + * Created by milan on 23.3.15. + */ +public class User { + + private UUID UID = new UUID(0, 0), lastUID = new UUID(0, 0); + private UserManager manager; + private boolean loggedIn = false, inGame = false; + // info + private String name; + private String passwordEncrypted = "00000000000000000000000000000000"; + private boolean isOp = false; + // stats: + private int wins = 0, losses = 0, casualties = 0, touchdownsScored = 0, touchdownsReceived = 0; + private Date lastOnline = new Date(System.currentTimeMillis()); + + /** + * Create a default user. + * @param manager The manager that manages this user. + */ + public User(UserManager manager) { + this.manager = manager; + name = coolName(); + } + + /** + * Create a user with just a name. + * @param name the name of this user. + */ + public User(String name) { + this.name = name; + } + + /** + * Create a new user from a string. + * @param manager The manager that manages this user. + * @param dataString The string to parse the new user from. + */ + public User(UserManager manager, String dataString) { + this.manager = manager; + this.name = coolName(); // set name to a cool name in case stringToUser fails + stringToUser(dataString); + } + + /** + * Create a new user with a name and an encrypted password. + * @param manager The manager that manages this user. + * @param name The name of the user. + * @param password_encrypted The encrypted password of the user. + */ + public User(UserManager manager, String name, String password_encrypted) { + this.manager = manager; + this.name = name; + this.passwordEncrypted = password_encrypted; + } + + /** + * Create a new user with a name, an encrypted password and a UID. + * @param manager The manager that manages this user. + * @param name The name of the user. + * @param password_encrypted The encrypted password of the user. + * @param UID The UID of the user. + */ + public User(UserManager manager, String name, String password_encrypted, UUID UID) { + this.manager = manager; + this.name = name; + this.passwordEncrypted = password_encrypted; + this.UID = UID; + } + + /** + * Get whether the UID equals the UID of this user. + * @param UID The UID to authenticate with. + * @return Whether the UID equals the UID of this user. + */ + public boolean authenticate(UUID UID) { + return this.UID.equals(UID); + } + + /** + * Get whether this user is authenticated. (Has no default UID) + * @return Whether this user is authenticated. (Has no default UID) + */ + public boolean isAuthenticated() { + return !UID.equals(new UUID(0, 0)); + } + + public UUID getUID() { + return UID; + } + + public void setUID(UUID UID) { + this.UID = UID; + } + + public UUID getLastUID() { + return lastUID; + } + + public void setLastUID(UUID lastUID) { + this.lastUID = lastUID; + } + + /** + * Writes the user parameters to a string. + * @return The string representation of the user that can be written to file. + */ + public String resUoTgnirts() { + String s = "\", \""; + return "[\""+ name.replace("\"", "'") + s + + passwordEncrypted + s + + isOp + "\", [\"" + + wins + s + + losses + s + + touchdownsScored + s + + touchdownsReceived + s + + casualties + s + + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(lastOnline) + +"\"]]"; + } + + /** + * Parses the parameters of a user from a string. + * @param p The string to parse from. + */ + public void stringToUser(String p) { + if(p.startsWith("[") && p.endsWith("]")) { // p starts and ends correctly + // strip [ and ] from string if there + p = p.substring(1, p.length() - 1); + // return if string is empty after stripping + if(p.equals("")) return; + // prepare position counter + int c = 0; + try { + if (p.charAt(c) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring name, password hash, op status, wins, losses, casualties and last online."); return; } + + c++; // skip first quote + + // get name + name = getNextSubparameter(p, c); + c += 4 + name.length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring password hash, op status, wins, losses, casualties, touchdowns received, touchdowns scored and last online."); return; } + + // get password hash + passwordEncrypted = getNextSubparameter(p, c); + c += 4 + passwordEncrypted.length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring op status, wins, losses, casualties, touchdowns received, touchdowns scored and last online."); return; } + + // get op status + if(getNextSubparameter(p, c).toLowerCase().equals("true") + || getNextSubparameter(p, c).toLowerCase().equals("yes") + || getNextSubparameter(p, c).toLowerCase().equals("oui")) + isOp = true; + c += 5 + getNextSubparameter(p, c).length(); // skip closing quote, comma, space and opening bracket + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring wins, losses, casualties, touchdowns received, touchdowns scored and last online."); return; } + + // get wins + wins = Integer.parseInt(getNextSubparameter(p, c)); + c += 4 + getNextSubparameter(p, c).length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring losses, casualties, touchdowns received, touchdowns scored and last online."); return; } + + // get losses + losses = Integer.parseInt(getNextSubparameter(p, c)); + c += 4 + getNextSubparameter(p, c).length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring casualties, touchdowns received, touchdowns scored and last online."); return; } + + // get losses + touchdownsScored = Integer.parseInt(getNextSubparameter(p, c)); + c += 4 + getNextSubparameter(p, c).length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring casualties, touchdowns received and last online."); return; } + + // get losses + touchdownsReceived = Integer.parseInt(getNextSubparameter(p, c)); + c += 4 + getNextSubparameter(p, c).length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring casualties and last online."); return; } + + // get casualties + casualties = Integer.parseInt(getNextSubparameter(p, c)); + c += 4 + getNextSubparameter(p, c).length(); // skip closing quote, comma and space + if (p.charAt(c-1) != '"') { manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring last online."); return; } + + // get last online + try { + String lastOnlineString = getNextSubparameter(p, c); + lastOnline = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).parse(lastOnlineString); + } catch (ParseException e) { + manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring last online."); + } + } catch (IndexOutOfBoundsException e) { + manager.getParent().log(Level.SEVERE, "Malformed user string. Ignoring name, password hash, op status, wins, losses, casualties, touchdowns received, touchdowns scored and last online."); + } + } + } + + /** + * Helper method to get the next subparamater in the string that is being parsed. (From the char at position c to the next quote) + * @param p The string that is being parsed. + * @param c The position of the parser in the string. + * @return The subparameter after the position c. + */ + private String getNextSubparameter(String p, int c) { + String sp = ""; + // get simple subparameter + while (p.charAt(c) != '"') { // loop until a closing quote appears + if(c >= p.length()-1) { + // string is malformed + return ""; + } + // add character to subparameter stringbuilder and count c up + sp += p.charAt(c); + c++; + } + return sp; + } + + /** + * Returns a cool name. + * @return A cool name. + */ + private String coolName() { + // Choose randomly from one of 10 cool names + switch ((int) (Math.random()*10)) { + case 0: + return "gooblewuddly"; + case 1: + return "poofhoneybunny"; + case 2: + return "smooshfoof"; + case 3: + return "wooglewookum"; + case 4: + return "moopsiedumpling"; + case 5: + return "dookmolph"; + case 6: + return "pung"; + case 7: + return "dook"; + case 8: + return "knabok"; + case 9: + return "dringdigebeck"; + default: + return "yourNameIsStrange"; + } + } + + /** + * This user lost a game. + * @param game The game this user lost. + */ + public void lostGame(GameController game) { + setLossesWithoutWrite(getLosses() + 1); + getStatsFromGame(game); + } + + /** + * This user won a game. + * @param game The game this user won. + */ + public void wonGame(GameController game) { + setWinsWithoutWrite(getWins() + 1); + getStatsFromGame(game); + } + + /** + * Gets the stats (touchdowns scored/received and casualties) from a game and write it to this user. + * @param game The game to get the stats from. + */ + private void getStatsFromGame(GameController game) { + try { + int teamIndex = game.getTeam(0).getCoach().getName().equals(getName()) ? 0 : 1; // set the index of the team where this user was coach + setTouchdownsScoredWithoutWrite(getTouchdownsScored() + game.getScoreFromTeam(teamIndex)); // add the score of the team where this user was coach. + setTouchdownsReceivedWithoutWrite(getTouchdownsReceived() + game.getScoreFromTeam(teamIndex == 0 ? 1 : 0)); // add the score of the team where this user was NOT coach. + for(Player casualty: game.getCasualties()) + if(casualty.getTeam().getCoach().getName().equals(getName())) + setCasualties(getCasualties() + 1); + } catch (NullPointerException e) { // the teams have not been set up yet + manager.getParent().log(Level.FINEST, "Match has not yet started. No touchdowns have happened."); + } finally { + writeUser(); + } + } + + /** + * Writes this user if the user manager isn't null. + */ + public void writeUser() { + if(manager != null) manager.writeUsers(); + } + + // GETTERS & SETTERS + + public boolean isLoggedIn() { + return loggedIn; + } + + public void setLoggedIn() { + this.loggedIn = true; + this.lastOnline = new Date(System.currentTimeMillis()); + writeUser(); + } + + public void setLoggedOut() { + this.loggedIn = false; + this.inGame = false; + setLastUID(getUID()); + setUID(new UUID(0, 0)); + writeUser(); + } + + public boolean isInGame() { + return inGame; + } + + public void setInGame(boolean inGame) { + this.inGame = inGame; + } + + public String getPasswordEncrypted() { + return passwordEncrypted; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + writeUser(); + } + + public void setNameWithoutWrite(String name) { + this.name = name; + } + + public boolean isOp() { + return isOp; + } + + public void setIsOp(boolean isOp) { + this.isOp = isOp; + writeUser(); + } + + public void setIsOpWithoutWrite(boolean isOp) { + this.isOp = isOp; + } + + public int getWins() { + return wins; + } + + public void setWins(int wins) { + this.wins = wins; + writeUser(); + } + + public void setWinsWithoutWrite(int wins) { + this.wins = wins; + } + + public int getLosses() { + return losses; + } + + public void setLosses(int losses) { + this.losses = losses; + writeUser(); + } + + public void setLossesWithoutWrite(int losses) { + this.losses = losses; + } + + public int getCasualties() { + return casualties; + } + + public void setCasualties(int casualties) { + this.casualties = casualties; + writeUser(); + } + + public void setCasualtiesWithoutWrite(int casualties) { + this.casualties = casualties; + } + + public int getTouchdownsScored() { + return touchdownsScored; + } + + public void setTouchdownsScored(int touchdownsScored) { + this.touchdownsScored = touchdownsScored; + writeUser(); + } + + public void setTouchdownsScoredWithoutWrite(int touchdownsScored) { + this.touchdownsScored = touchdownsScored; + } + + public int getTouchdownsReceived() { + return touchdownsReceived; + } + + public void setTouchdownsReceived(int touchdownsReceived) { + this.touchdownsReceived = touchdownsReceived; + writeUser(); + } + + public void setTouchdownsReceivedWithoutWrite(int touchdownsReceived) { + this.touchdownsReceived = touchdownsReceived; + } + + public Date getLastOnline() { + return lastOnline; + } + + public void setLastOnline(Date lastOnline) { + this.lastOnline = lastOnline; + writeUser(); + } + + public void setLastOnlineWithoutWrite(Date last_online) { + this.lastOnline = last_online; + } +} diff --git a/src/server/logic/UserManager.java b/src/server/logic/UserManager.java new file mode 100644 index 0000000..b6aa6a8 --- /dev/null +++ b/src/server/logic/UserManager.java @@ -0,0 +1,218 @@ +package server.logic; + +import util.SBApplication; +import util.SBLogger; + +import java.io.*; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +/** + * A class to manage multiple users and their stored data on disk. + * Created by milan on 23.3.15. + */ +public class UserManager { + + private Vector users = new Vector(); + private File file; + private SBApplication parent; + + /** + * Constuct a vector of all valid users in the file. Ignores invalid users. + * @param parent The parent to log to. + * @param file The file to read users from. + */ + public UserManager(SBApplication parent, File file) { + this.file = file; + this.parent = parent; + // read users from database + if(file.exists()) { // only read file if it exists + try { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String newUserString; + while((newUserString = reader.readLine()) != null) { + User newUser = new User(this, newUserString); + addUser(newUser); + } + } catch (IOException e) { + getParent().log(Level.SEVERE, "Could not read user file. Shutting down not to accidentally overwrite users in unread file."); + System.exit(-1); + } + } + } + + /** + * Add a new user to users without writing file. + * @param user The user to be added. + */ + public void addUser(User user) { + // check if name exists already + boolean existsAlready; + do { + existsAlready = false; + for (User userWithSameNameMaybeBaby: users) { + if (userWithSameNameMaybeBaby.getName().equals(user.getName())) { + user.setNameWithoutWrite(countUpName(user.getName())); + existsAlready = true; + break; + } + } + } while(existsAlready); + users.add(user); + } + + /** + * Count up the name of the user that is being added. + * @param name The name to count up. + * @return The next generation (counted up) name. + */ + private String countUpName(String name) { + if(name.endsWith("XIX")) // 19, 29, ... + return name.replaceAll("XIX$", "XX"); + if(name.endsWith("XVIII")) // 18, 28, ... + return name.replaceAll("XVIII$", "XIX"); + if(name.endsWith("XIV")) // 14, 24, ... + return name.replaceAll("XIV$", "XV"); + if(name.endsWith("XIII")) // 13, 23, ... + return name.replaceAll("XIII$", "XIV"); + if(name.endsWith("IX")) // 9 + return name.replaceAll("IX$", "X"); + if(name.endsWith("VIII")) // 8 + return name.replaceAll("VIII$", "IX"); + for (int i = 1; i <= 3; i++) // 5, 6, 7, 10, 11, 12, 15, 16, 17, 20, 21, 22, ... + if(name.substring(name.length() - i).matches("(? 0) + backupWriter.write(backupBuffer, 0, length); + backupWriter.close(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Could not backup file. Not writing to disk."); + return false; + } + // delete and rewrite file + boolean deletedFile = file.delete(); + PrintWriter writer; + try { + writer = new PrintWriter(new FileWriter(file)); + boolean createdFile = file.createNewFile(); + } catch (IOException e) { + getParent().log(Level.SEVERE, "Could not write file."); + return false; + } + for(User user: users) { + writer.println(user.resUoTgnirts()); + } + if(writer.checkError()) { + getParent().log(Level.SEVERE, "Could not write file."); + return false; + } else { + writer.close(); + // remove backup file again + boolean deletedBackup = backup.delete(); + return true; + } + } + + /** + * Get a clone of the users vector (to iterate over, etc.). + * @return A clone of the users vector. + */ + @SuppressWarnings("unchecked") + public Vector getUsers() { + return (Vector) users.clone(); + } + + public SBApplication getParent() { + return parent; + } +} diff --git a/src/util/FinishedGame.java b/src/util/FinishedGame.java new file mode 100644 index 0000000..9078ade --- /dev/null +++ b/src/util/FinishedGame.java @@ -0,0 +1,293 @@ +package util; + +import gameLogic.GameController; +import gameLogic.Player; +import gameLogic.Team; +import network.SBProtocolCommand; +import server.logic.User; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Vector; +import java.util.logging.Level; + +/** + * A class to represent unplayable, finished games. + * Created by milan on 9.4.15. + */ +public class FinishedGame extends GameController { + + private static final SBLogger L = new SBLogger(FinishedGame.class.getName(), util.SBLogger.LOG_LEVEL); + + /** + * An opponent in arrays like: ["name", "teamName", "teamType", "score"]
+ * Default is ["UNRECORDED", "UNRECORDED", "UNRECORDED", "0"] + */ + private String[] opponent1 = new String[]{"UNRECORDED", "UNRECORDED", "UNRECORDED", "0"}, + opponent2 = new String[]{"UNRECORDED", "UNRECORDED", "UNRECORDED", "0"}; + /** + * The winner of the game.
+ * Default is "UNRECORDED" + */ + private String winner = "UNRECORDED"; + /** + * When the game was finished.
+ * Default is January 1, 1970, 00:00:00 GMT. + */ + private Date finishedOn = new Date(0); + /** + * The players that had accidents during the match.
+ * Default is an empty array. + */ + private ArrayList casualties = new ArrayList(); + + /** + * A game constructor to construct from a text string from a file. (To represent logged games in the application) + * @param dataString the string with the game data. + */ + public FinishedGame(String dataString) { + super(dataString); + fromLogString(dataString); + } + + /** + * A finished game cannot be started. + */ + @Override + public void run() {} + + /** + * Parse this finished game from a string that was probably read from a file. + * @param p The string to parse. + */ + public void fromLogString(String p) { + p = p.replaceAll("\\s+",""); // remove whitespace in this string (because string is escaped) + + if(p.startsWith("[") && p.endsWith("]")) { // p starts and ends correctly + // strip [ and ] from string if there + p = p.substring(1, p.length() - 1); + // return if string is empty after stripping + if(p.equals("")) return; + // prepare position counter + int c = 0; + try { + if (p.charAt(c) != '[') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + c += 2; // skip first quote and opening bracket + + // get name of opponent 1 + opponent1[0] = getNextSubparameter(p, c); + c += 4 + opponent1[0].length(); // skip closing quote, comma, space and opening bracket + opponent1[0] = unescape(opponent1[0]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get name of team 1 + opponent1[1] = getNextSubparameter(p, c); + c += 3 + opponent1[1].length(); // skip closing quote, comma and space + opponent1[1] = unescape(opponent1[1]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get type of team 1 + opponent1[2] = getNextSubparameter(p, c); + c += 4 + opponent1[2].length(); // skip closing quote, comma, space and closing bracket + opponent1[2] = unescape(opponent1[2]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get score of team 1 + opponent1[3] = getNextSubparameter(p, c); + c += 5 + opponent1[3].length(); // skip closing quote, comma, space, closing bracket and opening bracket + opponent1[3] = unescape(opponent1[3]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get name of opponent 2 + opponent2[0] = getNextSubparameter(p, c); + c += 4 + opponent2[0].length(); // skip closing quote, comma, space and opening bracket + opponent2[0] = unescape(opponent2[0]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get name of team 2 + opponent2[1] = getNextSubparameter(p, c); + c += 3 + opponent2[1].length(); // skip closing quote, comma and space + opponent2[1] = unescape(opponent2[1]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get type of team 2 + opponent2[2] = getNextSubparameter(p, c); + c += 4 + opponent2[2].length(); // skip closing quote, comma, space and closing bracket + opponent2[2] = unescape(opponent2[2]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get score of team 2 + opponent2[3] = getNextSubparameter(p, c); + c += 4 + opponent2[3].length(); // skip closing quote, comma, space and closing bracket + opponent2[3] = unescape(opponent2[3]); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get winner + winner = getNextSubparameter(p, c); + c += 3 + winner.length(); // skip closing quote, comma and space + winner = unescape(winner); + if (p.charAt(c-1) != '"') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get finished on + String finishedOnString = getNextSubparameter(p, c); + c += 3 + finishedOnString.length(); // skip closing quote, comma, space and opening bracket but not quote after opening bracket. + if(finishedOnString.length() > 0) setFinishedOn(unescape(finishedOnString)); + if (p.charAt(c-1) != '"' && p.charAt(c-1) != '[') { L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); return; } + + // get casualties + while(p.charAt(c) != ']') { + c++; // skip quote + String casualtyString = getNextSubparameter(p, c); + c += 1 + casualtyString.length(); + addCasualty(casualtyString); + } + + } catch (IndexOutOfBoundsException e) { + L.log(Level.SEVERE, "Malformed game string "+p+".\nIgnoring rest of string."); + } + } + } + + /** + * Helper method to get the next subparamater in the string that is being parsed. (From the char at position c to the next quote) + * @param p The string that is being parsed. + * @param c The position of the parser in the string. + * @return The subparameter after the position c. + */ + private String getNextSubparameter(String p, int c) { + String sp = ""; + // get simple subparameter + while (p.charAt(c) != '"') { // loop until a closing quote appears + if(c >= p.length()-1) { + // string is malformed + return ""; + } + // add character to subparameter stringbuilder and count c up + sp += p.charAt(c); + c++; + } + return sp; + } + + /** + * Create a string of this game that can be written to file and read again. + * @return The string that represents this game. + */ + public String toLogString() { + String b = "\", \"", bA = "\", [\"", eA = "\"], \"", bAfA = "\"], [\""; + + String r = "[[\"" + escape(getCoach1Name()) + bA + escape(getTeam1Name()) + b + escape(getTeam1Type())+ eA + escape(getTeam1Score()) + bAfA + + escape(getCoach2Name())+ bA + escape(getTeam2Name())+ b + escape(getTeam2Type()) + eA + escape(getTeam2Score()) + eA + + escape(getWinnerString()) + b + + escape((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(getFinishedOn())); + if(casualties.size() > 0) { + r += bA; // add the beginning of the casualties array + for (String casualty: casualties) r += escape(casualty) + b; // add all casualties + r = r.replaceAll(b+"$", "\"]]"); // replace the last b with the end of the string + } else r += "\", []]"; // add an empty casualties array + return r; + } + + // GETTERS & SETTERS + + public String getCoach1Name() { + return opponent1[0]; + } + + public String getTeam1Name() { + return opponent1[1]; + } + + public String getTeam1Type() { + return opponent1[2]; + } + + public String getTeam1Score() { + return opponent1[3]; + } + + public String getCoach2Name() { + return opponent2[0]; + } + + public String getTeam2Name() { + return opponent2[1]; + } + + public String getTeam2Type() { + return opponent2[2]; + } + + public String getTeam2Score() { + return opponent2[3]; + } + + public String getWinnerString() { + return winner; + } + + public String getLooserString() { + if(getWinnerString().equals(getCoach1Name())) return getCoach2Name(); + else return getCoach1Name(); + } + + public Date getFinishedOn() { + return finishedOn; + } + + public void setFinishedOn(Date finishedOn) { + this.finishedOn = finishedOn; + } + + /** + * Try to get the finishedOn date from a String. + * @param finishedOnString The string to try to parse the date from. + * @return Whether the Date was successfully parsed. + */ + public boolean setFinishedOn(String finishedOnString) { + try { + this.finishedOn = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).parse(finishedOnString); + return true; + } catch (ParseException e) { + L.log(Level.SEVERE, "Malformed string. Ignoring date."); + return false; + } + } + + /** + * Get the vector with all casualties as strings. In FinishedGame, this is the only method to get the casualties. + * @return The vector with all casualties as strings. + */ + public ArrayList getCasualtiesString() { + return casualties; + } + + /** + * This always returns null in FinishedGame. Use getCasualtiesString() instead. + * @return Always null. Use getCasualtiesString() instead. + */ + public Vector getCasualties() { return null; } + + public void setCasualties(ArrayList casualties) { + this.casualties = casualties; + } + + public void addCasualty(String casualty) { + casualties.add(casualty); + } + + + // UNUSED + + @Override + public void sendMessage(User destinationUser, SBProtocolCommand command, String... parameters) {} + + @Override + public void touchdown(Team t) { + // TODO Auto-generated method stub + + } +} diff --git a/src/util/MainApplication.java b/src/util/MainApplication.java new file mode 100644 index 0000000..b190d5d --- /dev/null +++ b/src/util/MainApplication.java @@ -0,0 +1,104 @@ +package util; + +import client.Client; +import network.SBSocketManager; +import server.Server; + +import java.net.InetAddress; +import java.util.UUID; +import java.util.logging.Level; + +/** + * The main application that can be started as server or client. + * Created by milan on 9.4.15. + */ +public class MainApplication extends SBApplication { + + private static final SBLogger L = new SBLogger(MainApplication.class.getName(), util.SBLogger.LOG_LEVEL); + private static SBApplication serverOrClient; + + public static void main(String[] args) { + if(args.length == 2) { + if(args[0].equals("server")) { + try { + int port = Integer.parseInt(args[1]); + Server server = new Server(); + serverOrClient = server; + server.runServer(); + server.start(port); + } catch(NumberFormatException e) { + L.log(Level.SEVERE, "Illegal port. Run with 'server '."); + System.exit(-1); + } + } else if(args[0].equals("client")) { + try { + int port = Integer.parseInt(args[1].replaceAll("^[^:]+:", "")); + String address = args[1].replaceAll(":.*$", ""); + Client client = new Client(); + serverOrClient = client; + client.runClient(); + client.connect(address, port); + } catch(NumberFormatException e) { + L.log(Level.SEVERE, "Illegal port. Run with 'client :'."); + System.exit(-1); + } + } else { + L.log(Level.SEVERE, "Unknown command. Run with 'server ' or 'client :'."); + System.exit(-1); + } + } else { + L.log(Level.SEVERE, "Wrong number of arguments. Run with 'server ' or 'client :'."); + System.exit(-1); + } + } + + @Override + public SBSocketManager getSocketManager() { + return serverOrClient.getSocketManager(); + } + + @Override + public void processAnswers() { + serverOrClient.processAnswers(); + } + + @Override + public void processMessages() { + serverOrClient.processMessages(); + } + + @Override + public void logOutUserWithUID(UUID UID) { + serverOrClient.logOutUserWithUID(UID); + } + + @Override + public void logOutAllUsers() { + serverOrClient.logOutAllUsers(); + } + + @Override + public void lostConnection() { + serverOrClient.lostConnection(); + } + + @Override + public void isConnecting() { + serverOrClient.isConnecting(); + } + + @Override + public void hasConnected(InetAddress address, int port, boolean connected) { + serverOrClient.hasConnected(address, port, connected); + } + + @Override + public boolean checkIfUserExists(String name) { + return serverOrClient.checkIfUserExists(name); + } + + @Override + public void log(Level level, String message) { + serverOrClient.log(level, message); + } +} diff --git a/src/util/MessageProcessor.java b/src/util/MessageProcessor.java new file mode 100644 index 0000000..ed603f9 --- /dev/null +++ b/src/util/MessageProcessor.java @@ -0,0 +1,17 @@ +package util; + +import network.SBProtocolMessage; + +/** + * An interface for Server and Client message processors. + * Created by milan on 1.4.15. + */ +public interface MessageProcessor { + + void processMessage(SBProtocolMessage message); + void processAnswer(SBProtocolMessage answer); + + void returnSuccessMessage(SBProtocolMessage returnTo, String... parameters); + void returnFailureMessage(SBProtocolMessage returnTo, String... parameters); + +} diff --git a/src/util/ResourceManager.java b/src/util/ResourceManager.java new file mode 100644 index 0000000..c0e09b8 --- /dev/null +++ b/src/util/ResourceManager.java @@ -0,0 +1,143 @@ +package util; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Manages resources. Heh. + * Created by milan on 15.4.15. + */ +public class ResourceManager { + public static final int IMAGE_WIDTH = 429, + IMAGE_HEIGHT = 500, + PEDESTAL_WIDTH = 351, + DIE_IMAGE_WIDTH = 500; + public static final double IMAGE_RATIO = (double) IMAGE_WIDTH / IMAGE_HEIGHT; + + public static final String IMAGES_PATH = "/images/"; + + public static final String MODERATORS_PATH = IMAGES_PATH + "moderators/"; + public static final String DICE_PATH = IMAGES_PATH + "dice/"; + + @SuppressWarnings("SuspiciousNameCombination") + public static BufferedImage MODERATOR_TURTLE = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + MODERATOR_WALRUS = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + + BACKGROUND = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + + PROP_CROSS = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + PROP_STAR = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB), + PROP_BAND_AID = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB), + PROP_BAND_AID_DOUBLE = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB), + PROP_RED_CARD = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB), + PROP_FOOTBALL = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + + DEFAULT_PLAYER_R = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + DEFAULT_PLAYER_L = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB), + + DIE_PUSHED = new BufferedImage(DIE_IMAGE_WIDTH, DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB), + DIE_ATTACKER_DOWN = new BufferedImage(DIE_IMAGE_WIDTH, DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB), + DIE_BOTH_DOWN = new BufferedImage(DIE_IMAGE_WIDTH, DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB), + DIE_DEFENDER_DOWN = new BufferedImage(DIE_IMAGE_WIDTH, DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB), + DIE_DEFENDER_STUMBLES = new BufferedImage(DIE_IMAGE_WIDTH, DIE_IMAGE_WIDTH, BufferedImage.TYPE_INT_ARGB); + + static { + try { + + BACKGROUND = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "background.png")); + + // MODERATORS + + MODERATOR_TURTLE = ImageIO.read(ResourceManager.class.getResource(MODERATORS_PATH + "turtle.png")); + MODERATOR_WALRUS = ImageIO.read(ResourceManager.class.getResource(MODERATORS_PATH + "walrus.png")); + + // PROPS + + PROP_CROSS = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "remove_player.png")); + PROP_STAR = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "stunned_star.png")); + PROP_BAND_AID = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "band_aid.png")); + PROP_BAND_AID_DOUBLE = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "band_aid_double.png")); + PROP_RED_CARD = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "red_card.png")); + PROP_FOOTBALL = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "football.png")); + + // SPECIAL + + DEFAULT_PLAYER_R = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "defaultR.png")); + DEFAULT_PLAYER_L = ImageIO.read(ResourceManager.class.getResource(IMAGES_PATH + "defaultL.png")); + + // DIE + + DIE_PUSHED = ImageIO.read(ResourceManager.class.getResource(DICE_PATH + "die_pushed.png")); + DIE_ATTACKER_DOWN = ImageIO.read(ResourceManager.class.getResource(DICE_PATH + "die_attacker_down.png")); + DIE_BOTH_DOWN = ImageIO.read(ResourceManager.class.getResource(DICE_PATH + "die_both_down.png")); + DIE_DEFENDER_DOWN = ImageIO.read(ResourceManager.class.getResource(DICE_PATH + "die_defender_down.png")); + DIE_DEFENDER_STUMBLES = ImageIO.read(ResourceManager.class.getResource(DICE_PATH + "die_defender_stumbles.png")); + + } catch (IOException e) { e.printStackTrace(); } + } + + /** + * Extract the whole file structure of a zip directory into a target directory. + * @param file The zip file to read. + * @param destination The destination to write the contents of the file to. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void extractZipFile(File file, File destination) { + try { + + ZipInputStream inputStream = new ZipInputStream(new FileInputStream(file)); + ZipEntry entry; + String entryName, dir; + + while ((entry = inputStream.getNextEntry()) != null) { + entryName = entry.getName(); + if(entry.isDirectory()) { + + // create directory + new File(destination, entryName).mkdirs(); + + } else { + + // create parent directories if necessary + int s = entryName.lastIndexOf( File.separatorChar ); + dir = s == -1 ? null : entryName.substring( 0, s ); + if(dir != null) new File(destination, dir).mkdirs(); + + // write file + byte[] buffer = new byte[4096]; + int count; + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(new File(destination, entryName))); + while ((count = inputStream.read(buffer)) != -1) outputStream.write(buffer, 0, count); + outputStream.close(); + + } + } + + inputStream.close(); + + } catch(IOException e) { + e.printStackTrace(); + } + } + + /** + * Recursively delete a directory. + * @param file The directory to delete. + * @throws IOException If there was an error while deleting the directory. + */ + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void deleteDirectory(File file) throws IOException{ + + if(file.isDirectory()) { + if(file.list().length == 0) file.delete(); + else { + for (String nextChild: file.list()) deleteDirectory(new File(file, nextChild)); + file.delete(); + } + } else file.delete(); + + } +} \ No newline at end of file diff --git a/src/util/SBApplication.java b/src/util/SBApplication.java new file mode 100644 index 0000000..b3312f9 --- /dev/null +++ b/src/util/SBApplication.java @@ -0,0 +1,145 @@ +package util; + +import gameLogic.GameController; +import gameLogic.TeamManager; +import network.SBSocketManager; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.UUID; +import java.util.Vector; +import java.util.logging.Level; + +/** + * An abstract class to group client.Client and server.Server. + * Created by milan on 28.3.15. + */ +public abstract class SBApplication { + + public boolean logmatches = true, autologin = false, automatchstart = false; // for debugging purposes + + public static final String MODERATOR_NAME = "Al Capone"; + public UUID UID; + private Vector finishedGames = new Vector(); + private TeamManager teamManager; + + /** + * Loads the available Teams from this jar + */ + public void loadAvailableTeamsLocally() { + try { + File jarFile = new File(SBApplication.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); + if(jarFile.getAbsolutePath().endsWith(".jar")) { + log(Level.INFO, "Loading Teams from jar."); + File destination = new File(jarFile.getAbsolutePath().replaceAll(".jar$", UUID.randomUUID().toString().substring(0, 8))); // create temporary storage for whole jar + ResourceManager.extractZipFile(jarFile, destination); + File teamsDir = new File(destination.getAbsolutePath() + "/teams"); + teamManager = new TeamManager(this, teamsDir); + ResourceManager.deleteDirectory(destination); // delete the temporary directory again + } else { + log(Level.INFO, "Loading Teams from dir."); + URL teamsURL = SBApplication.class.getResource("/teams"); + if(teamsURL != null) teamManager = new TeamManager(this, new File(teamsURL.toURI())); + else { + log(Level.SEVERE, "Could not find dir /teams"); + teamManager = new TeamManager(this); + } + } + log(Level.INFO, "Loaded teams."); + } catch (IOException e) { + log(Level.SEVERE, "Could not load available teams."); + teamManager = new TeamManager(this); + } catch (URISyntaxException e) { + log(Level.SEVERE, "Could not load available teams because of misformed URL."); + teamManager = new TeamManager(this); + } + } + + /** + * Loads the available teams from a zip on a url and creates a team manager. + * @param host The host where the zip file is stored. + * @param file The path of the zip file on the host. + */ + public void loadAvailableTeams(String host, String file) { + URL teamsURL = null; + try { + teamsURL = new URL("http", host, 80, file); + } catch (MalformedURLException e) { + log(Level.SEVERE, "Could not load available teams. Make sure you are connected to the Internet and the URL is correct."); + e.printStackTrace(); + } + teamManager = new TeamManager(this, teamsURL); + } + + /** + * Reads logged games from file. + * @param pathname The path to get the logged games file from. + */ + protected void getLoggedGames(String pathname) { + // read games from log + File gamesFile = new File(pathname); + if(gamesFile.exists()) { // only read file if it exists + try { + BufferedReader reader = new BufferedReader(new FileReader(gamesFile)); + String gameString; + while((gameString = reader.readLine()) != null) + addFinishedGame(new FinishedGame(gameString)); + log(Level.INFO, "Welcome back!"); + } catch (IOException e) { + log(Level.SEVERE, "Could not read games file. Shutting down not to accidentally overwrite games in unread file."); + System.exit(-1); + } + } else log(Level.INFO, "Al Capone: Seems like you're new to SafariBowl! Welcome!"); + } + + public void addFinishedGame(String game) { + this.finishedGames.add(game); + } + + public void addFinishedGame(GameController game) { + this.finishedGames.add(game.toLogString()); + } + + /** + * Get a clone of the finished games vector (to iterate over, etc.). + * @return A clone of the finished games vector. + */ + @SuppressWarnings("unchecked") + public Vector getFinishedGames() { + return (Vector) finishedGames.clone(); + } + + public TeamManager getTeamManager() { + return teamManager; + } + + public abstract SBSocketManager getSocketManager(); + + public UUID getUID() { + return UID; + } + + public abstract void processAnswers(); + + public abstract void processMessages(); + + public abstract void logOutUserWithUID(UUID UID); + + public abstract void logOutAllUsers(); + + public abstract void lostConnection(); + + public abstract void isConnecting(); + + public abstract void hasConnected(InetAddress address, int port, boolean connected); + + public abstract boolean checkIfUserExists(String name); + + public abstract void log(Level level, String message); +} diff --git a/src/util/SBException.java b/src/util/SBException.java new file mode 100644 index 0000000..13c4b53 --- /dev/null +++ b/src/util/SBException.java @@ -0,0 +1,13 @@ +package util; + +/** + * Group all exceptions of SafariBowl in SBException. + */ +public class SBException extends Exception { + public SBException() { + } + + public SBException(String message) { + super(message); + } +} diff --git a/src/util/SBLogger.java b/src/util/SBLogger.java new file mode 100644 index 0000000..7a0c772 --- /dev/null +++ b/src/util/SBLogger.java @@ -0,0 +1,104 @@ +package util; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; +import java.util.logging.*; + +/** + * A logger class with log levels which writes all messages to file and specified level messages to stdout. + * Created by milan on 19.3.15. + */ +public class SBLogger extends Logger { + + public static final Level LOG_LEVEL = Level.INFO; + static private final Level DEFAULT_LEVEL = Level.INFO; + + static private FileHandler logFile; + static private SBFormatter formatterTxt = new SBFormatter(); + + static { + try { + File logDir = new File("log"); + + boolean madeDir = logDir.mkdirs(); + logFile = new FileHandler("log/main.log", true); + } catch (IOException e) { + System.out.println("Couldn't set up logger."); + e.printStackTrace(); + System.exit(-100); + } + logFile.setFormatter(formatterTxt); + logFile.setLevel(Level.FINE); + } + + /** + * Protected method to construct a logger for a named subsystem.
+ * The logger will be initially configured with a null Level + * and with useParentHandlers set to true. + * + * @param name A name for the logger. This should + * be a dot-separated name and should normally + * be based on the package name or class name + * of the subsystem, such as java.net + * or javax.swing. It may be null for anonymous Loggers. + * @param resourceBundleName name of ResourceBundle to be used for localizing + * messages for this logger. May be null if none + * of the messages require localization. + */ + public SBLogger(String name, String resourceBundleName) { + super(name, resourceBundleName); + setup(DEFAULT_LEVEL); + } + + /** + * SBLogger constructor unlocalized. + * @param name A name for the logger. Usually MyClass.class.getName(). + * @param level The level at which this logger should log to the console. + */ + public SBLogger(String name, Level level) { + super(name, null); + setup(level); + } + + /** + * Log message with set level. + * @param message The message to log. + */ + public void log(String message) { + super.log(Level.SEVERE, message); + } + + /** + * Private method to setup logger with level. + * @param level The level to set for the log console. + */ + private void setup(Level level) { + setLevel(Level.FINEST); + // setup file + addHandler(logFile); + // setup console logger + ConsoleHandler logConsole = new ConsoleHandler(); + logConsole.setFormatter(formatterTxt); + logConsole.setLevel(level); + addHandler(logConsole); + } + + /** + * Private formatter class to format log messages. + */ + private static class SBFormatter extends SimpleFormatter { + public String format(LogRecord logRecord) { + // format date + Date date = new Date(logRecord.getMillis()); + // format level + String level = logRecord.getLevel()+":"; + for (int i = level.length(); i < 10; i++) level += " "; + // format message + SimpleDateFormat dateFormat = new SimpleDateFormat("d.M.y H:m:s.S"); + return dateFormat.format(date)+" - "+logRecord.getLoggerName().replaceAll("([^\\.]*\\.)+(?=[^\\.]+)", "")+", "+level+formatMessage(logRecord)+"\n"; + } + } +} \ No newline at end of file