Passed
Push — master ( f51b57...4bee7d )
by Vincent
04:18 queued 11s
created

fr.quatrevieux.araknemu.game.monster.environment.MonsterEnvironmentService   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 133
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
dl 0
loc 133
ccs 55
cts 55
cp 1
rs 10
c 1
b 0
f 0
wmc 13

9 Methods

Rating   Name   Duplication   Size   Complexity  
A MonsterEnvironmentService(ActivityService,FightService,MonsterGroupFactory,MonsterGroupPositionRepository,MonsterGroupDataRep 0 7 1
$Listener$.event() 0 3 ?
$Listener$.on(MapLoaded) 0 6 ?
A respawn(LivingMonsterGroupPosition,Duration) 0 6 1
A listeners() 0 15 3
A preload(Logger) 0 29 2
A byMap(int) 0 18 4
A groups() 0 2 1
A createByPosition(MonsterGroupPosition) 0 8 1
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2021 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.monster.environment;
21
22
import fr.quatrevieux.araknemu.core.event.EventsSubscriber;
23
import fr.quatrevieux.araknemu.core.event.Listener;
24
import fr.quatrevieux.araknemu.data.world.entity.monster.MonsterGroupData;
25
import fr.quatrevieux.araknemu.data.world.entity.monster.MonsterGroupPosition;
26
import fr.quatrevieux.araknemu.data.world.repository.monster.MonsterGroupDataRepository;
27
import fr.quatrevieux.araknemu.data.world.repository.monster.MonsterGroupPositionRepository;
28
import fr.quatrevieux.araknemu.game.GameConfiguration;
29
import fr.quatrevieux.araknemu.game.PreloadableService;
30
import fr.quatrevieux.araknemu.game.activity.ActivityService;
31
import fr.quatrevieux.araknemu.game.activity.SimpleTask;
32
import fr.quatrevieux.araknemu.game.exploration.map.ExplorationMap;
33
import fr.quatrevieux.araknemu.game.exploration.map.event.MapLoaded;
34
import fr.quatrevieux.araknemu.game.fight.FightService;
35
import fr.quatrevieux.araknemu.game.listener.map.monster.LaunchMonsterFight;
36
import fr.quatrevieux.araknemu.game.monster.group.MonsterGroupFactory;
37
import org.apache.logging.log4j.Logger;
38
39
import java.time.Duration;
40
import java.util.ArrayList;
41
import java.util.Collection;
42
import java.util.Collections;
43
import java.util.Map;
44
import java.util.concurrent.ConcurrentHashMap;
45
import java.util.concurrent.ConcurrentMap;
46
import java.util.function.Function;
47
import java.util.stream.Collectors;
48
import java.util.stream.Stream;
49
50
/**
51
 * Handle environment interactions with monsters
52
 */
53
public final class MonsterEnvironmentService implements EventsSubscriber, PreloadableService {
54
    private final ActivityService activityService;
55
    private final FightService fightService;
56
    private final MonsterGroupFactory factory;
57
    private final MonsterGroupPositionRepository positionRepository;
58
    private final MonsterGroupDataRepository dataRepository;
59
    private final GameConfiguration.ActivityConfiguration configuration;
60
61
    /**
62
     * Groups indexed by map id
63
     */
64 1
    private final ConcurrentMap<Integer, Collection<LivingMonsterGroupPosition>> groupsByMap = new ConcurrentHashMap<>();
65
66
    /**
67
     * Groups are preloaded ?
68
     * If true, consider that all data are loaded, so loading maps without loaded groups do not execute a query
69
     */
70 1
    private boolean preloaded = false;
71
72 1
    public MonsterEnvironmentService(ActivityService activityService, FightService fightService, MonsterGroupFactory factory, MonsterGroupPositionRepository positionRepository, MonsterGroupDataRepository dataRepository, GameConfiguration.ActivityConfiguration configuration) {
73 1
        this.activityService = activityService;
74 1
        this.fightService = fightService;
75 1
        this.factory = factory;
76 1
        this.positionRepository = positionRepository;
77 1
        this.dataRepository = dataRepository;
78 1
        this.configuration = configuration;
79 1
    }
80
81
    @Override
82
    public void preload(Logger logger) {
83 1
        logger.info("Loading monster groups data...");
84
85 1
        final Map<Integer, MonsterGroupData> groupsData = dataRepository.all().stream()
86 1
            .collect(Collectors.toMap(MonsterGroupData::id, Function.identity()))
87
        ;
88
89 1
        logger.info("{} monster groups loaded", groupsData.size());
90
91 1
        logger.info("Loading monster groups positions...");
92
93 1
        for (MonsterGroupPosition position : positionRepository.all()) {
94 1
            groupsByMap
95 1
                .computeIfAbsent(position.position().map(), mapId -> new ArrayList<>())
96 1
                .add(createByPosition(position))
97
            ;
98 1
        }
99
100 1
        logger.info("{} Map positions loaded", groupsByMap.size());
101
102 1
        preloaded = true;
103
104 1
        activityService.periodic(
105
            new MoveMonsters(
106
                this,
107 1
                Duration.ofSeconds(configuration.monsterMoveInterval()),
108 1
                configuration.monsterMovePercent(),
109 1
                configuration.monsterMoveDistance()
110
            )
111
        );
112 1
    }
113
114
    @Override
115
    public Listener[] listeners() {
116 1
        return new Listener[] {
117 1
            new Listener<MapLoaded>() {
118
                @Override
119
                public void on(MapLoaded event) {
120 1
                    final ExplorationMap map = event.map();
121
122 1
                    byMap(map.id()).forEach(group -> group.populate(map));
123 1
                    map.dispatcher().add(new LaunchMonsterFight());
124 1
                }
125
126
                @Override
127
                public Class<MapLoaded> event() {
128 1
                    return MapLoaded.class;
129
                }
130
            },
131
        };
132
    }
133
134
    /**
135
     * Get list of groups for a map
136
     *
137
     * @param mapId The map id to load
138
     */
139
    public Collection<LivingMonsterGroupPosition> byMap(int mapId) {
140 1
        if (groupsByMap.containsKey(mapId)) {
141 1
            return groupsByMap.get(mapId);
142
        }
143
144 1
        if (preloaded) {
145 1
            return Collections.emptyList();
146
        }
147
148 1
        final Collection<LivingMonsterGroupPosition> groups = new ArrayList<>();
149
150 1
        for (MonsterGroupPosition position : positionRepository.byMap(mapId)) {
151 1
            groups.add(createByPosition(position));
152 1
        }
153
154 1
        groupsByMap.put(mapId, groups);
155
156 1
        return groups;
157
    }
158
159
    /**
160
     * Put the respawn to the activity service
161
     */
162
    void respawn(LivingMonsterGroupPosition position, Duration delay) {
163 1
        activityService.execute(
164 1
            new SimpleTask(logger -> position.spawn())
165 1
                .setDelay(delay.dividedBy(configuration.monsterRespawnSpeedFactor()))
166 1
                .setMaxTries(2)
167 1
                .setName("Respawn")
168
        );
169 1
    }
170
171
    /**
172
     * Get all loaded groups
173
     */
174
    Stream<LivingMonsterGroupPosition> groups() {
175 1
        return groupsByMap.values().stream().flatMap(Collection::stream);
176
    }
177
178
    private LivingMonsterGroupPosition createByPosition(MonsterGroupPosition position) {
179 1
        return new LivingMonsterGroupPosition(
180
            factory,
181
            this,
182
            fightService,
183 1
            dataRepository.get(position.groupId()),
184 1
            SpawnCellSelector.forPosition(position.position()),
185 1
            position.position().cell() != -1
186
        );
187
    }
188
}
189