Passed
Pull Request — master (#172)
by Vincent
13:00
created

createArguments()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
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-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.admin.player.teleport;
21
22
import fr.quatrevieux.araknemu.common.account.Permission;
23
import fr.quatrevieux.araknemu.data.value.Position;
24
import fr.quatrevieux.araknemu.game.admin.AbstractCommand;
25
import fr.quatrevieux.araknemu.game.admin.AdminPerformer;
26
import fr.quatrevieux.araknemu.game.admin.exception.AdminException;
27
import fr.quatrevieux.araknemu.game.exploration.interaction.action.move.ChangeMap;
28
import fr.quatrevieux.araknemu.game.exploration.map.ExplorationMapService;
29
import fr.quatrevieux.araknemu.game.player.GamePlayer;
30
import org.kohsuke.args4j.Argument;
31
import org.kohsuke.args4j.Option;
32
33
import java.util.LinkedHashMap;
34
import java.util.List;
35
import java.util.Map;
36
37
/**
38
 * Teleport the player to the desired location
39
 */
40
public final class Goto extends AbstractCommand<Goto.Arguments> {
41
    private final GamePlayer player;
42
    private final ExplorationMapService mapService;
43 1
    private final Map<String, LocationResolver> resolvers = new LinkedHashMap<>();
44
45
    /**
46
     * @param player The teleported player
47
     * @param resolvers Position resolvers
48
     */
49 1
    public Goto(GamePlayer player, ExplorationMapService mapService, LocationResolver[] resolvers) {
50 1
        this.player = player;
51 1
        this.mapService = mapService;
52
53 1
        for (LocationResolver resolver : resolvers) {
54 1
            register(resolver);
55
        }
56 1
    }
57
58
    @Override
59
    protected void build(Builder builder) {
60 1
        builder
61 1
            .help(
62
                formatter -> {
63 1
                    formatter
64 1
                        .description("Teleport the player to the desired location")
65 1
                        .synopsis("goto [type] [target]...")
66 1
                        .option("type", "Define the target type (available types are defined bellow). If not provided, will try all available resolvers.")
67 1
                        .option("target", "Required. The target. This value depends of the type.")
68 1
                        .option("--force", "Force the teleporation even if the player is busy or in fight.")
69
                    ;
70
71 1
                    for (LocationResolver resolver : resolvers.values()) {
72 1
                        formatter.option("type: " + resolver.name(), resolver.help());
73 1
                    }
74
75 1
                    formatter
76 1
                        .example("goto map 10340", "Teleport to the map id 10340")
77 1
                        .example("goto map 10340 cell 45", "Teleport to the map id 10340 at cell 45")
78 1
                        .example("goto player John", "Teleport to the John's map")
79 1
                        .example("goto position 3;5", "Teleport by geolocation")
80 1
                        .example("${player:John} goto map 10340", "Teleport John to map id 10340")
81 1
                        .example("${player:John} goto player Alan", "Teleport John to the Alan's map")
82 1
                        .example("goto 3;5 cell 42", "Command can works without [type] argument, if not ambiguous")
83
                    ;
84 1
                }
85
            )
86 1
            .requires(Permission.MANAGE_PLAYER)
87
        ;
88 1
    }
89
90
    @Override
91
    public String name() {
92 1
        return "goto";
93
    }
94
95
    @Override
96
    public void execute(AdminPerformer performer, Arguments arguments) throws AdminException {
97 1
        if ((!player.isExploring() || player.exploration().interactions().busy()) && !arguments.force) {
98 1
            throw new AdminException("The player is busy, and cannot be teleported. Use --force to force the teleportation.");
99
        }
100
101 1
        final Target target = parseTarget(arguments.targets);
102
103 1
        teleportToTarget(performer, target);
104 1
        performer.success("Teleport {} to {}", player.name(), target);
105 1
    }
106
107
    /**
108
     * Parse the target from the command arguments
109
     */
110
    private Target parseTarget(List<String> arguments) throws AdminException {
111 1
        final Target target = new Target(
112 1
            player.isExploring() ? player.exploration().map() : mapService.load(player.position().map()),
113 1
            player.position().cell()
114
        );
115
116 1
        for (int argIndex = 0; argIndex < arguments.size(); ++argIndex) {
117 1
            final String argument = arguments.get(argIndex);
118
119 1
            if (resolvers.containsKey(argument)) {
120 1
                ++argIndex;
0 ignored issues
show
Performance introduced by
Assigning the loop counter from inside the loop can be inefficient. Consider refactoring the loop.
Loading history...
121
122 1
                if (arguments.size() < argIndex + 1) {
123 1
                    throw new AdminException("Missing argument for type " + argument);
124
                }
125
126
                try {
127 1
                    resolvers.get(argument).resolve(arguments.get(argIndex), target);
128 1
                } catch (IllegalArgumentException e) {
129 1
                    throw new AdminException(e);
130 1
                }
131
132
                continue;
133
            }
134
135 1
            if (!autoResolve(argument, target)) {
136 1
                throw new AdminException("Cannot resolve the argument or type " + argument);
137
            }
138
        }
139
140 1
        return target;
141
    }
142
143
    /**
144
     * Try to auto resolve the argument
145
     *
146
     * @param argument The argument to parse
147
     * @param target The target
148
     *
149
     * @return true on success
150
     */
151
    private boolean autoResolve(String argument, Target target) {
152 1
        for (LocationResolver resolver : resolvers.values()) {
153
            try {
154 1
                resolver.resolve(argument, target);
155
156 1
                return true;
157 1
            } catch (IllegalArgumentException e) {
158
                // ignore resolve exception
159
            }
160 1
        }
161
162 1
        return false;
163
    }
164
165
    /**
166
     * Perform the teleportation
167
     *
168
     * @param performer Command performer
169
     * @param target The teleportation target
170
     */
171
    private void teleportToTarget(AdminPerformer performer, Target target) {
172 1
        if (!player.isExploring()) {
173 1
            performer.info("Player is not in exploration. Define the position for the next exploration session.");
174 1
            player.setPosition(new Position(target.map().id(), target.cell()));
175 1
            return;
176
        }
177
178 1
        if (player.exploration().interactions().busy()) {
179 1
            performer.info("Player is busy. Stop all there interactions.");
180 1
            player.exploration().interactions().stop();
181
        }
182
183 1
        player.exploration().interactions().push(new ChangeMap(player.exploration(), target.map(), target.cell()));
184 1
    }
185
186
    /**
187
     * Add a new location resolver for the command
188
     */
189
    private void register(LocationResolver resolver) {
190 1
        resolvers.put(resolver.name(), resolver);
191 1
    }
192
193
    @Override
194
    public Arguments createArguments() {
195 1
        return new Arguments();
196
    }
197
198 1
    public static final class Arguments {
199 1
        @Option(name = "--force", usage = "Force the teleporation even if the player is busy or in fight.")
200
        private boolean force = false;
201
202
        @Argument(metaVar = "[type] [target]...", multiValued = true)
203
        private List<String> targets;
204
205
        public boolean force() {
206
            return force;
207
        }
208
209
        public void setForce(boolean force) {
210
            this.force = force;
211
        }
212
213
        public List<String> targets() {
214
            return targets;
215
        }
216
217
        public void setTargets(List<String> targets) {
218
            this.targets = targets;
219
        }
220
    }
221
}
222