Skip to content

Commit

Permalink
feat(fight): Start base for static invocation (Arakne#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent4vx committed Jun 8, 2023
1 parent 65c0089 commit e520a4e
Show file tree
Hide file tree
Showing 61 changed files with 843 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void execute(AdminPerformer performer, Void arguments) {
performer.info("Online : {} sessions and {} players", gameService.sessions().size(), playerService.online().size());
performer.info("Fights : {} fights with {} fighters",
fightService.fights().size(),
fightService.fights().stream().mapToLong(fight -> fight.fighters().size()).sum()
fightService.fights().stream().mapToLong(fight -> fight.fighters().all().size()).sum()
);
performer.info(
"RAM usage : {} / {}",
Expand Down
17 changes: 5 additions & 12 deletions src/main/java/fr/quatrevieux/araknemu/game/fight/Fight.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Handle fight
Expand All @@ -75,6 +74,7 @@ public final class Fight implements Dispatcher, Sender {
private final ScheduledExecutorService executor;
private final Spectators spectators;
private final ActionsFactory<Fighter> actions;
private final FighterList fighters;

private final Lock executorLock = new ReentrantLock();
private final EffectsHandler effects = new EffectsHandler();
Expand All @@ -94,6 +94,7 @@ public Fight(int id, FightType type, FightMap map, List<FightTeam.Factory> teams
this.executor = executor;
this.dispatcher = new DefaultListenerAggregate(logger);
this.spectators = new Spectators(this);
this.fighters = new FighterList(this);
this.actions = actions;
}

Expand Down Expand Up @@ -132,17 +133,8 @@ public FightTeam team(int number) {
/**
* Get all fighters on the fight
*/
public List<Fighter> fighters() {
final FightTurnList turnList = this.turnList;
final Stream<Fighter> fighterStream = turnList != null
? turnList.fighters().stream()
: teams.stream().flatMap(fightTeam -> fightTeam.fighters().stream())
;

return fighterStream
.filter(Fighter::isOnFight)
.collect(Collectors.toList())
;
public FighterList fighters() {
return fighters;
}

/**
Expand Down Expand Up @@ -409,6 +401,7 @@ public void destroy() {
map.destroy();
attachments.clear();
spectators.clear();
fighters.clear();
}

/**
Expand Down
126 changes: 126 additions & 0 deletions src/main/java/fr/quatrevieux/araknemu/game/fight/FighterList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* This file is part of Araknemu.
*
* Araknemu is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Araknemu is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Araknemu. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (c) 2017-2023 Vincent Quatrevieux
*/

package fr.quatrevieux.araknemu.game.fight;

import fr.quatrevieux.araknemu.game.fight.event.FighterRemoved;
import fr.quatrevieux.araknemu.game.fight.fighter.Fighter;
import fr.quatrevieux.araknemu.game.fight.map.FightCell;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Stream;

/**
* Handle the fighters list of a fight
*/
public final class FighterList implements Iterable<Fighter> {
private final Fight fight;
private final Set<Fighter> fighters = new LinkedHashSet<>();

public FighterList(Fight fight) {
this.fight = fight;
}

@Override
public @NonNull Iterator<Fighter> iterator() {
return fighters.iterator();
}

/**
* Get all fighters as collection.
*/
public Collection<Fighter> all() {
return Collections.unmodifiableCollection(fighters);
}

/**
* Add a fighter to the fight
*
* @param fighter The fighter to add
* @param cell The cell where the fighter will be placed
*
* @see Fighter#joinFight(Fight, FightCell) Will be called by this method
* @see #joinTurnList(Fighter, FightCell) Call this method if you want to add the fighter to the turn list
*/
public void join(Fighter fighter, FightCell cell) {
fighter.joinFight(fight, cell);
fighters.add(fighter);
}

/**
* Add a fighter to the fight and to the turn list
*
* Note: this method can only be called if the fight is active, during placement state, use {@link #join(Fighter, FightCell)} instead
*
* @param fighter The fighter to add
* @param cell The cell where the fighter will be placed
*
* @see fr.quatrevieux.araknemu.game.fight.turn.FightTurnList#add(Fighter) Will be called by this method
*/
public void joinTurnList(Fighter fighter, FightCell cell) {
join(fighter, cell);
fight.turnList().add(fighter);
}

/**
* Remove a fighter from the fight
* Will trigger {@link FighterRemoved} event
*
* Note: if the fighter is on the turn list, it will be removed too
*
* @param fighter The fighter to remove
*/
public void leave(Fighter fighter) {
fighters.remove(fighter);

if (fight.active()) {
fight.turnList().remove(fighter); // @todo discriminate active vs static fighter
}

fight.dispatch(new FighterRemoved(fighter, fight));
}

/**
* Get a sequential {@code Stream} with this fighter list as its source.
*/
public Stream<Fighter> stream() {
return fighters.stream();
}

/**
* Get all alive fighters as stream
*
* @see Fighter#dead()
*/
public Stream<Fighter> alive() {
return fighters.stream().filter(fighter -> !fighter.dead());
}

/**
* Internal method for clear all fighters objects.
*/
void clear() {
fighters.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public BattlefieldMap map() {

@Override
public Stream<? extends FighterData> fighters() {
return fight.fighters().stream().filter(other -> !other.dead());
return fight.fighters().alive();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ public void handle(FightCastScope cast, FightCastScope.EffectScope effect) {
cast.caster()
));

invocation.joinFight(fight, cast.target());
fight.turnList().add(invocation);
fight.fighters().joinTurnList(invocation, cast.target());

invocation.init();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of Araknemu.
*
* Araknemu is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Araknemu is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Araknemu. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (c) 2017-2021 Vincent Quatrevieux Jean-Alexandre Valentin
*/

package fr.quatrevieux.araknemu.game.fight.castable.effect.handler.invocations;

import fr.quatrevieux.araknemu.game.fight.Fight;
import fr.quatrevieux.araknemu.game.fight.castable.FightCastScope;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.EffectHandler;
import fr.quatrevieux.araknemu.game.fight.fighter.Fighter;
import fr.quatrevieux.araknemu.game.fight.fighter.FighterFactory;
import fr.quatrevieux.araknemu.game.fight.fighter.invocation.InvocationFighter;
import fr.quatrevieux.araknemu.game.monster.MonsterService;
import fr.quatrevieux.araknemu.network.game.out.fight.action.ActionEffect;

/**
* Handle invocation of a static creature
* Unlike {@link MonsterInvocationHandler}, the invoked monster does not appear in timeline, and cannot perform any action
*
* A new fighter will be created and added to fight but not in timeline
*
* Effect parameters :
* - #1 (min) : monster id
* - #2 (max) : grade number
*
* @see InvocationFighter Invoked fighter @todo change
*/
public final class StaticInvocationHandler implements EffectHandler {
private final MonsterService monsterService;
private final FighterFactory fighterFactory;
private final Fight fight;

public StaticInvocationHandler(MonsterService monsterService, FighterFactory fighterFactory, Fight fight) {
this.monsterService = monsterService;
this.fighterFactory = fighterFactory;
this.fight = fight;
}

@Override
public void buff(FightCastScope cast, FightCastScope.EffectScope effect) {
throw new UnsupportedOperationException("StaticInvocationHandler cannot be used as buff");
}

@Override
public void handle(FightCastScope cast, FightCastScope.EffectScope effect) {
// @todo do not use InvocationFighter
final Fighter invocation = fighterFactory.generate(id -> new InvocationFighter(
id,
monsterService.load(effect.effect().min()).get(effect.effect().max()),
cast.caster().team(),
cast.caster()
));

fight.fighters().join(invocation, cast.target());
invocation.init();

fight.send(ActionEffect.addStaticInvocation(cast.caster(), invocation));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import fr.quatrevieux.araknemu.game.fight.Fight;
import fr.quatrevieux.araknemu.game.fight.castable.effect.EffectsHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.invocations.MonsterInvocationHandler;
import fr.quatrevieux.araknemu.game.fight.castable.effect.handler.invocations.StaticInvocationHandler;
import fr.quatrevieux.araknemu.game.fight.fighter.Fighter;
import fr.quatrevieux.araknemu.game.fight.fighter.FighterFactory;
import fr.quatrevieux.araknemu.game.fight.fighter.event.FighterDie;
Expand Down Expand Up @@ -51,6 +52,7 @@ public MonsterInvocationModule(MonsterService monsterService, FighterFactory fig
public void effects(EffectsHandler handler) {
// moving creatures
handler.register(181, new MonsterInvocationHandler(monsterService, fighterFactory, fight));
handler.register(185, new StaticInvocationHandler(monsterService, fighterFactory, fight));
}

@Override
Expand All @@ -73,7 +75,7 @@ public void on(FighterDie event) {

// If the creature is an invocation, delete from turn list
if (event.fighter().invoked()) {
fight.turnList().remove(event.fighter());
fight.fighters().leave(event.fighter());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public synchronized void leave(Fighter fighter) {
}

fighter.team().kick(fighter);
fight.turnList().remove(fighter);
fight.fighters().leave(fighter);

final FightRewardsSheet rewardsSheet = fight.type().rewards().generate(
new EndFightResults(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
import fr.quatrevieux.araknemu.core.event.EventsSubscriber;
import fr.quatrevieux.araknemu.core.event.Listener;
import fr.quatrevieux.araknemu.game.fight.Fight;
import fr.quatrevieux.araknemu.game.fight.FighterList;
import fr.quatrevieux.araknemu.game.fight.JoinFightError;
import fr.quatrevieux.araknemu.game.fight.ending.EndFightResults;
import fr.quatrevieux.araknemu.game.fight.ending.reward.FightRewardsSheet;
import fr.quatrevieux.araknemu.game.fight.event.FightJoined;
import fr.quatrevieux.araknemu.game.fight.event.FightLeaved;
import fr.quatrevieux.araknemu.game.fight.event.FighterAdded;
import fr.quatrevieux.araknemu.game.fight.event.FighterPlaceChanged;
import fr.quatrevieux.araknemu.game.fight.event.FighterRemoved;
import fr.quatrevieux.araknemu.game.fight.exception.FightException;
import fr.quatrevieux.araknemu.game.fight.exception.FightMapException;
import fr.quatrevieux.araknemu.game.fight.exception.InvalidFightStateException;
Expand Down Expand Up @@ -289,8 +289,10 @@ private void punishDeserter(Fighter fighter) {
@RequiresNonNull("fight")
@SuppressWarnings("dereference.of.nullable") // cellsGenerators.get(fighter.team()) cannot be null
private void addFighters(Collection<Fighter> fighters) {
final FighterList fightersList = fight.fighters();

for (Fighter fighter : fighters) {
fighter.joinFight(fight, NullnessUtil.castNonNull(cellsGenerators).get(fighter.team()).next());
fightersList.join(fighter, NullnessUtil.castNonNull(cellsGenerators).get(fighter.team()).next());
}

for (Fighter fighter : fighters) {
Expand All @@ -307,8 +309,8 @@ private void addFighters(Collection<Fighter> fighters) {
*/
@RequiresNonNull("fight")
private void removeFighter(Fighter fighter) {
fight.fighters().leave(fighter);
fighter.dispatch(new FightLeaved());
fight.dispatch(new FighterRemoved(fighter, fight));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@

import fr.quatrevieux.araknemu.game.fight.Fight;
import fr.quatrevieux.araknemu.game.fight.fighter.Fighter;
import fr.quatrevieux.araknemu.game.fight.map.FightCell;
import fr.quatrevieux.araknemu.game.fight.turn.event.NextTurnInitiated;
import fr.quatrevieux.araknemu.game.fight.turn.event.TurnListChanged;
import fr.quatrevieux.araknemu.game.fight.turn.order.FighterOrderStrategy;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.util.NullnessUtil;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

Expand Down Expand Up @@ -73,7 +73,7 @@ public void remove(Fighter fighter) {
final int index = fighters.indexOf(fighter);

if (index == -1) {
throw new NoSuchElementException("Fighter " + fighter.id() + " is not found on the turn list");
return;
}

fighters.remove(index);
Expand All @@ -95,6 +95,8 @@ public void remove(Fighter fighter) {
/**
* Add a fighter after the current one
*
* Note: this method should not be called directly, use {@link fr.quatrevieux.araknemu.game.fight.FighterList#joinTurnList(Fighter, FightCell)} instead
*
* @param fighter Fighter to add
*
* @see TurnListChanged Event triggered after the list is updated
Expand Down
Loading

0 comments on commit e520a4e

Please sign in to comment.