# Per-Competition Code

Now that you have an Arena class with functioning game logic, it's time to expand on that.

### Creating Competition Classes

As mentioned in the [Adding Game Logic](https://docs.battleplugins.org/books/developer-guide/page/adding-game-logic) page, you will need to create a custom Competition class. The following code below is how to create a custom Competition:

```java
public class MyCompetition extends LiveCompetition<MyCompetition> {

    public MyCompetition(MyArena arena, CompetitionType type, ArenaMap map) {
        super(arena, type, map);
    }

}
```

As seen above, you will need to extend the **LiveCompetition** class, which is created for competitions that are live on the server BattleArena is running on. On it's own, this will do nothing, so the next step is to create a custom **ArenaMap** which is responsible for creating the competition.

```java
public class MyCompetitionMap extends LiveCompetitionMap {
    public static final MapFactory FACTORY = MapyFactory.create(MyCompetitionMap.class, MyCompetitionMap::new);

    public MyCompetitionMap() {
    }

    public MyCompetitionMap(String name, Arena arena, MapType type, String world, @Nullable Bounds bounds, @Nullable Spawns spawns) {
        super(name, arena, type, world, bounds, spawns);
    }

    // Override this method in order to use your custom competition class
    @Override
    public LiveCompetition<?> createCompetition(Arena arena) {
        if (!(arena instanceof MyArena myArena)) {
            throw new IllegalArgumentException("Arena must be an instance of MyArena!");
        }
        
        return new MyCompetition(myArena, arena.getType(), this);
    }
}
```

As mentioned in an earlier segment of this documentation, maps can exist without necessarily having a competition bound to them, meaning they are responsible for actually creating a live competition. In the map class above, it can be seen the **createCompetition** method is overridden to instead create an instance of our **MyCompetition** class.

Additionally, a **MapFactory** is specified at the top of the class - this is important for the next step of linking this to your **MyArena** so BattleArena knows which ArenaMap (and therefore, Competition) to create for your Arena. It is also **very important** that both constructors are specified as seen in the example above.

And finally, the last step is to override the **getMapFactory** method in **MyArena**, and specify your factory like so:

```java
public class MyArena extends Arena {
    @ArenaOption(name = "infection-time", description = "How long a player should be infected once hit.")
    private Duration infectionTime = Duration.ofSeconds(5);

    private final Set<UUID> infectedPlayers = new HashSet<>();

    @Override
    public MapFactory getMapFactory() {
        return MyCompetitionMap.FACTORY;
    }

    ...
  }
```

### Per-Competition Code

Now that we have all the necessary classes created, you can now start creating code that will exist on a per-competition level. If we wanted to infect a random player every minute for instance the following could be done like so:

```java
public class MyCompetition extends LiveCompetition<MyCompetition> {

    private BukkitTask tickTask;

    public MyCompetition(MyArena arena, CompetitionType type, LiveCompetitionMap map) {
        super(arena, type, map);
    }

    public void startInfectTask() {
        this.tickTask = Bukkit.getScheduler().runTaskTimer(this.getArena().getPlugin(), this::infectPlayer, 0, 60 * 60 * 20);
    }

    public void stopInfectTask() {
        if (this.tickTask != null) {
            this.tickTask.cancel();
        }

        this.tickTask = null;
    }

    private void infectPlayer() {
        MyArena arena = (MyArena) this.getArena();

        // Infect a random player
        List<ArenaPlayer> uninfectedPlayers = this.getPlayers().stream().filter(player -> !arena.isInfected(player)).toList();
        if (uninfectedPlayers.isEmpty()) {
            return;
        }

        ArenaPlayer player = uninfectedPlayers.get((int) (Math.random() * uninfectedPlayers.size()));
        arena.infect(player.getPlayer());
    }
}
```

And with those changes, we also need to update **MyArena** to actually call the start and stop methods. Here is what the updated **MyArena** class would look like:

```java
public class MyArena extends Arena {
    private static final String INFECTED_METADATA = "infected";

    @ArenaOption(name = "infection-time", description = "How long a player should be infected once hit.")
    private Duration infectionTime = Duration.ofSeconds(5);

    @Override
    public MapFactory getMapFactory() {
        return MyCompetitionMap.FACTORY;
    }

    @ArenaEventHandler
    public void onDamageEntity(EntityDamageByEntityEvent event) {
        if (event.getDamager() instanceof Player damager && event.getEntity() instanceof Player player) {
            // Player is not infected, let's infect them :)
            if (!player.hasMetadata(INFECTED_METADATA)) {
                this.infect(player);

                damager.sendMessage("You have infected " + player.getName() + "!");
            }
        }
    }

    @ArenaEventHandler
    public void onMove(PlayerMoveEvent event) {
        if (event.getPlayer().hasMetadata(INFECTED_METADATA)) {
            event.getPlayer().sendMessage("You are infected! You cannot move!");
            event.setCancelled(true);
        }
    }

    @ArenaEventHandler
    public void onPhaseStart(ArenaPhaseStartEvent event, MyCompetition competition) {
        // Ensure we are ingame
        if (!CompetitionPhaseType.INGAME.equals(event.getPhase().getType())) {
            return;
        }
      
        competition.startInfectTask();
    }

    @ArenaEventHandler
    public void onPhaseComplete(ArenaPhaseCompleteEvent event, MyCompetition competition) {
        // Ensure we are ingame
        if (!CompetitionPhaseType.INGAME.equals(event.getPhase().getType())) {
            return;
        }

        competition.stopInfectTask();
    }

    public boolean isInfected(ArenaPlayer player) {
        return player.getPlayer().hasMetadata(INFECTED_METADATA);
    }

    public void infect(Player player) {
        player.sendMessage("You have been infected!");
        player.setMetadata(INFECTED_METADATA, new FixedMetadataValue(MyPlugin.getInstance(), true));

        // Infect the player for the given duration
        Bukkit.getScheduler().runTaskLater(MyPlugin.getInstance(), () -> {
            player.removeMetadata(INFECTED_METADATA, MyPlugin.getInstance());
            player.sendMessage("You are no longer infected!");
        }, this.infectionTime.toMillis() / 50);
    }
}
```

As seen above, when the game enters the **in-game** phase, we start running our task in the active **MyCompetition** to infect a random player every minute. Once the competition is no longer ingame, we stop the task.