moveFarEnemies()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 1
Metric Value
cc 1
eloc 2
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
c 1
b 1
f 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.fight.ai.action.builder;
21
22
import fr.quatrevieux.araknemu.game.fight.ai.AI;
23
import fr.quatrevieux.araknemu.game.fight.ai.action.ActionGenerator;
24
import fr.quatrevieux.araknemu.game.fight.ai.action.Attack;
25
import fr.quatrevieux.araknemu.game.fight.ai.action.Boost;
26
import fr.quatrevieux.araknemu.game.fight.ai.action.Debuff;
27
import fr.quatrevieux.araknemu.game.fight.ai.action.Heal;
28
import fr.quatrevieux.araknemu.game.fight.ai.action.MoveFarEnemies;
29
import fr.quatrevieux.araknemu.game.fight.ai.action.MoveNearAllies;
30
import fr.quatrevieux.araknemu.game.fight.ai.action.MoveNearEnemy;
31
import fr.quatrevieux.araknemu.game.fight.ai.action.MoveToAttack;
32
import fr.quatrevieux.araknemu.game.fight.ai.action.MoveToBoost;
33
import fr.quatrevieux.araknemu.game.fight.ai.action.TeleportNearEnemy;
34
import fr.quatrevieux.araknemu.game.fight.ai.action.logic.GeneratorAggregate;
35
import fr.quatrevieux.araknemu.game.fight.ai.action.logic.NullGenerator;
36
import fr.quatrevieux.araknemu.game.fight.ai.simulation.Simulator;
37
import fr.quatrevieux.araknemu.game.fight.fighter.ActiveFighter;
38
39
import java.util.ArrayList;
40
import java.util.List;
41
import java.util.function.Consumer;
42
import java.util.function.Predicate;
43
44
/**
45
 * Build an action generator pipeline using a simple builder
46
 *
47
 * Note: This class is not marked as final to allow extends to add custom actions methods
48
 *
49
 * Usage:
50
 * <pre>{@code
51
 * Simulator simulator = xxx;
52
 * GeneratorBuilder builder = new GeneratorBuilder();
53
 *
54
 * builder
55
 *     .boostSelf(simulator)
56
 *     .attackFromBestCell(simulator)
57
 *     .when(ai -> isLowLife(ai), cb -> cb
58
 *         .success(GeneratorBuilder::moveFarEnemies)
59
 *         .otherwise(GeneratorBuilder::moveNearEnemy)
60
 *     )
61
 *     .boostAllies(simulator)
62
 * ;
63
 * }</pre>
64
 */
