Factions/src/com/massivecraft/factions/Faction.java

673 lines
17 KiB
Java
Raw Normal View History

2011-07-18 22:06:02 +02:00
package com.massivecraft.factions;
2011-02-06 13:36:11 +01:00
import java.util.*;
import org.bukkit.Bukkit;
2011-02-06 13:36:11 +01:00
import org.bukkit.ChatColor;
2011-03-23 17:39:56 +01:00
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
2011-02-06 13:36:11 +01:00
import org.bukkit.entity.Player;
2011-07-18 22:06:02 +02:00
2011-10-12 17:25:01 +02:00
import com.massivecraft.factions.iface.EconomyParticipator;
import com.massivecraft.factions.iface.RelationParticipator;
import com.massivecraft.factions.integration.Econ;
import com.massivecraft.factions.integration.SpoutFeatures;
2011-07-18 22:06:02 +02:00
import com.massivecraft.factions.util.*;
import com.massivecraft.mcore.ps.PS;
import com.massivecraft.mcore.store.Entity;
import com.massivecraft.mcore.util.SenderUtil;
import com.massivecraft.mcore.xlib.gson.annotations.SerializedName;
2011-02-06 13:36:11 +01:00
2013-04-12 15:10:11 +02:00
public class Faction extends Entity<Faction> implements EconomyParticipator
{
// -------------------------------------------- //
// META
// -------------------------------------------- //
public static Faction get(Object oid)
{
return FactionColl.get().get(oid);
}
// -------------------------------------------- //
// OVERRIDE: ENTITY
// -------------------------------------------- //
@Override
public Faction load(Faction that)
{
2013-04-11 12:32:38 +02:00
this.relationWish = that.relationWish;
this.invitedPlayerIds = that.invitedPlayerIds;
this.open = that.open;
this.tag = that.tag;
this.setDescription(that.description);
2013-04-11 12:32:38 +02:00
this.home = that.home;
this.cape = that.cape;
this.powerBoost = that.powerBoost;
this.flagOverrides = that.flagOverrides;
this.permOverrides = that.permOverrides;
return this;
2013-04-12 08:56:26 +02:00
}
// -------------------------------------------- //
// FIELDS: RAW
// -------------------------------------------- //
private Map<String, Rel> relationWish;
// TODO
@SerializedName("invites")
private Set<String> invitedPlayerIds = null;
public TreeSet<String> getInvitedPlayerIds()
{
TreeSet<String> ret = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
if (this.invitedPlayerIds != null) ret.addAll(this.invitedPlayerIds);
return ret;
}
public void setInvitedPlayerIds(Collection<String> invitedPlayerIds)
{
2013-04-11 12:32:38 +02:00
if (invitedPlayerIds == null || invitedPlayerIds.isEmpty())
{
this.invitedPlayerIds = null;
}
else
{
2013-04-11 12:32:38 +02:00
TreeSet<String> target = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (String invitedPlayerId : invitedPlayerIds)
{
target.add(invitedPlayerId.toLowerCase());
}
2013-04-11 12:32:38 +02:00
this.invitedPlayerIds = target;
}
this.changed();
}
2011-03-22 17:20:21 +01:00
private boolean open;
public boolean isOpen() { return this.open; }
public void setOpen(boolean open) { this.open = open; }
// FIELD: tag
private String tag;
public String getTag() { return this.tag; }
public String getTag(String prefix) { return prefix+this.tag; }
public String getTag(RelationParticipator observer)
{
if (observer == null)
{
return getTag();
}
return this.getTag(this.getColorTo(observer).toString());
}
public void setTag(String str)
{
2013-04-09 13:15:25 +02:00
if (ConfServer.factionTagForceUpperCase)
{
str = str.toUpperCase();
}
this.tag = str;
}
public String getComparisonTag() { return MiscUtil.getComparisonString(this.tag); }
// FIELD: description
private String description;
public boolean hasDescription()
{
return this.description != null;
}
public String getDescription()
{
if (this.hasDescription()) return this.description;
return Lang.FACTION_NODESCRIPTION;
}
public void setDescription(String description)
{
if (description != null)
{
description = description.trim();
// This code should be kept for a while to clean out the previous default text that was actually stored in the database.
if (description.length() == 0 || description.equalsIgnoreCase("Default faction description :("))
{
description = null;
}
}
this.description = description;
}
2011-03-23 17:39:56 +01:00
// FIELD: home
// TODO: Use a PS instead!
private LazyLocation home;
public void setHome(Location home) { this.home = new LazyLocation(home); }
public boolean hasHome() { return this.getHome() != null; }
public Location getHome()
{
confirmValidHome();
return (this.home != null) ? this.home.getLocation() : null;
}
public void confirmValidHome()
{
if (!ConfServer.homesMustBeInClaimedTerritory || this.home == null || (this.home.getLocation() != null && BoardColl.get().getFactionAt(PS.valueOf(this.home.getLocation())) == this))
return;
msg("<b>Your faction home has been un-set since it is no longer in your territory.");
this.home = null;
}
2011-10-12 17:25:01 +02:00
// FIELD: account (fake field)
// Bank functions
public String getAccountId()
2011-10-12 17:25:01 +02:00
{
String accountId = "faction-"+this.getId();
2011-10-12 18:48:47 +02:00
// We need to override the default money given to players.
if ( ! Econ.hasAccount(accountId))
{
Econ.setBalance(accountId, 0);
}
return accountId;
2011-10-12 17:25:01 +02:00
}
// FIELD: cape
private String cape;
public String getCape() { return cape; }
public void setCape(String val) { this.cape = val; SpoutFeatures.updateCape(this, null); }
// FIELD: powerBoost
// special increase/decrease to default and max power for this faction
private double powerBoost;
public double getPowerBoost() { return this.powerBoost; }
public void setPowerBoost(double powerBoost) { this.powerBoost = powerBoost; }
2011-10-23 20:50:49 +02:00
// FIELDS: Flag management
// TODO: This will save... defaults if they where changed to...
2013-04-11 12:32:38 +02:00
private Map<FFlag, Boolean> flagOverrides; // Contains the modifications to the default values
public boolean getFlag(FFlag flag)
2011-10-23 20:50:49 +02:00
{
Boolean ret = this.flagOverrides.get(flag);
if (ret == null) ret = flag.getDefault();
return ret;
}
public void setFlag(FFlag flag, boolean value)
2011-10-23 20:50:49 +02:00
{
2013-04-09 13:15:25 +02:00
if (ConfServer.factionFlagDefaults.get(flag).equals(value))
2011-10-23 20:50:49 +02:00
{
this.flagOverrides.remove(flag);
return;
}
this.flagOverrides.put(flag, value);
}
2011-10-23 20:50:49 +02:00
// FIELDS: Permission <-> Groups management
2011-10-24 01:37:51 +02:00
private Map<FPerm, Set<Rel>> permOverrides; // Contains the modifications to the default values
public Set<Rel> getPermittedRelations(FPerm perm)
{
Set<Rel> ret = this.permOverrides.get(perm);
if (ret == null) ret = perm.getDefault();
return ret;
}
2012-05-09 06:29:52 +02:00
/*
public void addPermittedRelation(FPerm perm, Rel rel)
{
Set<Rel> newPermittedRelations = EnumSet.noneOf(Rel.class);
newPermittedRelations.addAll(this.getPermittedRelations(perm));
newPermittedRelations.add(rel);
this.setPermittedRelations(perm, newPermittedRelations);
}
public void removePermittedRelation(FPerm perm, Rel rel)
{
Set<Rel> newPermittedRelations = EnumSet.noneOf(Rel.class);
newPermittedRelations.addAll(this.getPermittedRelations(perm));
newPermittedRelations.remove(rel);
this.setPermittedRelations(perm, newPermittedRelations);
2012-05-09 06:29:52 +02:00
}*/
public void setRelationPermitted(FPerm perm, Rel rel, boolean permitted)
{
Set<Rel> newPermittedRelations = EnumSet.noneOf(Rel.class);
newPermittedRelations.addAll(this.getPermittedRelations(perm));
if (permitted)
{
newPermittedRelations.add(rel);
}
else
{
newPermittedRelations.remove(rel);
}
this.setPermittedRelations(perm, newPermittedRelations);
}
public void setPermittedRelations(FPerm perm, Set<Rel> rels)
{
if (perm.getDefault().equals(rels))
{
this.permOverrides.remove(perm);
return;
}
this.permOverrides.put(perm, rels);
}
public void setPermittedRelations(FPerm perm, Rel... rels)
{
Set<Rel> temp = new HashSet<Rel>();
temp.addAll(Arrays.asList(rels));
this.setPermittedRelations(perm, temp);
}
2011-10-23 20:50:49 +02:00
2011-10-22 18:12:15 +02:00
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public Faction()
{
this.relationWish = new LinkedHashMap<String, Rel>();
2013-04-09 13:15:25 +02:00
this.open = ConfServer.newFactionsDefaultOpen;
this.tag = "???";
this.description = null;
this.powerBoost = 0.0;
this.flagOverrides = new LinkedHashMap<FFlag, Boolean>();
2011-10-24 01:37:51 +02:00
this.permOverrides = new LinkedHashMap<FPerm, Set<Rel>>();
New "peaceful" status for factions which can only be set by server admins/moderators. Members of peaceful factions cannot deal or receive PvP damage (unless in a war zone which has friendly fire enabled), cannot claim land from another faction and likewise can't have their land claimed, and cannot be considered as ally or enemy of any other faction. Faction admins and moderators of peaceful factions can enable/disable all explosions inside their faction's territory at will. The main purpose of this is to provide a way for more peaceful players who don't want to take part in faction wars (or just want to take a break from them) to still have fun on the server. It is also meant to allow groups of players to make protected buildings, monuments, grand constructions, and so forth without having to worry about another faction destroying them. New conf.json settings: "peacefulTerritoryDisablePVP" (default true) prevents PvP damage for anyone inside a peaceful faction's territory "peacefulTerritoryDisableMonsters" (default false) provides protection against monsters spawning or attacking inside a peaceful faction's territory "peacefulMembersDisablePowerLoss" (default true) which keeps members of peaceful factions from suffering power loss when they die. New commands: /f peaceful [faction tag] - toggle the indicated faction's "peaceful" status /f noboom - enable/disable explosions inside your faction's territory; only available to faction admin and faction moderators for peaceful factions New permission nodes: factions.setPeaceful - ability to use the /f peaceful command (admins) factions.peacefulExplosionToggle - ability to use /f noboom (everyone)
2011-08-05 10:50:47 +02:00
}
// -------------------------------------------- //
// FIELDS: EXTRA
// -------------------------------------------- //
// TODO: Make use of a player name extractor?
public boolean addInvitedPlayerId(String playerId)
{
TreeSet<String> invitedPlayerIds = this.getInvitedPlayerIds();
if (invitedPlayerIds.add(playerId.toLowerCase()))
{
this.setInvitedPlayerIds(invitedPlayerIds);
return true;
}
return false;
}
public boolean removeInvitedPlayerId(String playerId)
{
TreeSet<String> invitedPlayerIds = this.getInvitedPlayerIds();
if (invitedPlayerIds.remove(playerId.toLowerCase()))
{
this.setInvitedPlayerIds(invitedPlayerIds);
return true;
}
return false;
}
public boolean isInvited(FPlayer fplayer)
{
return this.getInvitedPlayerIds().contains(fplayer.getId());
}
// -------------------------------------------- //
// ACTIONS
// -------------------------------------------- //
public void invite(FPlayer fplayer)
{
this.addInvitedPlayerId(fplayer.getId());
}
public void deinvite(FPlayer fplayer)
{
this.removeInvitedPlayerId(fplayer.getId());
}
New "peaceful" status for factions which can only be set by server admins/moderators. Members of peaceful factions cannot deal or receive PvP damage (unless in a war zone which has friendly fire enabled), cannot claim land from another faction and likewise can't have their land claimed, and cannot be considered as ally or enemy of any other faction. Faction admins and moderators of peaceful factions can enable/disable all explosions inside their faction's territory at will. The main purpose of this is to provide a way for more peaceful players who don't want to take part in faction wars (or just want to take a break from them) to still have fun on the server. It is also meant to allow groups of players to make protected buildings, monuments, grand constructions, and so forth without having to worry about another faction destroying them. New conf.json settings: "peacefulTerritoryDisablePVP" (default true) prevents PvP damage for anyone inside a peaceful faction's territory "peacefulTerritoryDisableMonsters" (default false) provides protection against monsters spawning or attacking inside a peaceful faction's territory "peacefulMembersDisablePowerLoss" (default true) which keeps members of peaceful factions from suffering power loss when they die. New commands: /f peaceful [faction tag] - toggle the indicated faction's "peaceful" status /f noboom - enable/disable explosions inside your faction's territory; only available to faction admin and faction moderators for peaceful factions New permission nodes: factions.setPeaceful - ability to use the /f peaceful command (admins) factions.peacefulExplosionToggle - ability to use /f noboom (everyone)
2011-08-05 10:50:47 +02:00
// -------------------------------------------- //
// NONE OR NORMAL?
// -------------------------------------------- //
2011-10-23 20:50:49 +02:00
public boolean isNone()
{
return this.getId().equals(Const.FACTIONID_NONE);
2011-03-23 17:39:56 +01:00
}
public boolean isNormal()
{
return ! this.isNone();
2011-03-23 17:39:56 +01:00
}
// -------------------------------------------- //
// RELATION AND COLORS
// -------------------------------------------- //
2011-03-22 17:20:21 +01:00
2011-10-12 17:25:01 +02:00
@Override
2011-10-24 11:07:06 +02:00
public String describeTo(RelationParticipator observer, boolean ucfirst)
2011-10-12 17:25:01 +02:00
{
2011-10-24 11:07:06 +02:00
return RelationUtil.describeThatToMe(this, observer, ucfirst);
2011-10-12 17:25:01 +02:00
}
@Override
2011-10-24 11:07:06 +02:00
public String describeTo(RelationParticipator observer)
2011-10-12 17:25:01 +02:00
{
2011-10-24 11:07:06 +02:00
return RelationUtil.describeThatToMe(this, observer);
2011-10-12 17:25:01 +02:00
}
@Override
2011-10-24 11:07:06 +02:00
public Rel getRelationTo(RelationParticipator observer)
2011-10-12 17:25:01 +02:00
{
2011-10-24 11:07:06 +02:00
return RelationUtil.getRelationOfThatToMe(this, observer);
2011-10-12 17:25:01 +02:00
}
@Override
2011-10-24 11:07:06 +02:00
public Rel getRelationTo(RelationParticipator observer, boolean ignorePeaceful)
2011-10-12 17:25:01 +02:00
{
2011-10-24 11:07:06 +02:00
return RelationUtil.getRelationOfThatToMe(this, observer, ignorePeaceful);
2011-10-12 17:25:01 +02:00
}
@Override
2011-10-24 11:07:06 +02:00
public ChatColor getColorTo(RelationParticipator observer)
2011-10-12 17:25:01 +02:00
{
2011-10-24 11:07:06 +02:00
return RelationUtil.getColorOfThatToMe(this, observer);
2011-10-12 17:25:01 +02:00
}
public Rel getRelationWish(Faction otherFaction)
{
if (this.relationWish.containsKey(otherFaction.getId()))
{
2011-03-22 17:20:21 +01:00
return this.relationWish.get(otherFaction.getId());
}
return Rel.NEUTRAL;
2011-03-22 17:20:21 +01:00
}
public void setRelationWish(Faction otherFaction, Rel relation)
{
if (this.relationWish.containsKey(otherFaction.getId()) && relation.equals(Rel.NEUTRAL))
{
2011-03-22 17:20:21 +01:00
this.relationWish.remove(otherFaction.getId());
}
else
{
2011-03-22 17:20:21 +01:00
this.relationWish.put(otherFaction.getId(), relation);
}
}
2011-11-24 16:53:59 +01:00
public Map<Rel, List<String>> getFactionTagsPerRelation(RelationParticipator rp)
{
return getFactionTagsPerRelation(rp, false);
}
// onlyNonNeutral option provides substantial performance boost on large servers for listing only non-neutral factions
public Map<Rel, List<String>> getFactionTagsPerRelation(RelationParticipator rp, boolean onlyNonNeutral)
{
Map<Rel, List<String>> ret = new HashMap<Rel, List<String>>();
for (Rel rel : Rel.values())
{
ret.put(rel, new ArrayList<String>());
}
for (Faction faction : FactionColl.get().getAll())
{
Rel relation = faction.getRelationTo(this);
if (onlyNonNeutral && relation == Rel.NEUTRAL) continue;
2011-11-24 16:53:59 +01:00
ret.get(relation).add(faction.getTag(rp));
}
return ret;
}
2011-10-24 03:02:25 +02:00
// TODO: Implement a has enough feature.
// -------------------------------------------- //
// POWER
// -------------------------------------------- //
public double getPower()
{
if (this.getFlag(FFlag.INFPOWER))
2011-10-22 18:12:15 +02:00
{
2011-10-23 20:50:49 +02:00
return 999999;
2011-10-22 18:12:15 +02:00
}
double ret = 0;
for (FPlayer fplayer : this.getFPlayers())
{
2011-03-23 12:00:38 +01:00
ret += fplayer.getPower();
2011-02-06 13:36:11 +01:00
}
2013-04-09 13:15:25 +02:00
if (ConfServer.powerFactionMax > 0 && ret > ConfServer.powerFactionMax)
{
2013-04-09 13:15:25 +02:00
ret = ConfServer.powerFactionMax;
}
return ret + this.powerBoost;
2011-02-06 13:36:11 +01:00
}
public double getPowerMax()
{
if (this.getFlag(FFlag.INFPOWER))
2011-10-22 18:12:15 +02:00
{
2011-10-23 20:50:49 +02:00
return 999999;
2011-10-22 18:12:15 +02:00
}
double ret = 0;
for (FPlayer fplayer : this.getFPlayers())
{
2011-03-23 12:00:38 +01:00
ret += fplayer.getPowerMax();
2011-02-06 13:36:11 +01:00
}
2013-04-09 13:15:25 +02:00
if (ConfServer.powerFactionMax > 0 && ret > ConfServer.powerFactionMax)
{
2013-04-09 13:15:25 +02:00
ret = ConfServer.powerFactionMax;
}
return ret + this.powerBoost;
2011-02-06 13:36:11 +01:00
}
public int getPowerRounded()
{
2011-02-06 13:36:11 +01:00
return (int) Math.round(this.getPower());
}
public int getPowerMaxRounded()
{
2011-02-06 13:36:11 +01:00
return (int) Math.round(this.getPowerMax());
}
public int getLandCount()
{
return BoardColl.get().getCount(this);
2011-02-06 13:36:11 +01:00
}
public int getLandCountInWorld(String worldName)
{
return BoardColl.get().get(worldName).getCount(this);
}
public boolean hasLandInflation()
{
return this.getLandCount() > this.getPowerRounded();
2011-02-06 13:36:11 +01:00
}
// -------------------------------------------- //
// FPLAYERS
// -------------------------------------------- //
public List<FPlayer> getFPlayers()
{
List<FPlayer> ret = new ArrayList<FPlayer>();
2013-04-12 08:56:26 +02:00
for (FPlayer fplayer : FPlayerColl.get().getAll())
{
if (fplayer.getFaction() != this) continue;
ret.add(fplayer);
2011-02-06 13:36:11 +01:00
}
return ret;
}
public List<FPlayer> getFPlayersWhereOnline(boolean online)
{
List<FPlayer> ret = new ArrayList<FPlayer>();
for (FPlayer fplayer : FPlayerColl.get().getAll())
{
if (fplayer.getFaction() != this) continue;
if (fplayer.isOnline() != online) continue;
ret.add(fplayer);
}
2011-02-06 13:36:11 +01:00
return ret;
}
public List<FPlayer> getFPlayersWhereRole(Rel role)
{
List<FPlayer> ret = new ArrayList<FPlayer>();
for (FPlayer fplayer : FPlayerColl.get().getAll())
{
if (fplayer.getFaction() != this) continue;
if (fplayer.getRole() != role) continue;
ret.add(fplayer);
2011-02-06 13:36:11 +01:00
}
return ret;
}
public FPlayer getLeader()
{
for (FPlayer fplayer : FPlayerColl.get().getAll())
{
if (fplayer.getFaction() != this) continue;
if (fplayer.getRole() != Rel.LEADER) continue;
return fplayer;
}
return null;
}
public List<CommandSender> getOnlineCommandSenders()
2011-10-22 17:42:13 +02:00
{
List<CommandSender> ret = new ArrayList<CommandSender>();
for (CommandSender player : SenderUtil.getOnlineSenders())
2011-10-22 17:42:13 +02:00
{
FPlayer fplayer = FPlayerColl.get().get(player);
if (fplayer.getFaction() != this) continue;
ret.add(player);
2011-02-06 13:36:11 +01:00
}
return ret;
}
public List<Player> getOnlinePlayers()
{
List<Player> ret = new ArrayList<Player>();
for (Player player : Bukkit.getOnlinePlayers())
{
2013-04-12 08:56:26 +02:00
FPlayer fplayer = FPlayerColl.get().get(player);
if (fplayer.getFaction() != this) continue;
ret.add(player);
2011-02-06 13:36:11 +01:00
}
return ret;
}
// used when current leader is about to be removed from the faction; promotes new leader, or disbands faction if no other members left
public void promoteNewLeader()
{
if ( ! this.isNormal()) return;
2013-04-09 13:15:25 +02:00
if (this.getFlag(FFlag.PERMANENT) && ConfServer.permanentFactionsDisableLeaderPromotion) return;
FPlayer oldLeader = this.getLeader();
// get list of officers, or list of normal members if there are no officers
List<FPlayer> replacements = this.getFPlayersWhereRole(Rel.OFFICER);
if (replacements == null || replacements.isEmpty())
{
replacements = this.getFPlayersWhereRole(Rel.MEMBER);
}
if (replacements == null || replacements.isEmpty())
{ // faction leader is the only member; one-man faction
if (this.getFlag(FFlag.PERMANENT))
{
if (oldLeader != null)
{
oldLeader.setRole(Rel.MEMBER);
}
return;
}
// no members left and faction isn't permanent, so disband it
2013-04-09 13:15:25 +02:00
if (ConfServer.logFactionDisband)
{
Factions.get().log("The faction "+this.getTag()+" ("+this.getId()+") has been disbanded since it has no members left.");
}
2013-04-12 08:56:26 +02:00
for (FPlayer fplayer : FPlayerColl.get().getAllOnline())
{
fplayer.msg("The faction %s<i> was disbanded.", this.getTag(fplayer));
}
this.detach();
}
else
{ // promote new faction leader
if (oldLeader != null)
{
oldLeader.setRole(Rel.MEMBER);
}
replacements.get(0).setRole(Rel.LEADER);
this.msg("<i>Faction leader <h>%s<i> has been removed. %s<i> has been promoted as the new faction leader.", oldLeader == null ? "" : oldLeader.getName(), replacements.get(0).getName());
Factions.get().log("Faction "+this.getTag()+" ("+this.getId()+") leader was removed. Replacement leader: "+replacements.get(0).getName());
}
}
// -------------------------------------------- //
// MESSAGES
// -------------------------------------------- //
// These methods are simply proxied in from the SenderEntity class using a for loop.
2013-04-10 10:32:04 +02:00
// CONVENIENCE SEND MESSAGE
public boolean sendMessage(String message)
{
for (FPlayer fplayer : this.getFPlayers())
{
fplayer.sendMessage(message);
}
2013-04-12 08:56:26 +02:00
return true;
}
public boolean sendMessage(String... messages)
{
for (FPlayer fplayer : this.getFPlayers())
{
fplayer.sendMessage(messages);
2011-02-06 13:36:11 +01:00
}
return true;
2011-02-06 13:36:11 +01:00
}
public boolean sendMessage(Collection<String> messages)
{
for (FPlayer fplayer : this.getFPlayers())
{
2011-03-22 17:20:21 +01:00
fplayer.sendMessage(messages);
2011-02-06 13:36:11 +01:00
}
return true;
}
// CONVENIENCE MSG
public boolean msg(String msg)
{
for (FPlayer fplayer : this.getFPlayers())
{
fplayer.msg(msg);
}
return true;
2011-02-06 13:36:11 +01:00
}
public boolean msg(String msg, Object... args)
{
for (FPlayer fplayer : this.getFPlayers())
{
fplayer.msg(msg, args);
}
return true;
}
public boolean msg(Collection<String> msgs)
{
for (FPlayer fplayer : this.getFPlayers())
{
fplayer.msg(msgs);
}
return true;
}
2011-03-18 17:33:23 +01:00
2011-02-06 13:36:11 +01:00
}