• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rusocode / ao-server / 17193670034

24 Aug 2025 08:55PM UTC coverage: 57.057% (-2.4%) from 59.47%
17193670034

push

github

rusocode
refactor: migrated from ini4j to apache.commons due to vulnerabilities

- New IniUtils class to read values from ini
- Converted the City.java class to record and now handles map limits
- obj.dat
 # Almost all keys were renamed in English for better consistency.
 # The “magical_weapon” flag was added to differentiate it from the ‘magical_power’ key (formerly called “StaffPower”), so now we have three types
   of weapons: RANGED_WEAPON, MAGICAL_WEAPON, and MELEE_WEAPON
 # Now the blacksmithing skill covers both the manufacture of items that require ingots and the smelting of ore to obtain them. This means
   that the “MinSkill” key, which was used to represent the skill required to smelt ore and also served to represent
   the skill required to use ships (boat, galley, and galleon), has been removed. Now, in order to use ships, the “navigation_skill” skill has been created, and the skill for smelting ore (formerly ‘MinSkill’) is integrated into “smithing_skill.”

580 of 1187 branches covered (48.86%)

Branch coverage included in aggregate %.

2545 of 4290 relevant lines covered (59.32%)

2.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.58
server/src/main/java/com/ao/data/dao/ini/NPCPropertiesDAOIni.java
1
package com.ao.data.dao.ini;
2

3
import com.ao.data.dao.NPCCharacterPropertiesDAO;
4
import com.ao.data.dao.WorldObjectPropertiesDAO;
5
import com.ao.data.dao.exception.DAOException;
6
import com.ao.model.character.Alignment;
7
import com.ao.model.character.NPCType;
8
import com.ao.model.character.attack.AttackStrategy;
9
import com.ao.model.character.behavior.Behavior;
10
import com.ao.model.character.behavior.NullBehavior;
11
import com.ao.model.character.movement.MovementStrategy;
12
import com.ao.model.character.movement.QuietMovementStrategy;
13
import com.ao.model.character.npc.Drop;
14
import com.ao.model.character.npc.drop.DropEverything;
15
import com.ao.model.character.npc.drop.Dropable;
16
import com.ao.model.character.npc.drop.RandomDrop;
17
import com.ao.model.character.npc.properties.*;
18
import com.ao.model.inventory.Inventory;
19
import com.ao.model.inventory.InventoryImpl;
20
import com.ao.model.map.City;
21
import com.ao.model.map.Heading;
22
import com.ao.model.spell.Spell;
23
import com.ao.model.worldobject.Item;
24
import com.ao.model.worldobject.WorldObjectType;
25
import com.ao.model.worldobject.factory.WorldObjectFactory;
26
import com.ao.model.worldobject.factory.WorldObjectFactoryException;
27
import com.ao.model.worldobject.properties.WorldObjectProperties;
28
import com.google.inject.Inject;
29
import com.google.inject.name.Named;
30
import org.apache.commons.configuration2.INIConfiguration;
31
import org.apache.commons.configuration2.ex.ConfigurationException;
32
import org.slf4j.Logger;
33
import org.slf4j.LoggerFactory;
34

35
import java.io.BufferedReader;
36
import java.io.IOException;
37
import java.io.InputStream;
38
import java.io.InputStreamReader;
39
import java.util.*;
40

41
/**
42
 * Ini-backed implementation of the World Object DAO.
43
 */
44