65 1
public class GeneratorBuilder<F extends ActiveFighter> {
66 1
    private final List<ActionGenerator<F>> generators = new ArrayList<>();
67
68
    /**
69
     * Append a new action generator at the end of the pipeline
70
     *
71
     * Note: prefer use of helpers methods instead
72
     *
73
     * Usage:
74
     * <pre>{@code
75
     * builder
76
     *     .add(new MyProprietaryAction())
77
     *     .add(new NextAction())
78
     * ;
79
     * }</pre>
80
     *
81
     * @param generator The generator instance
82
     *
83
     * @return The builder instance
84
     */
85
    public final GeneratorBuilder<F> add(ActionGenerator<F> generator) {
86 1
        generators.add(generator);
87
88 1
        return this;
89
    }
90
91
    /**
92
     * Add a conditional action
93
     *
94
     * Usage:
95
     * <pre>{code
96
     * builder.when(ai -> checkCanPerform(ai), cb -> cb
97
     *     .success(GeneratorBuilder::attack)
98
     *     .otherwise(GeneratorBuilder::boostSelf)
99
     * )
100
     * }</pre>
101
     *
102
     * @param configurator The builder configurator
103
     *
104
     * @return The builder instance
105
     *
106
     * @see fr.quatrevieux.araknemu.game.fight.ai.util.Predicates For the condition parameter
107
     * @see ConditionalBuilder
108
     * @see fr.quatrevieux.araknemu.game.fight.ai.action.logic.ConditionalGenerator
109
     */
110
    public final GeneratorBuilder<F> when(Predicate<AI<F>> condition, Consumer<ConditionalBuilder<F>> configurator) {
111 1
        final ConditionalBuilder<F> builder = new ConditionalBuilder<>(condition);
112
113 1
        configurator.accept(builder);
114
115 1
        return add(builder.build());
116
    }
117
118
    /**
119
     * Try to attack from the best cell (i.e. move to maximize damages)
120
     *
121
     * Try to perform in order :
122
     * - Move to the best cell for cast attack
123
     * - Cast the attack spell
124
     *
125
     * Note: this method is equivalent to `{@code builder.moveToAttack(simulator).attack(simulator)}`
126
     *
127
     * @param simulator Simulator used by AI
128
     *
129
     * @return The builder instance
130
     *
131
     * @see GeneratorBuilder#attackFromNearestCell(Simulator) Same action (move and attack), but configured to use the minimal amount of MP
132
     * @see GeneratorBuilder#moveToAttack(Simulator) For only perform the move action, without attack
133
     * @see GeneratorBuilder#attack(Simulator) For only perform the attack, without move
134
     */
135
    public final GeneratorBuilder<F> attackFromBestCell(Simulator simulator) {
136 1
        return moveToAttack(simulator).attack(simulator);
137
    }
138
139
    /**
140
     * Try to move to the best cell for cast an attack spell
141
     *
142
     * To ensure that the move will be performed, add the attack action after this one.
143
     * Otherwise, if an attack is possible from the current cell it will be performed,
144
     * which will results to sub-optimal action.
145
     *
146
     * The action will not be performed if there is a tackle chance and if an attack is possible from the current cell
147
     *
148
     * @param simulator Simulator used by AI
149
     *
150
     * @return The builder instance
151
     *
152
     * @see GeneratorBuilder#attackFromBestCell(Simulator) For perform move and attack
153
     * @see MoveToAttack#bestTarget(Simulator) The used action generator
154
     */
155
    public final GeneratorBuilder<F> moveToAttack(Simulator simulator) {
156 1
        return add(MoveToAttack.bestTarget(simulator));
157
    }
158
159
    /**
160
     * Try to attack from the nearest cell
161
     *
162
     * Try to perform in order :
163
     * - Cast the attack spell
164
     * - Move to the nearest cell for cast attack
165
     *
166
     * If an attack can be performed from the current cell, the move will be ignored
167
     *
168
     * Note: this method is equivalent to `{@code builder.attack(simulator).add(MoveToAttack.nearest(simulator))}`
169
     *
170
     * @param simulator Simulator used by AI
171
     *
172
     * @return The builder instance
173
     *
174
     * @see GeneratorBuilder#attackFromBestCell(Simulator) Same action (move and attack), but configured to maximize damage
175
     * @see GeneratorBuilder#attack(Simulator) For only perform the attack, without move
176
     */
177
    public final GeneratorBuilder<F> attackFromNearestCell(Simulator simulator) {
178 1
        return attack(simulator).add(MoveToAttack.nearest(simulator));
179
    }
180
181
    /**
182
     * Try to attack from the current cell
183
     *
184
     * @param simulator Simulator used by AI
185
     *
186
     * @return The builder instance
187
     *
188
     * @see Attack The used action generator
189
     * @see GeneratorBuilder#attackFromBestCell(Simulator) For perform move and attack action
190
     * @see GeneratorBuilder#attackFromNearestCell(Simulator) For perform move and attack action
191
     */
192
    public final GeneratorBuilder<F> attack(Simulator simulator) {
193 1
        return add(new Attack<>(simulator));
194
    }
195
196
    /**
197
     * Try to boost oneself
198
     *
199
     * Note: The action will not boost only self fighter, but also allies, with lower priority
200
     *
201
     * @param simulator Simulator used by AI
202
     *
203
     * @return The builder instance
204
     *
205
     * @see Boost#self(Simulator) The used action generator
206
     * @see GeneratorBuilder#boostAllies(Simulator) To boost allies in priority
207
     */
208
    public final GeneratorBuilder<F> boostSelf(Simulator simulator) {
209 1
        return add(Boost.self(simulator));
210
    }
211
212
    /**
213
     * Try to boost allies
214
     *
215
     * @param simulator Simulator used by AI
216
     *
217
     * @return The builder instance
218
     *
219
     * @see Boost#allies(Simulator) The used action generator
220
     * @see GeneratorBuilder#boostSelf(Simulator) To boost oneself in priority
221
     */
222
    public final GeneratorBuilder<F> boostAllies(Simulator simulator) {
223 1
        return add(Boost.allies(simulator));
224
    }
225
226
    /**
227
     * Try to move to the best cell for cast a boost spell
228
     *
229
     * To ensure that the move will be performed, add the boost action after this one.
230
     * The action will not be performed if there is a tackle chance and if an boost is possible from the current cell
231
     *
232
     * @param simulator Simulator used by AI
233
     *
234
     * @return The builder instance
235
     *
236
     * @see MoveToBoost The used action generator
237
     */
238
    public final GeneratorBuilder<F> moveToBoost(Simulator simulator) {
239 1
        return add(new MoveToBoost<>(simulator)).boostAllies(simulator);
240
    }
241
242
    /**
243
     * Try to heal allies or self
244
     *
245
     * @param simulator Simulator used by AI
246
     *
247
     * @return The builder instance
248
     *
249
     * @see Heal The used action generator
250
     */
251
    public final GeneratorBuilder<F> heal(Simulator simulator) {
252 1
        return add(new Heal<>(simulator));
253
    }
254
255
    /**
256
     * Try to debuff (i.e. apply negative buff) enemies
257
     *
258
     * @param simulator Simulator used by AI
259
     *
260
     * @return The builder instance
261
     *
262
     * @see Debuff The used action generator
263
     */
264
    public final GeneratorBuilder<F> debuff(Simulator simulator) {
265 1
        return add(new Debuff<>(simulator));
266
    }
267
268
    /**
269
     * Try to move near the selected enemy
270
     *
271
     * @return The builder instance
272
     *
273
     * @see MoveNearEnemy The used action generator
274
     * @see AI#enemy() The selected enemy
275
     * @see GeneratorBuilder#moveOrTeleportNearEnemy() The move using MP or teleport spell
276
     */
277
    public final GeneratorBuilder<F> moveNearEnemy() {
278 1
        return add(new MoveNearEnemy<>());
279
    }
280
281
    /**
282
     * Try to teleport near the selected enemy
283
     *
284
     * @return The builder instance
285
     *
286
     * @see TeleportNearEnemy The used action generator
287
     * @see AI#enemy() The selected enemy
288
     * @see GeneratorBuilder#moveOrTeleportNearEnemy() The move using MP or teleport spell
289
     */
290
    public final GeneratorBuilder<F> teleportNearEnemy() {
291 1
        return add(new TeleportNearEnemy<>());
292
    }
293
294
    /**
295
     * Try to move or teleport near the selected enemy
296
     *
297
     * This is equivalent to `{@code builder.moveNearEnemy().teleportNearEnemy()}`
298
     *
299
     * @return The builder instance
300
     *
301
     * @see AI#enemy() The selected enemy
302
     * @see GeneratorBuilder#moveNearEnemy()
303
     * @see GeneratorBuilder#moveFarEnemies()
304
     */
305
    public final GeneratorBuilder<F> moveOrTeleportNearEnemy() {
306 1
        return moveNearEnemy().teleportNearEnemy();
307
    }
308
309
    /**
310
     * Try to move far all enemies
311
     *
312
     * The selected cell is the cell with the highest minimal distance from enemies
313
     *
314
     * @return The builder instance
315
     *
316
     * @see MoveFarEnemies The used action generator
317
     */
318
    public final GeneratorBuilder<F> moveFarEnemies() {
319 1
        return add(new MoveFarEnemies<>());
320
    }
321
322
    /**
323
     * Try to move near allies
324
     *
325
     * The selected cell is the cell with the lowest minimal and average distance from allies
326
     *
327
     * @return The builder instance
328
     *
329
     * @see MoveNearAllies The used action generator
330
     */
331
    public final GeneratorBuilder<F> moveNearAllies() {
332 1
        return add(new MoveNearAllies<>());
333
    }
334
335
    /**
336
     * Build the action generator object
337
     *
338
     * If the pipeline is empty, and {@link NullGenerator} will be returned
339
     * If there is only one action generator, it will be returned
340
     * Else, create a {@link GeneratorAggregate}
341
     */
342
    @SuppressWarnings("unchecked")
343
    public final ActionGenerator<F> build() {
344 1
        if (generators.isEmpty()) {
345 1
            return NullGenerator.get();
346
        }
347
348 1
        if (generators.size() == 1) {
349 1
            return generators.get(0);
350
        }
351
352 1
        return new GeneratorAggregate<>(generators.toArray(new ActionGenerator[0]));
353
    }
354
}
355