package com.massivecraft.factions; import java.util.HashSet; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.entity.Player; import com.massivecraft.factions.event.FPlayerLeaveEvent; import com.massivecraft.factions.event.LandClaimEvent; import com.massivecraft.factions.iface.EconomyParticipator; import com.massivecraft.factions.iface.RelationParticipator; import com.massivecraft.factions.integration.Econ; import com.massivecraft.factions.integration.LWCFeatures; import com.massivecraft.factions.integration.SpoutFeatures; import com.massivecraft.factions.integration.Worldguard; import com.massivecraft.factions.util.RelationUtil; import com.massivecraft.factions.zcore.persist.PlayerEntity; import com.massivecraft.mcore.util.Txt; /** * Logged in players always have exactly one FPlayer instance. * Logged out players may or may not have an FPlayer instance. They will always have one if they are part of a faction. * This is because only players with a faction are saved to disk (in order to not waste disk space). * * The FPlayer is linked to a minecraft player using the player name. * * The same instance is always returned for the same player. * This means you can use the == operator. No .equals method necessary. */ // TODO: The players are saved in non order. public class FPlayer extends PlayerEntity implements EconomyParticipator { // FIELD: lastStoodAt private transient FLocation lastStoodAt = new FLocation(); // Where did this player stand the last time we checked? public FLocation getLastStoodAt() { return this.lastStoodAt; } public void setLastStoodAt(FLocation flocation) { this.lastStoodAt = flocation; } // FIELD: factionId private String factionId; public Faction getFaction() { if(this.factionId == null) {return null;} return FactionColl.i.get(this.factionId); } public String getFactionId() { return this.factionId; } public boolean hasFaction() { return ! factionId.equals("0"); } public void setFaction(Faction faction) { Faction oldFaction = this.getFaction(); if (oldFaction != null) oldFaction.removeFPlayer(this); faction.addFPlayer(this); this.factionId = faction.getId(); SpoutFeatures.updateTitle(this, null); SpoutFeatures.updateTitle(null, this); } // FIELD: role private Rel role; public Rel getRole() { return this.role; } public void setRole(Rel role) { this.role = role; SpoutFeatures.updateTitle(this, null); } // FIELD: title private String title; public String getTitle() { return this.title; } public void setTitle(String title) { this.title = title; } // FIELD: power private double power; // FIELD: powerBoost // special increase/decrease to min and max power for this player private double powerBoost; public double getPowerBoost() { return this.powerBoost; } public void setPowerBoost(double powerBoost) { this.powerBoost = powerBoost; } // FIELD: lastPowerUpdateTime private long lastPowerUpdateTime; // FIELD: lastLoginTime private long lastLoginTime; // FIELD: mapAutoUpdating private transient boolean mapAutoUpdating; public void setMapAutoUpdating(boolean mapAutoUpdating) { this.mapAutoUpdating = mapAutoUpdating; } public boolean isMapAutoUpdating() { return mapAutoUpdating; } // FIELD: autoClaimEnabled private transient Faction autoClaimFor; public Faction getAutoClaimFor() { return autoClaimFor; } public void setAutoClaimFor(Faction faction) { this.autoClaimFor = faction; } private transient boolean hasAdminMode = false; public boolean hasAdminMode() { return this.hasAdminMode; } public void setHasAdminMode(boolean val) { this.hasAdminMode = val; } // FIELD: loginPvpDisabled private transient boolean loginPvpDisabled; // FIELD: account public String getAccountId() { return this.getId(); } // -------------------------------------------- // // Construct // -------------------------------------------- // // GSON need this noarg constructor. public FPlayer() { this.resetFactionData(false); this.power = ConfServer.powerPlayerStarting; this.lastPowerUpdateTime = System.currentTimeMillis(); this.lastLoginTime = System.currentTimeMillis(); this.mapAutoUpdating = false; this.autoClaimFor = null; this.loginPvpDisabled = (ConfServer.noPVPDamageToOthersForXSecondsAfterLogin > 0) ? true : false; this.powerBoost = 0.0; if ( ! ConfServer.newPlayerStartingFactionID.equals("0") && FactionColl.i.exists(ConfServer.newPlayerStartingFactionID)) { this.factionId = ConfServer.newPlayerStartingFactionID; } } public final void resetFactionData(boolean doSpoutUpdate) { if (this.factionId != null && FactionColl.i.exists(this.factionId)) // Avoid infinite loop! TODO: I think that this is needed is a sign we need to refactor. { Faction currentFaction = this.getFaction(); if (currentFaction != null) { currentFaction.removeFPlayer(this); } } this.factionId = "0"; // The default neutral faction this.role = Rel.MEMBER; this.title = ""; this.autoClaimFor = null; if (doSpoutUpdate) { SpoutFeatures.updateTitle(this, null); SpoutFeatures.updateTitle(null, this); SpoutFeatures.updateCape(this.getPlayer(), null); } } public void resetFactionData() { this.resetFactionData(true); } // -------------------------------------------- // // Getters And Setters // -------------------------------------------- // public long getLastLoginTime() { return lastLoginTime; } public void setLastLoginTime(long lastLoginTime) { losePowerFromBeingOffline(); this.lastLoginTime = lastLoginTime; this.lastPowerUpdateTime = lastLoginTime; if (ConfServer.noPVPDamageToOthersForXSecondsAfterLogin > 0) { this.loginPvpDisabled = true; } } public boolean hasLoginPvpDisabled() { if (!loginPvpDisabled) { return false; } if (this.lastLoginTime + (ConfServer.noPVPDamageToOthersForXSecondsAfterLogin * 1000) < System.currentTimeMillis()) { this.loginPvpDisabled = false; return false; } return true; } //----------------------------------------------// // Title, Name, Faction Tag and Chat //----------------------------------------------// public String getName() { return getId(); } public String getTag() { if ( ! this.hasFaction()) { return ""; } return this.getFaction().getTag(); } // Base concatenations: public String getNameAndSomething(String something) { String ret = this.role.getPrefix(); if (something.length() > 0) { ret += something+" "; } ret += this.getName(); return ret; } public String getNameAndTitle() { return this.getNameAndSomething(this.getTitle()); } public String getNameAndTag() { return this.getNameAndSomething(this.getTag()); } // Colored concatenations: // These are used in information messages public String getNameAndTitle(Faction faction) { return this.getColorTo(faction)+this.getNameAndTitle(); } public String getNameAndTitle(FPlayer fplayer) { return this.getColorTo(fplayer)+this.getNameAndTitle(); } // Chat Tag: // These are injected into the format of global chat messages. public String getChatTag() { if ( ! this.hasFaction()) { return ""; } return String.format(ConfServer.chatTagFormat, this.role.getPrefix()+this.getTag()); } // Colored Chat Tag public String getChatTag(Faction faction) { if ( ! this.hasFaction()) { return ""; } return this.getRelationTo(faction).getColor()+getChatTag(); } public String getChatTag(FPlayer fplayer) { if ( ! this.hasFaction()) { return ""; } return this.getColorTo(fplayer)+getChatTag(); } // ------------------------------- // Relation and relation colors // ------------------------------- @Override public String describeTo(RelationParticipator observer, boolean ucfirst) { return RelationUtil.describeThatToMe(this, observer, ucfirst); } @Override public String describeTo(RelationParticipator observer) { return RelationUtil.describeThatToMe(this, observer); } @Override public Rel getRelationTo(RelationParticipator observer) { return RelationUtil.getRelationOfThatToMe(this, observer); } @Override public Rel getRelationTo(RelationParticipator observer, boolean ignorePeaceful) { return RelationUtil.getRelationOfThatToMe(this, observer, ignorePeaceful); } public Rel getRelationToLocation() { return BoardOld.getFactionAt(new FLocation(this)).getRelationTo(this); } @Override public ChatColor getColorTo(RelationParticipator observer) { return RelationUtil.getColorOfThatToMe(this, observer); } //----------------------------------------------// // Health //----------------------------------------------// public void heal(int amnt) { Player player = this.getPlayer(); if (player == null) { return; } player.setHealth(player.getHealth() + amnt); } //----------------------------------------------// // Power //----------------------------------------------// public double getPower() { this.updatePower(); return this.power; } protected void alterPower(double delta) { this.power += delta; if (this.power > this.getPowerMax()) this.power = this.getPowerMax(); else if (this.power < this.getPowerMin()) this.power = this.getPowerMin(); } public double getPowerMax() { return ConfServer.powerPlayerMax + this.powerBoost; } public double getPowerMin() { return ConfServer.powerPlayerMin + this.powerBoost; } public int getPowerRounded() { return (int) Math.round(this.getPower()); } public int getPowerMaxRounded() { return (int) Math.round(this.getPowerMax()); } public int getPowerMinRounded() { return (int) Math.round(this.getPowerMin()); } protected void updatePower() { if (this.isOffline()) { losePowerFromBeingOffline(); if (!ConfServer.powerRegenOffline) { return; } } long now = System.currentTimeMillis(); long millisPassed = now - this.lastPowerUpdateTime; this.lastPowerUpdateTime = now; Player thisPlayer = this.getPlayer(); if (thisPlayer != null && thisPlayer.isDead()) return; // don't let dead players regain power until they respawn int millisPerMinute = 60*1000; double powerPerMinute = ConfServer.powerPerMinute; if(ConfServer.scaleNegativePower && this.power < 0) { powerPerMinute += (Math.sqrt(Math.abs(this.power)) * Math.abs(this.power)) / ConfServer.scaleNegativeDivisor; } this.alterPower(millisPassed * powerPerMinute / millisPerMinute); } protected void losePowerFromBeingOffline() { if (ConfServer.powerOfflineLossPerDay > 0.0 && this.power > ConfServer.powerOfflineLossLimit) { long now = System.currentTimeMillis(); long millisPassed = now - this.lastPowerUpdateTime; this.lastPowerUpdateTime = now; double loss = millisPassed * ConfServer.powerOfflineLossPerDay / (24*60*60*1000); if (this.power - loss < ConfServer.powerOfflineLossLimit) { loss = this.power; } this.alterPower(-loss); } } public void onDeath() { this.updatePower(); this.alterPower(-ConfServer.powerPerDeath); } //----------------------------------------------// // Territory //----------------------------------------------// public boolean isInOwnTerritory() { return BoardOld.getFactionAt(new FLocation(this)) == this.getFaction(); } /*public boolean isInOthersTerritory() { Faction factionHere = Board.getFactionAt(new FLocation(this)); return factionHere != null && factionHere.isNormal() && factionHere != this.getFaction(); }*/ /*public boolean isInAllyTerritory() { return Board.getFactionAt(new FLocation(this)).getRelationTo(this) == Rel.ALLY; }*/ /*public boolean isInNeutralTerritory() { return Board.getFactionAt(new FLocation(this)).getRelationTo(this) == Rel.NEUTRAL; }*/ public boolean isInEnemyTerritory() { return BoardOld.getFactionAt(new FLocation(this)).getRelationTo(this) == Rel.ENEMY; } public void sendFactionHereMessage() { if (SpoutFeatures.updateTerritoryDisplay(this)) { return; } Faction factionHere = BoardOld.getFactionAt(this.getLastStoodAt()); String msg = Txt.parse("")+" ~ "+factionHere.getTag(this); if (factionHere.getDescription().length() > 0) { msg += " - "+factionHere.getDescription(); } this.sendMessage(msg); } // ------------------------------- // Actions // ------------------------------- public void leave(boolean makePay) { Faction myFaction = this.getFaction(); makePay = makePay && Econ.shouldBeUsed() && ! this.hasAdminMode(); if (myFaction == null) { resetFactionData(); return; } boolean perm = myFaction.getFlag(FFlag.PERMANENT); if (!perm && this.getRole() == Rel.LEADER && myFaction.getFPlayers().size() > 1) { msg("You must give the admin role to someone else first."); return; } if (!ConfServer.canLeaveWithNegativePower && this.getPower() < 0) { msg("You cannot leave until your power is positive."); return; } // if economy is enabled and they're not on the bypass list, make sure they can pay if (makePay && ! Econ.hasAtLeast(this, ConfServer.econCostLeave, "to leave your faction.")) return; FPlayerLeaveEvent leaveEvent = new FPlayerLeaveEvent(this,myFaction,FPlayerLeaveEvent.PlayerLeaveReason.LEAVE); Bukkit.getServer().getPluginManager().callEvent(leaveEvent); if (leaveEvent.isCancelled()) return; // then make 'em pay (if applicable) if (makePay && ! Econ.modifyMoney(this, -ConfServer.econCostLeave, "to leave your faction.", "for leaving your faction.")) return; // Am I the last one in the faction? if (myFaction.getFPlayers().size() == 1) { // Transfer all money if (Econ.shouldBeUsed()) Econ.transferMoney(this, myFaction, this, Econ.getBalance(myFaction.getAccountId())); } if (myFaction.isNormal()) { for (FPlayer fplayer : myFaction.getFPlayersWhereOnline(true)) { fplayer.msg("%s left %s.", this.describeTo(fplayer, true), myFaction.describeTo(fplayer)); } if (ConfServer.logFactionLeave) Factions.get().log(this.getName()+" left the faction: "+myFaction.getTag()); } this.resetFactionData(); if (myFaction.isNormal() && !perm && myFaction.getFPlayers().isEmpty()) { // Remove this faction for (FPlayer fplayer : FPlayerColl.i.getOnline()) { fplayer.msg("%s was disbanded.", myFaction.describeTo(fplayer, true)); } myFaction.detach(); if (ConfServer.logFactionDisband) Factions.get().log("The faction "+myFaction.getTag()+" ("+myFaction.getId()+") was disbanded due to the last player ("+this.getName()+") leaving."); } } public boolean canClaimForFactionAtLocation(Faction forFaction, Location location, boolean notifyFailure) { String error = null; FLocation flocation = new FLocation(location); Faction myFaction = getFaction(); Faction currentFaction = BoardOld.getFactionAt(flocation); int ownedLand = forFaction.getLandRounded(); if (ConfServer.worldGuardChecking && Worldguard.checkForRegionsInChunk(location)) { // Checks for WorldGuard regions in the chunk attempting to be claimed error = Txt.parse("This land is protected"); } else if (ConfServer.worldsNoClaiming.contains(flocation.getWorldName())) { error = Txt.parse("Sorry, this world has land claiming disabled."); } else if (this.hasAdminMode()) { return true; } else if (forFaction == currentFaction) { error = Txt.parse("%s already own this land.", forFaction.describeTo(this, true)); } else if ( ! FPerm.TERRITORY.has(this, forFaction, true)) { return false; } else if (forFaction.getFPlayers().size() < ConfServer.claimsRequireMinFactionMembers) { error = Txt.parse("Factions must have at least %s members to claim land.", ConfServer.claimsRequireMinFactionMembers); } else if (ownedLand >= forFaction.getPowerRounded()) { error = Txt.parse("You can't claim more land! You need more power!"); } else if (ConfServer.claimedLandsMax != 0 && ownedLand >= ConfServer.claimedLandsMax && ! forFaction.getFlag(FFlag.INFPOWER)) { error = Txt.parse("Limit reached. You can't claim more land!"); } else if ( ! ConfServer.claimingFromOthersAllowed && currentFaction.isNormal()) { error = Txt.parse("You may not claim land from others."); } else if (currentFaction.getRelationTo(forFaction).isAtLeast(Rel.TRUCE) && ! currentFaction.isNone()) { error = Txt.parse("You can't claim this land due to your relation with the current owner."); } else if ( ConfServer.claimsMustBeConnected && ! this.hasAdminMode() && myFaction.getLandRoundedInWorld(flocation.getWorldName()) > 0 && !BoardOld.isConnectedLocation(flocation, myFaction) && (!ConfServer.claimsCanBeUnconnectedIfOwnedByOtherFaction || !currentFaction.isNormal()) ) { if (ConfServer.claimsCanBeUnconnectedIfOwnedByOtherFaction) error = Txt.parse("You can only claim additional land which is connected to your first claim or controlled by another faction!"); else error = Txt.parse("You can only claim additional land which is connected to your first claim!"); } else if (currentFaction.isNormal()) { if ( ! currentFaction.hasLandInflation()) { // TODO more messages WARN current faction most importantly error = Txt.parse("%s owns this land and is strong enough to keep it.", currentFaction.getTag(this)); } else if ( ! BoardOld.isBorderLocation(flocation)) { error = Txt.parse("You must start claiming land at the border of the territory."); } } if (notifyFailure && error != null) { msg(error); } return error == null; } public boolean attemptClaim(Faction forFaction, Location location, boolean notifyFailure) { // notifyFailure is false if called by auto-claim; no need to notify on every failure for it // return value is false on failure, true on success FLocation flocation = new FLocation(location); Faction currentFaction = BoardOld.getFactionAt(flocation); int ownedLand = forFaction.getLandRounded(); if ( ! this.canClaimForFactionAtLocation(forFaction, location, notifyFailure)) return false; // TODO: Add flag no costs?? // if economy is enabled and they're not on the bypass list, make sure they can pay boolean mustPay = Econ.shouldBeUsed() && ! this.hasAdminMode(); double cost = 0.0; EconomyParticipator payee = null; if (mustPay) { cost = Econ.calculateClaimCost(ownedLand, currentFaction.isNormal()); if (ConfServer.econClaimUnconnectedFee != 0.0 && forFaction.getLandRoundedInWorld(flocation.getWorldName()) > 0 && !BoardOld.isConnectedLocation(flocation, forFaction)) cost += ConfServer.econClaimUnconnectedFee; if(ConfServer.bankEnabled && ConfServer.bankFactionPaysLandCosts && this.hasFaction()) payee = this.getFaction(); else payee = this; if ( ! Econ.hasAtLeast(payee, cost, "to claim this land")) return false; } LandClaimEvent claimEvent = new LandClaimEvent(flocation, forFaction, this); Bukkit.getServer().getPluginManager().callEvent(claimEvent); if(claimEvent.isCancelled()) return false; // then make 'em pay (if applicable) if (mustPay && ! Econ.modifyMoney(payee, -cost, "to claim this land", "for claiming this land")) return false; if (LWCFeatures.getEnabled() && forFaction.isNormal() && ConfServer.onCaptureResetLwcLocks) LWCFeatures.clearOtherChests(flocation, this.getFaction()); // announce success Set informTheseFPlayers = new HashSet(); informTheseFPlayers.add(this); informTheseFPlayers.addAll(forFaction.getFPlayersWhereOnline(true)); for (FPlayer fp : informTheseFPlayers) { fp.msg("%s claimed land for %s from %s.", this.describeTo(fp, true), forFaction.describeTo(fp), currentFaction.describeTo(fp)); } BoardOld.setFactionAt(forFaction, flocation); SpoutFeatures.updateTerritoryDisplayLoc(flocation); if (ConfServer.logLandClaims) Factions.get().log(this.getName()+" claimed land at ("+flocation.getCoordString()+") for the faction: "+forFaction.getTag()); return true; } // -------------------------------------------- // // Persistance // -------------------------------------------- // @Override public boolean shouldBeSaved() { if (this.hasFaction()) return true; if (this.getPowerRounded() != this.getPowerMaxRounded() && this.getPowerRounded() != (int) Math.round(ConfServer.powerPlayerStarting)) return true; return false; } public void msg(String str, Object... args) { this.sendMessage(Txt.parse(str, args)); } }