45
public class NPCPropertiesDAOIni implements NPCCharacterPropertiesDAO {
46

47
    private static final Logger LOGGER = LoggerFactory.getLogger(NPCPropertiesDAOIni.class);
3✔
48

49
    private static final int MAX_SOUNDS = 3;
50

51
    private static final String INIT_HEADER = "INIT";
52
    private static final String NPC_SECTION_PREFIX = "NPC";
53
    private static final String NUM_NPCS_KEY = "num_npcs";
54

55
    private static final String NAME_KEY = "name";
56
    private static final String NPC_TYPE_ID_KEY = "npc_type_id";
57
    private static final String DESCRIPTION_KEY = "description";
58
    private static final String HEAD_KEY = "head";
59
    private static final String BODY_KEY = "body";
60
    private static final String HEADING_KEY = "heading";
61
    private static final String MOVEMENT_ID_KEY = "movement_id";
62
    private static final String ATTACKABLE_KEY = "attackable";
63
    private static final String RESPAWNABLE_KEY = "respawnable";
64
    private static final String HOSTILE_KEY = "hostile";
65
    private static final String TAMEABLE_KEY = "domable"; // Domable?
66
    private static final String ALIGNMENT_KEY = "alineacion";
67
    private static final String COMMERCIABLE_KEY = "comercia";
68
    private static final String ITEMS_TYPE_KEY = "TipoItems";
69
    private static final String ITEMS_AMOUNT_KEY = "NROITEMS";
70
    private static final String OBJECT_INVENTORY_PREFIX = "Obj";
71
    private static final String DROP_PREFIX = "Drop";
72
    private static final String CITY_KEY = "Ciudad";
73
    private static final String EXPERIENCE_KEY = "GiveEXP";
74
    private static final String GOLD_KEY = "GiveGLD";
75
    private static final String INVENTORY_RESPAWN_KEY = "InvReSpawn";
76
    private static final String MAX_HP_KEY = "MaxHp";
77
    private static final String MIN_HP_KEY = "MinHp";
78
    private static final String MAX_HIT_KEY = "MaxHIT";
79
    private static final String MIN_HIT_KEY = "MinHIT";
80
    private static final String DEFENSE_KEY = "def";
81
    private static final String SPELLS_AMOUNT_KEY = "LanzaSpells";
82
    private static final String SPELL_PREFIX = "SP";
83
    private static final String ATTACK_POWER_KEY = "PoderAtaque";
84
    private static final String DODGE_POWER_KEY = "PoderEvasion";
85
    private static final String CAN_WATER_KEY = "AguaValida";
86
    private static final String CAN_EARTH_KEY = "TierraInvalida";
87
    private static final String CAN_POISON_KEY = "Veneno";
88
    private static final String PARALYZABLE_KEY = "AfectaParalisis";
89
    private static final String SOUND_PREFIX = "SND";
90
    private static final String MAGIC_DEFENSE_KEY = "DefM";
91
    private static final String CREATURES_AMOUNT_KEY = "NroCriaturas";
92
    private static final String CREATURE_ID_PREFIX = "CI";
93
    private static final String CREATURE_NAME_PREFIX = "CN";
94
    private static final String ORIGINAL_POSITION_KEY = "PosOrig";
95

96

97
    private static final Map<LegacyNPCType, NPCType> npcTypeMapper;
98

99
    static {
100
        // Populate mappings from old object types to new ones
101
        npcTypeMapper = new HashMap<>();
4✔
102
        // Notice COMMON is not listed since it may be a merchant or a hostile npc
103
        npcTypeMapper.put(LegacyNPCType.DRAGON, NPCType.DRAGON);
5✔
104
        npcTypeMapper.put(LegacyNPCType.TRAINER, NPCType.TRAINER);
5✔
105
        npcTypeMapper.put(LegacyNPCType.GOVERNOR, NPCType.GOVERNOR);
5✔
106
        npcTypeMapper.put(LegacyNPCType.CHAOS_GUARD, NPCType.CHAOS_GUARD);
5✔
107
        npcTypeMapper.put(LegacyNPCType.ROYAL_GUARD, NPCType.ROYAL_GUARD);
5✔
108
        npcTypeMapper.put(LegacyNPCType.NOBLE, NPCType.NOBLE);
5✔
109
        npcTypeMapper.put(LegacyNPCType.NEWBIE_RESUCITATOR, NPCType.NEWBIE_RESUCITATOR);
5✔
110
        npcTypeMapper.put(LegacyNPCType.RESUCITATOR, NPCType.RESUCITATOR);
5✔
111
        npcTypeMapper.put(LegacyNPCType.GAMBLER, NPCType.GAMBLER);
5✔
112
        npcTypeMapper.put(LegacyNPCType.BANKER, NPCType.BANKER);
5✔
113
    }
1✔
114

115
    private final String npcsFilePath;
116

117
    private final WorldObjectPropertiesDAO worldObjectPropertiesDAO;
118
    private final WorldObjectFactory worldObjectFactory;
119

120
    private NPCProperties[] npcs;
121

122
    /**
123
     * Creates a new NPCDAOIni instance.
124
     *
125
     * @param npcsFilePath             path to the file with all objects definitions
126
     * @param worldObjectPropertiesDAO DAO for World Object Properties
127
     * @param worldObjectFactory       factory to create world object instances
128
     */
129
    @Inject
130
    public NPCPropertiesDAOIni(@Named("npcsFilePath") String npcsFilePath, WorldObjectPropertiesDAO worldObjectPropertiesDAO, WorldObjectFactory worldObjectFactory) {
2✔
131
        this.npcsFilePath = npcsFilePath;
3✔
132
        this.worldObjectPropertiesDAO = worldObjectPropertiesDAO;
3✔
133
        this.worldObjectFactory = worldObjectFactory;
3✔
134
    }
1✔
135

136
    @Override
137
    public NPCProperties[] loadAll() throws DAOException {
138
        INIConfiguration ini = null;
2✔
139
        LOGGER.info("Loading all NPCs from {}", npcsFilePath);
5✔
140
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(npcsFilePath);
7✔
141
        if (inputStream == null)
2!
142
            throw new IllegalArgumentException("The file " + npcsFilePath + " was not found in the classpath");
×
143
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
8✔
144
            ini = new INIConfiguration();
4✔
145
            ini.read(reader);
3✔
146
            LOGGER.info("NPCs loaded successfully!");
3✔
147
        } catch (IOException | ConfigurationException e) {
×
148
            LOGGER.error("Error loading NPCs!", e);
×
149
            System.exit(-1);
×
150
        }
1✔
151

152
        // Required key
153
        int numNpcs = IniUtils.getRequiredInt(ini, INIT_HEADER + "." + NUM_NPCS_KEY);
4✔
154

155
        npcs = new NPCProperties[numNpcs];
4✔
156

157
        for (int i = 1; i < numNpcs; i++)
7✔
158
            npcs[i - 1] = loadNpc(i, ini);
10✔
159

160
        return npcs;
3✔
161
    }
162

163
    /**
164
     * Loads a npc.
165
     *
166
     * @param id  npc's id
167
     * @param ini ini configuration
168
     * @return new npc
169
     */
170
    private NPCProperties loadNpc(int id, INIConfiguration ini) {
171

172
        String section = NPC_SECTION_PREFIX + id;
3✔
173

174
        String name = IniUtils.getString(ini, section + "." + NAME_KEY, "");
6✔
175
        int npcTypeId = IniUtils.getInt(ini, section + "." + NPC_TYPE_ID_KEY, -1);
6✔
176

177
        short body = (short) IniUtils.getInt(ini, section + "." + BODY_KEY, 0);
7✔
178
        short head = getHead(ini, section);
5✔
179
        Heading heading = Heading.get((byte) (IniUtils.getInt(ini, section + "." + HEADING_KEY, 1) - 1));
10✔
180
        boolean respawnable = isRespawnable(ini, section);
5✔
181
        String description = getDescription(ini, section);
5✔
182

183
        // TODO Unir si es posible
184
        Class<? extends Behavior> behavior = getBehavior(id, ini, section);
6✔
185
        Class<? extends AttackStrategy> attackStrategy = getAttackStrategy(id, ini, section);
6✔
186
        Class<? extends MovementStrategy> movementStrategy = getMovementStrategy(id, ini, section);
6✔
187

188
        LegacyNPCType npcType = LegacyNPCType.findById(npcTypeId);
3✔
189

190
        if (npcType == null) {
2✔
191
            LOGGER.error("Unknown npc_type_id={} for NPC{}.", npcTypeId, id);
7✔
192
            return null;
2✔
193
        }
194

195
        NPCProperties npc = null;
2✔
196

197
        switch (npcType) {
5!
198
            case COMMON:
199
                if (isCommerciable(ini, section))
5!
200
                    npc = loadMerchant(NPCType.MERCHANT, id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
×
201
                else
202
                    npc = loadCreature(NPCType.COMMON, id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
16✔
203
                break;
1✔
204
            case DRAGON:
205
            case PRETORIAN:
206
                npc = loadCreature(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
207
                break;
1✔
208
            case TRAINER:
209
                npc = loadTrainer(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
210
                break;
1✔
211
            case GOVERNOR:
212
                npc = loadGovernor(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
213
                break;
1✔
214
            case ROYAL_GUARD:
215
            case CHAOS_GUARD:
216
                npc = loadGuard(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
217
                break;
1✔
218
            case NOBLE:
219
                npc = loadNoble(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
220
                break;
1✔
221
            case NEWBIE_RESUCITATOR:
222
            case RESUCITATOR:
223
            case GAMBLER:
224
            case BANKER:
225
                npc = loadBasicNpc(npcTypeMapper.get(npcType), id, name, body, head, heading, respawnable, description, behavior, attackStrategy, movementStrategy, ini, section);
19✔
226
                break;
1✔
227
            default:
228
                LOGGER.error("Unexpected AI type found: {} for NPC with id {}", npcType, id);
×
229
        }
230

231
        return npc;
2✔
232
    }
233

234
    private NPCProperties loadNoble(NPCType type, int id, String name,
235
                                    short body, short head, Heading heading, boolean respawn,
236
                                    String desc, Class<? extends Behavior> behavior,
237
                                    Class<? extends AttackStrategy> attackStrategy,
238
                                    Class<? extends MovementStrategy> movementStrategy, INIConfiguration ini, String section) {
239
        Alignment alignment = getAlignment(ini, section);
5✔
240
        return new NobleNPCProperties(type, id, name, body, head, heading, respawn, desc, behavior, attackStrategy, movementStrategy, alignment);
16✔
241
    }
242

243
    /**
244
     * Creates a NPC's properties from the given section.
245
     *
246
     * @param type             the npc's type
247
     * @param id               the npc's id
248
     * @param name             the npc's name
249
     * @param body             the npc's body
250
     * @param head             the npc's head
251
     * @param heading          the npc's heading
252
     * @param respawn
253
     * @param desc
254
     * @param behavior
255
     * @param attackStrategy
256
     * @param movementStrategy
257
     * @param section          section of the ini file containing the world object to be loaded
258
     * @return the NPC created
259
     */
260
    private NPCProperties loadBasicNpc(NPCType type, int id, String name,
261
                                       short body, short head, Heading heading, boolean respawn,
262
                                       String desc, Class<? extends Behavior> behavior,
263
                                       Class<? extends AttackStrategy> attackStrategy,
264
                                       Class<? extends MovementStrategy> movementStrategy,
265
                                       INIConfiguration ini,
266
                                       String section) {
267
        return new NPCProperties(type, id, name, body, head, heading, respawn, desc, behavior, attackStrategy, movementStrategy);
15✔
268
    }
269

270
    /**
271
     * Creates a Governor NPC's properties from the given section.
272
     *
273
     * @param type             the npc's type
274
     * @param id               the npc's id
275
     * @param name             the npc's name
276
     * @param body             the npc's body
277
     * @param head             the npc's head
278
     * @param heading          the npc's heading
279
     * @param respawn
280
     * @param movementStrategy
281
     * @param attackStrategy
282
     * @param behavior
283
     * @param desc
284
     * @param section          section of the ini file containing the world object to be loaded
285
     * @return the NPC created
286
     */
287
    private NPCProperties loadGovernor(NPCType type, int id, String name,
288
                                       short body, short head, Heading heading, boolean respawn,
289
                                       String desc, Class<? extends Behavior> behavior,
290
                                       Class<? extends AttackStrategy> attackStrategy,
291
                                       Class<? extends MovementStrategy> movementStrategy,
292
                                       INIConfiguration ini,
293
                                       String section) {
294
        City city = getCity(ini, section);
5✔
295
        return new GovernorNPCProperties(type, id, name, body, head, heading, respawn, desc, behavior, attackStrategy, movementStrategy, city);
16✔
296
    }
297

298
    /**
299
     * Creates a Merchant NPC's properties from the given section.
300
     *
301
     * @param type             the npc's type
302
     * @param id               the npc's id
303
     * @param name             the npc's name
304
     * @param body             the npc's body
305
     * @param head             the npc's head
306
     * @param heading          the npc's heading
307
     * @param respawn
308
     * @param movementStrategy
309
     * @param attackStrategy
310
     * @param behavior
311
     * @param desc
312
     * @param section          section of the ini file containing the world object to be loaded
313
     * @return the NPC created
314
     */
315
    private NPCProperties loadMerchant(NPCType type, int id, String name,
316
                                       short body, short head, Heading heading, boolean respawn,
317
                                       String desc, Class<? extends Behavior> behavior,
318
                                       Class<? extends AttackStrategy> attackStrategy,
319
                                       Class<? extends MovementStrategy> movementStrategy,
320
                                       INIConfiguration ini,
321
                                       String section) {
322

323
        Inventory inventory = getInventory(ini, section);
×
324

325
        boolean respawnInventory = hasInventoryRespawn(ini, section);
×
326
        Set<WorldObjectType> acceptedTypes = getItemsType(ini, section);
×
327

328
        return new MerchantNPCProperties(type, id, name, body, head, heading, respawn,
×
329
                behavior, attackStrategy, movementStrategy, desc, inventory,
330
                respawnInventory, acceptedTypes
331
        );
332
    }
333

334
    /**
335
     * Creates a Trainer NPC's properties from the given section.
336
     *
337
     * @param type             the npc's type
338
     * @param id               the npc's id
339
     * @param name             the npc's name
340
     * @param body             the npc's body
341
     * @param head             the npc's head
342
     * @param heading          the npc's heading
343
     * @param respawn
344
     * @param movementStrategy
345
     * @param attackStrategy
346
     * @param behavior
347
     * @param desc
348
     * @param section          section of the ini file containing the world object to be loaded
349
     * @return the NPC created
350
     */
351
    private NPCProperties loadTrainer(NPCType type, int id, String name, short body,
352
                                      short head, Heading heading, boolean respawn, String desc,
353
                                      Class<? extends Behavior> behavior,
354
                                      Class<? extends AttackStrategy> attackStrategy,
355
                                      Class<? extends MovementStrategy> movementStrategy,
356
                                      INIConfiguration ini,
357
                                      String section) {
358
        Map<Integer, String> creatures = getCreatures(ini, section);
5✔
359
        return new TrainerNPCProperties(type, id, name, body, head, heading, respawn, desc, behavior, attackStrategy, movementStrategy, creatures);
16✔
360
    }
361

362
    /**
363
     * Creates a Creature NPC's properties from the given section.
364
     *
365
     * @param type             the npc's type
366
     * @param id               the npc's id
367
     * @param name             the npc's name
368
     * @param body             the npc's body
369
     * @param head             the npc's head
370
     * @param heading          the npc's heading
371
     * @param respawn
372
     * @param movementStrategy
373
     * @param attackStrategy
374
     * @param behavior
375
     * @param desc
376
     * @param section          section of the ini file containing the world object to be loaded
377
     * @return the NPC created
378
     */
379
    private NPCProperties loadCreature(NPCType type, int id, String name, short body,
380
                                       short head, Heading heading, boolean respawn, String desc,
381
                                       Class<? extends Behavior> behavior, Class<? extends AttackStrategy> attackStrategy,
382
                                       Class<? extends MovementStrategy> movementStrategy,
383
                                       INIConfiguration ini,
384
                                       String section) {
385

386
        int experience = getExperience(ini, section);
5✔
387
        int gold = getGold(ini, section);
5✔
388
        int minHP = getMinHP(ini, section);
5✔
389
        int maxHP = getMaxHP(ini, section);
5✔
390
        int minDamage = getMinDamage(ini, section);
5✔
391
        int maxDamage = getMaxDamage(ini, section);
5✔
392
        short defense = getDefense(ini, section);
5✔
393
        short magicDefense = getMagicDefense(ini, section);
5✔
394
        short accuracy = getAccuracy(ini, section);
5✔
395
        short dodge = getDodge(ini, section);
5✔
396
        List<Spell> spells = getSpells(ini, section);
5✔
397
        boolean canSwim = canSwim(ini, section);
5✔
398
        boolean canWalk = canWalk(ini, section);
5✔
399
        boolean attackable = isAttackable(ini, section);
5✔
400
        boolean poison = canPoison(ini, section);
5✔
401
        boolean paralyzable = isParalyzable(ini, section);
5✔
402
        boolean hostile = isHostile(ini, section);
5✔
403
        boolean tameable = isTameable(ini, section);
5✔
404
        Drop drops = getDrops(ini, section);
5✔
405

406
        return new CreatureNPCProperties(type, id, name, body, head, heading, respawn, desc,
34✔
407
                behavior, attackStrategy, movementStrategy, experience, gold, minHP, maxHP,
408
                minDamage, maxDamage, defense, magicDefense, accuracy, dodge, spells, canSwim, canWalk,
409
                attackable, poison, paralyzable, hostile, tameable, drops);
410
    }
411

412
    /**
413
     * Creates a Guard NPC's properties from the given section.
414
     *
415
     * @param type             the npc's type
416
     * @param id               the npc's id
417
     * @param name             the npc's name
418
     * @param body             the npc's body
419
     * @param head             the npc's head
420
     * @param heading          the npc's heading
421
     * @param respawn
422
     * @param movementStrategy
423
     * @param attackStrategy
424
     * @param behavior
425
     * @param desc
426
     * @param section          section of the ini file containing the world object to be loaded
427
     * @return the NPC created
428
     */
429
    private NPCProperties loadGuard(NPCType type, int id, String name, short body,
430
                                    short head, Heading heading, boolean respawn, String desc,
431
                                    Class<? extends Behavior> behavior,
432
                                    Class<? extends AttackStrategy> attackStrategy,
433
                                    Class<? extends MovementStrategy> movementStrategy,
434
                                    INIConfiguration ini,
435
                                    String section) {
436
        int experience = getExperience(ini, section);
5✔
437
        int gold = getGold(ini, section);
5✔
438
        int minHP = getMinHP(ini, section);
5✔
439
        int maxHP = getMaxHP(ini, section);
5✔
440
        int minDamage = getMinDamage(ini, section);
5✔
441
        int maxDamage = getMaxDamage(ini, section);
5✔
442
        short defense = getDefense(ini, section);
5✔
443
        short magicDefense = getMagicDefense(ini, section);
5✔
444
        short accuracy = getAccuracy(ini, section);
5✔
445
        short dodge = getDodge(ini, section);
5✔
446
        List<Spell> spells = getSpells(ini, section);
5✔
447
        boolean canSwim = canSwim(ini, section);
5✔
448
        boolean canWalk = canWalk(ini, section);
5✔
449
        boolean attackable = isAttackable(ini, section);
5✔
450
        boolean poison = canPoison(ini, section);
5✔
451
        boolean paralyzable = isParalyzable(ini, section);
5✔
452
        boolean hostile = isHostile(ini, section);
5✔
453
        boolean tameable = isTameable(ini, section);
5✔
454
        Drop drops = getDrops(ini, section);
5✔
455
        boolean originalPosition = hasOriginalPosition(ini, section);
5✔
456

457
        return new GuardNPCProperties(type, id, name, body, head, heading, respawn, desc,
35✔
458
                behavior, attackStrategy, movementStrategy, experience, gold, minHP, maxHP, minDamage,
459
                maxDamage, defense, magicDefense, accuracy, dodge, spells, canSwim, canWalk, attackable,
460
                poison, paralyzable, hostile, tameable, drops, originalPosition);
461
    }
462

463
    /**
464
     * Gets a npc's experience.
465
     *
466
     * @return the npc's experience
467
     */
468
    private int getExperience(INIConfiguration ini, String section) {
469
        String giveEXP = ini.getString(section + "." + EXPERIENCE_KEY);
5✔
470
        if (giveEXP == null) {
2✔
471
            LOGGER.warn("Missing 'giveEXP' value for section: {}", section);
4✔
472
            return 0;
2✔
473
        }
474
        return Integer.parseInt(giveEXP);
3✔
475
    }
476

477
    /**
478
     * Gets a npc's gold.
479
     *
480
     * @return the npc's gold
481
     */
482
    private int getGold(INIConfiguration ini, String section) {
483
        String giveGLD = ini.getString(section + "." + GOLD_KEY);
5✔
484
        if (giveGLD == null) {
2✔
485
            LOGGER.warn("Missing 'giveGLD' value for section: {}", section);
4✔
486
            return 0;
2✔
487
        }
488
        return Integer.parseInt(giveGLD);
3✔
489
    }
490

491
    /**
492
     * Gets a npc's min hp.
493
     *
494
     * @return the npc's min hp.
495
     */
496
    private int getMinHP(INIConfiguration ini, String section) {
497
        String minHp = ini.getString(section + "." + MIN_HP_KEY);
5✔
498
        if (minHp == null) {
2!
499
            LOGGER.warn("Missing 'minHP' value for section: {}", section);
4✔
500
            return 0;
2✔
501
        }
502
        return Integer.parseInt(minHp);
×
503
    }
504

505
    /**
506
     * Gets a npc's max hp.
507
     *
508
     * @return the npc's max hp
509
     */
510
    private int getMaxHP(INIConfiguration ini, String section) {
511
        String maxHp = ini.getString(section + "." + MAX_HP_KEY);
5✔
512
        if (maxHp == null) {
2!
513
            LOGGER.warn("Missing 'maxHP' value for section: {}", section);
4✔
514
            return 0;
2✔
515
        }
516
        return Integer.parseInt(maxHp);
×
517
    }
518

519
    /**
520
     * Gets a npc's min damage.
521
     *
522
     * @return the npc's min damage
523
     */
524
    private int getMinDamage(INIConfiguration ini, String section) {
525
        String minHIT = ini.getString(section + "." + MIN_HIT_KEY);
5✔
526
        if (minHIT == null) {
2✔
527
            LOGGER.warn("Missing 'minHIT' value for section: {}", section);
4✔
528
            return 0;
2✔
529
        }
530
        return Integer.parseInt(minHIT);
3✔
531
    }
532

533
    /**
534
     * Gets a npc's max damage
535
     *
536
     * @return the npc's max damage
537
     */
538
    private int getMaxDamage(INIConfiguration ini, String section) {
539
        String maxHIT = ini.getString(section + "." + MAX_HIT_KEY);
5✔
540
        if (maxHIT == null) {
2✔
541
            LOGGER.warn("Missing 'maxHIT' value for section: {}", section);
4✔
542
            return 0;
2✔
543
        }
544
        return Integer.parseInt(maxHIT);
3✔
545
    }
546

547
    /**
548
     * Gets a npc's defense
549
     *
550
     * @return the npc's defense
551
     */
552
    private short getDefense(INIConfiguration ini, String section) {
553
        String def = ini.getString(section + "." + DEFENSE_KEY);
5✔
554
        if (def == null) {
2!
555
            LOGGER.warn("Missing 'def' value for section: {}", section);
4✔
556
            return 0;
2✔
557
        }
558
        return Short.parseShort(def);
×
559
    }
560

561
    /**
562
     * Gets a npc's magic defense
563
     *
564
     * @return the npc's magic defense
565
     */
566
    private short getMagicDefense(INIConfiguration ini, String section) {
567
        String defM = ini.getString(section + "." + MAGIC_DEFENSE_KEY);
5✔
568
        if (defM == null) {
2✔
569
            LOGGER.warn("Missing 'defM' value for section: {}", section);
4✔
570
            return 0;
2✔
571
        }
572
        return Short.parseShort(defM);
3✔
573
    }
574

575
    /**
576
     * Gets a npc's acc
577
     *
578
     * @return the npc's acc
579
     */
580
    private short getAccuracy(INIConfiguration ini, String section) {
581
        String poderAtaque = ini.getString(section + "." + ATTACK_POWER_KEY);
5✔
582
        if (poderAtaque == null) {
2✔
583
            LOGGER.warn("Missing 'poderAtaque' value for section: {}", section);
4✔
584
            return 0;
2✔
585
        }
586
        return Short.parseShort(poderAtaque);
3✔
587
    }
588

589
    /**
590
     * Gets a npc's dodge
591
     *
592
     * @return the npc's dodge
593
     */
594
    private short getDodge(INIConfiguration ini, String section) {
595
        String poderEvasion = ini.getString(section + "." + DODGE_POWER_KEY);
5✔
596
        if (poderEvasion == null) {
2✔
597
            LOGGER.warn("Missing 'poderEvasion' value for section: {}", section);
4✔
598
            return 0;
2✔
599
        }
600
        return Short.parseShort(poderEvasion);
3✔
601
    }
602

603
    /**
604
     * Gets a npc's spell amount
605
     *
606
     * @return the npc's spell amount
607
     */
608
    private byte getSpellAmount(INIConfiguration ini, String section) {
609
        String lanzaSpells = ini.getString(section + "." + SPELLS_AMOUNT_KEY);
5✔
610
        if (lanzaSpells == null) {
2✔
611
            LOGGER.warn("Missing 'lanzaSpells' value for section: {}", section);
4✔
612
            return 0;
2✔
613
        }
614
        return Byte.parseByte(lanzaSpells);
3✔
615
    }
616

617
    /**
618
     * Gets a npc's head from
619
     *
620
     * @return the npc's head or 0 if none was found
621
     */
622
    private short getHead(INIConfiguration ini, String section) {
623
        String head = ini.getString(section + "." + HEAD_KEY);
5✔
624
        if (head == null) {
2✔
625
            LOGGER.warn("Missing head value for section: {}", section);
4✔
626
            return 0;
2✔
627
        }
628
        try {
629
            return Short.parseShort(head);
3✔
630
        } catch (NumberFormatException e) {
×
631
            LOGGER.error("Error parsing head value for section {}: {}", section, e.getMessage());
×
632
            return 0;
×
633
        }
634
    }
635

636
    /**
637
     * Gets a list with the spells.
638
     *
639
     * @return the npc's spells
640
     */
641
    private List<Spell> getSpells(INIConfiguration ini, String section) {
642
        byte amount = getSpellAmount(ini, section);
5✔
643
        if (amount <= 0) return null;
4✔
644

645
        List<Spell> spells = null;
2✔
646
        String spell;
647
        for (byte i = 1; i <= amount; i++) {
11✔
648
            spell = ini.getString(SPELL_PREFIX + i);
5✔
649
            if (spell != null) {
2!
650
                // TODO Implement Spells!
651
            }
652
        }
653
        return spells;
2✔
654
    }
655

656
    // ?
657
    private boolean canSwim(INIConfiguration ini, String section) {
658
        String aguaValida = ini.getString(section + "." + CAN_WATER_KEY);
5✔
659
        if (aguaValida == null) {
2✔
660
            LOGGER.warn("Missing 'aguaValida' value for section: {}", section);
4✔
661
            return false;
2✔
662
        }
663
        return !"0".equals(aguaValida);
8✔
664
    }
665

666
    // ?
667
    private boolean canWalk(INIConfiguration ini, String section) {
668
        String tierraInvalida = ini.getString(section + "." + CAN_EARTH_KEY);
5✔
669
        if (tierraInvalida == null) {
2✔
670
            LOGGER.warn("Missing 'tierraInvalida' value for section: {}", section);
4✔
671
            return false;
2✔
672
        }
673
        return !"0".equals(tierraInvalida);
8✔
674
    }
675

676
    /**
677
     * Checks if the npc is attackable.
678
     *
679
     * @return true if the npc is attackable, false otherwise
680
     */
681
    private boolean isAttackable(INIConfiguration ini, String section) {
682
        String attackable = ini.getString(section + "." + ATTACKABLE_KEY);
5✔
683
        if (attackable == null) {
2!
684
            LOGGER.warn("Missing 'attackable' value for section: {}", section);
×
685
            return false;
×
686
        }
687
        return !"0".equals(attackable);
8✔
688
    }
689

690
    /**
691
     * Checks if the npc is hostile.
692
     *
693
     * @return true if the npc is hostile, false otherwise
694
     */
695
    private boolean isHostile(INIConfiguration ini, String section) {
696
        String hostile = ini.getString(section + "." + HOSTILE_KEY);
5✔
697
        if (hostile == null) {
2!
698
            LOGGER.warn("Missing 'hostile' value for section: {}", section);
×
699
            return false;
×
700
        }
701
        return !"0".equals(hostile);
8✔
702
    }
703

704
    /**
705
     * Checks if the section corresponds to a tameable npc.
706
     *
707
     * @return true if the item is tameable, false otherwise
708
     */
709
    private boolean isTameable(INIConfiguration ini, String section) {
710
        String domable = ini.getString(section + "." + TAMEABLE_KEY);
5✔
711
        if (domable == null) {
2✔
712
            LOGGER.warn("Missing 'domable' value for section: {}", section);
4✔
713
            return false;
2✔
714
        }
715
        return !"0".equals(domable);
8✔
716
    }
717

718
    /**
719
     * Checks if the npc can poison.
720
     *
721
     * @return true if the npc can poison, false otherwise
722
     */
723
    private boolean canPoison(INIConfiguration ini, String section) {
724
        String veneno = ini.getString(section + "." + CAN_POISON_KEY);
5✔
725
        if (veneno == null) {
2✔
726
            LOGGER.warn("Missing 'veneno' value for section: {}", section);
4✔
727
            return false;
2✔
728
        }
729
        return !"0".equals(veneno);
7!
730
    }
731

732
    /**
733
     * Checks if the npc has an original position.
734
     *
735
     * @return true if the npc has the original position, false otherwise
736
     */
737
    private boolean hasOriginalPosition(INIConfiguration ini, String section) {
738
        String posOrig = ini.getString(section + "." + ORIGINAL_POSITION_KEY);
5✔
739
        if (posOrig == null) {
2✔
740
            LOGGER.warn("Missing 'posOrig' value for section: {}", section);
4✔
741
            return false;
2✔
742
        }
743
        return !"0".equals(posOrig);
7!
744
    }
745

746
    /**
747
     * Checks if the npc is paralyzable.
748
     *
749
     * @return true if the npc is paralyzable, false otherwise
750
     */
751
    private boolean isParalyzable(INIConfiguration ini, String section) {
752
        String afectaParalisis = ini.getString(section + "." + PARALYZABLE_KEY);
5✔
753
        if (afectaParalisis == null) {
2✔
754
            LOGGER.warn("Missing 'afectaParalisis' value for section: {}", section);
4✔
755
            return false;
2✔
756
        }
757
        return !"0".equals(afectaParalisis);
7!
758
    }
759

760
    /**
761
     * Generates a drop logic object based on the configuration specified in an INI file section. This method determines how items
762
     * are dropped by NPCs based on predefined configurations.
763
     *
764
     * @return a {@link Drop} object that contains the drop logic for the NPC, or null if configuration is invalid or missing
765
     */
766
    private Drop getDrops(INIConfiguration ini, String section) {
767
        String nroItems = ini.getString(section + "." + ITEMS_AMOUNT_KEY);
5✔
768
        if (nroItems == null) {
2✔
769
            LOGGER.warn("Missing 'nroItems' value for section: {}", section);
4✔
770
            return null;
2✔
771
        }
772

773
        // Is it a Pretorian NPC?
774
        String movement = ini.getString(MOVEMENT_ID_KEY);
4✔
775

776
        if (movement == null) {
2!
777
            LOGGER.warn("Missing 'movement' value for section: {}", section);
4✔
778
            return null;
2✔
779
        }
780

781
        // Pretorian NPCs drop everything... a little bit hard coded, but we want to be compatible with old AO
782
        LegacyAIType aiType = LegacyAIType.findById(Integer.parseInt(movement));
×
783
        if (aiType.isPretorian()) return new DropEverything(getInventory(ini, section));
×
784

785
        int count = Integer.parseInt(nroItems);
×
786

787
        List<Dropable> dropables = new LinkedList<>();
×
788
        String slot;
789

790
        /*
791
         * In Argentum, there is a 10% chance of dropping nothing; the other 90%
792
         * is split among the count items in such a way that each has 10% the
793
         * chance of the previous one.
794
         */
795
        float chance = 0.9f;
×
796
        for (int i = 1; i <= count; i++) {
×
797
            slot = ini.getString(DROP_PREFIX + i);
×
798
            if (slot != null) {
×
799
                String[] slotInfo = slot.split("-");
×
800
                // In every step except the last one, leave a 10% chance for the next level
801
                float curChance = i == count ? chance : chance * 0.9f;
×
802
                dropables.add(new Dropable(Integer.parseInt(slotInfo[0]), Integer.parseInt(slotInfo[1]), curChance));
×
803
            }
804

805
            // The chance for the next step is 10% of the current one
806
            chance *= 0.1f;
×
807
        }
808

809
        if (!dropables.isEmpty()) return new RandomDrop(dropables);
×
810

811
        return null;
×
812
    }
813

814
    // TODO Use this!
815

816
    /**
817
     * Gets a list of sound for all npc's.
818
     *
819
     * @return a list of sound for all npc's
820
     */
821
    private List<Integer> getSounds(INIConfiguration ini) {
822
        List<Integer> sounds = new LinkedList<>();
×
823
        String sound;
824
        for (int i = 1; i <= MAX_SOUNDS; i++) {
×
825
            sound = ini.getString(SOUND_PREFIX + i);
×
826
            if (sound != null) sounds.add(Integer.parseInt(sound));
×
827
        }
828
        return sounds;
×
829
    }
830

831
    /**
832
     * Gets the inventory configuration for a specific section from the provided INI configuration. The method parses the
833
     * configuration file to create an inventory object by reading the items and their amounts associated with the specified
834
     * section.
835
     *
836
     * @return an Inventory object populated with the specified items from the given section, or null if the required
837
     * configuration is missing or invalid
838
     */
839
    private Inventory getInventory(INIConfiguration ini, String section) {
840
        String nroItems = ini.getString(section + "." + ITEMS_AMOUNT_KEY);
×
841
        if (nroItems == null) {
×
842
            LOGGER.warn("Missing 'nroItems' value for section: {}", section);
×
843
            return null;
×
844
        }
845

846
        String slot;
847
        byte inventorySize = Byte.parseByte(nroItems);
×
848

849
        Inventory inventory = new InventoryImpl(inventorySize);
×
850

851
        for (byte i = 1; i <= inventorySize; i++) {
×
852
            slot = ini.getString(OBJECT_INVENTORY_PREFIX + i);
×
853
            if (slot != null) {
×
854
                String[] slotInfo = slot.split("-");
×
855
                int objId = Integer.parseInt(slotInfo[0]);
×
856
                int amount = Integer.parseInt(slotInfo[1]);
×
857
                WorldObjectProperties woProperties = worldObjectPropertiesDAO.getWorldObjectProperties(objId);
×
858
                Item worldObject;
859
                try {
860
                    worldObject = worldObjectFactory.getWorldObject(woProperties, amount);
×
861
                } catch (WorldObjectFactoryException e) {
×
862
                    LOGGER.warn("An NPC has an item in it's inventory that can't be created. Object id: {}. Ignoring it...", objId, e);
×
863
                    continue;
×
864
                }
×
865
                if (worldObject != null) inventory.addItem(worldObject);
×
866
                else
867
                    LOGGER.error("An NPC has the object with id {} in it's inventory, but the object is not an item. Ignoring it...", objId);
×
868
            }
869
        }
870

871
        return inventory;
×
872
    }
873

874
    /**
875
     * Gets a map of creatures where the key is the creature ID and the value is the creature name. The creature information is
876
     * parsed from the specified section of the provided INIConfiguration.
877
     *
878
     * @return a map containing creature IDs as keys and their corresponding names as values or null if the configuration is
879
     * incomplete or missing
880
     */
881
    private Map<Integer, String> getCreatures(INIConfiguration ini, String section) {
882
        String nroCriaturas = ini.getString(section + "." + CREATURES_AMOUNT_KEY);
5✔
883
        if (nroCriaturas == null) {
2!
884
            LOGGER.warn("Missing 'nroCriaturas' value for section: {}", section);
×
885
            return null;
×
886
        }
887
        String creatureId, creatureName;
888
        Map<Integer, String> creatures = null;
2✔
889
        for (int i = 1; i <= Integer.parseInt(nroCriaturas); i++) {
8✔
890
            creatureId = ini.getString(CREATURE_ID_PREFIX + i);
5✔
891
            creatureName = ini.getString(CREATURE_NAME_PREFIX + i);
5✔
892
            if (creatureId != null && creatureName != null) {
2!
893
                if (null == creatures) creatures = new HashMap<>();
×
894
                creatures.put(Integer.parseInt(creatureId), creatureName);
×
895
            }
896
        }
897
        return creatures;
2✔
898
    }
899

900
    /**
901
     * Gets npc's alignment.
902
     *
903
     * @return the npc's alignment
904
     */
905
    private Alignment getAlignment(INIConfiguration ini, String section) {
906
        String alineacion = ini.getString(section + "." + ALIGNMENT_KEY);
5✔
907
        if (alineacion == null) {
2!
908
            LOGGER.warn("Missing 'alineacion' value for section: {}", section);
×
909
            return null;
×
910
        }
911
        if (!"0".equals(alineacion.trim())) return Alignment.CRIMINAL;
5!
912
        return Alignment.CITIZEN;
2✔
913
    }
914

915
    /**
916
     * Gets a set of item types based on the specified section and configuration.
917
     *
918
     * @return a set of WorldObjectType representing the types of items defined in the given section, or null if the type
919
     * specification is missing
920
     */
921
    private Set<WorldObjectType> getItemsType(INIConfiguration ini, String section) {
922
        String tipoItems = ini.getString(section + "." + ITEMS_TYPE_KEY);
×
923
        if (tipoItems == null) {
×
924
            LOGGER.warn("Missing 'tipoItems' value for section: {}", section);
×
925
            return null;
×
926
        }
927
        Set<WorldObjectType> acceptedTypes;
928
        LegacyWorldObjectType objectType = LegacyWorldObjectType.findById(Integer.parseInt(tipoItems));
×
929
        if (objectType == null) {
×
930
            // All item types are accepted
931
            acceptedTypes = new HashSet<>();
×
932
            acceptedTypes.addAll(Arrays.asList(WorldObjectType.values()));
×
933
        } else acceptedTypes = objectType.getPlausibleCurrentTypes();
×
934
        return acceptedTypes;
×
935
    }
936

937
    /**
938
     * Checks if the specified section in the INI configuration is marked as {@code InvReSpawn}.
939
     *
940
     * @return true if the section is marked as {@code InvReSpawn} and not equal to "0", false otherwise
941
     */
942
    private boolean hasInventoryRespawn(INIConfiguration ini, String section) {
943
        String invRespawn = ini.getString(section + "." + INVENTORY_RESPAWN_KEY);
×
944
        if (invRespawn == null) {
×
945
            LOGGER.warn("Missing 'invRespawn' value for section: {}", section);
×
946
            return false;
×
947
        }
948
        return !"0".equals(invRespawn.trim());
×
949
    }
950

951
    private boolean isRespawnable(INIConfiguration ini, String section) {
952
        return IniUtils.getBoolean(ini, section + "." + RESPAWNABLE_KEY, true);
6✔
953
    }
954

955
    /**
956
     * Gets npc's city.
957
     *
958
     * @return the npc's city
959
     */
960
    private City getCity(INIConfiguration ini, String section) {
961
        String ciudad = ini.getString(section + "." + CITY_KEY);
5✔
962
        if (ciudad == null) {
2!
963
            LOGGER.warn("Missing 'ciudad' value for section: {}", section);
×
964
            return null;
×
965
        }
966
        // TODO Implementar Cities :d
967
        return null;
2✔
968
    }
969

970
    /**
971
     * Checks if the specified section in the INI configuration is marked as {@code comercia}.
972
     *
973
     * @return true if the section is marked as {@code comercia} and not equal to "0", false otherwise
974
     */
975
    private boolean isCommerciable(INIConfiguration ini, String section) {
976
        String comercia = ini.getString(section + "." + COMMERCIABLE_KEY);
5✔
977
        if (comercia == null) {
2!
978
            LOGGER.warn("Missing 'comercia' value for section: {}", section);
4✔
979
            return false;
2✔
980
        }
981
        return !"0".equals(comercia.trim());
×
982
    }
983

984
    /**
985
     * Gets npc's description.
986
     *
987
     * @return the npc's description, empty string otherwise
988
     */
989
    private String getDescription(INIConfiguration ini, String section) {
990
        return IniUtils.getString(ini, section + "." + DESCRIPTION_KEY, "");
6✔
991
    }
992

993
    /**
994
     * Gets npc's behavior.
995
     *
996
     * @return the npc's behavior, null if no behavior is specified
997
     */
998
    private Class<? extends Behavior> getBehavior(int id, INIConfiguration ini, String section) {
999
        int movementId = IniUtils.getInt(ini, section + "." + MOVEMENT_ID_KEY, 0);
6✔
1000
        LegacyAIType aiType = LegacyAIType.findById(movementId);
3✔
1001
        if (aiType == null) {
2✔
1002
            LOGGER.warn("Unknown movement_id={} for OBJ{}.", movementId, id);
7✔
1003
            return null;
2✔
1004
        }
1005
        return aiType.getBehavior();
3✔
1006
    }
1007

1008
    /**
1009
     * Gets npc's attackStrategy.
1010
     *
1011
     * @return the npc's attack strategy
1012
     */
1013
    private Class<? extends AttackStrategy> getAttackStrategy(int id, INIConfiguration ini, String section) {
1014
        int movementId = IniUtils.getInt(ini, section + "." + MOVEMENT_ID_KEY, 0);
6✔
1015
        LegacyAIType aiType = LegacyAIType.findById(movementId);
3✔
1016
        if (aiType == null) {
2✔
1017
            LOGGER.warn("Unknown movement_id={} for OBJ{}.", movementId, id);
7✔
1018
            return null;
2✔
1019
        }
1020
        return aiType.getAttackStrategy();
3✔
1021
    }
1022

1023
    /**
1024
     * Gets npc's movementStrategy.
1025
     *
1026
     * @return the npc's movement strategy
1027
     */
1028
    private Class<? extends MovementStrategy> getMovementStrategy(int id, INIConfiguration ini, String section) {
1029
        int movementId = IniUtils.getInt(ini, section + "." + MOVEMENT_ID_KEY, 0);
6✔
1030
        LegacyAIType aiType = LegacyAIType.findById(movementId);
3✔
1031
        if (aiType == null) {
2✔
1032
            LOGGER.warn("Unknown movement_id={} for OBJ{}.", movementId, id);
7✔
1033
            return null;
2✔
1034
        }
1035
        return aiType.getMovementStrategy();
3✔
1036
    }
1037

1038
    /**
1039
     * NPC Type enumeration, as it was known in the old days of Visual Basic.
1040
     */
1041
    private enum LegacyNPCType {
3✔
1042

1043
        COMMON(0), // TODO No deberia empezar en 1?
7✔
1044
        RESUCITATOR(1),
7✔
1045
        ROYAL_GUARD(2),
7✔
1046
        TRAINER(3),
7✔
1047
        BANKER(4),
7✔
1048
        NOBLE(5),
7✔
1049
        DRAGON(6),
7✔
1050
        GAMBLER(7),
7✔
1051
        CHAOS_GUARD(8),
7✔
1052
        NEWBIE_RESUCITATOR(9),
7✔
1053
        PRETORIAN(10),
7✔
1054
        GOVERNOR(11);
7✔
1055

1056
        private final int id;
1057

1058
        /**
1059
         * Creates a new LegacyWorldObjectType.
1060
         *
1061
         * @param id value corresponding to the object type. Should be unique
1062
         */
1063
        LegacyNPCType(int id) {
4✔
1064
            this.id = id;
3✔
1065
        }
1✔
1066

1067
        public static LegacyNPCType findById(int id) {
1068
            for (LegacyNPCType type : LegacyNPCType.values())
16✔
1069
                if (type.id == id) return type;
6✔
1070
            return null;
2✔
1071
        }
1072

1073
    }
1074

1075
    /**
1076
     * Enum with legacy NPCTypes, which are now useless.
1077
     */
1078
    private enum LegacyAIType {
3✔
1079
        // TODO Complete this as we code the behaviors!
1080
        STATIC(1, NullBehavior.class, null, QuietMovementStrategy.class),
10✔
1081
        RANDOM(2, null, null, null),
10✔
1082
        BAD_ATTACKS_GOOD(3, null, null, null),
10✔
1083
        DEFENSIVE(4, null, null, null),
10✔
1084
        GUARD_ATTACK_CRIMINALS(5, null, null, null),
10✔
1085
        NPC_OBJECT(6, null, null, QuietMovementStrategy.class),
10✔
1086
        FOLLW_MASTER(8, null, null, null),
10✔
1087
        ATTACK_NPC(9, null, null, null),
10✔
1088
        PATHFINDING(10, null, null, null),
10✔
1089
        PRETORIAN_PRIEST(20, null, null, null),
10✔
1090
        PRETORIAN_WARRIOR(21, null, null, null),
10✔
1091
        PRETORIAN_MAGE(22, null, null, null),
10✔
1092
        PRETORIAN_HUNTER(23, null, null, null),
10✔
1093
        PRETORIAN_KING(24, null, null, null);
10✔
1094

1095
        private final int id;
1096
        private final Class<? extends Behavior> behavior;
1097
        private final Class<? extends AttackStrategy> attackStrategy;
1098
        private final Class<? extends MovementStrategy> movementStrategy;
1099

1100
        /**
1101
         * @param id               numeric value associated with the AI Type
1102
         * @param behavior         behavior class to be used
1103
         * @param attackStrategy   attack strategy class to be used
1104
         * @param movementStrategy movement strategy class to be used
1105
         */
1106
        LegacyAIType(int id, Class<? extends Behavior> behavior,
1107
                     Class<? extends AttackStrategy> attackStrategy,
1108
                     Class<? extends MovementStrategy> movementStrategy) {
4✔
1109
            this.id = id;
3✔
1110
            this.behavior = behavior;
3✔
1111
            this.attackStrategy = attackStrategy;
3✔
1112
            this.movementStrategy = movementStrategy;
3✔
1113
        }
1✔
1114

1115
        public static LegacyAIType findById(int id) {
1116
            for (LegacyAIType aiType : LegacyAIType.values())
16✔
1117
                if (aiType.id == id) return aiType;
6✔
1118
            return null;
2✔
1119
        }
1120

1121
        public Class<? extends Behavior> getBehavior() {
1122
            return behavior;
3✔
1123
        }
1124

1125
        public Class<? extends AttackStrategy> getAttackStrategy() {
1126
            return attackStrategy;
3✔
1127
        }
1128

1129
        public Class<? extends MovementStrategy> getMovementStrategy() {
1130
            return movementStrategy;
3✔
1131
        }
1132

1133
        /**
1134
         * Checks if the current NPC is pretorian
1135
         *
1136
         * @return true if the NPC is pretorian, false otherwise
1137
         */
1138
        public boolean isPretorian() {
1139
            return this == PRETORIAN_HUNTER || this == PRETORIAN_KING || this == PRETORIAN_MAGE || this == PRETORIAN_PRIEST || this == PRETORIAN_WARRIOR;
×
1140
        }
1141

1142
    }
1143

1144
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc