Compare commits

...

360 Commits

Author SHA1 Message Date
bashermens
f5c84ee7ff server_crash_fix: OnPlayerGiveXP (#1929)
[fef0ed4072c8_worldserver.exe_.18-12_23-30-44.txt](https://github.com/user-attachments/files/24258638/fef0ed4072c8_worldserver.exe_.18-12_23-30-44.txt)

- Just added common logic and defense coding
- Optimized isRandombot while at it

Just for clarification i am aware the trigger of bug lies with the
progression mod. Regardless it should not happen.
2025-12-19 23:08:18 +01:00
Keleborn
b6f882886d FixListSpellsWithCache (#1931)
This is based off of Wishmasters rewrite of spell PR. #1912 and #1843,
and partial #1918
I created a new cache singleton with the required code to reference as
needed.
I clarified some variable names for additional clarity in how they
functioned.

This requires a wiki update to better describe the functionality that
was already defined in code.

Commands
Spells - Returns all spells
Spells <Profession> Returns only the spells in that profession
+<profession> Returns only the recipies that the bot has materials to
craft
<profession> `x - y` Craftable items within those levels
<profession> <slot> e.g. Chest, returns craftable items within that
slot.

Its messy whether what combinations work for commands, but fixing that
will come when bot professions are enabled.

Edit:
To test you can teach a bot various professions by going to a trainer
with them.
Using gm command .setskill you increase their skill level and with
maintenance teach the commands.

From wishmasters PR he detailed various commands to test

spells
spells first aid
spells tailoring
spells 20-40
spells +tailoring
spells head

---------

Co-authored-by: Wishmaster117 <140754794+Wishmaster117@users.noreply.github.com>
2025-12-19 23:08:01 +01:00
HennyWilly
c1222da8b0 Disable bot XP gain when master has XP turned off (#1910)
Addresses #1846

This PR disables XP gain for bots whose non-bot master has disabled XP
gain via an "Experience Eliminator" NPC.

If the current master has disabled XP gain, randombots in the group and
addclass-bots belonging to the player won't gain XP.
Randombots not grouped with a player will continue to gain XP as normal.

Discussed points:
- `Should this feature only be enabled via a new configuration value?`:
Comments currently tend towards no config value.

Open points:
- Should bots be allowed to gain XP until they reach the player's level,
even if the player has disabled XP gain?
2025-12-16 22:23:34 +01:00
NoxMax
00cb177c86 Fix: Allow bots to duel in PVP prohibited areas (#1906)
Noticed that if you ask a bot to duel in a PVP prohibited area, it will
accept, and do nothing. I thought about making the bot reject the
request, but if you (the real player) want to duel with it, the duel
should happen. This is just a minor fix to allow bots to duel if you ask
them to in such areas.

Tested with bots in party, random bots of the same faction, and random
bots of the opposite faction. All behaved the same before and after fix.
An example place to test is Zim'Torga in Zul'Drak which is by default is
a PVP prohibited area.

- Before fix, you challenge a bot, they accept and turn red, then they
either just stay where they are or wander off.

- After fix, bot attacks you within the PVP prohibited area when the
duel starts.
2025-12-16 18:38:50 +01:00
privatecore
5f697e806e Rerwite is moving allowed logic + fix root flag heartbeat spam (#1908)
Okay, what have been done:

1. Fix heartbeat spam for root flag: check against `MOVEMENTFLAG_ROOT`
flag (`IsRooted`) instead of `HasRootAura`.
2. Rewrite `IsMovingAllowed` - place checks from most common to the
rarest.
3. Remove unnecessary checks: `HasRootAura`, `HasConfuseAura`,
`HasStunAura` - handled by AuraEffects and set unit state flags
`UNIT_STATE_ROOT`, `UNIT_STATE_CONFUSED`, `UNIT_STATE_STUNNED` -
`UNIT_STATE_LOST_CONTROL` already handles confused and stunned (rooted
checked with `IsRooted` method).
4. Combine traveling state checks for taxi flights:
`UNIT_STATE_IN_FLIGHT` + MM flag `FLIGHT_MOTION_TYPE`.
5. Simplify check against being in vehicle: use
`MOVEMENTFLAG_ONTRANSPORT` as an indicator that the unit is in the
vehicle.

Also, update `UpdateMovementState` method with simplified checks and the
updated logic (common > rare).

This should fix issues:
https://github.com/mod-playerbots/mod-playerbots/issues/1903 and
https://github.com/mod-playerbots/mod-playerbots/issues/1902

NOTE: The `PlayerbotAI` class has a method `CanMove` with the same
checks, but this method is only used once in the code. We should decide
how to properly check if the bot can move or not:

1. Place all logic into `IsMovingAllowed` and drop `CanMove`.
2. Place all logic into `CanMove` and use it inside `IsMovingAllowed`.
3. Use them for different approaches: 
- `CanMove`: simple checks (unit flags, CC state, death state, travel
state, vehicle state);
- `IsMovingAllowed`: everything from `CanMove` + MM flags checks (not
sure about rooted since it still checks for movement flags...).
2025-12-15 15:32:49 +01:00
Crow
934e73ae20 Add Ogri'la and Blackwind Landing to PvP Restricted Areas (#1915)
This PR adds a couple of neutral quest hubs in Outland to PvP restricted
areas (and makes a couple of very minor formatting fixes to the Karazhan
files).

3786: Ogri'la
3973: Blackwind Landing (Sha'tari Skyguard quest hub in Skettis)
2025-12-15 15:26:38 +01:00
Crow
f4b4d8967f Karazhan Refactor + Nightbane Strategy (#1847)
This PR completely refactors the Karazhan raid strategies to clean up
the code and polish up/introduce new strategies, including for
Nightbane.

General changes with respect to the code:
- Moved gating checks for action methods to triggers instead of relying
on isUseful functions
- Got rid of the nonsensical helper class I made and switched to a
helper namespace
- Broke up some longer action classes into separate methods and/or
private members
- Deleted/consolidated some excess code
- Made greater use of early returns
- Renamed methods, multipliers, and triggers to make them easier to
understand
- Generally made edits to conform with AC code standards

Below are the implemented strategies. I’m including all of them, not
just new/modified ones, because I don’t think the first set of
strategies was ever tested. So each boss could probably use complete
testing.

### **Trash**

I added strategies for a trash mob that I find particularly annoying:
- Mana Warp: These are, IMO, the most annoying trash mobs in Karazhan.
They blow up when low on health, and having two blow up in quick
succession is enough to take out a decent chunk of your raid. The
strategy directs bots to use pretty much every stun in the book on Mana
Warps when they’re low on HP, which is the only way to prevent the
explosion.

### **Attumen**

- During the first phase, bots will focus on Midnight (taking either
Midnight or Attumen to 25% starts the next phase). When Attumen spawns,
the assist tank will pick him up and move him away so that bots don’t
get cleaved.
- When Attumen mounts Midnight, starting phase 2, threat is wiped, and
bots will pause DPS for a few seconds to allow the main tank to get
aggro. All bots, other than the main tank and any bot that pulls aggro,
will stack behind Attumen (~6 yards for ranged so Hunters can still
attack).

### **Moroes**

- As before, bots will mark and prioritize adds and the boss in the
recommended kill order: Dorothea, Catriona, Keira, Rafe, Robin, Crispin,
and Moroes. In practice, the enemies will probably be stacked up, and
the bots will AoE them down in accordance with their typical AoE
strategies, but classes without AoE capabilities should still prioritize
the skull.
- Based on testing feedback, added a method for the main tank to
prioritize Moroes

### **Maiden of Virtue**

I’ve made only minor changes to Revision’s original strategy here. 

- The tank with aggro will position Maiden in the middle of the room and
move her to a healer when Repentance is cast so that the Holy Ground
will break the healer’s stun.
- Ranged bots have assigned positions between the columns around the
middle of the room (to prevent chain damage from Holy Wrath).

### **The Big Bad Wolf**

- The tank with aggro brings the boss to the front left of the stage. 
- If a bot gets turned into Little Red Riding Hood, it will run around
the stage in a counter-clockwise rectangle until the transformation
fades. I tweaked this strategy a bit; it's still not perfect, but it
works better than before.

### **Romulo and Julianne**

There are no substantive changes to this strategy. As before, in phase
3, when both bosses are active, the bots switch back-and-forth between
the bosses by alternating the skull icon based on which boss has lower
HP (10% differential).

### **The Wizard of Oz**

There are no substantive changes to this strategy. As before, bots mark
the bosses with a skull icon in their recommended kill order: Dorothee,
Tito (assuming he spawns before you kill Dorothee), Roar, Strawman,
Tinhead, and the Crone. Additionally, Mages will spam Scorch on Strawman
to daze him.

### **The Curator**

- The tank will drag the boss to a fixed spot down the hallway. Ranged
bots will spread out to avoid chain damage from Arcing Sear, and bots
will mark Astral Flares with the skull icon to prioritize them down.
- Those strategies already existed, but now I made the assist tank also
focus on the boss to try to stay second in aggro and therefore absorb
Hateful Bolts.
- Added a multiplier to save Bloodlust/Heroism until Evocation.

### **Terestian Illhoof**

There are no substantive changes to this strategy. The bots will mark
targets with the skull icon in the following order: Demonic Chains,
Kil'rek, and Illhoof.

### **Shade of Aran**
I redid the strategies a bit, and I think (hope) they work better.

- Flame Wreath: Bots will stop moving until the aura fades. There is a
bug in which Flame Wreath will sometimes persist long beyond its
supposed 20-second duration. If Aran casts Arcane Explosion during this
time, you will almost certainly wipe, so that’s frustrating. I made it
so that bots will stay in place still and eat the Arcane Explosion
because it’s the lesser of two evils, and if you are overgeared, you may
be able to survive. In the previous strategy, bots would stop actions
entirely, but now they should keep attacking/casting without moving.
- Arcane Explosion: No substantive changes here--bots will run out
immediately and stay out of range until the cast finishes.
- Conjured Elementals: No substantive changes here--they will be marked
one-by-one by the skull icon, except that the marking will skip any
elemental that is banished.
- Ranged Positioning: I redid this strategy. Ranged bots will now
maintain a distance between 11 and 15 yards from the boss. This keeps
them out of the 10-yard radius in which Aran silences while also keeping
them from getting too far away and getting stuck in the alcoves.

### **Netherspite**

I significantly refactored the action methods here, but substantively
the original strategy remains mostly intact.

- Red (Tank) Beam: One tank will be assigned to block the beam for each
Portal Phase. The assigned tank will dance in and out of the beam (5
seconds in, 5 seconds out). Tanks intentionally do not avoid Void Zones
(it was the lesser of two evils for them to take that damage vs. trying
to dynamically avoid them, moving the boss, and possibly getting
everybody out of position.
- Blue (DPS) Beam: DPS other than Rogues and Warriors are eligible to be
assigned (one-by-one) to block this beam. When the assigned blocker
reaches 25 stacks of the debuff, they will leave the beam, and the next
assigned blocker will take their place. If a Void Zone drops under the
assigned blocker, the bot will move along the beam to get out of the
Void Zone so that they do not stop blocking.
- Green (Healer) Beam: This works the same way as the Blue Beam, except
that eligible blockers are healers, Rogues, and DPS Warriors. Healers
that are assigned to block will swap in the same way as Blue Beam
blockers. If a Rogue or DPS Warrior is the assigned blocker, however,
they will stand in the beam for the entire Portal Phase since they do
not suffer any adverse effects from the beam. In this PR, I made the
strategy prioritize Rogues and DPS Warriors over healers to try to avoid
the need for bots to swap (and to avoid the irritating scenario in which
a healer would block the beam for the first half of a phase and then tag
in a Rogue or DPS Warrior, which would be wasted by blocking only half
of a phase).
- Non-Blockers: They will stay at least 5 yards away from each beam
until called to be an assigned blocker. They will also avoid Void Zones.
- Banish Phase: The only strategy I implemented was for bots to avoid
residual Void Zones from the Portal Phase.
- Phase Transitions: Bots should pause DPS at the beginning of the
encounter and whenever Netherspite transitions back from the Banish
Phase to the Portal Phase (which is an aggro reset). Note that this
doesn't wipe DOTs, and there's not much I can do about that.

### **Prince Malchezaar**

The action methods are significantly refactored, but the strategy
substantively is not changed very much.

- Bots will maintain distance from Infernals. The tank has a larger
avoidance radius to give DPS a little bit of margin to work with.
Depending on Infernal placement, it is possible for bots to get stuck in
some bad positions. In that case, the best solution is to put them on
“flee” and lead them to a better position.
- Bots that get Enfeebled will run out of Shadow Nova range. They should
pick a path that does not cross within any Infernal's Hellfire radius.
- Added a multiplier to save Bloodlust/Heroism until Phase 3.

### **Nightbane**

**Disclaimer**: Bots are terrible at this encounter, in large part
because the map is awful (even before the recent Core changes). So the
strategies are not ideal because they need to operate within the bots’
limitations. I STRONGLY suggest you clear the entire Livery Stables
(including the upper level) because the mobs in them have a high risk of
pulling through the floor of the Master’s Terrace. Ideally, you should
clear out the Scullery too.

The strategy uses waypoints toward the Northeastern door to the Master’s
Terrace. I tried several different locations, and that worked best for
me based on where Nightbane lands (note that he has a different landing
spot for the encounter start vs. the start subsequent ground phases).

- Ground Phase, main tank: The main tank uses two waypoints after it
picks up the boss—the movement pattern should be kind of like a reverse
checkmark, where the tank moves back along the inner edge of the
terrace, then pivots and moves at a bit of an angle to the outer edge. I
did this as a way to get the tank to face Nightbane sideways across the
terrace, which is how he’s supposed to be tanked. The main tank will not
get out of Charred Earth. This is intended. The tank cannot move
dynamically enough to avoid it while also not turning the boss and
wiping the raid.
- Ground phase, ranged: Ranged bots rotate between three waypoints. They
start stacked at the same position. If Charred Earth is dropped on that
position, the bots will rotate to the second position. If Charred Earth
is dropped on that position, they will rotate to the third position. The
maximum number of Charred Earths that can be active is two, so if one is
dropped on the third position, the ranged bots should rotate back to the
first position.
- Ground Phase, other melee: Melee bots have no coded Charred Earth
avoidance strategy. They do decently enough with the general “avoid aoe”
strategy.
- Flight Phase: Bots move to Nightbane’s flight position—Nightbane is
bugged and cannot be attacked in the air in AC, but all bots still need
to stay near him or he will wipe the raid with Fireball Barrage. Bots
stack on the same position; when Rain of Bones is cast (one time,
snapshotted on the target’s location), all bots will move away to a
second nearby position. They will then kill the Restless Skeletons. The
Flight Phase lasts for 45 seconds, but Nightbane emotes after 35 seconds
and goes to land—during the final 10-second period, bots are freed from
all strategies and will follow the master. You need to lead them back to
the Northeastern part of the terrace before Nightbane lands so that bots
can get immediately positioned when he lands.
- Phase Changes: Whenever Nightbane lands, bots should pause DPS for the
main tank to get aggro.
- Managing bots/pets: During the Flight Phase, bots and pets have a
tendency to chase Nightbane outside of the boundaries of the map and
pull mobs from other parts of the instance as well as cause Restless
Skeletons to spawn out of bounds (and then further cause bots/pets to go
out of bounds to attack the skeletons). The strategy should solve for
this, but if there are still issues, it should be noted. My resolution
was to implement the following: (1) when Nightbane takes flight, bots
stop attacking and mark him with the moon icon, and Hunters and Warlocks
put pets on passive and recall them (they will put pets on defensive
again when Nightbane lands), and (2) all temporary pets are disabled for
the duration of the fight (Water Elementals, Shaman wolves, Druid
treants, etc.).

**Known Issues:**

- The approach to getting Nightbane to turn sideways is not always spot
on since it’s a workaround to account for what should be dynamic
movement. He can end up at a bit of an angle. I’ve tweaked the positions
such that if he is at an angle, it should not be a situation in which
any ranged position is exposed to Smoldering Breath or Cleave. That does
increase the risk that poor positioning may expose a ranged position to
Tail Sweep. This is obviously suboptimal, but that attack is much more
survivable.
- Ranged bots move between the three waypoints by reading the presence
of the Charred Earth aura on themselves. Sometimes, a bot may reach the
next waypoint with the Charred Earth aura from the previous waypoint
still on them (depending on timing of ticks), in which case the bot will
keep moving to the next waypoint. This can cause an issue in which the
ranged group gets split. So you could have Charred Earth dropped on
position 1, and some ranged bots will go to position 2 but others will
go to position 3, and then if the second Charred Earth is dropped on
position 3, the bots at position 3 will move back to position 1 (which
is still in Charred Earth) before then moving to position 2. Balancing
spacing between waypoints, positioning with respect to the boss, line of
sight, and maximum distance is very difficult to do. I messed with the
positioning a lot, and I am not sure if this possibility cannot be
entirely avoided without creating other issues. I have at least reached
a result in which I have not seen any bots cycle indefinitely (though if
testing observes this, it is obviously a problem).

Ultimately, I wouldn’t say I’m totally satisfied with the strategy.
Wipes are still possible on account of bad RNG. But I think it does make
the fight a lot more manageable, as it is a fight that is very difficult
with IP nerfs in the absence of any strategy. Previously, I needed to
bring 3 healers, and they would still all be out of mana by the first
Flight Phase and reliant on MP5 for the remainder of the fight, and I
would wipe multiple times before a kill.
2025-12-10 21:08:25 +01:00
Nicolas Lebacq
910b8a9c53 fix: Made bots roll in a more reasonable time on group loots. (#1857)
# Description

This PR changes the way loot rolls are being evaluated.

It puts a maximum priority on the loot action so it does not hang for so
long.
2025-12-09 10:29:57 -08:00
Tecc
bb569b4d39 Fix: Arena - PersonalRating and MMR issue for bot teams (#1789)
# Fix: Arena PersonalRating and MMR issue for bot teams

## Problem
Bot arena teams are created with artificial random ratings (1000-2000
range), but when bots join these teams, their personal ratings and
matchmaker ratings (MMR) use default config values instead of being
adjusted to match the team's artificial rating. This causes matchmaking
issues since the system uses personal ratings for queue calculations.

## Root Cause
The issue occurred because `SetRatingForAll()` was called during team
creation but only affected the captain. When additional bots were added
later via `AddMember()`, they received default values from
`CONFIG_ARENA_START_PERSONAL_RATING` and
`CONFIG_ARENA_START_MATCHMAKER_RATING` instead of values appropriate for
the team's artificial rating.

## Solution
After bots are added to arena teams, the fix:

1. Uses `SetRatingForAll()` to align all personal ratings with team
rating
2. Adjusts matchmaker ratings based on team context vs default
configuration
3. Saves changes to both database tables with proper data types

## Impact
- Personal ratings now match team ratings for artificial bot teams
- MMR values are adjusted for artificial bot team ratings instead of
using default config values
- Arena matchmaking functions correctly for bot teams with random
ratings
- Only affects new arena team assignments after deployment
- Existing player teams and normal config behavior are unaffected

## Manual Database Update

For existing installations, the provided SQL script could be used to fix
bot teams created before this patch.

### Update personal rating
```sql
UPDATE arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
SET atm.personalRating = at.rating
WHERE a.username LIKE 'rndbot%'
  AND atm.personalRating != at.rating;
```

### Update MMR for existing entries
```sql
UPDATE character_arena_stats cas
JOIN characters c ON cas.guid = c.guid
JOIN auth.account a ON c.account = a.id
JOIN arena_team_member atm ON cas.guid = atm.guid
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
SET
    cas.matchMakerRating = GREATEST(at.rating, 1500),  -- Use team rating or 1500 minimum
    cas.maxMMR = GREATEST(cas.maxMMR, cas.matchMakerRating)  -- Update maxMMR if needed
WHERE
    a.username LIKE '%rndbot%'
    AND (
        -- Update if MMR doesn't match team context
        (at.rating > 1500 AND cas.matchMakerRating < at.rating) OR
        (at.rating <= 1500 AND cas.matchMakerRating != 1500) OR
        cas.matchMakerRating IS NULL
    )
    AND (
        -- Map arena team type to character_arena_stats slot
        (at.type = 2 AND cas.slot = 0) OR  -- 2v2 teams use slot 0
        (at.type = 3 AND cas.slot = 1) OR  -- 3v3 teams use slot 1
        (at.type = 5 AND cas.slot = 2)     -- 5v5 teams use slot 2
    );
```

### Insert missing MMR records for bots without character_arena_stats
entries
```sql
INSERT INTO character_arena_stats (guid, slot, matchMakerRating, maxMMR)
SELECT
    atm.guid,
    CASE
        WHEN at.type = 2 THEN 0  -- 2v2 -> slot 0
        WHEN at.type = 3 THEN 1  -- 3v3 -> slot 1
        WHEN at.type = 5 THEN 2  -- 5v5 -> slot 2
        ELSE 0
    END as slot,
    GREATEST(at.rating, 1500) as matchMakerRating,
    GREATEST(at.rating, 1500) as maxMMR
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
WHERE
    a.username LIKE '%rndbot%'
    AND NOT EXISTS (
        SELECT 1 FROM character_arena_stats cas2
        WHERE cas2.guid = atm.guid
        AND cas2.slot = CASE
            WHEN at.type = 2 THEN 0
            WHEN at.type = 3 THEN 1
            WHEN at.type = 5 THEN 2
            ELSE 0
        END
    )
    AND at.rating > 0;
```

## Related issues

Fixes: #1787 
Fixes: #1800


## Verification Queries

### Query 1: Check personal rating alignment
```sql
SELECT
    'Personal Rating Check' as check_type,
    COUNT(*) as total_bot_members,
    SUM(CASE WHEN atm.personalRating = at.rating THEN 1 ELSE 0 END) as correct_ratings,
    SUM(CASE WHEN atm.personalRating != at.rating THEN 1 ELSE 0 END) as incorrect_ratings,
    ROUND(AVG(at.rating), 2) as avg_team_rating,
    ROUND(AVG(atm.personalRating), 2) as avg_personal_rating
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
WHERE
    a.username LIKE '%rndbot%';
```

### Query 2: Check MMR alignment
```sql
SELECT
    'MMR Alignment Check' as check_type,
    COUNT(*) as total_mmr_records,
    SUM(CASE
        WHEN at.rating > 1500 AND cas.matchMakerRating >= at.rating THEN 1
        WHEN at.rating <= 1500 AND cas.matchMakerRating = 1500 THEN 1
        ELSE 0
    END) as correct_mmr,
    SUM(CASE
        WHEN at.rating > 1500 AND cas.matchMakerRating < at.rating THEN 1
        WHEN at.rating <= 1500 AND cas.matchMakerRating != 1500 THEN 1
        ELSE 0
    END) as incorrect_mmr,
    ROUND(AVG(at.rating), 2) as avg_team_rating,
    ROUND(AVG(cas.matchMakerRating), 2) as avg_mmr,
    ROUND(AVG(cas.maxMMR), 2) as avg_max_mmr
FROM arena_team_member atm
JOIN arena_team at ON atm.arenaTeamId = at.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
JOIN character_arena_stats cas ON atm.guid = cas.guid
WHERE
    a.username LIKE '%rndbot%'
    AND (
        (at.type = 2 AND cas.slot = 0) OR
        (at.type = 3 AND cas.slot = 1) OR
        (at.type = 5 AND cas.slot = 2)
    );
```

### Query 3: Detailed team-by-team analysis
```sql
SELECT
    at.arenaTeamId,
    at.name as team_name,
    at.type as team_type,
    at.rating as team_rating,
    COUNT(atm.guid) as member_count,
    GROUP_CONCAT(DISTINCT atm.personalRating) as personal_ratings,
    GROUP_CONCAT(DISTINCT cas.matchMakerRating) as mmr_values,
    CASE
        WHEN COUNT(DISTINCT atm.personalRating) = 1 AND MIN(atm.personalRating) = at.rating THEN 'OK'
        ELSE 'MISMATCH'
    END as personal_rating_status,
    CASE
        WHEN COUNT(DISTINCT cas.matchMakerRating) = 1 AND (
            (at.rating > 1500 AND MIN(cas.matchMakerRating) >= at.rating) OR
            (at.rating <= 1500 AND MIN(cas.matchMakerRating) = 1500)
        ) THEN 'OK'
        ELSE 'MISMATCH'
    END as mmr_status
FROM arena_team at
JOIN arena_team_member atm ON at.arenaTeamId = atm.arenaTeamId
JOIN characters c ON atm.guid = c.guid
JOIN auth.account a ON c.account = a.id
LEFT JOIN character_arena_stats cas ON atm.guid = cas.guid
    AND cas.slot = CASE
        WHEN at.type = 2 THEN 0
        WHEN at.type = 3 THEN 1
        WHEN at.type = 5 THEN 2
        ELSE 0
    END
WHERE
    a.username LIKE '%rndbot%'
GROUP BY at.arenaTeamId, at.name, at.type, at.rating
ORDER BY at.rating DESC;
```
2025-12-08 12:35:06 +01:00
NoxMax
dde16674c3 Fix: Stop pets from fighting in PVP prohibited zones (#1829)
Stripped down version of #1818. No new features. Refactors
IsPossibleTarget in AttackersValue.cpp to a better style and makes sure
pets don't attack in prohibited zones.

Testing:
Confirmed that aggro pets no longer attack in PVP prohibited areas, but
still do outside them. Zim'Torga in Zul'Drak is a good example to test
this (ID 4323). Lookout for death knights with a Risen Ally
(uncontrolled and naturally aggro) now they respect PVP prohibition like
their master.

Note: If you manually teleport a bot that is in mid combat to a PVP
prohibited area, its aggro pet might still attack, because its master is
still in combat strategy. Otherwise the pet will not attack if its
master has switched to non-combat.
2025-12-08 12:34:16 +01:00
HennyWilly
e5b2791053 Improve Molten Core Strategy (#1852)
This is my first attempt of implementing playerbot strategies.

A team of 40 can steamroll Molten Core relatively easy, even with lower
item levels.
Regardless, this PR adds resistance triggers and actions to mitigate
some damage.
Additionally, improvements were made for the encounters with Garr, Baron
Geddon, Shazzrah and Golemagg.
A short summary per boss is listed below.
All planned features are included, but feedback is of course
appreciated.

### Lucifron
- Shadow resistance: mitigate damage from [Impending
Doom](https://www.wowhead.com/classic/spell=19702/impending-doom) and
[Shadow
Shock](https://www.wowhead.com/classic/spell=19460/shadow-shock).

### Magmadar
- Fire resistance: mitigate damage from [Lava
Bomb](https://www.wowhead.com/classic/spell=19411/lava-bomb) and [Magma
Spit](https://www.wowhead.com/classic/spell=19450/magma-spit).
- Like King Dred and the fraction commander (Nexus), this fight might
profit from an anti-fear strategy in order to counter
[Panic](https://www.wowhead.com/classic/spell=19408/panic). Not
implemented here.

### Gehennas
- Shadow resistance: mitigate damage from [Shadow
Bolt](https://www.wowhead.com/classic/spell=19728/shadow-bolt) and
increase the chance to resist [Gehennas'
Curse](https://www.wowhead.com/classic/spell=19716/gehennas-curse).

### Garr
- Fire resistance: mitigate damage from the Firesworn adds
([Immolate](https://www.wowhead.com/classic/spell=20294/immolate) and
[Eruption](https://www.wowhead.com/classic/spell=19497/eruption)).
- Disabled dps aoe abilities via multiplier. This one is important
because multiple exploding adds at once might delete bots rather
quick...

### Baron Geddon
- Refactored the existing strategy.
- Fire resistance: mitigate damage from [Ignite
Mana](https://www.wowhead.com/classic/spell=19659/ignite-mana),
[Inferno](https://www.wowhead.com/classic/spell=19695/inferno) and
[Living Bomb](https://www.wowhead.com/classic/spell=20475/living-bomb).
- Better Inferno handling: Before moving away, bots stop attacking and
interrupt their spells. Additionally, the new multiplier prevents bots
from running back to Geddon while Inferno is still active.

### Shazzrah
- Ranged bots now position themselves in a sweet spot that prevents them
from getting hit with [Arcane
Explosion](https://www.wowhead.com/classic/spell=19712/arcane-explosion)
but still close enough to dps and heal.

### Sulfuron Harbinger
- Fire resistance: mitigate damage from [Hand of
Ragnaros](https://www.wowhead.com/classic/spell=19780/hand-of-ragnaros)
and [Immolate](https://www.wowhead.com/classic/spell=20294/immolate). To
be fair, this one is quite negligible...

### Golemagg
- Fire resistance: mitigate damage from [Magma
Splash](https://www.wowhead.com/classic/spell=13880/magma-splash) and
[Pyroblast](https://www.wowhead.com/classic/spell=20228/pyroblast).
- Disabled dps aoe abilities via multiplier. Kind of a preference on my
side. Otherwise, the Core Ragers spam emotes about not wanting to die.

### Majordomo Executus
- Shadow resistance: mitigate damage from [Aegis of
Ragnaros](https://www.wowhead.com/classic/spell=20620/aegis-of-ragnaros),
[Shadow Shock](https://www.wowhead.com/classic/spell=20603/shadow-shock)
and [Shadow
Bolt](https://www.wowhead.com/classic/spell=21077/shadow-bolt). This one
is also negligible, TBF.

### Ragnaros
- Fire resistance: mitigate damage from [Wrath of
Ragnaros](https://www.wowhead.com/classic/spell=20566/wrath-of-ragnaros)
and [Lava
Burst](https://www.wowhead.com/classic/spell=21158/lava-burst).
2025-12-08 12:29:07 +01:00
Keleborn
353c29dfc4 Bug: Fix bots leaving LFG groups before master (#1876)
I removed bots checking if they should leave group every tick, and will
rely on the LeaveGroupFarAway action. I also increased the timer from 5
seconds to 20 seconds. No need to check this that often.
2025-12-08 12:25:40 +01:00
Keleborn
52c3e96641 Rename groupmaster to groupleader and related variables. (#1875)
Fix the naming conventions. 
Master should be reserved to identify a bots Master. 
groupleaders are not necessarily group masters and it should be clear
what the bot is looking for. (In most solo cases leader=master)
2025-12-03 13:25:01 +01:00
Crow
38e2d8584b Add missing break in ApplyInstanceStrategies (#1887)
Well, I hope nobody has tried Magtheridon lately. It looks like this got
inadvertently deleted during the merge.
2025-11-28 19:00:12 +01:00
Keleborn
d5dbc4ddd7 Hotfix: prevent server crash when whisper 'logout' (#1874)
Temp Hotfix to resolve #1870.
2025-11-24 21:49:55 +01:00
Keleborn
2424f73bc4 Core Merge PR - Replace OnPlayerChat with OnPlayerCanUseChat (#1838)
First stab at getting this working. Im not sure if Im missing something,
but it seemed to be a pretty simple change overall.

Based on testing the bots do respond to commands via whisper and group.

Edit: Relevant PR this addresses.

50f8f145d2 (diff-baadebd8cd1117ca48225f316a5ab3fd5fd55b20963394d302341147183db067)
2025-11-23 20:45:31 +01:00
Crow
cf743a186a Fix Wrong Misdirection Spell ID for Gruul's Lair and Magtheridon Strategies (#1867)
Lol oops.

Confirmed with logs/in-game that the prior one was wrong (and thus
always returning false) and current one is correct.
2025-11-23 10:06:19 +01:00
blinkysc
10213d8381 Add thread safety for group operations (#1816)
Fixes crashes and race conditions when bots perform group/guild/arena
operations by moving thread-unsafe code to world thread.

Potentially fixes #1124

## Changes

- Added operation queue system that runs in world thread
- Group operations (invite, remove, convert to raid, set leader) now
queued
- Arena formation refactored to use queue
- Guild operations changed to use packet queueing

## Testing

Set `MapUpdate.Threads` > 1 in worldserver.conf to enable multiple map
threads, then test:
- Group formation and disbanding
- Arena team formation
- Guild operations (invite, promote, demote, remove)

- Run with TSAN
cmake ../ \
  -DCMAKE_CXX_FLAGS="-fsanitize=thread -g -O1" \
  -DCMAKE_C_FLAGS="-fsanitize=thread -g -O1" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \
  -DCMAKE_INSTALL_PREFIX=/path/to/install \
  -DCMAKE_BUILD_TYPE=RelWithDebInfo

build

export
TSAN_OPTIONS="log_path=tsan_report:halt_on_error=0:second_deadlock_stack=1"
./worldserver

The crashes/race conditions should no longer occur with concurrent map
threads.

## New Files

- `PlayerbotOperation.h` - Base class defining the operation interface
(Execute, IsValid, GetPriority)
- `PlayerbotOperations.h` - Concrete implementations:
GroupInviteOperation, GroupRemoveMemberOperation,
GroupConvertToRaidOperation, GroupSetLeaderOperation,
ArenaGroupFormationOperation
- `PlayerbotWorldThreadProcessor.h/cpp` - Singleton processor with
mutex-protected queue, processes operations in WorldScript::OnUpdate
hook, handles batch processing and validation

---------

Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
Co-authored-by: SaW <swerkhoven@outlook.com>
Co-authored-by: bash <hermensb@gmail.com>
2025-11-21 21:55:55 +01:00
bashermens
d97870facd fix: warning updating movement flags while rooted (#1858)
fixes https://github.com/mod-playerbots/mod-playerbots/issues/1854
-----
Also includes fixes for:
-----
* Bots swimming with waterWalk kept switching between swimming and
walking, as result jittering effect swimming under water when water
walking active
* Bots flying close above water they would land on water and start
walking, now they stay flying unless on solid ground they will land and
start walking by design

-----
Moved all flag setting to updateMovementState:
* So all movement flag are handled in updateMovementState which also
contains the restricted movement logic.
* Handle restricted movement logic and preventing SendMovementFlagUpdate
while being restricted.


-----
Known issue when flying the following bots feel a bit jittering, wont
touch for now at least till core movement changes quirks has been dealt
with.

The current code is the extended version of what is originally was
before core merge with refactored movements. Once the core movement
refactors are settled a bit more i would like to revisit this code; as i
would expect more imperative code and less manual flag setting e.g.
bot->SetWaterWalking, SetGravitiy..SetCanFly etc.
2025-11-21 20:45:23 +01:00
Alex Dcnh
0c1700c117 CORE - Improved language detection for bots (#1784)
I've had this problem for a long time, my bots only speak English even
though I'm playing on a French client.
I suppose this must be the case for some other people who do not have a
large number of players with the same local client.
If we use French DBCs, the bots bug because they only recognize US DBCs.
From what I understand, the language is chosen as follows:
On load, the module reads the entire `ai_playerbot_texts` table and
stores each text variant in a dictionary indexed by the locale ID: the
`text` column remains the default value (English), and the `text_loc1`
to `text_loc8` columns fill slots 1 through 8.

Whenever a real player connects, the module increments a counter for
that player's DBC locale using
`AddLocalePriority(player->GetSession()->GetSessionDbcLocale())`.

When a bot needs a text, `GetLocalePriority()` returns the most
frequently used locale index among currently connected players. The
corresponding string is then retrieved. if the box is empty, we fall
back to the English version (text[0]).

### This PR improve language detection.
**Summary**

- log both the client DBC locale and the account database locale when a
player logs in
- fall back to the account locale when the client reports enUS but the
account is configured for another locale
- keep the existing vote-based selection so bots always speak the
majority language among connected players

**Therefore, the original behavior is maintained. Bots still choose the
most represented language among connected players (the counter is simply
more efficient by prioritizing the account's locale when it differs from
the client's English). For example, if more English-speaking players are
connected, the language will revert to English, as the bots always share
the majority locale.**
2025-11-21 15:56:03 +01:00
Alex Dcnh
0b1b0eaecc Core - Fix RTSC SeeSpellAction crash on malformed WorldPacket (#1841)
## Summary

This PR fixes the Crash 1 Source from Issue
[#1840](https://github.com/mod-playerbots/mod-playerbots/issues/1840)
posted in a @Regrad posted logs, in `SeeSpellAction::Execute` when an
RTSC "see spell" event arrives with an empty or malformed `WorldPacket`.
In that case the code used to read from the packet without any
validation, causing a `ByteBufferException` and a crash in the map
thread.

## Fix

- Reset the packet read position and check that the RTSC header
(castCount + spellId + castFlags) fits into the packet before reading.
- Wrap `SpellCastTargets::Read` in a `try { } catch (ByteBufferException
const&) { }` block so truncated RTSC payloads are handled gracefully.
- Check that `targets.GetDst()` is not `nullptr` before accessing its
position.

For valid RTSC packets the behavior is unchanged; malformed packets are
now safely ignored instead of crashing the server.

## Testing

- Sent bots to multiple locations using RTSC and verified they still
move as before.
- Reproduced the previous crash scenario with malformed RTSC packets:
the worldserver no longer crashes and the event is simply ignored.

---------

Co-authored-by: bash <hermensb@gmail.com>
Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com>
2025-11-21 13:18:14 +01:00
Gonzalo
8e03371147 Balance-Druid-improve-Starfall-usage-and-add-CC-safety (#1713)
- Move Starfall from default actions to AOE strategy only
- Require 2+ enemies for Starfall usage (prevents single-target casting)
- Add CC safety: avoid casting Starfall near current CC targets
- Prioritize Starfall over Hurricane in medium AOE situations
- Remove Starfall from pull/opener rotation to prevent early
single-target usage

This prevents Balance druids from wasting Starfall on single targets
and breaking crowd control effects in group content.
2025-11-19 21:00:59 +01:00
SaW
27311b734d Revert "feat: Improve bot mount behavior to faster close distance between bot and master" (#1855)
Reverts mod-playerbots/mod-playerbots#1760

This, as it is causing issues in BG, where bots just don't dismount and
just stand there instead.

<img width="2336" height="1374" alt="image"
src="https://github.com/user-attachments/assets/b61d7a77-1561-4f05-a438-edbb9321e113"
/>
2025-11-18 20:06:24 +01:00
SaW
bb5ed37cd3 Update codestyle_cpp.yml for review events and concurrency (#1836)
Include ready_for_review type, to run when draft is converted to ready.

Include concurrency; cancels obsolete tasks on new commits.
2025-11-18 18:48:52 +01:00
Jay
e88c1b779b Minor README.md corrections (#1849)
- Removed double "is" in acknowledgements
- Made the acknowledgements section more grammatical by using "based on"
instead of "based off"
- Corrected misspelling of "implemented"
- Standardized instances of "Playerbots" to `mod-playerbots` or properly
capitalized `Playerbots`
- Minor change of "for the continued contributions" to "for their
continued contributions" in the Acknowledgements section
2025-11-18 18:08:16 +01:00
Tecc
05057ae9b5 feat: Improve bot mount behavior to faster close distance between bot and master (#1760)
# Fix bot mount behavior when master dismounts

## Summary

Improves bot mount/dismount logic to ensure better coordination with the
master player. The bot now remains mounted when closing distance to a
recently dismounted master and mounts up to assist the master in combat.

The changes have been tested using the testcases described in the second
half of the PR description, which provide some directions on how this PR
can be tested and verified.

Closes: #1660

## Changes

- Add masterInCombat variable to track master combat state
- Modify target-based logic to consider master combat state
- Change mount priority when bot is not in combat to favor
master-following
- Remove combatReach from distance calculations (duplicate)

## Implementation

Added two methods:
- `StayMountedToCloseDistance()` - prevents premature dismounting when
the master dismounts
- `ShouldMountToCloseDistance()` - determines when to mount for master
assistance, even if master is not mounted at this time

Modified Execute() method:
- Target-based shouldMount/shouldDismount considers the master's combat
state
- Combat assistance logic separated from general following
- Mount when master in combat, but the bot is not

Distance logic:
- Combat: dismount at CalculateDismountDistance() (18-25 yards)
- Non-combat: dismount at 10 yards
- Mount threshold: 21+ yards

## Result
- Bots mount to assist masters in combat
- Bots stay mounted longer during travel
- Different dismount distances based on context
- Existing mount selection logic unchanged


# Mount Behavior Testing

## Prerequisites

1. Add a test bot: `.playerbots bot add <charactername>` or `.playerbots
bot addclass <class>`
2. Test in a level-appropriate outdoor area (mobs should not die with
one hit, as this makes testing combat assistance impossible)
3. Both master and bot must have mounts available

## Test 1: Combat Assistance Mounting

**Objective**: Verify bot mounts to assist the master in combat

**Detailed Steps**:
1. Position both master and bot dismounted
2. Command bot to stay: `/w <botname> stay`
3. Move master 35+ yards away from bot
4. Target a nearby mob and attack the mob
5. Command bot to assist: `/w <botname> follow`

**Expected Results**:
- Bot immediately mounts up (within 1-2 seconds cast time)
- Bot rides toward master while mounted
- Bot dismounts at ~18-25 yards from master/mob
- Bot engages in combat to assist

**Failure Indicators**:
- Bot runs dismounted (old behavior)
- Bot never mounts despite distance
- Bot mounts but doesn't dismount at proper assist range

## Test 2: Non-Combat Dismount Distance

**Objective**: Verify bot stays mounted longer during peaceful travel

**Detailed Steps**:
1. Both master and bot start dismounted
2. Mount up: use any mount
3. Verify bot also mounts: `/w <botname> follow`
4. Travel together for 10+ seconds to establish following
5. While moving, dismount master but continue running
6. Observe bot behavior as you move away

**Expected Results**:
- Bot stays mounted while master runs dismounted
- Bot only dismounts when within ~10 yards of master

## Test 3: Target Interference Prevention

**Objective**: Verify target selection doesn't prevent mounting when
master needs help

**Detailed Steps**:
1. Position bot 35+ yards from master: `/w <botname> stay` then move
away
2. Find a mob visible to both master and bot
3. Target the mob (do not attack): click on mob to select it
4. Verify bot can see the target: `/w <botname> attack` (bot should
respond about target)
5. Cancel bot attack: `/w <botname> follow`
6. Now attack the mob yourself (master enters combat)
7. Observe bot behavior immediately after master engages

**Expected Results**:
- Bot mounts up despite having the same target selected
- Bot rides toward combat area while mounted
- Bot dismounts at assist range (~18-25 yards)
- Target selection does not prevent proper mount behavior

**Failure Indicators**:
- Bot runs dismounted toward target (target interference)
- Bot doesn't mount because it's "focused on target"

## Test 4: Basic Mount Following

**Objective**: Verify fundamental mount matching still works

**Detailed Steps**:
1. Start both master and bot dismounted
2. Ensure bot is following: `/w <botname> follow`
3. Mount up on any available mount
4. Wait 2-3 seconds and observe bot
5. Test different mount speeds if available (60%, 100%, 280% speed)
6. Test dismounting and remounting

**Expected Results**:
- Bot mounts within 2-3 seconds of master mounting
- Bot uses appropriate mount speed to match master
- Bot dismounts when master dismounts (basic following)
- No regression in basic mount following behavior

## Test 5: Distance-Based Mounting Threshold

**Objective**: Verify bot mounts at efficient distance threshold (21+
yards)

**Detailed Steps**:
1. Both master and bot start dismounted in safe area (no mobs)
2. Command bot to stay: `/w <botname> stay`
3. Record starting position: `.gps`
4. Walk exactly 15 yards away (short distance)
5. Command bot to follow: `/w <botname> follow`
6. Observe: bot should run dismounted (distance too short)
7. Command bot to stay again: `/w <botname> stay`
8. Walk to 25+ yards away from bot
9. Record new position: `.gps`
10. Command bot to follow: `/w <botname> follow`

**Expected Results**:
- At 15 yards: Bot runs dismounted (inefficient to mount)
- At 25+ yards: Bot mounts up immediately (efficient distance)
- Distance threshold should be ~21 yards based on mount cast time
efficiency

**Distance Calculation**: Use coordinates from `.gps` to verify exact
distances

## Test 6: Druid Form Integration (Druid Master Required)

**Objective**: Verify druid form compatibility with mount system

**Detailed Steps**:
1. Master must be druid with travel form available
2. Start both dismounted: `/w <botname> follow`
3. Shift master to travel form
4. Observe bot response (should mount or shift if druid)
5. Travel together for 10+ seconds
6. Shift master to normal form while moving
7. Observe bot dismount behavior
8. If available, test flight form in appropriate zones

**Expected Results**:
- Druid bot: matches master's form (travel form)
- Non-druid bot: uses equivalent mount speed
- Form changes trigger appropriate bot responses
- Speed matching works between forms and mounts

## Test 7: Battleground Mount Behavior

**Objective**: Verify mount behavior works in PvP environments

**Detailed Steps**:
1. Queue for battleground with bot in party
2. Enter battleground together
3. Test basic mount following in BG
4. Test flag-carrying restrictions (WSG/EotS)
5. Test mounting during BG combat scenarios

**Expected Results**:
- Bot mounts appropriately in battlegrounds
- Flag carrying prevents mounting
- Combat assistance mounting still works in PvP
2025-11-16 22:49:12 +01:00
bashermens
610a032379 Bots fly/follow (movePoint core refactor), water walking fixes (#1825)
Closer the original solution, i dont wanna drift to much away without
really good reason. At this point i still see NPC and bots moving
through the levels or even falling out the levels here and there. I
verified whether the MovePoint signature changes and related params
itself in playerbots has anything todo with it, even when params are
hardcoded the behavior remains. It could be deeper problem, but for now
it seems core problem. Therefore i dont wanna change to much until the
dust has settled a bit in core itself.

I havent implemented moveTakeOff or moveLand which are basically
responsible for the transitions phases between ground and air visa
versa. I have version where i use takeOff for the animation, which means
moving vertical. For now i am gonna leave for what it is.

PS: also includes additional movement fix for AreaTriggerAction which we
missed first time after the core update on movements.

@Wishmaster117 Been testing and trying a lot in the background on find
solutions and causes. The general solutions remains removed some tweaks,
altered code here and there. With the general idea to keep closer to the
original code for now at least.

Testing:
- Class abilities: Slow fall (priest), Flight Form (druid) : Green
- BG: Green
- Swimming and walking shallow waters: Green
- Takeoff and land when following master: Green
- Boat and zeppelins: Green
- Flymount and ground walking: Green
- Water Walking (shaman), Path of Frost (DK): Green
- Water Walking (shaman), Path of Frost (DK) transisions; flying,
swimming, water walking: Green


Skipping pets when group water walking, path of frost, once core pathing
changes has settled more i will add it. Which is easy but i dont wanna
add more edge cases and code branches for now.
2025-11-16 22:39:46 +01:00
Jay
ce2a990495 README.md edits (#1779)
I've noticed some sprawl in the README.md. The following edits have been
made around the goal of clarity, brevity, and emphases when needed:

- Removed dead link to the Spanish README.md at the top. The
README_ES.md file itself is not removed from the repo, but is however
out of date and does not seem useful at this time
- Removed the addons section. This seems to be covered by the Wiki in
the Documentation section unless I'm mistaken? We can add these back if
needed
- Reorganized installation instructions to emphasize the importance of
using the Playerbots branch of AzerothCore for all installations
- Moved the platform support from the Frequently Asked Questions section
to the installation instructions
- Modified punctuation of bulleted list. These can return if it irks
someone, but it seems easier to read without them
- Added a encouragement to star the project for updates and gain more
visibility
- Removed the Frequently Asked Questions section. I encourage this to be
covered in a wiki page but it can return if deemed necessary. I think a
lot of it can be moved to other sections if we really need them in there
- Added a "Contributing" section that replaces the Coding Standards
section
- Coding Standards are included in the Contributing section
- Added a second link to the Discord server in the Contributing section
- Removed a link to the main Playerbots branch from the introduction
section. This is covered and emphasized in the installation section
instead
- Migrated the encouragements to make bug reports from the introduction,
along with the message that this is still in active development, to the
contributing section
- Reduced redundant language when not necessary (e.g. "As noted above,")
- Updated language around custom branch and require branch for
uniformity. This will make it more clear to the users about the subject
- Removed "Classic" from the "Classic Installation" terminology for
being inaccurate. The subject is now known simply as "Cloning the
Repositories" while "Docker Installation" still remains distinct. This
will make what was formerly known as "Classic Installation" as de facto
default
- Appended the "Documentation" section with add-on information
- Unified all instances of AddOn to the Blizzard spelling without a
hyphen following the WoW 3.3.5a client spelling
- Appended "AzerothCore" to instances of "branch" to ensure readers they
are, in fact, installing AzerothCore here from our required branch
2025-11-15 22:00:19 +01:00
Alex Dcnh
6effabfa42 Core - Fix Bots can pickup the flag in Eye of the Strom instantly, without cast time from the spawn point (#1704)
# Eye of the Storm Flag Capture Behavior
 
## Previous Behavior
- Bots used to interact with the Netherstorm flag the moment they
reached interaction range, leading to instant pickups even before they
were correctly positioned.
- They did not respect the requirement to stand within the capture ring
or dismount before channeling, so the interaction finished immediately
without the intended delay.
 
## Current Behavior
- Bots now verify they are inside the Eye of the Storm capture circle
and will reposition toward the center flag or base flag until they are
within 2.5 yards of the game object before starting the channel.
- Once inside the circle they dismount, drop shapeshift forms, and come
to a full stop before beginning the channel cast.
- When channeling the center flag capture spell, bots keep checking for
the ongoing `SPELL_CAPTURE_BANNER` cast and wait for it to finish
instead of attempting repeated instant interactions.
- They will be interrupted if they receice any damage
 
These adjustments align the Eye of the Storm flow with the retail
mechanics and prevent bots from taking the flag instantly when it
spawns.

Fixes #1700.
2025-11-15 18:19:16 +01:00
Nicolas Lebacq
cadbcbd447 fix: Resolved an issue where the open spell was being cast by bots on despawned game objects. (#1842)
# Description

This addresses the infamous "Possible hacking attempt" error when bots
were furiously trying to interact with despawned game objects.
The problem was that the game objects were not being as spawned when
evaluating what the bot should do. It was only done when spells were
being cast on entities.
2025-11-15 10:27:33 +01:00
Crow
08c739f918 Align index with section update (#1831)
Update to index to reflect change to section title
2025-11-14 15:17:43 +01:00
privatecore
0729d14787 Fix PlayerbotAI constructors' members order and wrong type comparison (uint32 -> int32) (#1763)
Fix warnings: -Wsign-compare and -Wsign-compare.

I would also like to mention that there are issues with the following
methods:

`IsMainTank` -- in the last loop, it returns the result of comparing the
first alive tank it finds with the current bot, which seems odd.
`IsBotMainTank` -- it is used in only two places in the code (Yogg-Saron
strategy) and has a completely different logic for checking. In the last
loop, it only checks for a suitable tank-bot with a lower index (if it's
not the bot itself). Additionally, there is a logic issue in the loop:
if none of the conditions are met after the first iteration, the method
returns `false`.

Can someone from the maintainers take a look at this section of the code
for possible errors and logic issues?
2025-11-13 00:51:24 +01:00
nick
a37dd2b9ae Clarify random bot timing configuration section and parameter descriptions (#1826)
This update reorganizes and rewrites the random bot timing configuration
section for clarity and accuracy. The previous section was mislabeled as
"INTERVALS" and lacked precise descriptions. The new version:

1. Renames the header to RANDOM BOT TIMING AND BEHAVIOR
2. Adds concise, standardized comments for each parameter
3. Corrects misleading terminology (not all values are intervals)
4. Documents defaults and actual behavior clearly for easier tuning and
maintenance
5. No functional code changes — documentation and readability only.

Note, this is derived information from reading the code.
Please double check if I have captured each param accurately!
2025-11-11 09:21:13 +01:00
SaW
9c8ba42c64 FIX: Battlegrounds - Unset bot's master when current master left BG (#1819)
Adds a check for if current master left the BG and group, if so release
set master and carry on in BG.

This prevents multiple bots in (potentially multiple different) BG's to
still consider you as their master when you yourself have left.

- Applies when you join BGs with a party of bots.
2025-11-11 09:10:09 +01:00
kadeshar
e5bc495dbe Fixed codestyle fix (#1815)
Fix for this changes
<img width="1055" height="193" alt="obraz"
src="https://github.com/user-attachments/assets/a3d64f5b-80b0-4150-9760-0bcd4847f5d2"
/>
2025-11-07 17:49:33 +01:00
kadeshar
85c7009fe1 Codestyle fix (#1797)
Warning:
Dont change this PR as draft to make it testable


DONT REVIEW UNTIL Codestyle C++ workflow dont pass
2025-11-05 21:10:17 +01:00
Keleborn
ce51191e8f Fix. Leave group actions (#1774)
Fix for issue #1768 where the bot master was not getting reset. 
I also cleaned up leave group action to focus up function scope.
I moved the resets from #612 to the actual leaving function.

Disclosure: LLMs were NOT used in authoring this PR.

Test cases to consider for testers.
1. Master disbands group with random bots. Bots should go about their
business.
2. If you can dual box, create a raid where two real player both invite
random bots. One player leaves group, their bots should also leave.
(edge case, if a random bot that is supposed to leave the group becomes
leader they may disband the whole group.
2025-11-05 15:38:14 +01:00
Crow
d02d61e690 Implement Magtheridon strategy (#1721)
I'm marking this as a draft for now because I haven't done a detailed
review of the code, but I'm posting it now in case anybody wants to give
it a try.

Here's what the strategy (should) do.

### **Channeler Phase**

While you can probably AoE down all five Hellfire Channelers, that's
more dicey with IP nerfs and it's no fun, so the strategy takes what
would have still been considered an aggressive approach back in the day
by (1) assigning the Main Tank to the first Channeler, (2) having
Hunters misdirect two more Channelers to the MT, and (3) one Off Tank
picking up each of the fourth and fifth Channelers and dragging them out
of Shadow Volley Range from the main group. Sometimes the pull gets a
little wonky and one of the OTs might end up with one of the Channelers
that was intended for the MT, but it should work out in the end.

DPS will move through Channelers from Square -> Star -> Circle ->
Diamond -> Triangle. Once Square, Star, and Circle are down, the MT will
go sit by Magtheridon and wait for him to become active instead of
helping with the last two Channelers. I could have made the MT help with
the fourth Channeler too, but it's not needed, and positioning to pick
up Magtheridon after the third Channeler is a failsafe for low DPS
groups that aren't able to get four down before Magtheridon breaks free.

The top priority for Warlocks is to banish/fear the Burning Abyssals,
and they will continue to do so even after Magtheridon becomes active
(you are not supposed to kill the Abyssals; they have a gazillion HP and
automatically despawn after a minute). Their next priority is to put
Curse of Tongues on the Channelers.

### **Magtheridon Positioning**

The MT will pick up Magtheridon and pull him (moving backwards because
Magtheridon kind of hits like a bus) to a position up against the far
wall. Ranged DPS will spread out from a point roughly in the center of
the room, and Healers will spread out from a point that is a little
closer to Magtheridon. I have not built in aoe avoidance (except for
cube clickers, see below) because the general avoid aoe strategy seems
to work fine for this fight.

### **Clicking Manticron Cubes**

Now, the fun part. Bots will be assigned to clicking cubes by standard
group selection order (reverse join order), but assignment is done via
two passes. The first pass will look to select five ranged DPS bots that
are _not_ Warlocks. This is because Warlocks may still be needed to keep
Abyssals banished/feared and because Warlocks of all three specs put out
by far the most damage of all ranged DPS at level 70 in pre-raid/T4
gear. If there are not five non-Warlock ranged DPS bots available, then
the logic goes to the second pass, which can pick any bot that is not a
Tank.

Cube clicking works on a timer:

1. 48 seconds after Magtheridon breaks free, assigned cube clickers move
near their cubes (but a few yards outside of interact distance). During
this time, they should move around still to avoid Debris (by maintaining
distance from the triggering NPC) and Conflagration (by maintaining
distance from the triggering gameobject). Blast Nova is on a 54.35s to
55.4s timer, and I found 48s to always be ample time to get to the
cubes, but YMMV so this is a good thing to test. Going to a cube too
early not only takes away DPS but also risks more hazards appearing
on/around the cube that will then cause problems when the cube needs to
be clicked.
2. Blast Nova is a 2s cast, followed by a 10s channel (if not
interrupted by the cubes). As soon as the cast begins, bots will move
into interaction range and click the cube. Well, there is a randomized
delay between 200ms (about the fastest possible human reaction time to
visual stimuli) and 1500ms. It didn’t happen to me in a few runs, but it
may be possible that the delay causes the raid to eat one tick of Blast
Nova (I’m not sure if the first blast comes as soon as the channel
starts). Again, another good thing to test, but also one tick is not
going to kill anybody, and it’s arguably good to introduce some degree
of imperfection.
3. Once Blast Nova stops channeling (i.e., all five cubes have been
clicked and channeled simultaneously), bots will interrupt their cube
clicking and go back to regularly scheduled activities. Again, I’ve
introduced a randomized delay, this time between 200ms and 3000ms. Note
that bots can easily be perfect at this—if I don’t do the randomized
delay, they click and let go so fast that you can barely even see the
beams appear. It’s so atrocious for immersion that I consider the lack
of any randomization to be totally unacceptable for this strategy.
4. 48s after Blast Nova, bots will go back to their near-cube positions,
rinse and repeat.

If an assigned cube clicker dies, another bot should be immediately
assigned. All bots in the raid track the same timer so the new bot
should step into the prior bot’s shoes. Of course, if Blast Nova is
about to go off and a clicker dies next to a cube, you’re probably
wiping because I didn’t assign backups to stand in place. That’s too
much of a dad guild-level strategy even for me.

And that’s about it. Figuring out the cubes was a tremendous pain in the
ass, but I’ve really enjoyed the learning process.

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-11-05 14:53:16 +01:00
avirar
80dbd22ba1 Fixes equip bug with random suffix rings (#1757)
* Check item score of rings/trinkets to determine the correct slot to equip

* Early return, removed unecessary if statements, single line statements

Simplify logic for equipping items by reducing nested conditions.
2025-11-04 23:27:50 +01:00
privatecore
26a135a1ec Rewrite RandomPlayerbotFactory for improved maintainability and future updates (#1758)
* Rewrite RandomPlayerbotFactory - rewrite constructor and utility methods, simplify checks and logic

* Update the comment to clarify the original logic for race selection

* Remove magic numbers from CombineRaceAndGender method (gender)

* Add checks for races and classes disabled during random bot creation
2025-11-04 23:25:59 +01:00
Crow
983a55da86 Implement Gruul's Lair strategy (#1647)
* Implement Gruul's Lair strategy

* minor non-gameplay tweaks to code

* Use multiplier for tank assist

* HKM warlock & Gruul tank tweaks

* Fixed declarations

* rewrote HKM + minor Gruul tweaks

* Update PlayerbotAI.cpp

* modernize code and address comments

* clean ups to tank logic

* Oops.

* Remove post-move delay

For actions like positioning bosses, the standard post-movement delay should be overridden IMO since a player would be sequencing their actions in this type of situation

* Update RaidGruulsLairActions.cpp

* Replace break statements with return true

* enum class to enum

* moved all isuseful checks to triggers

* Split multipliers and improved banish logic

* Update for comments

* changes to int

* use helpers for marking icons

* address compile errors

* correct MoveTo and use RTI helper

* address comments and rename actions/triggers

* adjust alignment of location constants

* fix some crappy returns

* allow return true when changing targets

* change indents and move enums inside namespace

* style changes, trim trailing whitespaces, address comments
2025-11-04 23:01:30 +01:00
kadeshar
e35900f9d0 Merge pull request #1699 from IainD92/RandomBotGuildTotals_fix
Random bot guild count / generation fix
2025-11-04 20:50:02 +01:00
kadeshar
d9f0d5a555 Merge pull request #1808 from Raz0r1337/typo_fix
Update 2025_10_27_00_ai_playerbot_german_texts.sql
2025-11-04 20:19:44 +01:00
St0ny
7d5c9e3ee0 Update 2025_10_27_00_ai_playerbot_german_texts.sql
line error fixed
2025-11-04 14:48:24 +01:00
Revision
43164e74e1 Normalize line endings for 2025_10_27_00_ai_playerbot_german_texts.sql to LF (#1795) 2025-11-02 13:28:26 +01:00
privatecore
f7fea456ca Fix PositionInfo constructors' members order (#1776) 2025-11-02 13:27:25 +01:00
Revision
12a5132c33 Merge pull request #1793 from kadeshar/codestyle-azerothcore
Codestyle cpp azerothcore
2025-11-01 21:15:31 +01:00
kadeshar
bbbf71d40c - added missing var for codestyle azerothcore 2025-11-01 17:38:11 +01:00
kadeshar
586c4d9d05 - Added codestyle azerothcore python script 2025-11-01 17:27:26 +01:00
kadeshar
cb099bcaf4 Update repository condition for C++ job 2025-11-01 17:25:07 +01:00
St0ny
5d3e64800f important bugfix for the last PR #1675 (#1782)
* Update 2025_10_27_00_ai_playerbot_german_texts.sql

Fix bug that adds holes in the german text_loc3 collumn
2025-10-29 17:39:06 +01:00
St0ny
235f0249b2 insert of deDE localized chatter texts into text_loc3 (#1675)
* insert of german chatter texts into text_loc3

restore of original file ai_playerbot_texts.sql
2025-10-28 21:56:42 +01:00
bash
7237b154e0 Added intelliJ project folder on exclusion list (#1764) 2025-10-25 00:54:27 +02:00
NoxMax
c3fd97b6c0 Fix: Prevent addClass bots from getting realm firsts (#1745)
* Random/Addclass bots cannot get first achievements

* Use firsts achievement flags
2025-10-23 20:10:35 +02:00
Alex Dcnh
286213eb8b Fix Playerbots won't fly #1753 (#1761) 2025-10-23 01:33:22 +02:00
Iain Donnelly
1d19dea974 Update RandomPlayerbotFactory.cpp 2025-10-22 23:53:42 +01:00
SaW
6e1c9114df FIX: part of logic in picking quest reward (#1752)
* FIX: part of logic in picking reward

Avoids dereferencing begin() after confirming the set is empty.

* Update TalkToQuestGiverAction.cpp
2025-10-21 10:36:10 +02:00
bash
7e810f8174 Update README.md (#1750) 2025-10-20 21:46:51 +02:00
bash
e0df6558f5 Added shaman default combat strategy names on top of custom (#1739) 2025-10-20 18:20:46 +02:00
bash
50ac6e5b95 core_merge_changes (#1747) 2025-10-20 14:12:14 +02:00
Alex Dcnh
2c5185a7cb Update StatsWeightCalculator.cpp (#1744) 2025-10-20 13:17:59 +02:00
Crow
f874d2c79e Fix typo in server loading message (#1742) 2025-10-20 01:05:45 +02:00
SaW
5a4acbe36c Update PvpValues.cpp (#1746)
Set uninitialized variable
Remove std::move
2025-10-20 01:04:11 +02:00
SaW
e693b208be FIX: ICC - default return position for BQL (#1737) 2025-10-18 22:54:52 +02:00
bash
10ce94e065 Removal space lel (#1740) 2025-10-18 22:32:43 +02:00
kikiviki
553b8276eb Heirloom quality auto-equip calculation implemented (#1732)
* Heirloom quality auto-equip calculation implemented
2025-10-18 22:17:44 +02:00
Crow
f791ab61c4 Update server loading message 2025-10-16 22:43:49 +02:00
Crow
3260ca1429 Cleanups to config and source (#1720)
* general edits

* Clarify comment for bot teleportation map IDs
2025-10-14 15:43:18 +02:00
Yunfan Li
e1fa733aa5 Preparation for project transfer (#1733) 2025-10-14 00:11:54 +08:00
kadeshar
525eceb5a2 Merge pull request #1728 from Wishmaster117/Fix-Opcode-dispatch,-trusts-every-queued-packet-to-have-a-handler
Fix Opcode dispatch, trusts every queued packet to have a handler
2025-10-11 13:22:51 +02:00
kadeshar
bd13d6be80 Merge pull request #1727 from Wishmaster117/Prevent-Crash-if-sTaxiPathStore.LookupEntry-return-nullptr
Prevent Crash if sTaxiPathStore.LookupEntry return nullptr
2025-10-11 13:21:51 +02:00
Wishmaster117
d0ac9452f4 Fix Opcode dispatch, trusts every queued packet to have a handler 2025-10-11 11:26:09 +02:00
Wishmaster117
8a30d10617 Prevent Crash if sTaxiPathStore.LookupEntry return nullptr 2025-10-11 10:57:54 +02:00
kadeshar
5a0c27637e Merge pull request #1708 from hermensbas/feature/removeFromGroup_replaced_with_worldpackets
[fix crash] Crash on removeFromGroup
2025-10-10 19:59:21 +02:00
kadeshar
cea1e90f57 Merge pull request #1714 from avirar/fix/remove-auras-before-teleport
[fix crash] several crashes
2025-10-10 19:43:34 +02:00
kadeshar
1fb66e9d75 - Fixed issues in ai_playerbot_texts table scripts (#1723) 2025-10-09 23:45:58 +02:00
bash
31ed5cbb65 fixes 2025-10-09 20:52:32 +02:00
kadeshar
5b128b3300 - Update method in QueryItemUsageForEquip (#1701) 2025-10-09 08:02:44 +02:00
Iain Donnelly
3f050a4a77 Change LOG_INFO to LOG_DEBUG 2025-10-07 22:46:19 +01:00
avirar
5681f29060 Merge branch 'liyunfan1223:master' into fix/remove-auras-before-teleport 2025-10-07 11:13:57 +11:00
bash
cf4f0f6dc7 renamed function name 2025-10-06 21:07:43 +02:00
Revision
e00c8fca2a Updated the spell id for Spirit of Redemption (#1709) 2025-10-06 19:57:37 +02:00
root
c90b155a70 fix: Replace static m_botReleaseTimes with per-bot storage to prevent race condition
Fixes a thread safety issue where multiple bots dying in battlegrounds
simultaneously would corrupt the shared static unordered_map, causing
segmentation faults.

Changes:
- Remove: static m_botReleaseTimes map from AutoReleaseSpiritAction
- Add: bgReleaseAttemptTime member to PlayerbotAI (per-bot storage)
- Update: All references to use per-bot storage instead of static map

Why this fixes the crash:
- Each PlayerbotAI instance is accessed by only one map update thread
- No cross-thread access to shared data structures
- No mutex/locking required - thread-safe by design
- Automatic cleanup when bot is destroyed

Thread-safe solution: Per-bot state eliminates race conditions without
performance overhead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-06 11:22:45 +11:00
bash
780f6d60e0 fix build errors 2025-10-05 23:49:13 +02:00
bash
1faf20f567 removeFromGroup replaced with worldpackets 2025-10-05 20:05:56 +02:00
root
d26c2a3549 fix: Clean visibility references before bot teleport to prevent crash
Add PLAYERHOOK_ON_BEFORE_TELEPORT to proactively clean visibility
references when a bot teleports between maps. This prevents a race
condition where:
1. Bot A teleports and its visible objects start getting cleaned up
2. Bot B is simultaneously updating visibility and tries to access
   objects in Bot A's old visibility map
3. Those objects may already be freed, causing a segmentation fault
   at GridNotifiers.cpp:65 in IsWorldObjectOutOfSightRange()

The fix only affects bots to avoid changing behavior for real players.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 16:21:09 +11:00
Iain Donnelly
0e4c759e7f Wishmaster update
Optimised, better use of DB Query, avoids reallocations. Nice.
2025-10-04 12:56:06 +01:00
Iain Donnelly
24f841f728 Verbose logging.
Should now be able to see guild count values as the server inits
2025-10-04 12:56:06 +01:00
Iain Donnelly
444be2994e v2
Neatened some things up, removed obsolete code, added a break out of the loop if an empty guild name (none available) is returned from the playerbots_guild_names table.
2025-10-04 12:56:06 +01:00
Iain Donnelly
8a68de4476 Update RandomPlayerbotFactory.cpp
Fixed a typo
2025-10-04 12:56:06 +01:00
Iain Donnelly
7d50ceef3d Update RandomPlayerbotFactory.cpp
Added a query to count the number of guilds straight from the DB, then filter out player guilds. (instead of relying on accessing guilds from a list of random bots and adding them up)

This needs some formatting / tidying once I make sure we are counting guilds properly.
2025-10-04 12:56:06 +01:00
avirar
21ea3a7226 Merge branch 'liyunfan1223:master' into fix/remove-auras-before-teleport 2025-10-04 09:42:21 +10:00
bash
377ac199a7 Revert "Feat: Filter bot logins by level range" (#1705) 2025-10-03 22:58:30 +02:00
IainD92
2a340ce68f Update playerbots.conf.dist (#1698)
Removed comments from the end of lines (results in bad value)
2025-10-03 14:19:17 +02:00
IainD92
f2b5580495 Maintenance config for altbots (#1693)
* Maintenance config controls

bools in config

* Update TrainerAction.cpp

removed some note-to-selfs (personal config preferences)

* Set default to true

Also tidied up some comments

* Update playerbots.conf.dist

* Reorganised

Changed the description in conf to be less conversational.

Rearranged the order that options are arranged, grouping by what made sense to me (the type of gameplay made easier/skipped by the option being enabled).

Rearranged the order the variables and method calls are listed in the code to match the order they are presented in the conf to make future maintenance of maintenance (:P) more intuitive.

* Update playerbots.conf.dist

Revert previous commit (change to call order in MaintenanceAction::Execute)

conf settings grouped
2025-10-03 11:56:16 +02:00
root
387c491265 fix(Playerbots): Remove auras before teleporting to prevent crash
Add RemoveAurasWithInterruptFlags call before all TeleportTo operations
to prevent race condition crash in battlegrounds.

The crash occurs when area auras (like "Entering Battleground") are
queued for removal in Aura::UpdateTargetMap's targetsToRemove list,
but the unit is deleted before the 500ms update cycle completes,
causing SIGSEGV when accessing the dangling pointer.

This fix removes auras with AURA_INTERRUPT_FLAG_TELEPORTED and
AURA_INTERRUPT_FLAG_CHANGE_MAP before teleporting, matching the
behavior in Player::TeleportTo for real players.

Affected locations:
- BattleGround join/teleport
- Spirit healer/graveyard teleport
- Corpse resurrection teleport
- Meeting stone teleport
- Master follow teleport
- RPG unstuck teleport
- Random bot teleport
- Chat command teleport

Raid-specific teleports excluded as they require separate testing.
2025-10-03 15:58:36 +10:00
kadeshar
ffa8c6d94a Merge pull request #1623 from brighton-chi/karazhan
Implement Karazhan strategy
2025-10-03 06:30:39 +02:00
Alex Dcnh
24e69229e3 Fix: Shaman bots stuck spamming “Call of the Elements” / “set … totem” (totem rank detection & trigger loop) (#1659)
* Fix Issue #1648

Fix low level randombot shamans (~34) don't heal in dungeongroups, seems new "resto" strategy is broken #1648

* Final working fix, tested with all 3 shaman class specs.

* Update asked by review

* requested review changes

* Minor corrections of the cpp file

* boyscouting

Lets try and leave the code cleaner behind as we find it when we can.

* Oupsie fix ;)

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-10-02 20:47:09 +02:00
kadeshar
c503199422 Merge pull request #1663 from gacuna89/patch-3
Fix: Prevent priests in Spirit of Redemption form from using mana
2025-10-02 20:26:10 +02:00
bash
31b19aabc7 Minor correction, scope was to big. 2025-10-02 01:28:30 +02:00
kadeshar
aea58414b0 - Changed item usage value on new method (#1692) 2025-10-02 00:10:19 +02:00
bash
06e45300e1 Added const 2025-10-01 23:45:28 +02:00
kadeshar
1ea17e593a Merge pull request #1499 from NoxMax/login-range
Feat: Filter bot logins by level range
2025-10-01 17:42:39 +02:00
NoxMax
25726f54b1 Update RandomPlayerbotMgr.cpp 2025-10-01 13:16:00 +08:00
privatecore
7dac49cf9c Fix wrong prepared statement used for the PlayerbotDbStore::Reset issue #1172 (#1688) 2025-09-30 22:13:36 +02:00
Revision
a5120c0a7c Fix spacing issue and removed unnecessary spaces (#1684)
* Fix spacing issue and removed unnecessary spaces

* Added spaces where suggested
2025-09-30 15:25:53 +02:00
bash
0cc15411c1 license update (#1674) 2025-09-30 15:19:44 +02:00
avirar
01915ffa35 Added null check to Queue::findHighestRelevanceBasket() (#1686) 2025-09-30 15:17:45 +02:00
crow
2e1507b794 Various corrections 2025-09-30 00:19:35 -05:00
kadeshar
a19604024e Merge pull request #1662 from gacuna89/patch-2
Fix: Prevent bots from eating and drinking while mounted
2025-09-29 21:46:07 +02:00
crow
55b58a2ef6 Simplify checks & implement getbottext method 2025-09-28 21:28:54 -05:00
bash
972e2604ce Update NonCombatActions.cpp
This is better, even though aura check is tiny more expensive then most. Placing them into isUseful() is kinda confusing.
2025-09-29 00:14:28 +02:00
bash
aaa9e1a42c Placed cheap checks in isPossible() more expensive in isUseful()
combat en mounted are both HasUnitFlag checks, which are cheap calls to make.
2025-09-29 00:06:56 +02:00
crow
0afeca4300 Merge remote-tracking branch 'upstream/master' into karazhan 2025-09-28 17:06:55 -05:00
bash
df77668b4b Review 2025-09-28 20:37:01 +02:00
kadeshar
c3eecc0d7c Merge pull request #1676 from brighton-chi/spec-tab-names
Fix SPEC_TAB names
2025-09-28 20:08:39 +02:00
kadeshar
7ff56dfd07 Merge pull request #1673 from privatecore/fix-pull-power-spark
Fix wrong cast spell action passed to the PullPowerSparkAction constructor
2025-09-28 20:07:32 +02:00
kadeshar
873767db7c Merge pull request #1670 from icemansparks/fix-mount-state-logic
FIX: Allow following master's mount state regardless of group leader
2025-09-28 20:06:53 +02:00
kadeshar
03a56405f5 Merge pull request #1681 from IainD92/GuildSizeConfig
Config option to set max number of guild members in random bot guilds
2025-09-28 19:45:29 +02:00
Iain Donnelly
6f79193d7a Config option to set max number of guild members in random bot guilds 2025-09-28 14:04:09 +01:00
privatecore
b0f3de65f5 Fix warning: delete called on non-final that has virtual functions but non-virtual destructor (#1671) 2025-09-28 13:53:11 +02:00
privatecore
30bd58be67 Fix wrong PlayerbotAI parameter name passed to the constructor (#1672) 2025-09-28 13:42:03 +02:00
kadeshar
c20fb34470 - Added method to get translation bot text or default (#1678) 2025-09-28 13:39:59 +02:00
privatecore
34ce17fb3a Fix wrong cast spell action passed to the PullPowerSparkAction constructor 2025-09-28 09:13:36 +02:00
crow
e525c22d85 Fix SPEC_TAB names 2025-09-27 23:11:47 -05:00
crow
ec8e9db1ed Merge remote-tracking branch 'upstream/master' into karazhan 2025-09-27 23:03:18 -05:00
crow
59555b2248 Moved yells to database 2025-09-27 23:03:15 -05:00
bash
c8f32569a8 Merge branch 'master' into login-range 2025-09-28 00:27:05 +02:00
avirar
23d9931f65 Resolved crash in BGStatusAction (#1656)
* Prevent race condition and server crash

* Updated other direct packet handling lines to queue packets instead
2025-09-28 00:01:24 +02:00
bash
ec4ab34f94 Update NonCombatActions.cpp
Expensive checks last.
2025-09-27 23:31:16 +02:00
bash
62e2ca247a formatting 2025-09-27 23:28:48 +02:00
bash
d9b57fcfd4 Updated the locations of the checks, also added the checks as additional gatekeeper of the execute. 2025-09-27 23:26:29 +02:00
kadeshar
05d3a44481 Merge pull request #1651 from Tierisch/shaman_fix
Fix SetTotemAction - add `isUseful` and get array size instead of pointer size
2025-09-27 23:08:18 +02:00
bash
662e7f1b0b Update ShamanActions.cpp 2025-09-27 23:00:24 +02:00
bash
e042e3b12b Added shapeshift 2025-09-27 22:55:52 +02:00
Tecc
b9dbfe9646 fix: Allow following master's mount state regardless of group leader in CheckMountStateAction 2025-09-27 22:50:14 +02:00
bash
3228667121 Merge branch 'master' into patch-2 2025-09-27 22:40:39 +02:00
kadeshar
0547ce5cf7 - Code optimalizations 2025-09-27 20:57:10 +02:00
Crow
f5a6194808 Fixed some comments in the config (#1668) 2025-09-27 20:31:06 +02:00
Gonzalo
f23b2ea2f6 fix: Optimize DrinkAction to check mana in isUseful instead of Execute (#1666)
* fix: Optimize DrinkAction to check mana in isUseful instead of Execute

- Move HasManaValue check from Execute to isUseful for better performance
- Prevents non-mana classes from executing drink actions unnecessarily
- Improves AI efficiency by early filtering in isUseful method

Addresses reviewer feedback about HasManaValue usage optimization

* Update NonCombatActions.cpp

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-09-27 13:06:16 +02:00
crow
bdfd45c9a0 Merge remote-tracking branch 'upstream/master' into karazhan 2025-09-27 03:22:50 -05:00
crow
e8954f6cba resolve some comments + more
Formatting edits and improved Malchezaar
2025-09-27 03:22:28 -05:00
Gonzalo
dff4934dd1 Fix: Prevent priests in Spirit of Redemption form from using mana
This change modifies the `HasManaValue::Calculate()` function in `src/strategy/values/StatsValues.cpp`.

Previously, priests in Spirit of Redemption form (angel form when dead) could still attempt to use mana-restoring actions like drinking water, which is illogical since they cannot use mana in that form.

This fix adds a check for the Spirit of Redemption aura (spell ID 20711) to prevent mana usage:
- If the target has the Spirit of Redemption aura, the function returns false
- This prevents priests in angel form from attempting to drink water or use other mana-restoring actions
- Improves bot behavior realism by respecting the limitations of the Spirit of Redemption form

This change works in conjunction with the previous fix that prevents non-mana-using classes from attempting to restore mana, creating a more comprehensive solution for mana management.
2025-09-25 18:35:18 -03:00
Gonzalo
f26c4e99f6 Fix: Prevent bots from eating and drinking while mounted
This change modifies the `DrinkAction::Execute()` and `EatAction::Execute()` functions in `src/strategy/actions/NonCombatActions.cpp`.

Previously, playerbots could attempt to eat food or drink water while mounted, which is not possible in the game and creates unrealistic behavior.

This fix adds mount state checks to both actions:
1. `DrinkAction::Execute()` now checks `bot->IsMounted()` and returns false if mounted
2. `EatAction::Execute()` now checks `bot->IsMounted()` and returns false if mounted

This prevents bots from attempting to consume food or drinks while mounted, improving bot behavior realism and preventing unnecessary action attempts that would fail anyway.
2025-09-25 18:26:28 -03:00
Alex Dcnh
fc69dd5ddd FIX Random Bot Guilds not initialising random emblem, colors, etc #1636 (#1650)
* FIX Random Bot Guilds not initialising random emblem, colors, etc #1636

FIX Random Bot Guilds not initialising random emblem, colors, etc #1636

* Update RandomPlayerbotFactory.cpp

* Add sql patch an remove FixEmptyGuildEmblems() function
2025-09-25 21:42:34 +02:00
kadeshar
01f0b71a17 - Code refactoring 2025-09-25 20:31:42 +02:00
Spargel
f35e39e19c Removing unnecessary variable 2025-09-22 03:00:29 -05:00
Spargel
ca0dafd67b Fix SetTotemAction
Add isUseful and check array size instead of pointer size
2025-09-21 21:21:09 -05:00
crow
22d1cc9d57 BBW and other edits 2025-09-21 15:42:55 -05:00
Alex Dcnh
0b74820f80 Create 2025_09_18_00_ai_playerbot_french_texts.sql (#1641)
* Create 2025_09_18_00_ai_playerbot_french_texts.sql

* Update 2025_09_18_00_ai_playerbot_french_texts.sql

1:1 translation

* Update 2025_09_18_00_ai_playerbot_french_texts.sql
2025-09-21 20:32:01 +02:00
kadeshar
c9fb2e35cb - Possible loop fix for Call of the Elements (#1649) 2025-09-21 20:31:16 +02:00
kadeshar
7f9eb9f1a0 Merge pull request #1643 from noisiver/tabs-to-spaces
Replaced tabs with spaces
2025-09-20 11:16:15 +02:00
Revision
fcb956ec1b Removed unnecessary spaces 2025-09-19 22:43:50 +02:00
kadeshar
90518a59ae Merge pull request #1644 from Wishmaster117/Fix-for-2025_09_17_00_paladin_buff_reagent_texts
Fix for 2025_09_17_00_paladin_buff_reagent_texts
2025-09-19 22:32:21 +02:00
Wishmaster117
5c1e9576b7 Fix for 2025_09_17_00_paladin_buff_reagent_texts 2025-09-19 22:03:33 +02:00
Revision
ace813516d Replaced tabs with spaces 2025-09-19 21:00:09 +02:00
Alex Dcnh
e175eb1178 Paladin buff logic: Sanctuary+Kings synergy, role-aware targeting, safer Greater buffs (#1603)
* Paladin buff logic: Sanctuary+Kings synergy, role-aware targeting, safer Greater buffs

* Update PaladinActions.cpp

* Update PaladinActions.cpp

* All configs should be implement into PlayerbotAIConfig and sPlayerbotAIConfig used in code

* added: prayer of fortitude

* Magic number removed

* Update PaladinActions.cpp

* Update PaladinActions.cpp

* Update PaladinActions.cpp

* Update PaladinActions.cpp

* Update PaladinActions.cpp

* Add patch for solo paladin in group

* Correction review

* Update PaladinActions.cpp

* Add harcoded text to DB
2025-09-18 19:52:26 +02:00
privatecore
17f50f780d Update the Playerbots localized texts to include the Russian language (#1638) 2025-09-18 19:46:23 +02:00
kadeshar
4f69b7bb48 Merge pull request #1601 from brighton-chi/more-chat-filters
Add chat filters for aura, aggro, and spec and make all filters case insensitive
2025-09-17 22:46:45 +02:00
kadeshar
782bbc9d0e Merge pull request #1639 from brighton-chi/fix-debug-typo
Fix typo in RandomPlayerbotMgr.cpp
2025-09-17 18:53:18 +02:00
Crow
0362db94ad Exclude Log.h
Not needed
2025-09-17 11:09:59 -05:00
Crow
a41abb54cb Corrected inaccuracy in config comment 2025-09-16 14:51:23 -05:00
crow
8586796cec Fix typo in RandomPlayerbotMgr.cpp 2025-09-16 13:54:02 -05:00
Spargel
907f1aff61 Refactor GossipHelloAction with overload (#1628)
* Add overload for GossipHelloAction

* Refactor GossipHelloAction
2025-09-16 20:20:48 +02:00
ThePenguinMan96
b388657bf6 Shaman Overhaul (#1566)
* Shaman Overhaul

Hello everyone,

I bring to you the Shaman Overhaul. This was the most fun project I've had so far.
Here is a simplified list of the changes this brings:

1. Added Call of the Elements - making the shaman able to set 4 totems down simultaneously! This saves them multiple global cooldowns in combat.

2. Totems are now selected based on their combat strategies. These strategies set totems on the Call of the Elements bar, as well as change what totem is summoned if a single totem is missing. NOTE: Only one strategy of each elemental type (earth, fire, water, air) can be active at a time.
Earth - strength of earth, stoneskin, tremor, earthbind
Fire - searing, magma, flametongue, wrath, frost resistance
Water - healing stream, mana spring, cleansing, fire resistance
Air - wrath of air, windfury, nature resistance, grounding

There are a few exceptions to totems without strategies: Stoneclaw Totem, Fire Elemental Totem, and Mana Tide Totem. These each have triggers that fire under certain conditions:
Stoneclaw Totem: Resto/Ele shaman has low health and isn't in a group
Fire Elemental Totem: Boost trigger for Ele/Enhance
Mana Tide Totem: Resto Shaman medium mana

3. Added Totemic Recall - a spell that picks up the totems outside of combat to regain 25% of the mana spent. Useful to avoid patrols.

4. Changed the config slightly - Enhance uses Fire Nova Glyph for crazy AoE damage, and enhance pve spec uses both clearcasting and improved shock talent now.

5. Enhancement Shamans will use their Spirit Wolves' Spirit Walk ability - this helps them close the gap and improves their DPS on fights that require movement.

6. Boost Strategy - Moved Bloodlust/Heroism/Fire Elemental Totem from the generic strategy triggers to a new Boost strategy. Now you can control their uses with Boost! (co +boost or co -boost. Enabled by default.)

7. AoE Strategy - Unified both of the AoE strategies for Ele/Enhance to "aoe", rather than "caster aoe" and "melee aoe". NOTE: Healers will still aoe under "healer dps". (co +aoe or co -aoe. Enabled by default.)

8. Moved the weapon imbue strategies from combat to non-combat. I noticed that they were only casting their weapon imbues during combat - this fixes that.

9. Added logic for only using Lava Burst on targets with Flame Shock active, ensuring that it's as close to 100% crit chance as possible. I did notice on a sample size of 112 lava bursts in testing that it still fails to crit around 3 percent of the time - that is because the lava burst starts to cast while flame shock is on the target, but by the time the projectile lands, flame shock has worn off.

10. Added Earth Shock as an execute ability for elemental shamans only. This helps their DPS tremendously at low levels, as well as in PVP. There is logic in place to prevent the use of Earth Shock as elemental entirely on bosses (it's garbage on bosses), as it will only be used as an execute if the target has less than 1500 hp and it's at 25% hp or less.

10. Added chain lightning as an AoE option for enhancement shamans while maelstrom weapon is at 4 or 5 stacks. This continues to push the AoE dps on enhance higher!

Here is a file-by-file list of the changes:

conf\playerbots.conf.dist - Enhancement shamans use Fire Nova Glyph as early as level 15, they also use the clearcasting talent in elemental and improved shocks in enhance. It really helps their mana usage. Also swapped the position of two glyphs in resto.

src\AiFactory.cpp - Set the default spec that new altbot shamans start as to Elemental. Put Arcane, Fire, and Frost comments on the mage specs. Set the strategies of the shaman specs to "ele, resto, and enh", as well as added the default totem strategies for each spec. Also added in the aoe strategy. Removed bmana/bdps, as those were set for lightning/mana sheld - those have been moved to both the non-combat strategy, as well as to each spec combat strategy. Enhancement will use Lightning Shield, and Elemental and Resto will use Water Shield both in and out of combat.

src\strategy\shaman\CasterShamanStrategy.cpp/CasterShamanStrategy.h - Renamed to Elemental.

src\strategy\shaman\ElementalShamanStrategy.cpp/ElementalShamanStrategy.h - Renamed from CasterShamanStrategy, most logic is the same. Moved the totem of wrath passthrough to the GenericShamanStrategy. Moved the Weapon Imbue to the NonCombatShamanStrategy. Moved the AoE spells to GenericShamanStrategy, under the AoE strategy there. Added the use of Stoneclaw totem, as well as Earth Shock Execute. Changed the use of Thunderstorm from medium mana to high mana, so it can be used more often in longer fights. Moved the individual casting of totems to the totem strategies. Added the use of Call of the Elements.

src\strategy\shaman\EnhancementShamanStrategy.cpp/EnhancementShamanStrategy.h - Renamed from MeleeShamanStrategy. Moved the totem passthroughs to GenericShamanStrategy. Refined the priorities in the default actions and triggers to closer match guides online. Moved the weapon imbues to the non-combat strategy. Moved the individual casting of totems to the totem strategies. Moved the AoE spells to the AoE strategy in GenericShamanStrategy. Added the use of Call of the Elements (while in melee range) and Spirit Walk.

src\strategy\shaman\GenericShamanStrategy.cpp/GenericShamanStrategy.h - Moved weapon imbue passthroughs to non-combat. Set up all totem passthroughs here so lower level shamans could function well. Set up a boost strategy for heroism/bloodlust/fire elemental totem. Set up an AoE strategy for Elemental/Enhancement. Cleaned up tablature of the code. Moved the Healer DPS strategy to the RestoShamanStrategy. Added a medium mana trigger so they can use more mana potions in longer fights.

src\strategy\shaman\HealShamanStrategy.cpp/HealShamanStrategy.h - Renamed to RestoShamanStrategy.

src\strategy\shaman\MeleeShamanStrategy.cpp/MeleeShamanStrategy.h - Renamed to EnhancementShamanStrategy.

src\strategy\shaman\RestoShamanStrategy.cpp/RestoShamanStrategy.h - Renamed from  HealShamanStrategy. Moved weapon imbue to non-combat strategy. Moved the individual casting of totems to the totem strategies (except mana tide totem). Added in Healer DPS from genericshamanstrategy. Added in use of Stoneclaw totem and Call of the Elements.

src\strategy\shaman\ShamanActions.cpp/ShamanActions.h - Organized the actions by type. Removed the TTL check in the totem action and Flame Shock. Added logic in the Stoneclaw totem to only be used when not in a group. Added logic on LavaBurst Action to only be used when the target has flame shock debuff from the caster. Added custom logic for casting Spirit Walk (no code exists in AC/Playerbots to make a guardian cast a spell). Added in the "SetXTotemAction"s, which set a totem to the highest rank of the spell in the totem bar.

src\strategy\shaman\ShamanAiObjectContext.cpp/ShamanAiObjectContext.h - Cleaned up strategies, triggers, and actions as a whole. Renamed melee, heal, and caster to ele, enh, and resto. Changed the "totems" strategy to individualized elemental totem strategies.

src\strategy\shaman\ShamanNonCombatStrategy.cpp/ShamanNonCombatStrategy.cpp - Moved weapon imbues here. Cleaned up tablature. Added the Totemic Recall spell.

src\strategy\shaman\ShamanTriggers.cpp/ShamanTriggers.h - Removed the commented out section. Added triggers + logic for:
EarthShockExecute
Call of the Elements
Totemic Recall
"SetXTotemTrigger"
Spirit Walk
Elemental Mastery
No Earth/Fire/Water/Air Totem

src\strategy\shaman\TotemsShamanStrategy.h - Master hub for all defined constants and arrays used in other files, as well as names all of the totem strategy types.

src\strategy\shaman\TotemsShamanStrategy.cpp - Each strategy has a "set x totem" to change the bar totem, as well as a "no x totem" trigger with a corresponding "cast x totem". NOTE: some totems aren't learned by level 30 when a shaman learns call of the elements, so I had to set an alternative for those (Totem of Wrath, Wrath of Air, Cleansing Totem, and Windfury). Testing showed me that this is necessary - without this, the trigger would just fire over and over, and the shaman would recast the same totem over and over.

Scope of Testing:

8/5/25 (2 hours): Began work on the Shaman class. Cleaned up actions and triggers.
8/6/25 (3.5 hours): Tried what felt like everything to get the totem bar changed. Seems impossible to change a client-side thing with cpp.

8/9/25(3 hours): Initial totem strategies created. Through the help of Revision, I was able make a functioning action that would change the totem bar's spell. The spells are stored in the database! I was able to get a strategy of each type working, and Call of the Elements was casting the correct totems.

8/10/25 (3.5 hours): Testing on Noth the Plaguebringer. The elemental shaman's dps was low - I noticed that the shaman was casting lava burst regardless of if the target had flame shock. I fixed this. I also noticed that the enhancement shaman was casting call of the elements at max range, resulting in the magma totem never doing damage. Changed so enhance would only cast  Call of the Elements in melee range. Also, had to add "cast x totem" spells to each strategy, so magma totem would recast once expired (as well as other totems).

8/11/25 - (2.5 hours)Did a full run of Naxxramas. Enhancement did crazy dps for the gear it had. Elemental was consistently placing between 14-18th place of 18 dps. I noticed that totemic recall was messing up with the KT encounter, and had to look in the AC repo for logic to check if a boss encounter is active. Fixed it, and shamans weren't spamming totemic recall between packs. I wonder what I can do to fix elemental?

8/12/25 (1.5 hours) - Added a check in lava burst's isuseful to ensure flame shock was on the target. DPS went up a little bit. Also, made chain lightning the highest spell priority - DPS went up quite a bit after that. It is quite costly, but it is worth it - even on one target, but especially 3. Added spirit walk for enhancement.

8/13/25 - (5 hours) Tested on level 1-10 level, 15, 25, 35, 45, and 55 instances: I had a ton of bugs at level 35. Essentially, the shaman was standing there casting the totems it didn't know (wrath, cleansing, wrath of air). I had to add some checks and passthroughs for it to run smoothly again. Elemental was still doing just okay dps at lower levels, while enhancement was CRUSHING it. The fact that enhancement has the fire glyph from 15 on now is crazy.

8/14/2025 (2 hours) - Tested in 65/75 instances, as well as little bit of ulduar. I am pretty happy with the state of shamans now, they are consistently performing highly (enhance top 5, elemental top 8 in ulduar). Tweaked elemental mastery to cause lava burst to be instant cast, not chain lightning. This improved the DPS of elemental shamans right out of the gate.

Total testing - 23 hours

If y'all have any questions or comments, please let me know either here or on discord!

* Fixed the Bracket so the code will compile

Fixed the Bracket so the code will compile

* - Code refactoring

* - Non windows compilation error fixes

---------

Co-authored-by: kadeshar <kadeshar@gmail.com>
2025-09-16 20:09:00 +02:00
St0ny
2c383339d8 Update playerbots.conf.dist (#1635)
* Update playerbots.conf.dist

Added Half-Burried Bottle to the list of dissallowed GO's

* Update PlayerbotAIConfig.cpp

Added Half-Burried Bottle to the list of disallowed GO's in the standard values of the source to complete this PR
2025-09-15 20:26:36 +02:00
kadeshar
0eb7cdba64 - Fixed not applying xprate when bot is in group with real player (#1629) 2025-09-14 10:52:15 +02:00
kadeshar
21de08baab Fixed cross faction guilds not allowed by config (#1631)
* - Fixed cross faction guilds not allowed by config

* - Fixed doubled method bug

* - Restored deleted method
2025-09-14 10:51:18 +02:00
kadeshar
20025b7dfa - Added quests ending death knight questline to by default enable LFG for them (#1633) 2025-09-14 10:48:47 +02:00
crow
e60876a1cb Update Aran, Netherspite, Prince 2025-09-12 08:46:17 -05:00
crow
6d5717234a Merge remote-tracking branch 'upstream/master' into karazhan 2025-09-12 08:41:10 -05:00
kadeshar
92f1dbd3f1 Merge pull request #1626 from liyunfan1223/opcode-crash
Sell item opcode crash fix
2025-09-11 15:04:25 +02:00
Yunfan Li
f5f4f32799 Sell item packet opcode 2025-09-11 20:43:57 +08:00
Yunfan Li
11b96b51b7 Core update item packets (#1624) 2025-09-11 13:43:13 +08:00
crow
311bf32da5 Update Shadow Nova action
Fix error with isUseful check that caused bots not to run away from Shadow Nova when no Infernals were spawned + some tightening of code to avoid Shadow Nova
2025-09-10 01:05:56 -05:00
crow
fe9791e1ec Revert edit not intended for PR 2025-09-09 22:45:20 -05:00
Revision
19399c6f57 Implement Karazhan strategy 2025-09-09 21:23:27 -05:00
kadeshar
a41c1912ac Merge pull request #1611 from brighton-chi/fix-expansion-limits-for-enchants
Fix thresholds for LimitEnchantExpansion
2025-09-06 17:33:23 +02:00
Vanna White
21d8f32d24 Fix bots in Oculus not using Drake Mount (#1613)
* Fix bots sometimes not using drake mount

* change bot check

---------

Co-authored-by: wetbrownsauce <you@example.com>
2025-09-06 16:14:52 +02:00
zeb139
bf56154eee Add weighted bot to banker teleport logic and config (#1615)
* add weighted bot to banker teleport logic and config

* moved banker location lookup tables to top of file
2025-09-06 16:10:56 +02:00
kadeshar
e46269920a - Fixed update sql script name (#1619) 2025-09-05 21:38:26 +02:00
crow
3717c6133e clean-ups and fixes
simplified code
fixed bug where neutral creatures were not captured by the aggroby filter
trim white spaces so space between filter and message is permitted but not required
2025-09-05 12:27:58 -05:00
crow
1881ef1fe0 fix thresholds for LimitEnchantExpansion
And disallow Naxx40 shoulder enchants
2025-09-05 10:21:20 -05:00
crow
876455baca Merge remote-tracking branch 'upstream/master' into more-chat-filters 2025-09-02 17:10:42 -05:00
kadeshar
3c442a6b71 - Excluded additional Legendary Arcane Amalgamation from obtainable for bot enchantments (#1600) 2025-09-02 19:24:55 +02:00
kadeshar
750d557e6a Merge pull request #1604 from kadeshar/koralon-strategy
Added automatic resistance switch on Emalon and Koralon
2025-09-02 17:02:59 +02:00
kadeshar
8057a5d7ac Merge pull request #1593 from kadeshar/rtsc-despawn-bugfix
Fixed bug with RTSC despawning objects
2025-09-02 17:01:46 +02:00
Spargel
c218dbe653 Fix uses of restrictHealerDPS and randomBotCombatStrategies. (#1570) 2025-09-01 19:05:07 +02:00
kadeshar
6588ca5878 - Added automatic resistance switch on Emalon and Koralon 2025-08-30 15:25:47 +02:00
crow
f5aa484e8d Merge remote-tracking branch 'upstream/master' into more-chat-filters 2025-08-28 13:54:22 -05:00
crow
fbf8ed9256 Add chat filters for aura, aggro, and spec and make all filters case insensitive 2025-08-28 13:53:49 -05:00
kadeshar
df6b1490b1 - Fixed world sql scripts naming 2025-08-28 18:41:08 +02:00
kadeshar
179c34e3a9 Food cheat fixes (#1594)
* - Fixed bug with InitFood and food cheat
- Fixed food cheat description in config

* - Fixed bug with initself command
2025-08-28 18:25:13 +02:00
kadeshar
45d046f427 - Fixed bug where bot looting gameobject which is on respawn time (#1596) 2025-08-28 18:24:40 +02:00
kadeshar
31f2c6a20d - Fixed sql structure 2025-08-27 19:09:51 +02:00
kadeshar
37458f0dc5 -Fixed module sql structure 2025-08-27 19:04:01 +02:00
NoxMax
02343edc46 Merge branch 'master' into login-range 2025-08-26 22:50:29 -06:00
kadeshar
bc737ecc68 - Changed standalone config on cheat (#1585)
- Changed drink condition
2025-08-26 18:28:42 +02:00
Crow
704e02e9cc Add SMV area IDs to PvP Prohibited Areas (#1589)
* Add SMV area IDs to PvP Prohibited Areas

Sanctum of the Stars and the Altar of Sha’tar

* Update source default pvpProhibitedAreaIDs

* Update source default pvpProhibitedAreaIDs

Now with Sanctum of the Stars & Altar of Sha'tar as well
2025-08-26 18:27:57 +02:00
NoxMax
5f00b9bbd5 Crash fix for RPG weights 0 (#1590) 2025-08-26 18:25:19 +02:00
kadeshar
5469333465 - Fixed bug with RTSC despawning objects 2025-08-26 15:39:34 +02:00
Revision
2dad8bf01d Fixed a compiler warning (#1586) 2025-08-24 18:18:00 +02:00
kadeshar
78116fe37e Merge pull request #1510 from brighton-chi/bot-chat-filters
increase flexibility of multiple bot chatfiltering
2025-08-21 21:58:17 +02:00
NoxMax
5f8754123d Better error handling when no eligible bots for level login range 2025-08-21 07:30:52 -06:00
bash
c9b4cfa184 [Revert] Threading leftover which belonged to other related PRs's (once green needs be merged) (#1583)
* Revert "Correct side effects of  merge f5ef5bd1c2 (#1512)"

This reverts commit 966bf1d6af.

* Revert "Fix ACCESS_VIOLATION in mod-playerbots: purge stale AIs, add thread-safety, and harden HasRealPlayerMaster (#1507)"

This reverts commit f5ef5bd1c2.
2025-08-20 20:13:45 +02:00
HennyWilly
957a60cd1d Ignore GameObject IDs from vanilla dungeons (#1518)
* DisallowedGameObjects for vanilla dungeons

* Bots ignore trap crates in Stratholme -> Remove

* Updated AiPlayerbot.DisallowedGameObjects default value in source code

* Removed duplicate

* Added 123329

* Added 123329 and removed duplicated

* Update playerbots.conf.dist

153464 - no reason to ignore it

* 153464 - no reason to ignore it

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-08-20 18:24:00 +02:00
mtm84
b661264c53 Update HunterActions.cpp (#1576)
Removes compiler warning
2025-08-18 12:05:53 +02:00
kadeshar
77c2354c3f Yogg-Saron strategy (#1565)
* - wip

* - Added Yogg-Saron strategy

* - Added Yogg-Saron sanity strategy

* - WIP

* - WIP

* - WIP

* - WIP

* - Added Yogg-Saron strategy

* - code refactoring

* - Code fix after pr
2025-08-18 12:02:19 +02:00
NoxMax
0c3a799aae Merge branch 'liyunfan1223:master' into login-range 2025-08-18 03:48:11 -06:00
bash
b369b1f9ae MoveToTravelTargetAction prevent delay when in combat (#1558) 2025-08-18 02:38:06 +02:00
Vigerus
fa7b863035 NamedObjectContext improvement, remove unneccessary pass by copy, allow lambda ObjectCreators (#1561)
Co-authored-by: Viger <viger28@gmail.com>
2025-08-17 15:42:26 +02:00
bash
4f5f7d286e nullptr fix (#1555) 2025-08-16 15:29:44 +02:00
bash
6cb9f56c4e nullptr fix (#1557)
* nullptr fix

* Update PlayerbotFactory.cpp
2025-08-16 15:29:09 +02:00
Crow
3e0f23536d Update README.md (#1567) 2025-08-16 12:04:00 +02:00
kadeshar
12065a6ad5 Merge pull request #1486 from ThePenguinMan96/Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands)
Tame Chat Action / Pet Chat Action (stances/commands)
2025-08-16 10:27:17 +02:00
bash
8d51092d42 As requested revert for threadfixes last few days (#1552)
* Revert "[Large server fix] #1537 Serialize playerBots/botLoading with a mutex and use snapshot-based loops to fix concurrency crashes (#1540)"

This reverts commit 3fff58df1a.

* Revert "[Fix] teleport to invalid map or invalid coordinates (x , y , z  200000, o ) given when teleporting player (g UI d full type player low , name , map , x , y , z , o )  (#1538)"

This reverts commit ca2e2ef0db.

* Revert "Fix: prevent MoveSplineInitArgs::Validate velocity asserts (velocity > 0.01f) for bots, pets, and charmed units (#1534)"

This reverts commit 4e3ac609bd.

* Revert "[Fix issue #1527] : startup crash in tank target selection — add TOCTOU & null-safety guards (#1532)"

This reverts commit c6b0424c29.

* Revert "[Fix issue #1528] Close small window where the “in a BG/arena” state can change between the check (InBattleground() / InArena()) and grabbing the pointer (GetBattleground()), which leads to a null dereference. (#1530)"

This reverts commit 2e0a161623.

* Revert "Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks (#1529)"

This reverts commit e4ea8e2694.

* Revert "Dont wait to travel when in combat. (#1524)"

This reverts commit ddfa919154.

* Revert "nullptr fix (#1523)"

This reverts commit 380312ffd2.

* Revert "Playerbots/LFG: fix false not eligible & dungeon 0/type 0, add clear diagnostics (#1521)"

This reverts commit 872e417613.

* Revert "nullptr exception (#1520)"

This reverts commit 3d28a81508.

* Revert "Removed bot freezing at startup and system message, not relevant anymore (#1519)"

This reverts commit bcd6f5bc06.
2025-08-12 22:10:47 +02:00
NoxMax
f37ef41523 Clarify user conf 2025-08-12 10:23:44 -06:00
Alex Dcnh
3fff58df1a [Large server fix] #1537 Serialize playerBots/botLoading with a mutex and use snapshot-based loops to fix concurrency crashes (#1540)
* MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full

* Update BotMovementUtils.h

* Playerbots: guard against invalid-Z teleports

* Update PlayerbotMgr.cpp
2025-08-12 08:15:22 +02:00
Alex Dcnh
ca2e2ef0db [Fix] teleport to invalid map or invalid coordinates (x , y , z 200000, o ) given when teleporting player (g UI d full type player low , name , map , x , y , z , o ) (#1538)
* MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full

* Update BotMovementUtils.h

* Playerbots: guard against invalid-Z teleports
2025-08-12 01:54:17 +02:00
Alex Dcnh
4e3ac609bd Fix: prevent MoveSplineInitArgs::Validate velocity asserts (velocity > 0.01f) for bots, pets, and charmed units (#1534)
* MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full

* Update BotMovementUtils.h
2025-08-12 01:53:48 +02:00
Alex Dcnh
c6b0424c29 [Fix issue #1527] : startup crash in tank target selection — add TOCTOU & null-safety guards (#1532)
* Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks

* Fix Issue 1528

* Fix Issue #1527
2025-08-11 17:00:31 +02:00
Alex Dcnh
2e0a161623 [Fix issue #1528] Close small window where the “in a BG/arena” state can change between the check (InBattleground() / InArena()) and grabbing the pointer (GetBattleground()), which leads to a null dereference. (#1530)
* Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks

* Fix Issue 1528
2025-08-11 16:27:38 +02:00
Alex Dcnh
e4ea8e2694 Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks (#1529) 2025-08-11 16:27:25 +02:00
bash
ddfa919154 Dont wait to travel when in combat. (#1524)
Prevents bot adding a travel delay when in combat
2025-08-11 01:11:54 +02:00
bash
380312ffd2 nullptr fix (#1523) 2025-08-10 22:59:34 +02:00
Alex Dcnh
872e417613 Playerbots/LFG: fix false not eligible & dungeon 0/type 0, add clear diagnostics (#1521)
Tested
2025-08-10 21:23:02 +02:00
NoxMax
cb8c7a84db Merge branch 'liyunfan1223:master' into login-range 2025-08-10 12:49:41 -06:00
bash
3d28a81508 nullptr exception (#1520) 2025-08-10 19:31:10 +02:00
bash
93975cc898 Merge branch 'master' into login-range 2025-08-10 19:29:12 +02:00
bash
bcd6f5bc06 Removed bot freezing at startup and system message, not relevant anymore (#1519) 2025-08-10 19:11:39 +02:00
ThePenguinMan96
c5010b3809 Merge branch 'liyunfan1223:master' into Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands) 2025-08-10 09:59:34 -07:00
bash
15f138aab0 Don't apply XPRate multiplier when bot is in group with real player (#1495)
* dont apply XPRate if bot is in group with real player

https://github.com/liyunfan1223/mod-playerbots/issues/1490

* Optimize code

* Oops minor correction

* Defense check on the player itself

* Safer way to check the leader is real player.

* Added abit more defense programming, should be needed still ..why not
2025-08-10 18:28:39 +02:00
ThePenguinMan96
5759a98d5a Warlock Soul Shard Cap Increase / Firestone and Spellstone Modes (#1514)
Hello everyone,

This is a small change to warlocks that accomplishes 2 things:

1. Changes the firestone and spellstone weapon enchants so only one of them can be active - players reported to me that both strategies could be present before, resulting in a bug where the bot repeatedly applied the enchant.
2. Changes the soul shard deletion cap from 6 or more to 26 or more - players will now be able to stockpile soul shards up to 25 in a bot's inventory before the bot starts deleting them one at a time back down 25. I chose 25 because if it was higher, drain soul would get multiple shards above the 32 unique cap, and spam the player "I can't carry any more of those". It was super annoying, and with testing, I have not seen this error at 25. This aims to address issue #1502 .
2025-08-10 11:13:01 +02:00
NoxMax
0cf41ad690 Merge branch 'master' into login-range 2025-08-09 16:56:39 -06:00
brighton-chi
13fca4398d Remove EPL from pvp prohibited zones (#1511)
* Remove EPL from pvp prohibited zones

* fixed unrelated error in comments

* Update default value
2025-08-09 20:59:55 +02:00
ThePenguinMan96
86dbf54584 Merge branch 'liyunfan1223:master' into Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands) 2025-08-09 09:51:27 -07:00
NoxMax
c38303cba7 Merge branch 'liyunfan1223:master' into login-range 2025-08-09 10:29:45 -06:00
Yunfan Li
a307eb2f08 VisitAllObjects to VisitObjects (sync with acore) (#1513) 2025-08-09 19:17:33 +08:00
Alex Dcnh
966bf1d6af Correct side effects of merge f5ef5bd1c2 (#1512)
* Update PlayerbotMgr.h

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.h

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.h

* Update PlayerbotMgr.cpp

* Update PlayerbotMgr.h

* Update PlayerbotMgr.h
2025-08-08 20:36:03 +02:00
crow
2144c95311 increase flexibility of multiple bot chatfiltering 2025-08-07 16:25:47 -05:00
Alex Dcnh
f5ef5bd1c2 Fix ACCESS_VIOLATION in mod-playerbots: purge stale AIs, add thread-safety, and harden HasRealPlayerMaster (#1507) 2025-08-07 00:31:00 +02:00
ThePenguinMan96
28de238422 Merge branch 'liyunfan1223:master' into Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands) 2025-08-05 20:49:00 -07:00
ThePenguinMan96
0afcf29490 Warlock Dismount Pet Fix (#1501)
Hello everyone,

This PR is to address #1489, where the warlock summons a pet when they dismount.

A tester found that the cause was the "wrong pet" triggering "summon (pet)". I looked into the "wrong pet" trigger, and noticed that there was not a clause if there was no active pet. It was inadvertently casting "summon (pet)" because for a brief second after dismounting, the warlock didn't technically have a pet.

I was able to recreate the issue based on tester feedback (dismounting with a warlock bot that has a pet). I tested this fix locally - it seems to work as intended. The warlock no longer attempts to summon a pet when dismounting. I tested it with nc +debug, and noticed that the "wrong pet" trigger was no longer firing. I also checked the logs - nothing.

Thank y'all for the testing and feedback!
2025-08-05 09:10:06 +02:00
ThePenguinMan96
ab345b8847 Changes requested
I fixed what was requested of me. Built it, tested it - all functions are working.
2025-08-04 17:55:29 -07:00
ThePenguinMan96
683c6e39e4 Merge branch 'liyunfan1223:master' into Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands) 2025-08-04 17:06:47 -07:00
NoxMax
95410c5710 formatting 2025-08-03 18:58:30 -06:00
Your Name
106117003f formatting 2025-08-03 17:41:13 -06:00
Your Name
3900237ffd Filter bot logins by level range 2025-08-03 17:36:02 -06:00
brighton-chi
a6c07ca16d Improve attackaction failure message system (#1498) 2025-08-03 23:12:33 +02:00
Yunfan Li
ee99b66d04 Sync with azerothcore (#1492) 2025-08-02 16:10:49 +08:00
ThePenguinMan96
548746c25f Revert "Update ChatActionContext.h"
This reverts commit f7e64589e8.
2025-08-01 23:55:25 -07:00
ThePenguinMan96
f7e64589e8 Update ChatActionContext.h 2025-08-01 23:54:12 -07:00
ThePenguinMan96
8545225923 Merge branch 'master' into Tame-Chat-Action-/-Pet-Chat-Action-(stances/commands) 2025-08-01 23:30:23 -07:00
Revision
ede7697784 Changed OnPlayerBeforeAchievementComplete to allow random bots to unlock achievements (#1487) 2025-08-01 21:48:01 +02:00
Alex Dcnh
ba9cb5a256 Add optional gender parameter to .playerbot bot addclass command (#1491) 2025-08-01 21:45:29 +02:00
Keleborn
a1dd6f6fc5 New roll for item action (#1482)
* New roll for item action

* Add general roll command as well.

* Update ChatCommandHandlerStrategy.cpp

Add accidental removal of glyph equip

---------

Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
2025-08-01 21:44:52 +02:00
brighton-chi
e950f65a83 Add config to disable hunters from generating specified pet families (#1485)
* Add config to disable hunters from generating specified pet families

* Fixed unrelated typo in config
2025-08-01 19:29:51 +02:00
brighton-chi
938872564a Revise bot logic for initializing and using consumables (#1483)
Bots will now add level- and spec-appropriate oils and stones when maintaining and, with respect to randombots, leveling. All bots (other than those with class-specific temporary weapon enchants) will apply oils and stones to their weapons. General clean-ups to associated code were made.
2025-08-01 19:28:13 +02:00
Yunfan Li
baa1aa9e9d Fix initself crash (#1488) 2025-08-01 19:15:59 +02:00
kadeshar
65a3bf481c - Added generic boss shadow aura trigger and action (#1480)
- Added automatic aura for General Vezax and Yogg-Saron
- Added support for all rank for boss aura triggers
2025-08-01 21:31:44 +08:00
ThePenguinMan96
ffc96b664e Turning off Spirit Wolf Leap
I had to add an exception to turn off autocasting for the Spirit Wolf Leap - they were still leaping into battle despite their stance.
2025-08-01 01:35:11 -07:00
ThePenguinMan96
aa6f8153a1 Tame Chat Action / Pet Chat Action (stances/commands)
Hello everyone,

I am on a quest to make bot's pets completely functional, focuses on solving issues #1351 , #1230 , and #1137 . This PR achieves the following:

1. Changes the current "pet" chat command to "tame", which is more intuitive that only hunters can use it. The modes are "tame name (name)", "tame id (id)", "tame family (family)", "tame rename (new name)", and "tame abandon". Tame abandon is new - it simply abandons the current pet. Also, now, if you type in "tame family" by itself, it will list the available families. See pictures below for examples.
2. Added new "pet" chat command, with the following modes: "pet passive", "pet aggressive", "pet defensive", "pet stance" (shows current pet stance), "pet attack", "pet follow", and "pet stay". Previously, the pet's stance was not changeable, and there were some less than desired effects from summonable pets - see the issues above.
3. New config option: AiPlayerbot.DefaultPetStance, which changes the stance that all bot's pets are summoned as. This makes sure when feral spirits or treants are summoned by shamans and druids, they are immediately set to this configured stance. Set as 1 as default, which is defensive. (0 = Passive, 1 = Defensive, 2 = Aggressive)
4. New config option: AiPlayerbot.PetChatCommandDebug, which enables debug messages for the "pet" chat command. By default it is set to 0, which is disabled, but if you would like to see when pet's stances are changed, or when you tell the pet to attack/follow/stay, and when pet spells are auto-toggled, this is an option. I made this for myself mainly to test the command - if anyone notices any wierd pet behavior, this will be an option to help them report it as well.
5. Modified FollowActions to not constantly execute the petfollow action, overriding any stay or attack commands issued.
6. Modified GenericActions to have TogglePetSpellAutoCastAction optionally log when spells are toggled based on AiPlayerbot.PetChatCommandDebug.
7. Modified PetAttackAction to not attack if the pet is set to passive, and not override the pet's stance to passive every time it was executed.
8. Modified CombatStrategy.cpp to not constantly issue the petattack command, respecting the "pet stay" and "pet follow" commands. Pets will still automatically attack the enemy if set to aggressive or defensive.
9. Warlocks, Priests, Hunters, Shamans, Mages, Druids, and DKs (all classes that have summons): Added a "new pet" trigger that executes the "set pet stance" action. The "new pet" trigger happens only once, upon summoning a pet. It sets the pet's stance from AiPlayerbot.DefaultPetStance's value.
2025-08-01 01:18:16 -07:00
kadeshar
989b48f491 Merge pull request #1477 from ThePenguinMan96/Channel-Cancel-checks-for-Channeled-AOE-DPS-Spells
Channel Cancel checks for Channeled AOE DPS Spells
2025-07-31 21:25:53 +02:00
kadeshar
40874624a8 Merge pull request #1475 from EricksOliveira/patch-33
Improve Arena Bot Behavior When Target is Behind Obstacles
2025-07-31 17:25:42 +02:00
kadeshar
f76435b4c4 Merge pull request #1473 from Tierisch/master
Add option to remove 'healer dps' strategy based on specified map.
2025-07-31 16:50:06 +02:00
kadeshar
df3c44419d Merge pull request #1474 from ThePenguinMan96/Druid-Flight-Form-Bug-Fix
Druid Flight Form Bug Fix
2025-07-28 18:23:01 +02:00
EricksOliveira
b7b8c60d17 Fix 2025-07-28 12:12:53 -03:00
EricksOliveira
e5f1446b9f . 2025-07-28 12:05:22 -03:00
EricksOliveira
e92029dd6e The bot immediately moves to a new position if the target is lost by LoS even during the cast. 2025-07-28 11:48:15 -03:00
EricksOliveira
b8c0a54f92 .. 2025-07-28 09:03:20 -03:00
EricksOliveira
18d1821dab Fix 2025-07-28 08:57:19 -03:00
EricksOliveira
8a8571c54f . 2025-07-28 08:45:20 -03:00
EricksOliveira
21bcbece7a Improve Bot Repositioning Based on Line of Sight and Vertical Distance
This update enhances bot behavior by adding a check for both line of sight (LoS) and significant vertical height differences between the bot and its target. If the bot cannot see its target or if the height difference exceeds 5.0f, it calculates a valid path and moves closer to regain visibility and combat effectiveness. This resolves cases where bots would previously stand still when enemies jumped from elevated terrain or were behind obstacles.
2025-07-28 08:36:28 -03:00
ThePenguinMan96
e40c2b21f2 Channel Cancel checks for Channeled AOE DPS Spells
Hello everyone,

I liked the channel cancel I did on mage recently, where they cancel blizzard if there is only 1 enemy left. I decided to do the same for the following classes:

Druid: Hurricane
Hunter: Volley
Priest: Mind Sear
Warlock: Rain of Fire

I moved the "ChannelCancel" action to it's own file, so other classes could benefit from it. I removed it from the mageactions.
2025-07-28 01:35:56 -07:00
EricksOliveira
8a9a833c98 fix 2025-07-27 19:31:54 -03:00
EricksOliveira
101c7f3046 . 2025-07-27 18:45:04 -03:00
EricksOliveira
d8d94f33ee Improve Arena Bot Behavior When Target is Behind Obstacles
This update enhances bot behavior in arena scenarios by addressing the issue where bots remain idle if their target moves behind line-of-sight (LoS obstacles). The bot now attempts to reposition near the target to regain LoS instead of standing still, preventing situations where enemies can recover without pressure.

Could someone test it?
2025-07-27 18:35:43 -03:00
kadeshar
eef2e8c1ef Merge pull request #1456 from brighton-chi/worldbuff
Make world buff strategy conditions configurable
2025-07-27 12:32:26 +02:00
kadeshar
a675e74d97 Merge pull request #1466 from Wishmaster117/Add-glyphs-and-glyph-equip-commands
Add /w botname "glyphs" and  "glyph equip" commands
2025-07-27 12:30:54 +02:00
ThePenguinMan96
de9f8fbbea Druid Flight Form Bug Fix
Hello everyone,

This is a fix for issue #1233

I have added a clause in the CanCastSpell check that checks if the bot is in flight form/swift flight form & not in combat. It will no longer attempt to shift into a caster form  and repetitively try and heal/buff party members while flying. Tested on my PC with the changes and druid now successfully flies as fast as the player with no stopping.

This was causing the issue:

static ActionNode* thorns([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("thorns",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ nullptr,
                          /*C*/ nullptr);
}

static ActionNode* thorns_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("thorns on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ nullptr,
                          /*C*/ nullptr);
}

static ActionNode* mark_of_the_wild([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("mark of the wild",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ nullptr,
                          /*C*/ nullptr);
}

static ActionNode* mark_of_the_wild_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("mark of the wild on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ nullptr,
                          /*C*/ nullptr);
}
static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("regrowth on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ NULL,
                          /*C*/ NULL);
}
static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("rejuvenation on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ NULL,
                          /*C*/ NULL);
}
static ActionNode* remove_curse_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("remove curse on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ NULL,
                          /*C*/ NULL);
}
static ActionNode* abolish_poison_on_party([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("abolish poison on party",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ NULL,
                          /*C*/ NULL);
}
static ActionNode* revive([[maybe_unused]] PlayerbotAI* botAI)
{
    return new ActionNode("revive",
                          /*P*/ NextAction::array(0, new NextAction("caster form"), nullptr),
                          /*A*/ NULL,
                          /*C*/ NULL);
}

The *P* stands for prior, aka before this action is taken, do this other action "caster form". This is the Caster Form action:

class CastCasterFormAction : public CastBuffSpellAction
{
public:
    CastCasterFormAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "caster form") {}

    bool isUseful() override;
    bool isPossible() override { return true; }
    bool Execute(Event event) override;
};

And this:

bool CastCasterFormAction::isUseful()
{
    return botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form",
                               "flight form", "swift flight form", "moonkin form", nullptr) &&
           AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->mediumHealth;
}

bool CastCasterFormAction::Execute(Event event)
{
    botAI->RemoveShapeshift();
    return true;
}

So basically, the druid was triggering to heal/buff the party, and before it could do that, it had to switch into "caster form" which executed RemoveShapeshift(). After it did that, mounting back up into flight form/swift flight form had the highest priority, so it would loop the triggers/actions.
2025-07-27 01:10:31 -07:00
Spargel
66b326d79e Merge branch 'master' into master 2025-07-27 02:57:03 -05:00
Spargel
0d8e8fbd61 Second pass for adding 'healer dps' check based on map ID. Now works when teleporting in addition to on init. 2025-07-27 02:29:07 -05:00
kadeshar
66c88d4815 Merge pull request #1472 from liyunfan1223/drink_aura
Change drink aura (free food) to speed up
2025-07-27 08:53:09 +02:00
kadeshar
4c3906c243 Merge pull request #1468 from ThePenguinMan96/Mage-Overhaul
Mage Overhaul
2025-07-27 08:46:23 +02:00
Spargel
64b09fd3ca First pass for adding 'healer dps' check based on map ID. 2025-07-27 01:20:57 -05:00
NoxMax
db7a17ffde Fix: Properly track RNDbot and AddClass accounts, and login faction balance issue (#1434)
* AssignAccountTypes & AddRandomBots

Fix: Properly track RNDbot and AddClass accounts, and login faction balance issue

* code style edits

* fix addclass init on first build of playerbots_account_type
2025-07-27 14:13:20 +08:00
kadeshar
1e33b28abe - Added raid cheat to configuration to add posibility to turn off (#1465)
- Added General Vezax strategy
2025-07-27 13:51:45 +08:00
Yunfan Li
e59bad26c4 Change drink aura for free food 2025-07-27 11:36:43 +08:00
ThePenguinMan96
a632fa2194 Migrate important cooldowns over to BoostStrategy
This commit focuses on the changes requested - Migrate important cooldowns over to BoostStrategy.

Just tested it, and it is working fine - The player has more control over when they can boost. Also, added Mirror Image to the Boost Strategy, after other cooldowns are used - this way the mirror images get the buffs.
2025-07-26 13:06:41 -07:00
Alex Dcnh
c6005449e0 Update EquipGlyphsAction.cpp 2025-07-26 21:56:53 +02:00
Alex Dcnh
2aca50c1c7 Update ChatTriggerContext.h 2025-07-26 19:28:58 +02:00
Alex Dcnh
179e3bbf71 Update ChatCommandHandlerStrategy.cpp 2025-07-26 19:28:31 +02:00
Alex Dcnh
00b03bd29d Update ChatTriggerContext.h 2025-07-26 19:28:11 +02:00
Alex Dcnh
e62da73706 Update ChatCommandHandlerStrategy.cpp 2025-07-26 19:27:48 +02:00
Alex Dcnh
5108f709c7 Update ChatTriggerContext.h 2025-07-26 19:27:25 +02:00
Alex Dcnh
2beee4aec9 Update ChatCommandHandlerStrategy.cpp 2025-07-26 19:27:01 +02:00
Alex Dcnh
1e128ea24f Update ChatTriggerContext.h 2025-07-26 19:26:38 +02:00
Alex Dcnh
e64da42f87 Update ChatCommandHandlerStrategy.cpp 2025-07-26 19:26:07 +02:00
Alex Dcnh
e0ef04e1b9 Update ChatTriggerContext.h 2025-07-26 19:25:31 +02:00
Alex Dcnh
c5c1274d3c Update ChatCommandHandlerStrategy.cpp 2025-07-26 19:24:39 +02:00
Alex Dcnh
849b21f916 Update ChatCommandHandlerStrategy.cpp
Fix indentation: replaced tabs with spaces
2025-07-26 19:23:39 +02:00
Alex Dcnh
4c9e4e7b0f Update ChatTriggerContext.h 2025-07-26 19:18:09 +02:00
Alex Dcnh
961629f4ce Update EquipGlyphsAction.cpp 2025-07-26 18:58:16 +02:00
crow
1801d7c314 Merge remote-tracking branch 'upstream/master' into worldbuff 2025-07-26 11:16:33 -05:00
kadeshar
237c0cffc4 Merge pull request #1467 from ThePenguinMan96/Warlock-Pet-Re-Summon-on-Strategy-Change
Warlock Pet Re-Summon on Strategy Change
2025-07-26 18:05:38 +02:00
ThePenguinMan96
ee245f73b5 Mage Overhaul
Hello everyone,

Back again with another class overhaul. Here is a list of what changes have been made:
1. Consolidated the AoE strategies into "aoe". For light aoe (2+ enemies) the mage will use Cone of Cold (frost)/Arcane Explosion (Arcane)/Multi-Dot with Living Bomb (Fire/Frostfire). For medium aoe (3+ enemies) they will use Flamestrike -> Blizzard. Also, the mage will automatically cancel channeling their blizzard if there is less than 2 enemies around. This is huge, since the mage would often stand there and finish their entire channel during a boss fight after the adds died.
2. Organized actions, triggers, and the aiobjectcontext
3. Enabled Deep Freeze to be casted on bosses regardless of their immune status. Big benefit for frost dps on boss fights.
4. Slight tweaks in the conf so Arcane gets Arcane Barrage and Frostfire gets 2/2 Firestarter
5. Streamlined Arcane DPS to use Missile Barrage proc when at 4 stacks of Arcane Blast
5. Streamlined Fire/Frostfire DPS to keep Improved Scorch active (5% spell crit) unless there is a debuff of equal type
6. Added "firestarter" strategy, that utilizes the Fire talent Firestarter better. The mage will multi-dot Living Bomb while running towards melee, and cast Dragon's Breath -> instant cast Flamestrike -> Blast Wave -> instant cast Flamestrike -> Blizzard for bonkers damage. Disabled by default - not everyone wants their mages running into melee. Enable by typing "co +firestarter" on fire and frostfire mages.
7. Streamlined Frost DPS by finally adding support for Cold Snap for mages. It will proc when both Icy Veins and Deep Freeze are on cooldown. There is an exception to this - if the mage is level 30-59, it will not check for Deep Freeze - only Icy Veins.
8. Added Conjure Mana Gem support in the generic non-combat strategy and Use Mana Gem support in the generic combat strategy. This might be the biggest benefit of the overhaul - the gem has a 90 second cooldown, not shared with mana potions. It really prevents the mage from gassing out in longer fights. And the mana gem has 3 charges!
9. Added Mana Shield ability, which triggers on low health.
10. Changed Mirror Image from a boost ability to an anti-threat tool. Not many people know this, but it's best use in PvE is it's anti-threat modifier: "Mod Total Threat - Temporary Value: -90000000". It also doesn't do good damage, and is essentially used best as a pre-pull spell. But until the mages know how to react to a pull-timer, it's going to be used to reduce threat.

Let me know what y'all think!
2025-07-26 01:49:49 -07:00
ThePenguinMan96
c04477b54d Warlock Pet Re-Summon on Strategy Change
Hello everyone,

This PR addresses issue #1464 - Now, when you change a strategy, the new pet will be summoned accordingly - as long as the warlock knows the pet. I tested this myself with level 80 warlocks, then moved to lower level warlocks. It was nice to see that when my affliction warlock went from level 29 to 30, it automatically summoned the felhunter (while having the succubus out prior).

List of changes per file:
src\AiFactory.cpp: Set default level 1 spec to demonology for altbots - it was affliction prior. This will allow them to use Curse of agony, corruption, and immolate between levels 1-10.

src\strategy\warlock\GenericWarlockNonCombatStrategy.cpp: Added the "wrong pet" trigger to each of the 5 pet strategies, coupled with the appropriate summon action.

src\strategy\warlock\WarlockAiObjectContext.cpp: Registered the "wrong pet" trigger and moved the unstable affliction static trigger all to 1 line, cleaning it up a bit.

src\strategy\warlock\WarlockTriggers.cpp: Added the logic for the wrong pet trigger here. I tried to leave detailed comments as much as possible explaining it's thought process.

src\strategy\warlock\WarlockTriggers.h: Added the WrongPetTrigger.
2025-07-25 19:00:37 -07:00
kadeshar
b65646170c Merge pull request #1463 from ThePenguinMan96/Warlock-Soul-Shard/Soulstone-ID-conversion
Warlock Soul Shard/Soulstone ID conversion
2025-07-26 00:53:23 +02:00
Wishmaster117
55a37c48eb Add /w botname "glyphs" and "glyph equip" commands 2025-07-26 00:15:46 +02:00
ThePenguinMan96
a33bb3b51e Changes requested
Updating based on changes requested for commit
2025-07-25 14:02:20 -07:00
Yunfan Li
feda619066 Engine optimization for better performance and mem usage (#1462)
* Optimize loot

* World channel talk

* General improvement

* Engine rebuild for performance and memory usage

* Fix crash with AutoDoQuest = 0
2025-07-25 12:11:03 +02:00
crow
564bb198fb Merge remote-tracking branch 'upstream/master' into worldbuff 2025-07-24 00:48:57 -05:00
ThePenguinMan96
5ac6e8751c Bagspace checks
This commit is adding checks to the createsoulshard and createsoulstone actions, to check and see if there is enough bagspace to create an item.
2025-07-23 12:45:02 -07:00
ThePenguinMan96
7d7edbd961 Warlock Soul Shard/Soulstone ID conversion
Hello everyone,

I'm still working on fixing issue #1439 , and I have a theory - it could be because the clients that are having the issue are not english, and the code currently checks for strings "soul shard" and "soulstone". This PR converts the check over to the item IDs, so it should work regardless of what client is being used. I tested it myself with the english client and there is functionally no difference than before - but I hope it solves the issue #1439 for the non-english clients and community.
2025-07-23 11:26:55 -07:00
Yunfan Li
4a00c954ed RPG update travel flight status (#1445) 2025-07-23 23:37:41 +08:00
kadeshar
e150b8281b - Replaced custom protection of soul shard by trigger check interval (#1453) 2025-07-23 00:15:22 +02:00
crow
0b03b277c2 Make world buff strategy conditions configurable 2025-07-20 21:37:54 -05:00
ThePenguinMan96
d6b7693b8b Warlock Soulstone/Soulshard hotfix (#1452)
Hello everybody,

This PR is to address issues #1439 and #1451.

I added a 1 second cooldown to the createsoulshard action, as the warlock wouldn't ever use more than 1 soul shard per second.

I also added a cooldown check to the soulstone trigger, so it doesn't simply try to use the ss healer, ss self, ss tank, or ss master if the soulstone is present in the inventory.  I checked the logs, and was able to recreate the issue of #1451 - the bot was shifting targets over and over again, and that was because previously the trigger was just checking to see if a soulstone was present at all. Now it won't fire if it's on cooldown.
2025-07-19 18:25:56 +02:00
ThePenguinMan96
551c698e37 Hunter Pet Chat Command (#1448)
Hello everyone,

I was working with hunter bots and I feel like I didn't have enough control over their pets. So I started working on a chat command for the hunter pets, and it's been a blast. Below is a description of what the commands do:

pet name <name>
Summons a tameable pet by its creature name (case-insensitive).
The bot checks if the pet is exotic and requires the Beast Mastery talent if so.
If successful, the bot announces the pet's new name and creature ID.
If not found or not tameable, an error message is given.

pet id <id>
Summons a tameable pet by its database creature ID.
The same exotic pet checks apply.
Success is announced with the pet's name and ID.
Errors are given for invalid IDs or untameable pets.

pet family <family>
Randomly selects and summons a tameable pet from the specified family (e.g., "cat", "wolf").
Only families with tameable pets are considered.
Exotic pet checks and talent requirements apply.
Success is announced with the pet's name and ID.
Errors are given if no suitable pet is found.

pet rename <new name>
Renames the hunter's current summoned pet to a new name.
The name must be 1-12 alphabetic characters (A-Z, a-z) and is automatically formatted (first letter capitalized, rest lowercased).
Forbidden or reserved names are disallowed.
After renaming, the bot sets the new name, saves the pet, and updates the client.
The bot dismisses and attempts to recall the pet using "Call Pet" (if the hunter knows it) to update the UI.
If the new name isn't visible immediately, the bot instructs the player to dismiss and recall the pet manually.
Confirmation and guidance messages are provided.

Additional Details:
All commands display errors if requirements are not met (wrong name/id/family, forbidden name, lack of Beast Mastery, or hunter below level 10).
After changing or summoning a pet (except renaming), the bot initializes the pet and its talents, then announces the result.

TLDR:
pet name <name>	Summon a tameable pet by name
pet id <id>	Summon a tameable pet by database creature ID
pet family <family>	Randomly summon a tameable pet of the given family
pet rename <new name>	Rename the current pet and refresh its name in the client UI

Description of files changed:

src\strategy\actions\ChatActionContext.h: Added the "pet" action for the whisper command.

src\strategy\actions\PetAction.cpp: New chat actions for all things related to hunter pets.

src\strategy\actions\PetAction.h: New header for the PetAction.cpp.

src\strategy\generic\ChatCommandHandlerStrategy.cpp: Linked the trigger and action in the chatcommandhandlerstrategy, for the bot to take action when whispered "pet" (trigger).

src\strategy\triggers\ChatTriggerContext.h: Added the "pet" trigger.
2025-07-17 13:51:06 +02:00
ThePenguinMan96
45694ad6e6 Warlock Soul Shard Hotfix (#1442)
Hello everyone,

This PR is to address an issue that was posted recently - a player has shown that soul shards are being created in excess, spamming the player's chat log. I am adding an isuseful() check to the createsoulshard action, so it will never be executed if they have more than 5 soul shards.

Also, out of an abundance of caution, I am lowering the cap for CastDrainSoulAction::isUseful() to 20 from 32. That way, if for some reason the warlock has 20+ shards, it won't attempt to collect any more / use drain soul.
2025-07-15 15:54:04 +02:00
kadeshar
761ef634da - Fixed loot trigger to respect stay strategy (#1437) 2025-07-14 19:27:16 +02:00
ThePenguinMan96
96cc0daea6 Hunter/Warlock AoE Fix (#1440)
Hello everyone,

This fixes these two classes so they respond to the command "co -aoe" and "co +aoe". This also fixes the survival hunter so that trap weave is not a default strategy - they will not walk into melee anymore.
2025-07-14 10:15:11 +02:00
kadeshar
5202ac8db3 Merge pull request #1435 from ThePenguinMan96/Hunter-Overhaul
Hunter Overhaul
2025-07-13 21:56:51 +02:00
kadeshar
fa79fff4f4 Merge pull request #1436 from ThePenguinMan96/Firestone-Error-Fix/Excess-Soul-Shard-Fix
Firestone Error Fix/Excess Soul Shard Fix
2025-07-13 21:56:36 +02:00
ThePenguinMan96
6d07d6febe Firestone Error Fix/Excess Soul Shard Fix
Hello everyone,

This PR addresses two errors that players have been getting with the new warlock changes:

Firestone - Fel Firestone, which is rank 6 of create firestone, learned at level 74, is creating an error in the worldserver that is quite annoying. That is because the database has an incorrect enchant effect of a chance on hit for that rank. This PR changes the CreateFirestoneAction to skip that spell rank entirely, thus never having that error. Note: You might need to wait a little while after the new change for the errors to go away - that is because there still be pre-existing fel firestones  on the warlocks, as well as enchanted on their weapons. Once those disappear, the error will not be there anymore.

Excess soul shards - There is an error that currently exists where if a Warlock uses Drain Soul while they have 32 soul shards, it will spam in the chat log "I can't carry anymore of those". This PR will automatically delete soul shards if they have 6 or more. This PR also will reduce the number of soul shards the warlock receives from maintenance to 5. I figured 5 is a good maximum so their inventory doesn't get clogged with 32 shards. Between "DestroySoulShard" and "CreateSoulShard" actions, they will always have between 1-5 soul shards.
2025-07-12 11:45:33 -07:00
ThePenguinMan96
8ca4ab1344 Hunter Overhaul
Hello everybody,

I saw that the hunter class had one strategy for all 3 specs, and decided to create specialized strategies for each one. Here is a list of the changes:

conf\playerbots.conf.dist: Redid the talent trees and glyphs slightly, for more consistent dps

src\AiFactory.cpp: Changed the default strategies assigned for each spec.

src\strategy\hunter\BeastMasteryHunterStrategy.cpp and src\strategy\hunter\BeastMasteryHunterStrategy.h: Strategy for BM hunters. Includes all of the original logic from DpsHunterStrategy, with the addition of kill command, bestial wrath, and intimidation.

src\strategy\hunter\DpsHunterStrategy.cpp and src\strategy\hunter\DpsHunterStrategy.h: Old Dps strategy used for all 3 specs - removed.

src\strategy\hunter\GenericHunterStrategy.cpp and src\strategy\hunter\GenericHunterStrategy.h: Tidied up code, added Dragonhawk passthrough to hawk, moved rapid fire to inittriggers for generichunterstrategy and increased its priority (it is still a boost trigger, so it will function the same).

src\strategy\hunter\HunterActions.cpp:
Added isuseful check for aspect of the hawk, to ensure it is never cast if the bot knows aspect of the dragonhawk.
Added isuseful check for arcane shot to never use it after explosive shot is learned (so the hunter can use it as it levels survival, but drops it after that). Also added a check for arcane shot, so it won't use it above 435 armor pen rating (steady shot is superior after this arp).
Added isuseful check for immolation trap so it won't use it after explosive shot is learned.

src\strategy\hunter\HunterActions.h:
General cleanup/alignment of actions based on type.
Added TTL checks to all DoTs and debuffs - hunters weren't using hunter's mark, serpent sting, black arrow, explosive shot on enemies that it thought would die too soon.
Black Arrow - added a strategy check here as well so the bot won't use it at all if trap weave is enabled. There was already a check in the trigger, but it was still getting cast, so I added this check.
Explosive Shot Rank 4, 3, 2, 1 actions- Added these so the hunter can downrank explosive shot dynamically based on the level when lock and load procs. So if the hunter is level 70, and a lock and load proc happens, it will fire rank 2 - rank 1 - rank 2, similar to how the level 80 version will fire 4-3-4.

src\strategy\hunter\HunterAiObjectContext.cpp:
Added strategy support for bm, mm, and surv (and their aoes)
Added trigger support for kill command, explosive shot, lock and load, silencing shot (as an spellcasting interrupt), and intimidation
Added action support for the 4 ranks of explosive shot and intimidation.

src\strategy\hunter\HunterTriggers.cpp: Kill command was completely non-functional because there was no buff trigger associated with it. Adding this will correct that, and the hunter will use kill command.

src\strategy\hunter\HunterTriggers.h: Added Kill command, silencing shot, intimidation, lock and load, and explosive shot triggers.

src\strategy\hunter\MarksmanshipHunterStrategy.cpp and src\strategy\hunter\MarksmanshipHunterStrategy.h: Strategy for MM hunters. Includes all of the original logic from DpsHunterStrategy, with the addition of kill command, silencing shot, chimera shot, and aimed shot.

src\strategy\hunter\SurvivalHunterStrategy.cpp and src\strategy\hunter\SurvivalHunterStrategy.h: Strategy for Survival hunters. Includes all of the original logic from DpsHunterStrategy, with the addition of kill command, explosive shot, black arrow, aimed shot, lock and load triggers + downranking explosive shot.
2025-07-11 17:10:03 -07:00
kadeshar
bc5d602326 Merge pull request #1430 from ThePenguinMan96/Warlock-Curse/Spellstone-and-Firestone-Strategies,-Soul-Shard-Replenish
Warlock Curse strategies, Spellstone and Firestone strategies, Soul Shard Replenish
2025-07-08 18:57:50 +02:00
Boxhead78
3611cfbdd3 Minor Battleground tactics improvements (#1431)
* Fix bots ignoring contested Nodes in Arathi Basin

* Use VMAP_INVALID_HEIGHT_VALUE instead of -200000.0f for valid height check
2025-07-08 18:31:08 +02:00
ThePenguinMan96
0400c1c8b3 Modified code to allow the build to complete on mac
Modified code to allow the build to complete on mac
2025-07-07 19:08:01 -07:00
ThePenguinMan96
393a65a15b Warlock Curse/Spellstone and Firestone Strategies, Soul Shard Replenish
This PR aims to achieve 4 main things:

1. Manual Curse Strategies - Each curse has it's own toggleable combat strategy, with curse of agony being the default for affliction and demonology, and curse of the elements being default for destro. The other curses that are available are curse of exhaustion (if specced for it), curse of doom, curse of weakness, and curse of tongues (6 total). You can add these by typing "co +curse of weakness", or similar. Note: Curses are part of the WarlockCurseStrategyFactoryInternal(), so there can only be one active.

2. Firestone/Spellstone Non-Combat Strategies - Players requested to me that they can decide if they want to use a spellstone or firestone for their weapon enchant, so I added them as non-combat strategies. Spellstone is the default for affliction and demonology, firestone is the default for destro. To add these, type "nc +firestone" or similar.

3. Soul Shard Replenishment - I noticed after hours of running a server (15+ hours) that altbots and rndbots would only cast imp and not use their soul shards. This is because they were actually running out of soul shards, without the ability to maintain themselves accordingly. I added a trigger (no soul shard) and action (create soul shard) that triggers if they are out of soul shards, creating only 1 soul shard (to not clog up the inventory). This way, you should never have a warlock using the wrong pet, or failing to cast shadowburn, failing to create soulstone/spellstone/firestone/healthstone, or failing to cast soul shatter.

4. Tidying up the code -

I removed the built-in curse code from the DPS strategies, and migrated it to the manual curse strategies.

I clumped the curse triggers and actions together in the associated files.

I added logic for Curse of Weakness to check for conflicting debuffs.

I moved the summoning strategies and curse strategies to their own strategy factories - WarlockPetStrategyFactoryInternal, and WarlockCurseStrategyFactoryInternal. This way they can only have one curse and one pet strategy active at once. I also renamed the "NonCombatBuffStrategyFactoryInternal" to "WarlockSoulstoneStrategyFactoryInternal", which was more accurate.

I changed a single talent point in the Affliction Warlock PVE spec, taking one from destructive reach and adding it into nightfall for those sweet, sweet free shadowbolts.

I added "ss self" as the default non-combat soulstone strategy, as before, they didn't have one assigned. The player can still of course remove that NC strategy and apply another, such as "ss master", "ss tank", or "ss healer".
2025-07-07 18:51:33 -07:00
kadeshar
8b9bcce3bc Merge pull request #1418 from Boxhead78/bg-tactics-rewrite
Bots Bg Tactics rewrite (WSG, AB, AV, EYE)
2025-07-07 21:45:35 +02:00
Yunfan Li
8ed053ca01 fix warnings (#1428) 2025-07-07 19:43:24 +02:00
Yunfan Li
51ed9c4649 fix trinket (#1429) 2025-07-07 19:41:47 +02:00
kadeshar
86390f90fd - Fixed bug with not respecting InstantFlightPaths config (#1410) 2025-07-06 19:38:15 +08:00
kadeshar
3f39a57fe2 Merge pull request #1426 from NoxMax/delete-all-the-orphans
Fix: DeleteRandomBotAccounts sometimes leaves orphans. Also fix accumulating orphan pet data in DB
2025-07-06 09:51:31 +02:00
kadeshar
102aa24bb8 Merge pull request #1425 from liyunfan1223/rpg_gear_incremental
Added config to make rndbots only get gear from looting/quests
2025-07-06 09:50:48 +02:00
kadeshar
50792e5646 Merge pull request #1422 from ThePenguinMan96/Warlock-Ranged-Designation/DPS-Strat-cleanup
Warlock Ranged Designation/DPS Strategy Cleanup
2025-07-06 09:50:24 +02:00
NoxMax
78f4bd6d29 Ensuring data orphans are deleted 2025-07-05 18:59:30 -06:00
Yunfan Li
b558e86df0 correct typo in conf 2025-07-05 22:03:31 +08:00
Yunfan Li
f0c6aaff0b Random bots gear related enhancements 2025-07-05 20:29:34 +08:00
ThePenguinMan96
1a20d549fe Eureka!
I re-implemented the pet strategies into the "general" strategy area of the WarlockAiObjectContext, and it worked!!! Finally! The issue before was they were under the "Buff" area of the aiobjectcontext, which can only have 1 active at any given time - this is why the soulstone strategy and the pet strategy were cancelling out each other. Now, pets are summoned via a non-combat strategy that is assigned with aifactory by spec!
2025-07-04 22:44:17 -07:00
kadeshar
64df7439d8 Merge pull request #1424 from Raz0r1337/master
Update playerbots.conf.dist
2025-07-04 22:06:13 +02:00
kadeshar
05a3c318d6 Merge pull request #1420 from Wishmaster117/Correction-Code
Revert GetEquipGearScore to Blizzard’s fixed‐slot average item level formula
2025-07-04 22:05:38 +02:00
ThePenguinMan96
1c69490290 Added range checks for running away if too close
Added range checks for running away if too close - The warlocks were happily standing in cleave range and dieing. Now, they will backpedal if too close to an enemy. I also added custom logic to check if in demonology form before backpedaling. I also removed shadow cleave, as the dps increase with negligible (0.1%) and it was causing errors in the actions, resulting in the warlock standing idle in combat.
2025-07-04 11:33:46 -07:00
Alex Dcnh
8fd188ff3b Update PlayerbotAI.cpp 2025-07-04 19:19:29 +02:00
St0ny
7fa6e5833a Update playerbots.conf.dist
Added more GameObject ID's for Bots to ignore
2025-07-04 17:49:05 +02:00
kadeshar
305f769a84 - Added chat command to wipe group (#1408) 2025-07-04 23:14:07 +08:00
kadeshar
c0aa55416b Added configuration on excluded prefixes for trade action (#1401)
* - Added configuration on excluded prefixes for trade action

* - LoadListString usage reference specified

* - Code refactoring
2025-07-04 23:13:19 +08:00
ThePenguinMan96
59af34809c Warlock Ranged Designation/DPS Strategy Cleanup
Hello community,

This PR focuses on 4 things:

Recognizing the Warlock as a "ranged" bot, so they will follow ranged commands and strategies, in GenericWarlockStrategy.h:
uint32 GetType() const override { return CombatStrategy::GetType() | STRATEGY_TYPE_RANGED | STRATEGY_TYPE_DPS; }

Cleanup/deletion of the DpsWarlockStrategy.cpp and .h (no longer used or referenced anywhere)

Fixes soulstone logic so multiple Warlocks don't soulstone the same target, and don't try to soulstone a target that is too far away or out of line of sight (WarlockActions.cpp)

Moved summoning of pets to the main non-combat strategy inittriggers:

// Pet-summoning triggers based on spec
    if (tab == 0)  // Affliction
    {
        triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), nullptr)));
    }
    else if (tab == 1)  // Demonology
    {
        triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felguard", 29.0f), nullptr)));
    }
    else if (tab == 2)  // Destruction
    {
        triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon imp", 29.0f), nullptr)));
    }
2025-07-04 03:32:29 -07:00
Alex Dcnh
326783ed4f PlayerbotAI – Fix GetEquipGearScore to mirror Blizzard’s average-ilvl rules 2025-07-04 10:23:37 +02:00
Alex Dcnh
9503d32d46 Merge branch 'liyunfan1223:master' into Correction-Code 2025-07-03 22:27:54 +02:00
Alex Dcnh
edc9241fa3 Update PlayerbotAI.cpp 2025-07-03 22:21:54 +02:00
kadeshar
40535f6e55 Merge pull request #1419 from Raz0r1337/master
Added GameObject IDs that bots ignore
2025-07-03 19:48:41 +02:00
St0ny
9a980c171e Update playerbots.conf.dist
Added some GameObject IDs to stop Bots from stealing it.
2025-07-03 17:59:33 +02:00
Boxhead78
ce91c335b3 Fix missing BotStrategy enums 2025-07-03 12:30:09 +02:00
Boxhead78
605fa223ce Move GetBotStrategyForTeam from core 2025-07-03 09:39:54 +02:00
Boxhead78
309d177dd8 Battleground Rewrite
- Refactored BattleGroundTactics.cpp
- Bots choose strategies to determine if they are more aggressive or defensive in objectives
- Largely improved bots tactics in WSG, AB, AV and EY
- Improved how bots chase flag carriers
- Fixed some bots stuck in action loops - especially in WSG and AV
- Fixed several other Bugs
2025-07-03 08:25:55 +02:00
kadeshar
36fd5b8f15 Merge pull request #1411 from ThePenguinMan96/PVP-Talents-&-InitGlyph-expansion
PVP Talents and InitGlyph changes, "maintenance" command changes, "talents spec" changes
2025-07-02 21:56:00 +02:00
bash
720c063716 Update PlayerbotMgr.cpp (#1414) 2025-07-02 21:54:52 +02:00
ThePenguinMan96
3f7814abb4 PVP Talents and InitGlyph changes
PVP Talents and InitGlyph changes

This PR adds 3 pvp specs for each class, as well as their glyphs. It also adds exceptions to the Initglyph function, based on pvp-based talents for each class.

conf\playerbots.conf.dist - Adds 3 pvp specs/glyphs for each class.

src\factory\PlayerbotFactory.cpp - InitGlyph in its current form is unable to correctly assign glyphs on specindexes (or tab) over 2 without an exception. That is why this exception already exists in the code:

if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
        tab = 3;

This checks if the class is a Druid, if the tab is feral, if they are equal to or above level 20, and they don't have the Thick Hide talent. If all of these are true, then it manually sets the tab = 3. I first discovered this when I noticed that my frostfire mage would never be assigned the correct glyphs in the config - aka glyph of frosfire. It is because the frostfire spec is tab = 3, and no such logic exists. When I started adding the additional pvp specs, I noticed that they never would assign the correct glyphs. I had to add an exception to all pvp specs, and have them check for certain pvp related talents to correlate the tab manually. This is because tab is derived from the AiFactory::GetPlayerSpecTab(bot); function. The only possible tab values from this function are 0, 1, and 2.
**TLDR: Added code to support Frostfire Mage, dual-aura Blood DK, and all the PvP specs for correct glyph assignment.**

src\strategy\actions\ChangeTalentsAction.cpp: When you pick a spec with "talents spec" function, such as "talents spec arms pve", it will now correctly assign glyphs without the player having to execute the maintenance command. Setting the InitGlyphs to false removes prior glyphs.

src\strategy\actions\TrainerAction.cpp - Changed factory.InitGlyphs(true); to factory.InitGlyphs(false);. This makes it so all prior glyphs that were assigned are correctly deleted. I first noticed this when switching between specs and using the maintenance command - I had to login to the bot and manually delete the old glyphs, in order for the maintenance command to assign the new, correct glyphs.
2025-07-01 14:35:16 -07:00
1070 changed files with 136627 additions and 111039 deletions

40
.github/workflows/codestyle_cpp.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: C++ Codestyle
on:
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
paths:
- src/**
- "!README.md"
- "!docs/**"
concurrency:
group: "codestyle-cppcheck-${{ github.event.pull_request.number }}"
cancel-in-progress: true
jobs:
triage:
runs-on: ubuntu-latest
name: C++
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v4
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: AzerothCore codestyle
run: python ./apps/codestyle/codestyle-cpp.py
- name: C++ Advanced
run: |
sudo apt update -y
sudo apt install -y cppcheck
cppcheck --force --inline-suppr --suppressions-list=./.suppress.cppcheck src/ --output-file=report.txt
if [ -s report.txt ]; then # if file is not empty
cat report.txt
exit 1 # let github action fails
fi

View File

@@ -38,7 +38,7 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Set reusable strings
@@ -50,7 +50,7 @@ jobs:
- name: Checkout Playerbot Module
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: Cache

View File

@@ -22,12 +22,12 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v4
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Checkout Playerbot Module
uses: actions/checkout@v4
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: Cache
uses: actions/cache@v4

View File

@@ -23,12 +23,12 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Checkout Playerbot Module
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.13

3
.gitignore vendored
View File

@@ -48,4 +48,5 @@ local.properties
.loadpath
.project
.cproject
.vscode
.vscode
.idea

1
.suppress.cppcheck Normal file
View File

@@ -0,0 +1 @@
cppcheckError

View File

@@ -1,7 +1,9 @@
<p align="center">
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README.md">English</a>
|
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_CN.md">中文</a>
|
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_ES.md">Español</a>
</p>
@@ -10,46 +12,50 @@
</div>
<div align="center">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
</div>
# Playerbots Module
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot). Features include:
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot).
- Bots that utilize real player data, allowing players to interact with their other characters, form parties, level up, and more;
- Random bots that wander through the world and behave like players, simulating the MMO experience;
- Bots capable of running raids and battlegrounds;
- Highly configurable settings to define how bots behave;
- Excellent performance, even when running thousands of bots.
Features include:
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/liyunfan1223/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
- The ability to log in alt characters as bots, allowing players to interact with their other characters, form parties, level up, and more
- Random bots that wander through the world, complete quests, and otherwise behave like players, simulating the MMO experience
- Bots capable of running most raids and battlegrounds
- Highly configurable settings to define how bots behave
- Excellent performance, even when running thousands of bots
**Playerbots Module** has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project.
We also have a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
## Installation
### Classic Installation
Supported platforms are Ubuntu, Windows, and macOS. Other Linux distributions may work, but may not receive support.
`mod-playerbots` requires a custom branch of AzerothCore to work: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
**All `mod-playerbots` installations require a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).** This branch allows the `mod-playerbots` module to build and function. Updates from the upstream are implemented regularly to this branch. Instructions for installing this required branch and this module are provided below.
### Cloning the Repositories
To install both the required branch of AzerothCore and the `mod-playerbots` module from source, run the following:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
For more information, refer to the [AzerothCore Installation Guide](https://www.azerothcore.org/wiki/installation) and [Installing a Module](https://www.azerothcore.org/wiki/installing-a-module) pages.
### Docker Installation
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
Docker installations are considered experimental (unofficial with limited support), and previous Docker experience is recommended. To install `mod-playerbots` on Docker, first clone the required branch of AzerothCore and this module:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
Afterwards, create a `docker-compose.override.yml` file in the `azerothcore-wotlk` directory. This override file allows for mounting the modules directory to the `ac-worldserver` service which is required for it to run. Put the following inside and save:
@@ -73,36 +79,34 @@ services:
- ./modules:/azerothcore/modules:ro
```
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/liyunfan1223/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/mod-playerbots/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
Use `docker compose up -d --build` to build and run the server. For more information, including how to create an account and taking backups, refer to the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) page.
## Documentation
The [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki) contains an extensive overview of addons, commands, and recommended configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of AddOns, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections, and contributions are welcome.
## Frequently Asked Questions
Bots are controlled via chat commands. For larger bot groups, this can be cumbersome. Because of this, community members have developed client AddOns to allow controlling bots through the in-game UI. We recommend you check out their projects listed in the [AddOns and Submodules](https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Addons-and-Sub%E2%80%90Modules) page.
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support.
- **Why isn't my source compiling?** Please [check the build status of our CI](https://github.com/liyunfan1223/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
## Contributing
## Addons
This project is still under development. We encourage anyone to make contributions, anything from pull requests to reporting issues. If you encounter any errors or experience crashes, we encourage you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
Typically, bots are controlled via chat commands. For larger bot groups, this can be unwieldy. As an alternative, community members have developed client Add-Ons to allow controlling bots through the in-game UI. We recommend you check out their projects:
If you make coding contributions, `mod-playerbots` complies with the [C++ Code Standards](https://www.azerothcore.org/wiki/cpp-code-standards) established by AzerothCore. Each Pull Request must include all test scenarios the author performed, along with their results, to demonstrate that the changes were properly verified.
- [Multibot](https://github.com/Macx-Lio/MultiBot) (by Macx-Lio)
- [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon) (Chinese version by Liyunfan)
- [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english) (English version translated by @Revision)
We recommend joining the [Discord server](https://discord.gg/NQm5QShwf9) to make your contributions to the project easier, as a lot of active support is carried out through this server.
Please click on the "⭐" button to stay up to date and help us gain more visibility on GitHub!
## Acknowledgements
`mod-playerbots` is is based off [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for the continued efforts in maintaining the module.
`mod-playerbots` is based on [ZhengPeiRu21/mod-playerbots](https://github.com/ZhengPeiRu21/mod-playerbots) and [celguar/mangosbot-bots](https://github.com/celguar/mangosbot-bots). We extend our gratitude to [@ZhengPeiRu21](https://github.com/ZhengPeiRu21) and [@celguar](https://github.com/celguar) for their continued efforts in maintaining the module.
Also, a thank you to the many contributors who've helped build this project:
<a href="https://github.com/liyunfan1223/mod-playerbots/graphs/contributors">
<img src="https://contrib.rocks/image?repo=liyunfan1223/mod-playerbots" />
<a href="https://github.com/mod-playerbots/mod-playerbots/graphs/contributors">
<img src="https://contrib.rocks/image?repo=mod-playerbots/mod-playerbots" />
</a>

View File

@@ -8,16 +8,16 @@
## 安装
请注意此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性您必须使用我fork的自定义分支来编译它可以在这里找到[liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot)。
请注意此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性您必须使用我fork的自定义分支来编译它可以在这里找到[mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot)。
要安装此模块请参考AzerothCore Wiki的详细说明[AzerothCore安装指南](https://www.azerothcore.org/wiki/installation)。
我们提供了一个简单的方法来克隆该模块:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
## 快速开始与文档
@@ -60,7 +60,7 @@ git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
- 我们支持Ubuntu、Windows和macOS。
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/liyunfan1223/mod-playerbots/actions)中查看构建状态。
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/mod-playerbots/mod-playerbots/actions)中查看构建状态。
- 如果最新的构建状态失败,请恢复到上一个提交。我们将尽快解决此问题。

View File

@@ -0,0 +1,263 @@
import io
import os
import sys
import re
# Get the src directory of the project
src_directory = os.path.join(os.getcwd(), 'src')
# Global variables
error_handler = False
results = {
"Multiple blank lines check": "Passed",
"Trailing whitespace check": "Passed",
"GetCounter() check": "Passed",
"Misc codestyle check": "Passed",
"GetTypeId() check": "Passed",
"NpcFlagHelpers check": "Passed",
"ItemFlagHelpers check": "Passed",
"ItemTemplateFlagHelpers check": "Passed"
}
# Main function to parse all the files of the project
def parsing_file(directory: str) -> None:
print("Starting AzerothCore CPP Codestyle check...")
print(" ")
print("Please read the C++ Code Standards for AzerothCore:")
print("https://www.azerothcore.org/wiki/cpp-code-standards")
print(" ")
for root, _, files in os.walk(directory):
for file in files:
if not file.endswith('.ico'): # Skip .ico files that cannot be read
file_path = os.path.join(root, file)
file_name = file
try:
with open(file_path, 'r', encoding='utf-8') as file:
multiple_blank_lines_check(file, file_path)
trailing_whitespace_check(file, file_path)
get_counter_check(file, file_path)
if not file_name.endswith('.cmake') and file_name != 'CMakeLists.txt':
misc_codestyle_check(file, file_path)
if file_name != 'Object.h':
get_typeid_check(file, file_path)
if file_name != 'Unit.h':
npcflags_helpers_check(file, file_path)
if file_name != 'Item.h':
itemflag_helpers_check(file, file_path)
if file_name != 'ItemTemplate.h':
itemtemplateflag_helpers_check(file, file_path)
except UnicodeDecodeError:
print(f"\nCould not decode file {file_path}")
sys.exit(1)
# Output the results
print("")
for check, result in results.items():
print(f"{check} : {result}")
if error_handler:
print("\nPlease fix the codestyle issues above.")
sys.exit(1)
else:
print(f"\nEverything looks good")
# Codestyle patterns checking for multiple blank lines
def multiple_blank_lines_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
consecutive_blank_lines = 0
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if line.strip() == '':
consecutive_blank_lines += 1
if consecutive_blank_lines > 1:
print(f"Multiple blank lines found in {file_path} at line {line_number - 1}")
check_failed = True
else:
consecutive_blank_lines = 0
# Additional check for the end of the file
if consecutive_blank_lines >= 1:
print(f"Multiple blank lines found at the end of: {file_path}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["Multiple blank lines check"] = "Failed"
# Codestyle patterns checking for whitespace at the end of the lines
def trailing_whitespace_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if line.endswith(' \n'):
print(f"Trailing whitespace found: {file_path} at line {line_number}")
if not error_handler:
error_handler = True
results["Trailing whitespace check"] = "Failed"
# Codestyle patterns checking for ObjectGuid::GetCounter()
def get_counter_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'ObjectGuid::GetCounter()' in line:
print(f"Please use ObjectGuid::ToString().c_str() instead ObjectGuid::GetCounter(): {file_path} at line {line_number}")
if not error_handler:
error_handler = True
results["GetCounter() check"] = "Failed"
# Codestyle patterns checking for GetTypeId()
def get_typeid_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'GetTypeId() == TYPEID_ITEM' in line or 'GetTypeId() != TYPEID_ITEM' in line:
print(f"Please use IsItem() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_UNIT' in line or 'GetTypeId() != TYPEID_UNIT' in line:
print(f"Please use IsCreature() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_PLAYER' in line or 'GetTypeId() != TYPEID_PLAYER' in line:
print(f"Please use IsPlayer() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_GAMEOBJECT' in line or 'GetTypeId() != TYPEID_GAMEOBJECT' in line:
print(f"Please use IsGameObject() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
if 'GetTypeId() == TYPEID_DYNOBJECT' in line or 'GetTypeId() != TYPEID_DYNOBJECT' in line:
print(f"Please use IsDynamicObject() instead of GetTypeId(): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["GetTypeId() check"] = "Failed"
# Codestyle patterns checking for NpcFlag helpers
def npcflags_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'GetUInt32Value(UNIT_NPC_FLAGS)' in line:
print(
f"Please use GetNpcFlags() instead of GetUInt32Value(UNIT_NPC_FLAGS): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use HasNpcFlag() instead of HasFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'SetUInt32Value(UNIT_NPC_FLAGS,' in line:
print(
f"Please use ReplaceAllNpcFlags() instead of SetUInt32Value(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'SetFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use SetNpcFlag() instead of SetFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
if 'RemoveFlag(UNIT_NPC_FLAGS,' in line:
print(
f"Please use RemoveNpcFlag() instead of RemoveFlag(UNIT_NPC_FLAGS, ...): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["NpcFlagHelpers check"] = "Failed"
# Codestyle patterns checking for ItemFlag helpers
def itemflag_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)' in line:
print(
f"Please use IsRefundable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE)' in line:
print(
f"Please use IsBOPTradable() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE): {file_path} at line {line_number}")
check_failed = True
if 'HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)' in line:
print(
f"Please use IsWrapped() instead of HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED): {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["ItemFlagHelpers check"] = "Failed"
# Codestyle patterns checking for ItemTemplate helpers
def itemtemplateflag_helpers_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'Flags & ITEM_FLAG' in line:
print(
f"Please use HasFlag(ItemFlag) instead of 'Flags & ITEM_FLAG_': {file_path} at line {line_number}")
check_failed = True
if 'Flags2 & ITEM_FLAG2' in line:
print(
f"Please use HasFlag2(ItemFlag2) instead of 'Flags2 & ITEM_FLAG2_': {file_path} at line {line_number}")
check_failed = True
if 'FlagsCu & ITEM_FLAGS_CU' in line:
print(
f"Please use HasFlagCu(ItemFlagsCustom) instead of 'FlagsCu & ITEM_FLAGS_CU_': {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["ItemTemplateFlagHelpers check"] = "Failed"
# Codestyle patterns checking for various codestyle issues
def misc_codestyle_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
check_failed = False
# used to check for "if/else (...) {" "} else" ignores "if/else (...) {...}" "#define ... if/else (...) {"
ifelse_curlyregex = r"^[^#define].*\s+(if|else)(\s*\(.*\))?\s*{[^}]*$|}\s*else(\s*{[^}]*$)"
# used to catch double semicolons ";;" ignores "(;;)"
double_semiregex = r"(?<!\()\s*;;(?!\))"
# used to catch tabs
tab_regex = r"\t"
# Parse all the file
for line_number, line in enumerate(file, start = 1):
if 'const auto&' in line:
print(
f"Please use the 'auto const&' syntax instead of 'const auto&': {file_path} at line {line_number}")
check_failed = True
if re.search(r'\bconst\s+\w+\s*\*\b', line):
print(
f"Please use the 'Class/ObjectType const*' syntax instead of 'const Class/ObjectType*': {file_path} at line {line_number}")
check_failed = True
if [match for match in [' if(', ' if ( '] if match in line]:
print(
f"Please use the 'if (XXXX)' syntax instead of 'if(XXXX)': {file_path} at line {line_number}")
check_failed = True
if re.match(ifelse_curlyregex, line):
print(
f"Curly brackets are not allowed to be leading or trailing if/else statements. Place it on a new line: {file_path} at line {line_number}")
check_failed = True
if re.search(double_semiregex, line):
print(
f"Double semicolon (;;) found in {file_path} at line {line_number}")
check_failed = True
if re.match(tab_regex, line):
print(
f"Tab found! Replace it to 4 spaces: {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["Misc codestyle check"] = "Failed"
# Main function
parsing_file(src_directory)

2
code_format.sh Executable file → Normal file
View File

@@ -15,4 +15,4 @@ for file in $cpp_files; do
$CLANG_FORMAT_PATH -i $file
done
echo "All .cpp or .h files have been formatted."
echo "All .cpp or .h files have been formatted."

View File

@@ -5,7 +5,7 @@
# Overview
# "Randombot": randomly generated bots that log in separately from players and populate the world. Depending on settings, randombots may automatically grind, quest, and upgrade equipment and can be invited to groups and given commands.
# "Altbot": characters created on player accounts, which may be logged in by the player and invited to groups and given commands like randombots. Depending on settings, altbots can be limited to characters on the active player's account or in the active player's guild.
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/liyunfan1223/mod-playerbots/wiki/Playerbot-Commands.
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands.
####################################################################################################
# SECTION INDEX
@@ -21,6 +21,7 @@
# THRESHOLDS
# QUESTS
# COMBAT
# PALADIN BUFFS STRATEGIES
# CHEATS
# SPELLS
# FLIGHTPATH
@@ -32,9 +33,10 @@
# ACTIVITIES
# SPELLS
# STRATEGIES
# RPG STRATEGY
# TELEPORTS
# BATTLEGROUND & ARENA & PVP
# INTERVALS
# RANDOM BOT TIMING AND BEHAVIOR
# PREMADE SPECS
# INFORMATION
# WARRIOR
@@ -157,6 +159,9 @@ AiPlayerbot.RandomBotGuildNearby = 0
# Number of guilds created by randombots
AiPlayerbot.RandomBotGuildCount = 20
# Maximum number of members in randombot guilds (minimum is hardcoded to 10)
AiPlayerbot.RandomBotGuildSizeMax = 15
# Delete all randombot guilds if set to 1
AiPlayerbot.DeleteRandomBotGuilds = 0
@@ -173,10 +178,6 @@ AiPlayerbot.SummonWhenGroup = 1
# Selfbot permission level (0 = disabled, 1 = GM only (default), 2 = all players, 3 = activate on login)
AiPlayerbot.SelfBotLevel = 1
# Give free food to bots
# Default: 1 (enabled)
AiPlayerbot.FreeFood = 1
# Non-GM player can only use init=auto to initialize bots based on their own level and gearscore
# Default: 0 (non-GM player can use any intialization commands)
AiPlayerbot.AutoInitOnly = 0
@@ -283,9 +284,6 @@ AiPlayerbot.TwoRoundsGearInit = 0
#
#
# Bots will say information about items when collecting them
AiPlayerbot.SayWhenCollectingItems = 1
# Bots keep looting when loot system is set to free for all
# Default: 0 (disabled)
AiPlayerbot.FreeMethodLoot = 0
@@ -359,15 +357,15 @@ AiPlayerbot.LootDelay = 1000
#
#
# Distances are in yards
AiPlayerbot.FarDistance = 20.0
AiPlayerbot.SightDistance = 75.0
AiPlayerbot.SightDistance = 100.0
AiPlayerbot.SpellDistance = 28.5
AiPlayerbot.ShootDistance = 5.0
AiPlayerbot.ReactDistance = 150.0
AiPlayerbot.GrindDistance = 75.0
AiPlayerbot.HealDistance = 38.5
AiPlayerbot.LootDistance = 15.0
AiPlayerbot.FleeDistance = 5.0
AiPlayerbot.AggroDistance = 22
AiPlayerbot.TooCloseDistance = 5.0
AiPlayerbot.MeleeDistance = 0.75
AiPlayerbot.FollowDistance = 1.5
@@ -375,7 +373,8 @@ AiPlayerbot.WhisperDistance = 6000.0
AiPlayerbot.ContactDistance = 0.45
AiPlayerbot.AoeRadius = 10
AiPlayerbot.RpgDistance = 200
AiPlayerbot.AggroDistance = 22
AiPlayerbot.GrindDistance = 75.0
AiPlayerbot.ReactDistance = 150.0
#
#
@@ -442,7 +441,7 @@ AiPlayerbot.AutoAvoidAoe = 1
AiPlayerbot.MaxAoeAvoidRadius = 15.0
# A whitelist of aoe spell IDs that should not be avoided
AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810
AiPlayerbot.AoeAvoidSpellWhitelist = 50759,57491,13810,29946
# Enable healer bot save mana strategy
# Default: 1 (enabled)
@@ -460,15 +459,62 @@ AiPlayerbot.FleeingEnabled = 1
#
####################################################################################################
####################################################################################################
# PALADIN BUFFS STRATEGIES
#
#
# Min group size to use Greater buffs (Paladin, Mage, Druid)
# Default: 3
AiPlayerbot.MinBotsForGreaterBuff = 3
# Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff
# Default: 30
AiPlayerbot.RPWarningCooldown = 30
#
#
#
####################################################################################################
####################################################################################################
# CHEATS
#
#
# Enable/Disable maintenance command (learn all available spells and skills, supplement consumables, repair, enchant equipment if bot's level is above AiPlayerbot.MinEnchantingBotLevel)
# Enable/Disable maintenance command
# Learn all available spells and skills, refresh consumables, repair, enchant equipment and socket gems if bot's level is above AiPlayerbot.MinEnchantingBotLevel
# Default: 1 (enabled)
AiPlayerbot.MaintenanceCommand = 1
# Enable/Disable specific maintenance command functionality for alt bots
# Disable to prevent players from giving free bags, spells, skill levels etc to their alt bots
# Default: 1 (enabled)
AiPlayerbot.AltMaintenanceAmmo = 1
AiPlayerbot.AltMaintenanceFood = 1
AiPlayerbot.AltMaintenanceReagents = 1
AiPlayerbot.AltMaintenanceConsumables = 1
AiPlayerbot.AltMaintenancePotions = 1
AiPlayerbot.AltMaintenanceBags = 1
AiPlayerbot.AltMaintenanceMounts = 1
AiPlayerbot.AltMaintenanceSkills = 1
AiPlayerbot.AltMaintenanceClassSpells = 1
AiPlayerbot.AltMaintenanceAvailableSpells = 1
AiPlayerbot.AltMaintenanceSpecialSpells = 1
AiPlayerbot.AltMaintenanceTalentTree = 1
AiPlayerbot.AltMaintenanceGlyphs = 1
AiPlayerbot.AltMaintenanceGemsEnchants = 1
AiPlayerbot.AltMaintenancePet = 1
AiPlayerbot.AltMaintenancePetTalents = 1
AiPlayerbot.AltMaintenanceReputation = 1
AiPlayerbot.AltMaintenanceAttunementQuests = 1
AiPlayerbot.AltMaintenanceKeyring = 1
# Enable/Disable autogear command, which automatically upgrades bots' gear; the quality is limited by AutoGearQualityLimit and AutoGearScoreLimit
# Default: 1 (enabled)
AiPlayerbot.AutoGearCommand = 1
@@ -490,19 +536,21 @@ AiPlayerbot.AutoGearQualityLimit = 3
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
# Wotlk
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
# Default: 0 (no limit)
AiPlayerbot.AutoGearScoreLimit = 0
# Enable/Disable cheats for bots
# "food" (bots eat or drink without using food or drinks from their inventory)
# "gold" (bots have infinite gold)
# "health" (bots have infinite health)
# "health" (bots immediately regenerate lost health)
# "mana" (bots have infinite mana)
# "power" (bots have infinite energy, rage, and runic power)
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,taxi")
# Default: taxi is enabled
AiPlayerbot.BotCheats = "taxi"
# "taxi" (bots may use all flight paths, though they will not actually learn them)
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
# Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid"
#
#
@@ -557,9 +605,11 @@ AiPlayerbot.RandomBotMaxLevel = 80
AiPlayerbot.SyncLevelWithPlayers = 0
# Mark many quests ≤ bot level as complete (slows down bot creation)
# Default: 0 (disabled)
AiPlayerbot.PreQuests = 0
# Enable LFG for randombots
# Default: 1 (enabled)
AiPlayerbot.RandomBotJoinLfg = 1
# Enable/Disable periodic online - offline of randombots to mimic the real-world scenario where not all players are online simultaneously
@@ -580,7 +630,8 @@ AiPlayerbot.RandomBotHordeRatio = 50
AiPlayerbot.DisableDeathKnightLogin = 0
# Enable simulated expansion limitation for talents and glyphs
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61 and 7 rows plus the middle talent of the 8th row for bots until level 71
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
# Default: 0 (disabled)
AiPlayerbot.LimitTalentsExpansion = 0
@@ -588,6 +639,9 @@ AiPlayerbot.LimitTalentsExpansion = 0
# Default: 1 (enabled)
AiPlayerbot.EnableRandomBotTrading = 1
# Configure message prefixes which will be excluded in analysis in trade action to open trade window
AiPlayerbot.TradeActionExcludedPrefixes = "RPLL_H_,DBMv4,{звезда} Questie,{rt1} Questie"
#
#
#
@@ -649,16 +703,20 @@ AiPlayerbot.RandomGearQualityLimit = 3
# Max iLVL Phase 1(Kara, Gruul, Mag) = 125 | Phase 1.5(ZA) = 138 | Phase 2(SC, TK) = 141 | Phase 3(Hyjal, BT) = 156 | Phase 4(Sunwell) = 164
# Wotlk
# Max iLVL Tier 7(10/25) = 200/213 | Tier 8(10/25) = 225/232 | Tier 9(10/25) = 232/245 | Tier 10(10/25/HC) = 251/264/290
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
# Max iLVL Phase 1(Naxx) = 224 | Phase 2(Ulduar) = 245 | Phase 3(ToC) = 258 | Phase 4(ICC) = 290
# Default: 0 (no limit)
AiPlayerbot.RandomGearScoreLimit = 0
# Set minimum level of bots that will enchant their equipment (Maxlevel + 1 to disable)
# If disabled, random bots can only upgrade equipment through looting and quests
# Default: 1 (enabled)
AiPlayerbot.IncrementalGearInit = 1
# Set minimum level of bots that will enchant their equipment (if greater than RandomBotMaxlevel, bots will not enchant equipment)
# Default: 60
AiPlayerbot.MinEnchantingBotLevel = 60
# Enable expansion limitation for bot enchants
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchanges until level 71
# Enable expansion limitation for bot enchants and gems
# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants and gems until level 71
# Default: 1 (enabled)
AiPlayerbot.LimitEnchantExpansion = 1
@@ -690,6 +748,20 @@ AiPlayerbot.AutoUpgradeEquip = 1
# Default: 0 (disabled)
AiPlayerbot.HunterWolfPet = 0
# Default pet stance when a bot summons a pet
# 0 = Passive, 1 = Defensive, 2 = Aggressive
# Default: 1 (Defensive)
AiPlayerbot.DefaultPetStance = 1
# Enable/disable debug messages about pet commands
# 0 = Disabled, 1 = Enabled
# Default = 0 (disabled)
AiPlayerbot.PetChatCommandDebug = 0
# Prohibit hunter bots from creating pets with any family ID listed below in ExcludedHunterPetFamilies
# See the creature_family database table for all pet families by ID (note: ID for spiders is 3)
AiPlayerbot.ExcludedHunterPetFamilies = ""
#
#
#
@@ -740,7 +812,7 @@ AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80
#
# Quest that will be completed and rewarded for all randombots
AiPlayerbot.RandomBotQuestIds = "7848,3802,5505,6502,7761,10277,10285,11492,24499,24511,24710,24712"
AiPlayerbot.RandomBotQuestIds = "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"
# Randombots will group with nearby randombots to do shared quests
AiPlayerbot.RandomBotGroupNearby = 0
@@ -749,11 +821,6 @@ AiPlayerbot.RandomBotGroupNearby = 0
# Default: 1 (enabled)
AiPlayerbot.AutoDoQuests = 1
# Randombots will behave more like real players (experimental)
# This option will override AiPlayerbot.AutoDoQuests, RandomBotTeleLowerLevel, and RandomBotTeleHigherLevel
# Default: 1 (enabled)
AiPlayerbot.EnableNewRpgStrategy = 1
# Quest items to keep in bots' inventories (do not destroy)
AiPlayerbot.RandomBotQuestItems = "5175,5176,5177,5178,6948,11000,12382,13704,16309"
@@ -796,51 +863,58 @@ AiPlayerbot.OpenGoSpell = 6477
#
# Additional randombot strategies
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec-based default strategies. These rules are processed after the defaults.
AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat"
# Strategies added here are applied to all randombots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
# Example: "+threat,-potions"
AiPlayerbot.RandomBotCombatStrategies = ""
AiPlayerbot.RandomBotNonCombatStrategies = ""
# Additional altbot strategies
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec-based default strategies. These rules are processed after the defaults.
# Strategies added here are applied to all altbots, in addition (or subtraction) to spec/role-based default strategies. These rules are processed after the defaults.
AiPlayerbot.CombatStrategies = ""
AiPlayerbot.NonCombatStrategies = ""
#
#
#
####################################################################################################
####################################################################################################
# TELEPORTS
#
#
# Maps where bots can be teleported to
AiPlayerbot.RandomBotMaps = 0,1,530,571
# Probabilty bots teleport to banker (city)
# Default: 0.25
AiPlayerbot.ProbTeleToBankers = 0.25
# How far randombots are teleported after death
AiPlayerbot.RandomBotTeleportDistance = 100
# How many levels below the lowest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 1 (randombot will leave if they are more than 1 level lower)
AiPlayerbot.RandomBotTeleLowerLevel = 1
# How many levels above the highest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 3 (randombot will leave if they are more than 3 levels higher)
AiPlayerbot.RandomBotTeleHigherLevel = 3
# Bots automatically teleport to another place for leveling on levelup
# Remove "healer dps" strategy on the maps specified below.
# Default: 1 (enabled)
AiPlayerbot.AutoTeleportForLevel = 1
AiPlayerbot.HealerDPSMapRestriction = 1
# List of Map IDs where "healer dps" strategy will be removed if AiPlayerbot.HealerDPSMapRestriction is enabled
# Default: (Dungeon and Raid maps) "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
AiPlayerbot.RestrictedHealerDPSMaps = "33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"
#
#
#
####################################################################################################
####################################################################################################
# RPG STRATEGY
#
#
# Randombots will behave more like real players (experimental)
# This option will override AiPlayerbot.AutoDoQuests, RandomBotTeleLowerLevel, and RandomBotTeleHigherLevel
# Default: 1 (enabled)
AiPlayerbot.EnableNewRpgStrategy = 1
# Control probability weights for RPG status of bots. Takes effect only when the status meets its premise.
# Sum of weights need not be 100. Set to 0 to disable the status.
#
# WanderRandom (Default: 15 Move randomly nearby to find and kill mobs)
# WanderNpc (Default: 20 Randomly interact with nearby NPCs)
# GoGrind (Default: 15 Go to nearby level-appropriate locations to grind for killing mobs)
# GoCamp (Default: 10 Return to a nearby camp depending on innkeeper/flightmaster)
# DoQuest (Default: 60 Select quest from the quest log and head to the location to attempt completion)
# TravelFlight (Default: 15 Go to the nearest flightmaster and fly to a level-appropriate area)
# Rest (Default: 5 Take a break for a while and do nothing)
AiPlayerbot.RpgStatusProbWeight.WanderRandom = 15
AiPlayerbot.RpgStatusProbWeight.WanderNpc = 20
AiPlayerbot.RpgStatusProbWeight.GoGrind = 15
AiPlayerbot.RpgStatusProbWeight.GoCamp = 10
AiPlayerbot.RpgStatusProbWeight.DoQuest = 60
AiPlayerbot.RpgStatusProbWeight.TravelFlight = 15
AiPlayerbot.RpgStatusProbWeight.Rest = 5
# Bots' minimum and maximum level when teleporting in and out of a zone, according to the new RPG strategy
# Requires EnableNewRpgStrategy enabled
# Format: AiPlayerbot.ZoneBracket.zoneID = minLevel,maxLevel
#
# Classic WoW - Low-level zones:
@@ -980,6 +1054,55 @@ AiPlayerbot.ZoneBracket.4197 = 79,80
#
####################################################################################################
####################################################################################################
# TELEPORTS
#
#
# Map IDs where bots can be teleported to
# Defaults: 0 = Eastern Kingdoms, 1 = Kalimdor, 530 = Outland, 571 = Northrend
AiPlayerbot.RandomBotMaps = 0,1,530,571
# Probabilty bots teleport to banker (city)
# Default: 0.25
AiPlayerbot.ProbTeleToBankers = 0.25
# Control probability weights for bots teleporting to Capital city bankers
# Sum of weights need not be 100. Set to 0 to disable teleporting to the city.
AiPlayerbot.EnableWeightTeleToCityBankers = 1
AiPlayerbot.TeleToStormwindWeight = 2
AiPlayerbot.TeleToIronforgeWeight = 1
AiPlayerbot.TeleToDarnassusWeight = 1
AiPlayerbot.TeleToExodarWeight = 1
AiPlayerbot.TeleToOrgrimmarWeight = 2
AiPlayerbot.TeleToUndercityWeight = 1
AiPlayerbot.TeleToThunderBluffWeight = 1
AiPlayerbot.TeleToSilvermoonCityWeight = 1
AiPlayerbot.TeleToShattrathCityWeight = 1
AiPlayerbot.TeleToDalaranWeight = 1
# How far randombots are teleported after death
AiPlayerbot.RandomBotTeleportDistance = 100
# How many levels below the lowest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 1 (randombot will leave if they are more than 1 level lower)
AiPlayerbot.RandomBotTeleLowerLevel = 1
# How many levels above the highest-level creature in a zone, can a bot be
# This will have no effect if AiPlayerbot.EnableNewRpgStrategy is enabled
# Default: 3 (randombot will leave if they are more than 3 levels higher)
AiPlayerbot.RandomBotTeleHigherLevel = 3
# Bots automatically teleport to another place for leveling on levelup
# Default: 1 (enabled)
AiPlayerbot.AutoTeleportForLevel = 1
#
#
#
####################################################################################################
####################################################################################################
# BATTLEGROUNDS & ARENAS & PVP
#
@@ -1059,10 +1182,10 @@ AiPlayerbot.RandomBotArenaTeamMinRating = 1000
AiPlayerbot.DeleteRandomBotArenaTeams = 0
# PvP Restricted Zones (bots don't pvp)
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,139"
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
# PvP Restricted Areas (bots don't pvp)
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080"
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
AiPlayerbot.FastReactInBG = 1
@@ -1073,24 +1196,46 @@ AiPlayerbot.FastReactInBG = 1
####################################################################################################
####################################################################################################
# INTERVALS
# RANDOM BOT TIMING AND BEHAVIOR
#
#
# All in seconds
# How often (in seconds) the random bot manager runs its main update loop
# Default: 20
AiPlayerbot.RandomBotUpdateInterval = 20
# Minimum and maximum seconds before the manager re-evaluates and adjusts total random bot count
# Defaults: 1800 (min), 7200 (max)
AiPlayerbot.RandomBotCountChangeMinInterval = 1800
AiPlayerbot.RandomBotCountChangeMaxInterval = 7200
# Minimum and maximum seconds a random bot will stay online before logging out
# Defaults: 600 (min), 28800 (max)
AiPlayerbot.MinRandomBotInWorldTime = 600
AiPlayerbot.MaxRandomBotInWorldTime = 28800
# Minimum and maximum seconds before a bot is eligible for re-randomization (gear, class, talents, etc.)
# Defaults: 7200 (min), 1209600 (max)
AiPlayerbot.MinRandomBotRandomizeTime = 7200
AiPlayerbot.MaxRandomBotRandomizeTime = 1209600
# Number of bots processed (login, logout, update) per manager update cycle
# Default: 60
AiPlayerbot.RandomBotsPerInterval = 60
# Minimum and maximum seconds after death before a bot revives
# Defaults: 60 (min), 300 (max)
AiPlayerbot.MinRandomBotReviveTime = 60
AiPlayerbot.MaxRandomBotReviveTime = 300
# Minimum and maximum seconds between bot teleports to new areas or zones
# Defaults: 3600 (min), 18000 (max)
AiPlayerbot.MinRandomBotTeleportInterval = 3600
AiPlayerbot.MaxRandomBotTeleportInterval = 18000
AiPlayerbot.PermanantlyInWorldTime = 31104000
# Number of seconds bots flagged as permanent stay in the world (31,104,000 ≈ 1 year)
# Default: 31104000
AiPlayerbot.PermanentlyInWorldTime = 31104000
#
#
@@ -1136,6 +1281,18 @@ AiPlayerbot.PremadeSpecName.1.2 = prot pve
AiPlayerbot.PremadeSpecGlyph.1.2 = 43424,43395,43425,43399,49084,45793
AiPlayerbot.PremadeSpecLink.1.2.60 = --053351225000210521030113321
AiPlayerbot.PremadeSpecLink.1.2.80 = 3500030023-301-053351225000210521030113321
AiPlayerbot.PremadeSpecName.1.3 = arms pvp
AiPlayerbot.PremadeSpecGlyph.1.3 = 43417,43397,43423,43396,49084,43421
AiPlayerbot.PremadeSpecLink.1.3.60 = 0320232023331100032212012221251
AiPlayerbot.PremadeSpecLink.1.3.80 = 0320332023335100232212013231251-3250001
AiPlayerbot.PremadeSpecName.1.4 = fury pvp
AiPlayerbot.PremadeSpecGlyph.1.4 = 43432,43397,43417,43395,43396,43418
AiPlayerbot.PremadeSpecLink.1.4.60 = -325000131500212250120511351
AiPlayerbot.PremadeSpecLink.1.4.80 = 03220300233-325000131500212250122511351
AiPlayerbot.PremadeSpecName.1.5 = prot pvp
AiPlayerbot.PremadeSpecGlyph.1.5 = 43425,43397,43415,43396,49084,45792
AiPlayerbot.PremadeSpecLink.1.5.60 = --250031220223012520332113321
AiPlayerbot.PremadeSpecLink.1.5.80 = 0502300123-3-250031220223012521332113321
#
#
@@ -1160,6 +1317,18 @@ AiPlayerbot.PremadeSpecGlyph.2.2 = 41092,43367,41099,43369,43365,43869
AiPlayerbot.PremadeSpecLink.2.2.60 = --05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.65 = -05-05230051203331302133231131
AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
AiPlayerbot.PremadeSpecName.2.3 = holy pvp
AiPlayerbot.PremadeSpecGlyph.2.3 = 41110,43367,45746,43366,43365,45747
AiPlayerbot.PremadeSpecLink.2.3.60 = 50332150300013050133215221
AiPlayerbot.PremadeSpecLink.2.3.80 = 50332150300013050133315221-5032013122
AiPlayerbot.PremadeSpecName.2.4 = prot pvp
AiPlayerbot.PremadeSpecGlyph.2.4 = 41092,43369,41101,43368,43365,45745
AiPlayerbot.PremadeSpecLink.2.4.60 = -15320130223122311323311321
AiPlayerbot.PremadeSpecLink.2.4.80 = -15320130223122321333312321-052300502
AiPlayerbot.PremadeSpecName.2.5 = ret pvp
AiPlayerbot.PremadeSpecGlyph.2.5 = 41095,43369,41102,43368,43365,45747
AiPlayerbot.PremadeSpecLink.2.5.60 = --05230250203331222133201321
AiPlayerbot.PremadeSpecLink.2.5.80 = -1532013022-05230250203331322133201321
#
#
@@ -1172,17 +1341,31 @@ AiPlayerbot.PremadeSpecLink.2.2.80 = 050501-05-05232051203331302133231331
#
AiPlayerbot.PremadeSpecName.3.0 = bm pve
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243110531051
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243120531251-025305101
AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.0.40 = 512002015051122301
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112233110531151
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112243130531351-005305101
AiPlayerbot.PremadeSpecName.3.1 = mm pve
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.1.60 = -025315101030013233125031051
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-025335101030013233135031351-5000002
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,45625,43351,43338,42914
AiPlayerbot.PremadeSpecLink.3.1.60 = -035305101030013233115031151
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002
AiPlayerbot.PremadeSpecName.3.2 = surv pve
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
AiPlayerbot.PremadeSpecGlyph.3.2 = 45733,43350,45731,43351,43338,45732
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321
AiPlayerbot.PremadeSpecName.3.3 = bm pvp
AiPlayerbot.PremadeSpecGlyph.3.3 = 42897,42900,42902,43356,43338,42900
AiPlayerbot.PremadeSpecLink.3.3.60 = 05203201505012233100531151
AiPlayerbot.PremadeSpecLink.3.3.80 = 05203201505012233100531351-005305101-03
AiPlayerbot.PremadeSpecName.3.4 = mm pvp
AiPlayerbot.PremadeSpecGlyph.3.4 = 42912,43351,42897,43338,43356,42904
AiPlayerbot.PremadeSpecLink.3.4.60 = -034305101030213231135031051
AiPlayerbot.PremadeSpecLink.3.4.80 = -035305101030213233135031051-53013020102
AiPlayerbot.PremadeSpecName.3.5 = surv pvp
AiPlayerbot.PremadeSpecGlyph.3.5 = 42912,43350,42904,43356,43338,45731
AiPlayerbot.PremadeSpecLink.3.5.60 = --2300302410233030533135001031
AiPlayerbot.PremadeSpecLink.3.5.80 = -005305201-2300302510233330533135001031
# HUNTER PET
#
@@ -1218,6 +1401,18 @@ AiPlayerbot.PremadeSpecName.4.2 = subtlety pve
AiPlayerbot.PremadeSpecGlyph.4.2 = 42967,43379,45764,43380,43378,45767
AiPlayerbot.PremadeSpecLink.4.2.60 = --5022012030321121350115031151
AiPlayerbot.PremadeSpecLink.4.2.80 = 30532010114--5022012030321121350115031151
AiPlayerbot.PremadeSpecName.4.3 = as pvp
AiPlayerbot.PremadeSpecGlyph.4.3 = 42974,43380,45768,43379,43376,42971
AiPlayerbot.PremadeSpecLink.4.3.60 = 005303103342102522103031--50002
AiPlayerbot.PremadeSpecLink.4.3.80 = 005303103342102522103031-004-532023203000012
AiPlayerbot.PremadeSpecName.4.4 = combat pvp
AiPlayerbot.PremadeSpecGlyph.4.4 = 42972,43380,45762,43376,43378,42971
AiPlayerbot.PremadeSpecLink.4.4.60 = -3250002050225010223102321251
AiPlayerbot.PremadeSpecLink.4.4.80 = 305120105-3250002050235010223102521251
AiPlayerbot.PremadeSpecName.4.5 = subtlety pvp
AiPlayerbot.PremadeSpecGlyph.4.5 = 42968,43376,45764,43380,43379,42971
AiPlayerbot.PremadeSpecLink.4.5.60 = --5120212030320121330133221251
AiPlayerbot.PremadeSpecLink.4.5.80 = 3023031-3-5120212030320121350135231251
#
#
@@ -1241,6 +1436,18 @@ AiPlayerbot.PremadeSpecName.5.2 = shadow pve
AiPlayerbot.PremadeSpecGlyph.5.2 = 42406,43371,42407,43374,43342,42415
AiPlayerbot.PremadeSpecLink.5.2.60 = --325003041203010323150301351
AiPlayerbot.PremadeSpecLink.5.2.80 = 0503203--325023051223010323152301351
AiPlayerbot.PremadeSpecName.5.3 = disc pvp
AiPlayerbot.PremadeSpecGlyph.5.3 = 42408,43371,45760,43370,43374,45756
AiPlayerbot.PremadeSpecLink.5.3.60 = 5003203130320512201323031051
AiPlayerbot.PremadeSpecLink.5.3.80 = 5003203130322512331013231151-23050113
AiPlayerbot.PremadeSpecName.5.4 = holy pvp
AiPlayerbot.PremadeSpecGlyph.5.4 = 42411,43371,42408,43370,43374,45755
AiPlayerbot.PremadeSpecLink.5.4.60 = -235501031000152430320031151
AiPlayerbot.PremadeSpecLink.5.4.80 = 500320313-235501031000152530320031351
AiPlayerbot.PremadeSpecName.5.5 = shadow pvp
AiPlayerbot.PremadeSpecGlyph.5.5 = 42407,43371,45753,43370,43374,42408
AiPlayerbot.PremadeSpecLink.5.5.60 = --005323241223112003102311351
AiPlayerbot.PremadeSpecLink.5.5.80 = 50332031003--005323241223112003102311351
#
#
@@ -1268,6 +1475,19 @@ AiPlayerbot.PremadeSpecName.6.3 = double aura blood pve
AiPlayerbot.PremadeSpecGlyph.6.3 = 45805,43673,43827,43544,43672,43554
AiPlayerbot.PremadeSpecLink.6.3.60 = 005512153330030320102013-305
AiPlayerbot.PremadeSpecLink.6.3.80 = 005512153330030320102013-3050505002023001-002
AiPlayerbot.PremadeSpecName.6.4 = blood pvp
AiPlayerbot.PremadeSpecGlyph.6.4 = 43534,43535,45799,43673,43672,45805
AiPlayerbot.PremadeSpecLink.6.4.60 = 2305021503003313201222101351
AiPlayerbot.PremadeSpecLink.6.4.80 = 2305021503003313201222101351--032232300023
AiPlayerbot.PremadeSpecName.6.5 = frost pvp
AiPlayerbot.PremadeSpecGlyph.6.5 = 43543,43539,45800,43673,43672,45806
AiPlayerbot.PremadeSpecLink.6.5.60 = -32015351022203012001233101251
AiPlayerbot.PremadeSpecLink.6.5.80 = 0055-32015351052203012001233131351-03
AiPlayerbot.PremadeSpecName.6.6 = unholy pvp
AiPlayerbot.PremadeSpecGlyph.6.6 = 45804,43539,43549,43673,43672,45805
AiPlayerbot.PremadeSpecLink.6.6.60 = --2301323301002152230101203103151
AiPlayerbot.PremadeSpecLink.6.6.80 = -320050410002-2301323301002152230101203133151
#
#
@@ -1284,13 +1504,26 @@ AiPlayerbot.PremadeSpecGlyph.7.0 = 41536,43385,41532,43386,44923,45776
AiPlayerbot.PremadeSpecLink.7.0.60 = 4530001520213351102301351
AiPlayerbot.PremadeSpecLink.7.0.80 = 3530001523213351322301351-005050031
AiPlayerbot.PremadeSpecName.7.1 = enh pve
AiPlayerbot.PremadeSpecGlyph.7.1 = 41542,43385,41539,43386,44923,45771
AiPlayerbot.PremadeSpecLink.7.1.60 = -30205033005001333031131131051
AiPlayerbot.PremadeSpecLink.7.1.80 = 053030052-30205033005021333031131131051
AiPlayerbot.PremadeSpecGlyph.7.1 = 41530,43385,41539,43386,44923,45771
AiPlayerbot.PremadeSpecLink.7.1.60 = -30305003105021333031121131051
AiPlayerbot.PremadeSpecLink.7.1.80 = 053030152-30305003105021333031131131051
AiPlayerbot.PremadeSpecName.7.2 = resto pve
AiPlayerbot.PremadeSpecGlyph.7.2 = 41517,43385,41527,43386,44923,45775
AiPlayerbot.PremadeSpecGlyph.7.2 = 41527,43385,41517,43386,44923,45775
AiPlayerbot.PremadeSpecLink.7.2.60 = --50005301235310501102321251
AiPlayerbot.PremadeSpecLink.7.2.80 = -00502033-50005331335310501122331251
AiPlayerbot.PremadeSpecName.7.3 = ele pvp
AiPlayerbot.PremadeSpecGlyph.7.3 = 45778,43388,45770,43725,43386,41524
AiPlayerbot.PremadeSpecLink.7.3.60 = 0533001503213051322301341
AiPlayerbot.PremadeSpecLink.7.3.80 = 0533051503213051322331351-023212001
AiPlayerbot.PremadeSpecName.7.4 = enh pvp
AiPlayerbot.PremadeSpecGlyph.7.4 = 45778,43388,41526,43725,43344,45771
AiPlayerbot.PremadeSpecLink.7.4.60 = -02305203105001333201131131151
AiPlayerbot.PremadeSpecLink.7.4.80 = 0503351-02305203105001333211131231251
AiPlayerbot.PremadeSpecName.7.5 = resto pvp
AiPlayerbot.PremadeSpecGlyph.7.5 = 45778,43388,45775,43725,43344,41535
AiPlayerbot.PremadeSpecLink.7.5.60 = --05032331331013501120321251
AiPlayerbot.PremadeSpecLink.7.5.80 = -023222301004-05032331331013501120331251
#
#
@@ -1304,8 +1537,8 @@ AiPlayerbot.PremadeSpecLink.7.2.80 = -00502033-50005331335310501122331251
AiPlayerbot.PremadeSpecName.8.0 = arcane pve
AiPlayerbot.PremadeSpecGlyph.8.0 = 42735,43339,44955,43364,43361,42751
AiPlayerbot.PremadeSpecLink.8.0.60 = 23000503110033014032310150532
AiPlayerbot.PremadeSpecLink.8.0.80 = 23000523310033015032310250532-03-203203001
AiPlayerbot.PremadeSpecLink.8.0.60 = 230005231100330150323102500321
AiPlayerbot.PremadeSpecLink.8.0.80 = 230005231100330150323102505321-03-203303001
AiPlayerbot.PremadeSpecName.8.1 = fire pve
AiPlayerbot.PremadeSpecGlyph.8.1 = 42739,43339,45737,43364,44920,42751
AiPlayerbot.PremadeSpecLink.8.1.60 = -0055030011302231053120321341
@@ -1317,7 +1550,20 @@ AiPlayerbot.PremadeSpecLink.8.2.80 = 23002303110003--053303031320310003015223135
AiPlayerbot.PremadeSpecName.8.3 = frostfire pve
AiPlayerbot.PremadeSpecGlyph.8.3 = 44684,44920,42751,43339,43364,45737
AiPlayerbot.PremadeSpecLink.8.3.60 = -2305032012303331053120300051
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120311351-023303031
AiPlayerbot.PremadeSpecLink.8.3.80 = -2305032012303331053120321351-023302031
AiPlayerbot.PremadeSpecName.8.4 = arcane pvp
AiPlayerbot.PremadeSpecGlyph.8.4 = 42735,43364,42738,43360,43357,42752
AiPlayerbot.PremadeSpecLink.8.4.60 = 205323200122032103303102015221
AiPlayerbot.PremadeSpecLink.8.4.80 = 205323200122032103303102015321-23002-303020301
AiPlayerbot.PremadeSpecName.8.5 = fire pvp
AiPlayerbot.PremadeSpecGlyph.8.5 = 42738,43364,42752,43360,43357,45737
AiPlayerbot.PremadeSpecLink.8.5.60 = -2305202312020031223122301351
AiPlayerbot.PremadeSpecLink.8.5.80 = 230321030122-2305212312020031223122301351
AiPlayerbot.PremadeSpecName.8.6 = frost pvp
AiPlayerbot.PremadeSpecGlyph.8.6 = 42738,43364,45740,43357,43360,42752
AiPlayerbot.PremadeSpecLink.8.6.60 = --3533203210203100232102231151
AiPlayerbot.PremadeSpecLink.8.6.80 = 23032103010203--3533203210203100232102231151
#
#
@@ -1333,7 +1579,7 @@ AiPlayerbot.PremadeSpecName.9.0 = affli pve
AiPlayerbot.PremadeSpecGlyph.9.0 = 45785,43390,50077,43394,43393,45779
AiPlayerbot.PremadeSpecLink.9.0.60 = 2350022001113510053500131151
AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001123510253500331151--55000005
AiPlayerbot.PremadeSpecName.9.1 = demo pve
AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051
@@ -1343,6 +1589,19 @@ AiPlayerbot.PremadeSpecName.9.2 = destro pve
AiPlayerbot.PremadeSpecGlyph.9.2 = 45785,43390,50077,43394,43393,42454
AiPlayerbot.PremadeSpecLink.9.2.60 = --05203215200231051305031151
AiPlayerbot.PremadeSpecLink.9.2.80 = 23-0302-05203215220331051335231351
AiPlayerbot.PremadeSpecName.9.3 = affli pvp
AiPlayerbot.PremadeSpecGlyph.9.3 = 50077,43392,42455,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.3.60 = 0350002231223011053502301151
AiPlayerbot.PremadeSpecLink.9.3.80 = 2350002231223111053502301151-2032003011302
AiPlayerbot.PremadeSpecName.9.4 = demo pvp
AiPlayerbot.PremadeSpecGlyph.9.4 = 42459,43392,45780,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.4.60 = -003203301135202530135001251
AiPlayerbot.PremadeSpecLink.9.4.80 = -003203301135202530135011351-052300152
AiPlayerbot.PremadeSpecName.9.5 = destro pvp
AiPlayerbot.PremadeSpecGlyph.9.5 = 42471,43392,42454,43390,43389,45783
AiPlayerbot.PremadeSpecLink.9.5.60 = --05230015220331351005031051
AiPlayerbot.PremadeSpecLink.9.5.80 = -2032003311302-05230015220331351005031051
#
#
@@ -1370,6 +1629,19 @@ AiPlayerbot.PremadeSpecName.11.3 = cat pve
AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,44922,45604
AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501
AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
AiPlayerbot.PremadeSpecName.11.4 = balance pvp
AiPlayerbot.PremadeSpecGlyph.11.4 = 40921,43331,45622,43674,43335,45623
AiPlayerbot.PremadeSpecLink.11.4.60 = 5012203115331002213032311231
AiPlayerbot.PremadeSpecLink.11.4.80 = 5022203125331003213035311231--230033012
AiPlayerbot.PremadeSpecName.11.5 = cat pvp
AiPlayerbot.PremadeSpecGlyph.11.5 = 40902,43331,45601,43674,43335,40901
AiPlayerbot.PremadeSpecLink.11.5.60 = -513202032322010053103030310501
AiPlayerbot.PremadeSpecLink.11.5.80 = -523202032322010053103030310511-205503012
AiPlayerbot.PremadeSpecName.11.6 = resto pvp
AiPlayerbot.PremadeSpecGlyph.11.6 = 40913,43331,40906,43335,43674,45623
AiPlayerbot.PremadeSpecLink.11.6.60 = --230033312031500511350013051
AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251
#
#
@@ -1387,43 +1659,14 @@ AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053100030310511-205503012
#
#
# Applies a permanent buff to all bots simulating effects of spells, flasks, food, runes, etc.
# Applies automatically refreshing buffs to bots simulating effects of spells, flasks, food, runes, etc.
# Requires sending the command "nc +worldbuff" in chat to a bot (or a group of bots) to enable
# Numbers after the equal signs are spell IDs and may be customized
# See Randombots Default Talent Specs for more info on each spec; they are listed in that section by the names in the parentheticals (e.g., arms pve, fury pve)
# Each entry in the matrix should be formatted as follows: Entry:FactionID,ClassID,SpecID,MinimumLevel,MaximumLevel:SpellID1,SpellID2,etc.;
# FactionID may be set to 0 for the entry to apply buffs to bots of either faction
# The default entries create a cross-faction list of level 80 buffs for each implemented pve spec from the "Premade Specs" section
# The default entries may be deleted or modified, and new custom entries may be added
AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS (arms pve)
AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY (fury pve)
AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION (prot pve)
AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY (holy pve)
AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION (prot pve)
AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION (ret pve)
AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST (bm pve)
AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP (mm pve)
AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL (surv pve)
AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION (as pve)
AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT (combat pve)
AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY (subtlety pve)
AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE (disc pve)
AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY (holy pve)
AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW (shadow pve)
AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD (blood pve)
AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST (frost pve)
AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY (unholy pve)
AiPlayerbot.WorldBuff.0.6.3.80.80 = 53760,57371 #DEATH KNIGHT BLOOD DPS (double aura blood pve)
AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL (ele pve)
AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT (enh pve)
AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION (resto pve)
AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE (arcane pve)
AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE (fire pve)
AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST (frost pve)
AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION (affli pve)
AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY (demo pve)
AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION (destro pve)
AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE (balance pve)
AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL BEAR (bear pve)
AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION (resto pve)
AiPlayerbot.WorldBuff.0.11.3.80.80 = 53760,57358 #DRUID FERAL CAT (cat pve)
AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358
#
#
@@ -1828,9 +2071,17 @@ AiPlayerbot.AllowedLogFiles = ""
####################################################################################################
# A list of gameObject GUID's that are not allowed for bots to interact with.
# Example: 176213 = Blood of Heroes
# Example: 17155 = Defias Gunpowder
AiPlayerbot.DisallowedGameObjects = 176213,17155
#
AiPlayerbot.DisallowedGameObjects = 176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,2857,179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,165739,165738,175245,175970,176325,176327,123329,2560
#
# List of GUID's:
# QuestItems:
# 176213 = Blood of Heroes, 17155 = Defias Gunpowder, 2656 = Waterlogged Envelope, 123329 = Baelogs Chest, 2560 = Half-Buried Bottle
# Chests:
# Large Solid Chest = 74448, Box of Assorted Parts = 19020, Food Crate = 3719, Water Barrel = 3658, Barrel of Milk = 3705, Barrel of sweet Nectar = 3706, Tattered Chest = 105579, Large bettered Chest = 75293, Solid Chest = 2857, Battered Foodlocker = 179490, Witch Doctor's Chest = 141596, Relic Coffer = 160836, Dark Coffer = 160845, Fengus's Chest = 179516, Supply Crate = 176224/181085, Malor's Strongbox = 176112
# Other:
# Shallow Grave (Zul'Farrak) = 128308/128403, Grim Guzzler Boar (Blackrock Depths) = 165739, Dark Iron Ale Mug (Blackrock Depths) = 165738, Father Flame (Blackrock Spire) = 175245, Unforged Runic Breastplate (Blackrock Spire) = 175970, Blacksmithing Plans (Stratholme) = 176325/176327
# Feel free to edit and help to complete.
#
####################################################################################################

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

View File

@@ -0,0 +1,8 @@
UPDATE guild
SET
EmblemStyle = FLOOR(RAND() * 181),
EmblemColor = FLOOR(RAND() * 18),
BorderStyle = FLOOR(RAND() * 8),
BorderColor = FLOOR(RAND() * 18),
BackgroundColor = FLOOR(RAND() * 52)
WHERE EmblemStyle=0 AND EmblemColor=0 AND BorderStyle=0 AND BorderColor=0 AND BackgroundColor=0;

View File

@@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` (
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */;
INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */;

View File

@@ -0,0 +1,8 @@
DROP TABLE IF EXISTS `playerbots_account_type`;
CREATE TABLE `playerbots_account_type` (
`account_id` int unsigned NOT NULL,
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';

View File

@@ -53,16 +53,16 @@ INSERT INTO `playerbots_dungeon_suggestion_definition` VALUES
(NULL, 'ub' , 'The Underbog' , 1, 0, 62, 70, NULL),
(NULL, 'mt' , 'Mana-Tombs' , 1, 0, 63, 70, NULL),
(NULL, 'ac' , 'Auchenai Crypts' , 1, 0, 64, 70, NULL),
(NULL, 'seth', 'Sethekk Halls' , 1, 0, 66, 70, NULL),
(NULL, 'seth', 'Sethekk Halls' , 1, 0, 66, 70, NULL),
(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 0, 66, 70, NULL),
(NULL, 'bm' , 'The Black Morass' , 1, 0, 68, 70, NULL),
(NULL, 'mech', 'The Mechanar' , 1, 0, 68, 70, NULL),
(NULL, 'bot' , 'The Botanica' , 1, 0, 69, 70, NULL),
(NULL, 'arc' , 'The Arcatraz' , 1, 0, 69, 70, NULL),
(NULL, 'sh' , 'The Shattered Halls' , 1, 0, 69, 70, NULL),
(NULL, 'sv' , 'The Steamvault' , 1, 0, 69, 70, NULL),
(NULL, 'sl' , 'Shadow Labyrinth' , 1, 0, 69, 70, NULL),
(NULL, 'mgt' , 'Magister''s Terrace' , 1, 0, 70, 70, NULL),
(NULL, 'bm' , 'The Black Morass' , 1, 0, 68, 70, NULL),
(NULL, 'mech', 'The Mechanar' , 1, 0, 68, 70, NULL),
(NULL, 'bot' , 'The Botanica' , 1, 0, 69, 70, NULL),
(NULL, 'arc' , 'The Arcatraz' , 1, 0, 69, 70, NULL),
(NULL, 'sh' , 'The Shattered Halls' , 1, 0, 69, 70, NULL),
(NULL, 'sv' , 'The Steamvault' , 1, 0, 69, 70, NULL),
(NULL, 'sl' , 'Shadow Labyrinth' , 1, 0, 69, 70, NULL),
(NULL, 'mgt' , 'Magister''s Terrace' , 1, 0, 70, 70, NULL),
-- == The Burning Crusade (Heroic) ==
@@ -72,51 +72,51 @@ INSERT INTO `playerbots_dungeon_suggestion_definition` VALUES
(NULL, 'ub' , 'The Underbog' , 1, 1, 70, 70, NULL),
(NULL, 'mt' , 'Mana-Tombs' , 1, 1, 70, 70, NULL),
(NULL, 'ac' , 'Auchenai Crypts' , 1, 1, 70, 70, NULL),
(NULL, 'seth', 'Sethekk Halls' , 1, 1, 70, 70, NULL),
(NULL, 'seth', 'Sethekk Halls' , 1, 1, 70, 70, NULL),
(NULL, 'oh' , 'Old Hillsbrad Foothills', 1, 1, 70, 70, NULL),
(NULL, 'bm' , 'The Black Morass' , 1, 1, 70, 70, NULL),
(NULL, 'mech', 'The Mechanar' , 1, 1, 70, 70, NULL),
(NULL, 'bot' , 'The Botanica' , 1, 1, 70, 70, NULL),
(NULL, 'arc' , 'The Arcatraz' , 1, 1, 70, 70, NULL),
(NULL, 'bm' , 'The Black Morass' , 1, 1, 70, 70, NULL),
(NULL, 'mech', 'The Mechanar' , 1, 1, 70, 70, NULL),
(NULL, 'bot' , 'The Botanica' , 1, 1, 70, 70, NULL),
(NULL, 'arc' , 'The Arcatraz' , 1, 1, 70, 70, NULL),
(NULL, 'sh' , 'The Shattered Halls' , 1, 1, 70, 70, NULL),
(NULL, 'sv' , 'The Steamvault' , 1, 1, 70, 70, NULL),
(NULL, 'sl' , 'Shadow Labyrinth' , 1, 1, 70, 70, NULL),
(NULL, 'sv' , 'The Steamvault' , 1, 1, 70, 70, NULL),
(NULL, 'sl' , 'Shadow Labyrinth' , 1, 1, 70, 70, NULL),
(NULL, 'mgt' , 'Magister''s Terrace' , 1, 1, 70, 70, NULL),
-- == Wrath of the Lich King ==
(NULL, 'uk' , 'Utgarde Keep' , 2, 0, 70, 72, NULL),
(NULL, 'nexus' , 'The Nexus' , 2, 0, 71, 73, NULL),
(NULL, 'an' , 'Azjol-Nerub' , 2, 0, 72, 74, NULL),
(NULL, 'uk' , 'Utgarde Keep' , 2, 0, 70, 72, NULL),
(NULL, 'nexus' , 'The Nexus' , 2, 0, 71, 73, NULL),
(NULL, 'an' , 'Azjol-Nerub' , 2, 0, 72, 74, NULL),
(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 0, 73, 75, NULL),
(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 0, 74, 76, NULL),
(NULL, 'vh' , 'Violet Hold' , 2, 0, 75, 77, NULL),
(NULL, 'gd' , 'Gundrak' , 2, 0, 76, 78, NULL),
(NULL, 'hos' , 'Halls of Stone' , 2, 0, 77, 79, NULL),
(NULL, 'hol' , 'Halls of Lightning' , 2, 0, 80, 80, NULL),
(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 0, 74, 76, NULL),
(NULL, 'vh' , 'Violet Hold' , 2, 0, 75, 77, NULL),
(NULL, 'gd' , 'Gundrak' , 2, 0, 76, 78, NULL),
(NULL, 'hos' , 'Halls of Stone' , 2, 0, 77, 79, NULL),
(NULL, 'hol' , 'Halls of Lightning' , 2, 0, 80, 80, NULL),
(NULL, 'cos' , 'The Culling of Stratholme' , 2, 0, 80, 80, NULL),
(NULL, 'oculus', 'The Oculus' , 2, 0, 80, 80, NULL),
(NULL, 'up' , 'Utgarde Pinnacle' , 2, 0, 80, 80, NULL),
(NULL, 'toc' , 'Trial of the Champion' , 2, 0, 80, 80, NULL),
(NULL, 'fos' , 'Forge of Souls' , 2, 0, 80, 80, NULL),
(NULL, 'pos' , 'Pit of Saron' , 2, 0, 80, 80, NULL),
(NULL, 'hor' , 'Halls of Reflection' , 2, 0, 80, 80, NULL),
(NULL, 'oculus', 'The Oculus' , 2, 0, 80, 80, NULL),
(NULL, 'up' , 'Utgarde Pinnacle' , 2, 0, 80, 80, NULL),
(NULL, 'toc' , 'Trial of the Champion' , 2, 0, 80, 80, NULL),
(NULL, 'fos' , 'Forge of Souls' , 2, 0, 80, 80, NULL),
(NULL, 'pos' , 'Pit of Saron' , 2, 0, 80, 80, NULL),
(NULL, 'hor' , 'Halls of Reflection' , 2, 0, 80, 80, NULL),
-- == Wrath of the Lich King (Heroic) ==
(NULL, 'uk' , 'Utgarde Keep' , 2, 1, 80, 80, NULL),
(NULL, 'nexus' , 'The Nexus' , 2, 1, 80, 80, NULL),
(NULL, 'an' , 'Azjol-Nerub' , 2, 1, 80, 80, NULL),
(NULL, 'uk' , 'Utgarde Keep' , 2, 1, 80, 80, NULL),
(NULL, 'nexus' , 'The Nexus' , 2, 1, 80, 80, NULL),
(NULL, 'an' , 'Azjol-Nerub' , 2, 1, 80, 80, NULL),
(NULL, 'ak' , 'Ahn''kahet: The Old Kingdom', 2, 1, 80, 80, NULL),
(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 1, 80, 80, NULL),
(NULL, 'vh' , 'Violet Hold' , 2, 1, 80, 80, NULL),
(NULL, 'gd' , 'Gundrak' , 2, 1, 80, 80, NULL),
(NULL, 'hos' , 'Halls of Stone' , 2, 1, 80, 80, NULL),
(NULL, 'hol' , 'Halls of Lightning' , 2, 1, 80, 80, NULL),
(NULL, 'dtk' , 'Drak''Tharon Keep' , 2, 1, 80, 80, NULL),
(NULL, 'vh' , 'Violet Hold' , 2, 1, 80, 80, NULL),
(NULL, 'gd' , 'Gundrak' , 2, 1, 80, 80, NULL),
(NULL, 'hos' , 'Halls of Stone' , 2, 1, 80, 80, NULL),
(NULL, 'hol' , 'Halls of Lightning' , 2, 1, 80, 80, NULL),
(NULL, 'cos' , 'The Culling of Stratholme' , 2, 1, 80, 80, NULL),
(NULL, 'oculus', 'The Oculus' , 2, 1, 80, 80, NULL),
(NULL, 'up' , 'Utgarde Pinnacle' , 2, 1, 80, 80, NULL),
(NULL, 'toc' , 'Trial of the Champion' , 2, 1, 80, 80, NULL),
(NULL, 'fos' , 'Forge of Souls' , 2, 1, 80, 80, NULL),
(NULL, 'pos' , 'Pit of Saron' , 2, 1, 80, 80, NULL),
(NULL, 'hor' , 'Halls of Reflection' , 2, 1, 80, 80, NULL);
(NULL, 'oculus', 'The Oculus' , 2, 1, 80, 80, NULL),
(NULL, 'up' , 'Utgarde Pinnacle' , 2, 1, 80, 80, NULL),
(NULL, 'toc' , 'Trial of the Champion' , 2, 1, 80, 80, NULL),
(NULL, 'fos' , 'Forge of Souls' , 2, 1, 80, 80, NULL),
(NULL, 'pos' , 'Pit of Saron' , 2, 1, 80, 80, NULL),
(NULL, 'hor' , 'Halls of Reflection' , 2, 1, 80, 80, NULL);

View File

@@ -25,226 +25,226 @@ CREATE TABLE `playerbots_enchants` (
/*!40000 ALTER TABLE `playerbots_enchants` DISABLE KEYS */;
DELETE FROM `playerbots_enchants`;
INSERT INTO `playerbots_enchants` (`class`, `spec`, `spellid`, `slotid`, `name`) VALUES
(1, 10, 20034, 15, 'Crusader '),
(1, 10, 22779, 17, '30 Hit '),
(1, 10, 27927, 10, '4 All Stats '),
(1, 10, 27927, 11, '4 All Stats '),
(1, 10, 27960, 4, '6 All Stats '),
(1, 10, 27984, 16, 'Mongoose '),
(1, 10, 29483, 2, '26 Attackpower 14 Crit '),
(1, 10, 33996, 9, '26 Attackpower '),
(1, 10, 34002, 8, '24 Attackpower '),
(1, 10, 34004, 14, '12 Agility '),
(1, 10, 34007, 7, 'Minor Speed 6 Agility'),
(1, 10, 35452, 0, '34 Attackpower 16 Hit '),
(1, 10, 35490, 6, '50 Attackpower 12 Crit '),
(1, 11, 20034, 15, 'Crusader '),
(1, 11, 20034, 16, 'Crusader '),
(1, 11, 22779, 17, '30 Hit '),
(1, 11, 27927, 10, '4 All Stats '),
(1, 11, 27927, 11, '4 All Stats '),
(1, 11, 27960, 4, '6 All Stats '),
(1, 11, 29483, 2, '26 Attackpower 14 Crit '),
(1, 11, 33996, 9, '26 Attackpower '),
(1, 11, 34002, 8, '24 Attackpower '),
(1, 11, 34004, 14, '12 Agility '),
(1, 11, 34007, 7, 'Minor Speed 6 Agility'),
(1, 11, 35452, 0, '34 Attackpower 16 Hit '),
(1, 11, 35490, 6, '50 Attackpower 12 Crit '),
(1, 12, 22779, 17, '30 Hit '),
(1, 12, 25072, 9, '2% Threat '),
(1, 12, 27906, 8, '12 Defense '),
(1, 12, 27927, 10, '4 All Stats '),
(1, 12, 27927, 11, '4 All Stats '),
(1, 12, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(1, 12, 27960, 4, '6 All Stats '),
(1, 12, 28004, 15, 'Battlemaster '),
(1, 12, 34009, 16, '18 Stamina '),
(1, 12, 35433, 2, '10 Dodge 15 Defense '),
(1, 12, 35443, 0, '16 Defense 17 Dodge '),
(1, 12, 35495, 6, '40 Stamina 12 Agility '),
(1, 12, 47051, 14, '12 Defense '),
(2, 20, 22779, 17, '30 Hit '),
(2, 20, 27926, 10, '20 Healing 7 Spelldamage '),
(2, 20, 27926, 11, '20 Healing 7 Spelldamage '),
(2, 20, 27945, 16, '12 Intellect '),
(2, 20, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(2, 20, 27960, 4, '6 All Stats '),
(2, 20, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(2, 20, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(2, 20, 33999, 9, '35 Healing 12 Spelldamage '),
(2, 20, 34001, 8, '12 Intellect '),
(2, 20, 34003, 14, '20 Spell Penetration '),
(2, 20, 34010, 15, '81 Healing 27 Spelldamage '),
(2, 20, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(2, 21, 22779, 17, '30 Hit '),
(2, 21, 25072, 9, '2% Threat '),
(2, 21, 27906, 8, '12 Defense '),
(2, 21, 27927, 10, '4 All Stats '),
(2, 21, 27927, 11, '4 All Stats '),
(2, 21, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(2, 21, 27960, 4, '6 All Stats '),
(2, 21, 28004, 15, 'Battlemaster '),
(2, 21, 34009, 16, '18 Stamina '),
(2, 21, 35433, 2, '10 Dodge 15 Defense '),
(2, 21, 35443, 0, '16 Defense 17 Dodge '),
(2, 21, 35495, 6, '40 Stamina 12 Agility '),
(2, 21, 47051, 14, '12 Defense '),
(2, 22, 20034, 15, 'Crusader '),
(2, 22, 22779, 17, '30 Hit '),
(2, 22, 27899, 8, '12 Strength '),
(2, 22, 27927, 10, '4 All Stats '),
(2, 22, 27927, 11, '4 All Stats '),
(2, 22, 27960, 4, '6 All Stats '),
(2, 22, 29483, 2, '26 Attackpower 14 Crit '),
(2, 22, 33995, 6, '50 Attackpower 12 Crit '),
(2, 22, 33996, 9, '15 Strength '),
(2, 22, 34004, 14, '12 Agility '),
(2, 22, 34007, 7, 'Minor Speed 6 Agility'),
(2, 22, 37891, 0, '17 Strength16 Intellect '),
(3, 30, 22779, 17, '30 Hit '),
(3, 30, 25080, 9, '15 Agility '),
(3, 30, 27927, 10, '4 All Stats '),
(3, 30, 27927, 11, '4 All Stats '),
(3, 30, 27951, 7, '12 Agility'),
(3, 30, 27960, 4, '6 All Stats '),
(3, 30, 29483, 2, '26 Attackpower 14 Crit '),
(3, 30, 34002, 8, '24 Attackpower '),
(3, 30, 34004, 14, '12 Agility '),
(3, 30, 35452, 0, '34 Attackpower 16 Hit '),
(3, 30, 35495, 6, '40 Stamina 12 Agility '),
(3, 30, 42620, 15, 'Greater Agility '),
(3, 30, 42620, 16, 'Greater Agility '),
(4, 40, 22779, 17, '30 Hit '),
(4, 40, 25080, 9, '15 Agility '),
(4, 40, 27927, 10, '4 All Stats '),
(4, 40, 27927, 11, '4 All Stats '),
(4, 40, 27951, 7, '12 Agility'),
(4, 40, 27960, 4, '6 All Stats '),
(4, 40, 27984, 15, 'Mongoose '),
(4, 40, 27984, 16, 'Mongoose '),
(4, 40, 29483, 2, '26 Attackpower 14 Crit '),
(4, 40, 34002, 8, '24 Attackpower '),
(4, 40, 34004, 14, '12 Agility '),
(4, 40, 35452, 0, '34 Attackpower 16 Hit '),
(4, 40, 35495, 6, '40 Stamina 12 Agility '),
(5, 50, 22779, 17, '30 Hit '),
(5, 50, 27926, 10, '20 Healing 7 Spelldamage '),
(5, 50, 27926, 11, '20 Healing 7 Spelldamage '),
(5, 50, 27945, 16, '12 Intellect '),
(5, 50, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(5, 50, 27960, 4, '6 All Stats '),
(5, 50, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(5, 50, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(5, 50, 33999, 9, '35 Healing 12 Spelldamage '),
(5, 50, 34001, 8, '12 Intellect '),
(5, 50, 34003, 14, '20 Spell Penetration '),
(5, 50, 34010, 15, '81 Healing 27 Spelldamage '),
(5, 50, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(7, 70, 22779, 17, '30 Hit '),
(7, 70, 27926, 10, '20 Healing 7 Spelldamage '),
(7, 70, 27926, 11, '20 Healing 7 Spelldamage '),
(7, 70, 27945, 16, '12 Intellect '),
(7, 70, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(7, 70, 27960, 4, '6 All Stats '),
(7, 70, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(7, 70, 33994, 9, '15 Spell Hit '),
(7, 70, 34001, 8, '12 Intellect '),
(7, 70, 34003, 14, '20 Spell Penetration '),
(7, 70, 34010, 15, '81 Healing 27 Spelldamage '),
(7, 70, 35406, 2, '18 Spelldamage 10 Crit '),
(7, 70, 35447, 0, '22 Spelldamage 14 Hit '),
(7, 71, 22779, 17, '30 Hit '),
(7, 71, 25080, 9, '15 Agility '),
(7, 71, 27927, 10, '4 All Stats '),
(7, 71, 27927, 11, '4 All Stats '),
(7, 71, 27951, 7, '12 Agility'),
(7, 71, 27960, 4, '6 All Stats '),
(7, 71, 27977, 15, '35 Agility '),
(7, 71, 27984, 16, 'Mongoose '),
(7, 71, 29483, 2, '26 Attackpower 14 Crit '),
(7, 71, 34002, 8, '24 Attackpower '),
(7, 71, 34004, 14, '12 Agility '),
(7, 71, 35452, 0, '34 Attackpower 16 Hit '),
(7, 71, 35495, 6, '40 Stamina 12 Agility '),
(7, 72, 22779, 17, '30 Hit '),
(7, 72, 27926, 10, '20 Healing 7 Spelldamage '),
(7, 72, 27926, 11, '20 Healing 7 Spelldamage '),
(7, 72, 27945, 16, '12 Intellect '),
(7, 72, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(7, 72, 27960, 4, '6 All Stats '),
(7, 72, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(7, 72, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(7, 72, 33999, 9, '35 Healing 12 Spelldamage '),
(7, 72, 34001, 8, '12 Intellect '),
(7, 72, 34003, 14, '20 Spell Penetration '),
(7, 72, 34010, 15, '81 Healing 27 Spelldamage '),
(7, 72, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(8, 80, 22779, 17, '30 Hit '),
(8, 80, 27927, 10, '4 All Stats '),
(8, 80, 27927, 11, '4 All Stats '),
(8, 80, 27945, 16, '12 Intellect '),
(8, 80, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(8, 80, 27960, 4, '6 All Stats '),
(8, 80, 27975, 15, '40 Spelldamage '),
(8, 80, 31372, 6, '35 Spelldamage 20 Stamina '),
(8, 80, 33994, 9, '15 Spell Hit '),
(8, 80, 34001, 8, '12 Intellect '),
(8, 80, 34003, 14, '20 Spell Penetration '),
(8, 80, 35406, 2, '18 Spelldamage 10 Crit '),
(8, 80, 35447, 0, '22 Spelldamage 14 Hit '),
(9, 90, 22779, 17, '30 Hit '),
(9, 90, 27924, 11, '12 Spelldamage '),
(9, 90, 27927, 10, '4 All Stats '),
(9, 90, 27945, 16, '12 Intellect '),
(9, 90, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(9, 90, 27960, 4, '6 All Stats '),
(9, 90, 27975, 15, '40 Spelldamage '),
(9, 90, 31372, 6, '35 Spelldamage 20 Stamina '),
(9, 90, 33994, 9, '15 Spell Hit '),
(9, 90, 34001, 8, '12 Intellect '),
(9, 90, 34003, 14, '20 Spell Penetration '),
(9, 90, 35406, 2, '18 Spelldamage 10 Crit '),
(9, 90, 35447, 0, '22 Spelldamage 14 Hit '),
(11, 110, 22779, 17, '30 Hit '),
(11, 110, 27926, 10, '20 Healing 7 Spelldamage '),
(11, 110, 27926, 11, '20 Healing 7 Spelldamage '),
(11, 110, 27945, 16, '12 Intellect '),
(11, 110, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(11, 110, 27960, 4, '6 All Stats '),
(11, 110, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(11, 110, 33994, 9, '15 Spell Hit '),
(11, 110, 34001, 8, '12 Intellect '),
(11, 110, 34003, 14, '20 Spell Penetration '),
(11, 110, 34010, 15, '81 Healing 27 Spelldamage '),
(11, 110, 35406, 2, '18 Spelldamage 10 Crit '),
(11, 110, 35447, 0, '22 Spelldamage 14 Hit '),
(11, 111, 22779, 17, '30 Hit '),
(11, 111, 25080, 9, '15 Agility '),
(11, 111, 27927, 10, '4 All Stats '),
(11, 111, 27927, 11, '4 All Stats '),
(11, 111, 27951, 7, '12 Agility'),
(11, 111, 27960, 4, '6 All Stats '),
(11, 111, 29483, 2, '26 Attackpower 14 Crit '),
(11, 111, 34002, 8, '24 Attackpower '),
(11, 111, 34004, 14, '12 Agility '),
(11, 111, 35452, 0, '34 Attackpower 16 Hit '),
(11, 111, 35495, 6, '40 Stamina 12 Agility '),
(11, 111, 42620, 15, 'Greater Agility '),
(11, 111, 42620, 16, 'Greater Agility '),
(11, 112, 22779, 17, '30 Hit '),
(11, 112, 27926, 10, '20 Healing 7 Spelldamage '),
(11, 112, 27926, 11, '20 Healing 7 Spelldamage '),
(11, 112, 27945, 16, '12 Intellect '),
(11, 112, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(11, 112, 27960, 4, '6 All Stats '),
(11, 112, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(11, 112, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(11, 112, 33999, 9, '35 Healing 12 Spelldamage '),
(11, 112, 34001, 8, '12 Intellect '),
(11, 112, 34003, 14, '20 Spell Penetration '),
(11, 112, 34010, 15, '81 Healing 27 Spelldamage '),
(11, 112, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 ');
(1, 10, 20034, 15, 'Crusader '),
(1, 10, 22779, 17, '30 Hit '),
(1, 10, 27927, 10, '4 All Stats '),
(1, 10, 27927, 11, '4 All Stats '),
(1, 10, 27960, 4, '6 All Stats '),
(1, 10, 27984, 16, 'Mongoose '),
(1, 10, 29483, 2, '26 Attackpower 14 Crit '),
(1, 10, 33996, 9, '26 Attackpower '),
(1, 10, 34002, 8, '24 Attackpower '),
(1, 10, 34004, 14, '12 Agility '),
(1, 10, 34007, 7, 'Minor Speed 6 Agility'),
(1, 10, 35452, 0, '34 Attackpower 16 Hit '),
(1, 10, 35490, 6, '50 Attackpower 12 Crit '),
(1, 11, 20034, 15, 'Crusader '),
(1, 11, 20034, 16, 'Crusader '),
(1, 11, 22779, 17, '30 Hit '),
(1, 11, 27927, 10, '4 All Stats '),
(1, 11, 27927, 11, '4 All Stats '),
(1, 11, 27960, 4, '6 All Stats '),
(1, 11, 29483, 2, '26 Attackpower 14 Crit '),
(1, 11, 33996, 9, '26 Attackpower '),
(1, 11, 34002, 8, '24 Attackpower '),
(1, 11, 34004, 14, '12 Agility '),
(1, 11, 34007, 7, 'Minor Speed 6 Agility'),
(1, 11, 35452, 0, '34 Attackpower 16 Hit '),
(1, 11, 35490, 6, '50 Attackpower 12 Crit '),
(1, 12, 22779, 17, '30 Hit '),
(1, 12, 25072, 9, '2% Threat '),
(1, 12, 27906, 8, '12 Defense '),
(1, 12, 27927, 10, '4 All Stats '),
(1, 12, 27927, 11, '4 All Stats '),
(1, 12, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(1, 12, 27960, 4, '6 All Stats '),
(1, 12, 28004, 15, 'Battlemaster '),
(1, 12, 34009, 16, '18 Stamina '),
(1, 12, 35433, 2, '10 Dodge 15 Defense '),
(1, 12, 35443, 0, '16 Defense 17 Dodge '),
(1, 12, 35495, 6, '40 Stamina 12 Agility '),
(1, 12, 47051, 14, '12 Defense '),
(2, 20, 22779, 17, '30 Hit '),
(2, 20, 27926, 10, '20 Healing 7 Spelldamage '),
(2, 20, 27926, 11, '20 Healing 7 Spelldamage '),
(2, 20, 27945, 16, '12 Intellect '),
(2, 20, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(2, 20, 27960, 4, '6 All Stats '),
(2, 20, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(2, 20, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(2, 20, 33999, 9, '35 Healing 12 Spelldamage '),
(2, 20, 34001, 8, '12 Intellect '),
(2, 20, 34003, 14, '20 Spell Penetration '),
(2, 20, 34010, 15, '81 Healing 27 Spelldamage '),
(2, 20, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(2, 21, 22779, 17, '30 Hit '),
(2, 21, 25072, 9, '2% Threat '),
(2, 21, 27906, 8, '12 Defense '),
(2, 21, 27927, 10, '4 All Stats '),
(2, 21, 27927, 11, '4 All Stats '),
(2, 21, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(2, 21, 27960, 4, '6 All Stats '),
(2, 21, 28004, 15, 'Battlemaster '),
(2, 21, 34009, 16, '18 Stamina '),
(2, 21, 35433, 2, '10 Dodge 15 Defense '),
(2, 21, 35443, 0, '16 Defense 17 Dodge '),
(2, 21, 35495, 6, '40 Stamina 12 Agility '),
(2, 21, 47051, 14, '12 Defense '),
(2, 22, 20034, 15, 'Crusader '),
(2, 22, 22779, 17, '30 Hit '),
(2, 22, 27899, 8, '12 Strength '),
(2, 22, 27927, 10, '4 All Stats '),
(2, 22, 27927, 11, '4 All Stats '),
(2, 22, 27960, 4, '6 All Stats '),
(2, 22, 29483, 2, '26 Attackpower 14 Crit '),
(2, 22, 33995, 6, '50 Attackpower 12 Crit '),
(2, 22, 33996, 9, '15 Strength '),
(2, 22, 34004, 14, '12 Agility '),
(2, 22, 34007, 7, 'Minor Speed 6 Agility'),
(2, 22, 37891, 0, '17 Strength16 Intellect '),
(3, 30, 22779, 17, '30 Hit '),
(3, 30, 25080, 9, '15 Agility '),
(3, 30, 27927, 10, '4 All Stats '),
(3, 30, 27927, 11, '4 All Stats '),
(3, 30, 27951, 7, '12 Agility'),
(3, 30, 27960, 4, '6 All Stats '),
(3, 30, 29483, 2, '26 Attackpower 14 Crit '),
(3, 30, 34002, 8, '24 Attackpower '),
(3, 30, 34004, 14, '12 Agility '),
(3, 30, 35452, 0, '34 Attackpower 16 Hit '),
(3, 30, 35495, 6, '40 Stamina 12 Agility '),
(3, 30, 42620, 15, 'Greater Agility '),
(3, 30, 42620, 16, 'Greater Agility '),
(4, 40, 22779, 17, '30 Hit '),
(4, 40, 25080, 9, '15 Agility '),
(4, 40, 27927, 10, '4 All Stats '),
(4, 40, 27927, 11, '4 All Stats '),
(4, 40, 27951, 7, '12 Agility'),
(4, 40, 27960, 4, '6 All Stats '),
(4, 40, 27984, 15, 'Mongoose '),
(4, 40, 27984, 16, 'Mongoose '),
(4, 40, 29483, 2, '26 Attackpower 14 Crit '),
(4, 40, 34002, 8, '24 Attackpower '),
(4, 40, 34004, 14, '12 Agility '),
(4, 40, 35452, 0, '34 Attackpower 16 Hit '),
(4, 40, 35495, 6, '40 Stamina 12 Agility '),
(5, 50, 22779, 17, '30 Hit '),
(5, 50, 27926, 10, '20 Healing 7 Spelldamage '),
(5, 50, 27926, 11, '20 Healing 7 Spelldamage '),
(5, 50, 27945, 16, '12 Intellect '),
(5, 50, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(5, 50, 27960, 4, '6 All Stats '),
(5, 50, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(5, 50, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(5, 50, 33999, 9, '35 Healing 12 Spelldamage '),
(5, 50, 34001, 8, '12 Intellect '),
(5, 50, 34003, 14, '20 Spell Penetration '),
(5, 50, 34010, 15, '81 Healing 27 Spelldamage '),
(5, 50, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(7, 70, 22779, 17, '30 Hit '),
(7, 70, 27926, 10, '20 Healing 7 Spelldamage '),
(7, 70, 27926, 11, '20 Healing 7 Spelldamage '),
(7, 70, 27945, 16, '12 Intellect '),
(7, 70, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(7, 70, 27960, 4, '6 All Stats '),
(7, 70, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(7, 70, 33994, 9, '15 Spell Hit '),
(7, 70, 34001, 8, '12 Intellect '),
(7, 70, 34003, 14, '20 Spell Penetration '),
(7, 70, 34010, 15, '81 Healing 27 Spelldamage '),
(7, 70, 35406, 2, '18 Spelldamage 10 Crit '),
(7, 70, 35447, 0, '22 Spelldamage 14 Hit '),
(7, 71, 22779, 17, '30 Hit '),
(7, 71, 25080, 9, '15 Agility '),
(7, 71, 27927, 10, '4 All Stats '),
(7, 71, 27927, 11, '4 All Stats '),
(7, 71, 27951, 7, '12 Agility'),
(7, 71, 27960, 4, '6 All Stats '),
(7, 71, 27977, 15, '35 Agility '),
(7, 71, 27984, 16, 'Mongoose '),
(7, 71, 29483, 2, '26 Attackpower 14 Crit '),
(7, 71, 34002, 8, '24 Attackpower '),
(7, 71, 34004, 14, '12 Agility '),
(7, 71, 35452, 0, '34 Attackpower 16 Hit '),
(7, 71, 35495, 6, '40 Stamina 12 Agility '),
(7, 72, 22779, 17, '30 Hit '),
(7, 72, 27926, 10, '20 Healing 7 Spelldamage '),
(7, 72, 27926, 11, '20 Healing 7 Spelldamage '),
(7, 72, 27945, 16, '12 Intellect '),
(7, 72, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(7, 72, 27960, 4, '6 All Stats '),
(7, 72, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(7, 72, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(7, 72, 33999, 9, '35 Healing 12 Spelldamage '),
(7, 72, 34001, 8, '12 Intellect '),
(7, 72, 34003, 14, '20 Spell Penetration '),
(7, 72, 34010, 15, '81 Healing 27 Spelldamage '),
(7, 72, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 '),
(8, 80, 22779, 17, '30 Hit '),
(8, 80, 27927, 10, '4 All Stats '),
(8, 80, 27927, 11, '4 All Stats '),
(8, 80, 27945, 16, '12 Intellect '),
(8, 80, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(8, 80, 27960, 4, '6 All Stats '),
(8, 80, 27975, 15, '40 Spelldamage '),
(8, 80, 31372, 6, '35 Spelldamage 20 Stamina '),
(8, 80, 33994, 9, '15 Spell Hit '),
(8, 80, 34001, 8, '12 Intellect '),
(8, 80, 34003, 14, '20 Spell Penetration '),
(8, 80, 35406, 2, '18 Spelldamage 10 Crit '),
(8, 80, 35447, 0, '22 Spelldamage 14 Hit '),
(9, 90, 22779, 17, '30 Hit '),
(9, 90, 27924, 11, '12 Spelldamage '),
(9, 90, 27927, 10, '4 All Stats '),
(9, 90, 27945, 16, '12 Intellect '),
(9, 90, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(9, 90, 27960, 4, '6 All Stats '),
(9, 90, 27975, 15, '40 Spelldamage '),
(9, 90, 31372, 6, '35 Spelldamage 20 Stamina '),
(9, 90, 33994, 9, '15 Spell Hit '),
(9, 90, 34001, 8, '12 Intellect '),
(9, 90, 34003, 14, '20 Spell Penetration '),
(9, 90, 35406, 2, '18 Spelldamage 10 Crit '),
(9, 90, 35447, 0, '22 Spelldamage 14 Hit '),
(11, 110, 22779, 17, '30 Hit '),
(11, 110, 27926, 10, '20 Healing 7 Spelldamage '),
(11, 110, 27926, 11, '20 Healing 7 Spelldamage '),
(11, 110, 27945, 16, '12 Intellect '),
(11, 110, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(11, 110, 27960, 4, '6 All Stats '),
(11, 110, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(11, 110, 33994, 9, '15 Spell Hit '),
(11, 110, 34001, 8, '12 Intellect '),
(11, 110, 34003, 14, '20 Spell Penetration '),
(11, 110, 34010, 15, '81 Healing 27 Spelldamage '),
(11, 110, 35406, 2, '18 Spelldamage 10 Crit '),
(11, 110, 35447, 0, '22 Spelldamage 14 Hit '),
(11, 111, 22779, 17, '30 Hit '),
(11, 111, 25080, 9, '15 Agility '),
(11, 111, 27927, 10, '4 All Stats '),
(11, 111, 27927, 11, '4 All Stats '),
(11, 111, 27951, 7, '12 Agility'),
(11, 111, 27960, 4, '6 All Stats '),
(11, 111, 29483, 2, '26 Attackpower 14 Crit '),
(11, 111, 34002, 8, '24 Attackpower '),
(11, 111, 34004, 14, '12 Agility '),
(11, 111, 35452, 0, '34 Attackpower 16 Hit '),
(11, 111, 35495, 6, '40 Stamina 12 Agility '),
(11, 111, 42620, 15, 'Greater Agility '),
(11, 111, 42620, 16, 'Greater Agility '),
(11, 112, 22779, 17, '30 Hit '),
(11, 112, 27926, 10, '20 Healing 7 Spelldamage '),
(11, 112, 27926, 11, '20 Healing 7 Spelldamage '),
(11, 112, 27945, 16, '12 Intellect '),
(11, 112, 27954, 7, '5% Root/Snare Resist 10 Hit'),
(11, 112, 27960, 4, '6 All Stats '),
(11, 112, 29475, 2, '31 Healing 11 Spelldamage 5 mp5 '),
(11, 112, 31370, 6, '66 Healing 22 Spelldamage 20 Stamina '),
(11, 112, 33999, 9, '35 Healing 12 Spelldamage '),
(11, 112, 34001, 8, '12 Intellect '),
(11, 112, 34003, 14, '20 Spell Penetration '),
(11, 112, 34010, 15, '81 Healing 27 Spelldamage '),
(11, 112, 35445, 0, '35 Healing 12 Spelldamage 7 mp5 ');
/*!40000 ALTER TABLE `playerbots_enchants` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;

View File

@@ -7,278 +7,278 @@ CREATE TABLE IF NOT EXISTS `playerbots_weightscale_data` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `playerbots_weightscale_data` (`id`, `field`, `val`) VALUES
(1, 'exprtng', 100),
(1, 'str', 82),
(1, 'critstrkrtng', 66),
(1, 'agi', 53),
(1, 'armorpenrtng', 52),
(1, 'hitrtng', 48),
(1, 'hastertng', 36),
(1, 'atkpwr', 31),
(1, 'armor', 5),
(2, 'exprtng', 100),
(2, 'str', 82),
(2, 'critstrkrtng', 66),
(2, 'agi', 53),
(2, 'armorpenrtng', 52),
(2, 'hitrtng', 48),
(2, 'hastertng', 36),
(2, 'atkpwr', 31),
(2, 'armor', 5),
(3, 'sta', 100),
(3, 'dodgertng', 90),
(3, 'defrtng', 86),
(3, 'block', 81),
(3, 'agi', 67),
(3, 'parryrtng', 67),
(3, 'blockrtng', 48),
(3, 'str', 48),
(3, 'exprtng', 19),
(3, 'hitrtng', 10),
(3, 'armorpenrtng', 10),
(3, 'critstrkrtng', 7),
(3, 'armor', 6),
(3, 'hastertng', 1),
(3, 'atkpwr', 1),
(4, 'int', 100),
(4, 'manargn', 88),
(4, 'splpwr', 58),
(4, 'critstrkrtng', 46),
(4, 'hastertng', 35),
(5, 'sta', 100),
(5, 'dodgertng', 94),
(5, 'block', 86),
(5, 'defrtng', 86),
(5, 'exprtng', 79),
(5, 'agi', 76),
(5, 'parryrtng', 76),
(5, 'hitrtng', 58),
(5, 'blockrtng', 52),
(5, 'str', 50),
(5, 'armor', 6),
(5, 'atkpwr', 6),
(5, 'splpwr', 4),
(5, 'critstrkrtng', 3),
(6, 'mledps', 470),
(6, 'hitrtng', 100),
(6, 'str', 80),
(6, 'exprtng', 66),
(6, 'critstrkrtng', 40),
(6, 'atkpwr', 34),
(6, 'agi', 32),
(6, 'hastertng', 30),
(6, 'armorpenrtng', 22),
(6, 'splpwr', 9),
(7, 'rgddps', 213),
(7, 'hitrtng', 100),
(7, 'agi', 58),
(7, 'critstrkrtng', 40),
(7, 'int', 37),
(7, 'atkpwr', 30),
(7, 'armorpenrtng', 28),
(7, 'hastertng', 21),
(8, 'rgddps', 379),
(8, 'hitrtng', 100),
(8, 'agi', 74),
(8, 'critstrkrtng', 57),
(8, 'armorpenrtng', 40),
(8, 'int', 39),
(8, 'atkpwr', 32),
(8, 'hastertng', 24),
(9, 'rgddps', 181),
(9, 'hitrtng', 100),
(9, 'agi', 76),
(9, 'critstrkrtng', 42),
(9, 'int', 35),
(9, 'hastertng', 31),
(9, 'atkpwr', 29),
(9, 'armorpenrtng', 26),
(10, 'mledps', 170),
(10, 'agi', 100),
(10, 'exprtng', 87),
(10, 'hitrtng', 83),
(10, 'critstrkrtng', 81),
(10, 'atkpwr', 65),
(10, 'armorpenrtng', 65),
(10, 'hastertng', 64),
(10, 'str', 55),
(11, 'mledps', 220),
(11, 'armorpenrtng', 100),
(11, 'agi', 100),
(11, 'exprtng', 82),
(11, 'hitrtng', 80),
(11, 'critstrkrtng', 75),
(11, 'hastertng', 73),
(11, 'str', 55),
(11, 'atkpwr', 50),
(12, 'mledps', 228),
(12, 'exprtng', 100),
(12, 'agi', 100),
(12, 'hitrtng', 80),
(12, 'armorpenrtng', 75),
(12, 'critstrkrtng', 75),
(12, 'hastertng', 75),
(12, 'str', 55),
(12, 'atkpwr', 50),
(13, 'splpwr', 100),
(13, 'manargn', 67),
(13, 'int', 65),
(13, 'hastertng', 59),
(13, 'critstrkrtng', 48),
(13, 'spi', 22),
(14, 'manargn', 100),
(14, 'int', 69),
(14, 'splpwr', 60),
(14, 'spi', 52),
(14, 'critstrkrtng', 38),
(14, 'hastertng', 31),
(15, 'hitrtng', 100),
(15, 'shasplpwr', 76),
(15, 'splpwr', 76),
(15, 'critstrkrtng', 54),
(15, 'hastertng', 50),
(15, 'spi', 16),
(15, 'int', 16),
(16, 'mledps', 360),
(16, 'armorpenrtng', 100),
(16, 'str', 99),
(16, 'hitrtng', 91),
(16, 'exprtng', 90),
(16, 'critstrkrtng', 57),
(16, 'hastertng', 55),
(16, 'atkpwr', 36),
(16, 'armor', 1),
(17, 'mledps', 337),
(17, 'hitrtng', 100),
(17, 'str', 97),
(17, 'exprtng', 81),
(17, 'armorpenrtng', 61),
(17, 'critstrkrtng', 45),
(17, 'atkpwr', 35),
(17, 'hastertng', 28),
(17, 'armor', 1),
(18, 'mledps', 419),
(18, 'parryrtng', 100),
(18, 'hitrtng', 97),
(18, 'str', 96),
(18, 'defrtng', 85),
(18, 'exprtng', 69),
(18, 'dodgertng', 61),
(18, 'agi', 61),
(18, 'sta', 61),
(18, 'critstrkrtng', 49),
(18, 'atkpwr', 41),
(18, 'armorpenrtng', 31),
(18, 'armor', 5),
(19, 'mledps', 209),
(19, 'str', 100),
(19, 'hitrtng', 66),
(19, 'exprtng', 51),
(19, 'hastertng', 48),
(19, 'critstrkrtng', 45),
(19, 'atkpwr', 34),
(19, 'armorpenrtng', 32),
(19, 'armor', 1),
(20, 'hitrtng', 100),
(20, 'splpwr', 60),
(20, 'hastertng', 56),
(20, 'critstrkrtng', 40),
(20, 'int', 11),
(21, 'mledps', 135),
(21, 'hitrtng', 100),
(21, 'exprtng', 84),
(21, 'agi', 55),
(21, 'int', 55),
(21, 'critstrkrtng', 55),
(21, 'hastertng', 42),
(21, 'str', 35),
(21, 'atkpwr', 32),
(21, 'splpwr', 29),
(21, 'armorpenrtng', 26),
(22, 'manargn', 100),
(22, 'int', 85),
(22, 'splpwr', 77),
(22, 'critstrkrtng', 62),
(22, 'hastertng', 35),
(23, 'hitrtng', 100),
(23, 'hastertng', 54),
(23, 'arcsplpwr', 49),
(23, 'splpwr', 49),
(23, 'critstrkrtng', 37),
(23, 'int', 34),
(23, 'frosplpwr', 24),
(23, 'firsplpwr', 24),
(23, 'spi', 14),
(24, 'hitrtng', 100),
(24, 'hastertng', 53),
(24, 'firsplpwr', 46),
(24, 'splpwr', 46),
(24, 'critstrkrtng', 43),
(24, 'frosplpwr', 23),
(24, 'arcsplpwr', 23),
(24, 'int', 13),
(25, 'hitrtng', 100),
(25, 'hastertng', 42),
(25, 'frosplpwr', 39),
(25, 'splpwr', 39),
(25, 'arcsplpwr', 19),
(25, 'firsplpwr', 19),
(25, 'critstrkrtng', 19),
(25, 'int', 6),
(26, 'hitrtng', 100),
(26, 'shasplpwr', 72),
(26, 'splpwr', 72),
(26, 'hastertng', 61),
(26, 'critstrkrtng', 38),
(26, 'firsplpwr', 36),
(26, 'spi', 34),
(26, 'int', 15),
(27, 'hitrtng', 100),
(27, 'hastertng', 50),
(27, 'firsplpwr', 45),
(27, 'shasplpwr', 45),
(27, 'splpwr', 45),
(27, 'critstrkrtng', 31),
(27, 'spi', 29),
(27, 'int', 13),
(28, 'hitrtng', 100),
(28, 'firsplpwr', 47),
(28, 'splpwr', 47),
(28, 'hastertng', 46),
(28, 'spi', 26),
(28, 'shasplpwr', 23),
(28, 'critstrkrtng', 16),
(28, 'int', 13),
(29, 'hitrtng', 100),
(29, 'splpwr', 66),
(29, 'hastertng', 54),
(29, 'critstrkrtng', 43),
(29, 'spi', 22),
(29, 'int', 22),
(30, 'agi', 100),
(30, 'sta', 75),
(30, 'dodgertng', 65),
(30, 'defrtng', 60),
(30, 'exprtng', 16),
(30, 'str', 10),
(30, 'armor', 10),
(30, 'hitrtng', 8),
(30, 'hastertng', 5),
(30, 'atkpwr', 4),
(30, 'feratkpwr', 4),
(30, 'critstrkrtng', 3),
(31, 'splpwr', 100),
(31, 'manargn', 73),
(31, 'hastertng', 57),
(31, 'int', 51),
(31, 'spi', 32),
(31, 'critstrkrtng', 11),
(32, 'agi', 100),
(32, 'armorpenrtng', 90),
(32, 'str', 80),
(32, 'critstrkrtng', 55),
(32, 'exprtng', 50),
(32, 'hitrtng', 50),
(32, 'feratkpwr', 40),
(32, 'atkpwr', 40),
(32, 'hastertng', 35);
(1, 'exprtng', 100),
(1, 'str', 82),
(1, 'critstrkrtng', 66),
(1, 'agi', 53),
(1, 'armorpenrtng', 52),
(1, 'hitrtng', 48),
(1, 'hastertng', 36),
(1, 'atkpwr', 31),
(1, 'armor', 5),
(2, 'exprtng', 100),
(2, 'str', 82),
(2, 'critstrkrtng', 66),
(2, 'agi', 53),
(2, 'armorpenrtng', 52),
(2, 'hitrtng', 48),
(2, 'hastertng', 36),
(2, 'atkpwr', 31),
(2, 'armor', 5),
(3, 'sta', 100),
(3, 'dodgertng', 90),
(3, 'defrtng', 86),
(3, 'block', 81),
(3, 'agi', 67),
(3, 'parryrtng', 67),
(3, 'blockrtng', 48),
(3, 'str', 48),
(3, 'exprtng', 19),
(3, 'hitrtng', 10),
(3, 'armorpenrtng', 10),
(3, 'critstrkrtng', 7),
(3, 'armor', 6),
(3, 'hastertng', 1),
(3, 'atkpwr', 1),
(4, 'int', 100),
(4, 'manargn', 88),
(4, 'splpwr', 58),
(4, 'critstrkrtng', 46),
(4, 'hastertng', 35),
(5, 'sta', 100),
(5, 'dodgertng', 94),
(5, 'block', 86),
(5, 'defrtng', 86),
(5, 'exprtng', 79),
(5, 'agi', 76),
(5, 'parryrtng', 76),
(5, 'hitrtng', 58),
(5, 'blockrtng', 52),
(5, 'str', 50),
(5, 'armor', 6),
(5, 'atkpwr', 6),
(5, 'splpwr', 4),
(5, 'critstrkrtng', 3),
(6, 'mledps', 470),
(6, 'hitrtng', 100),
(6, 'str', 80),
(6, 'exprtng', 66),
(6, 'critstrkrtng', 40),
(6, 'atkpwr', 34),
(6, 'agi', 32),
(6, 'hastertng', 30),
(6, 'armorpenrtng', 22),
(6, 'splpwr', 9),
(7, 'rgddps', 213),
(7, 'hitrtng', 100),
(7, 'agi', 58),
(7, 'critstrkrtng', 40),
(7, 'int', 37),
(7, 'atkpwr', 30),
(7, 'armorpenrtng', 28),
(7, 'hastertng', 21),
(8, 'rgddps', 379),
(8, 'hitrtng', 100),
(8, 'agi', 74),
(8, 'critstrkrtng', 57),
(8, 'armorpenrtng', 40),
(8, 'int', 39),
(8, 'atkpwr', 32),
(8, 'hastertng', 24),
(9, 'rgddps', 181),
(9, 'hitrtng', 100),
(9, 'agi', 76),
(9, 'critstrkrtng', 42),
(9, 'int', 35),
(9, 'hastertng', 31),
(9, 'atkpwr', 29),
(9, 'armorpenrtng', 26),
(10, 'mledps', 170),
(10, 'agi', 100),
(10, 'exprtng', 87),
(10, 'hitrtng', 83),
(10, 'critstrkrtng', 81),
(10, 'atkpwr', 65),
(10, 'armorpenrtng', 65),
(10, 'hastertng', 64),
(10, 'str', 55),
(11, 'mledps', 220),
(11, 'armorpenrtng', 100),
(11, 'agi', 100),
(11, 'exprtng', 82),
(11, 'hitrtng', 80),
(11, 'critstrkrtng', 75),
(11, 'hastertng', 73),
(11, 'str', 55),
(11, 'atkpwr', 50),
(12, 'mledps', 228),
(12, 'exprtng', 100),
(12, 'agi', 100),
(12, 'hitrtng', 80),
(12, 'armorpenrtng', 75),
(12, 'critstrkrtng', 75),
(12, 'hastertng', 75),
(12, 'str', 55),
(12, 'atkpwr', 50),
(13, 'splpwr', 100),
(13, 'manargn', 67),
(13, 'int', 65),
(13, 'hastertng', 59),
(13, 'critstrkrtng', 48),
(13, 'spi', 22),
(14, 'manargn', 100),
(14, 'int', 69),
(14, 'splpwr', 60),
(14, 'spi', 52),
(14, 'critstrkrtng', 38),
(14, 'hastertng', 31),
(15, 'hitrtng', 100),
(15, 'shasplpwr', 76),
(15, 'splpwr', 76),
(15, 'critstrkrtng', 54),
(15, 'hastertng', 50),
(15, 'spi', 16),
(15, 'int', 16),
(16, 'mledps', 360),
(16, 'armorpenrtng', 100),
(16, 'str', 99),
(16, 'hitrtng', 91),
(16, 'exprtng', 90),
(16, 'critstrkrtng', 57),
(16, 'hastertng', 55),
(16, 'atkpwr', 36),
(16, 'armor', 1),
(17, 'mledps', 337),
(17, 'hitrtng', 100),
(17, 'str', 97),
(17, 'exprtng', 81),
(17, 'armorpenrtng', 61),
(17, 'critstrkrtng', 45),
(17, 'atkpwr', 35),
(17, 'hastertng', 28),
(17, 'armor', 1),
(18, 'mledps', 419),
(18, 'parryrtng', 100),
(18, 'hitrtng', 97),
(18, 'str', 96),
(18, 'defrtng', 85),
(18, 'exprtng', 69),
(18, 'dodgertng', 61),
(18, 'agi', 61),
(18, 'sta', 61),
(18, 'critstrkrtng', 49),
(18, 'atkpwr', 41),
(18, 'armorpenrtng', 31),
(18, 'armor', 5),
(19, 'mledps', 209),
(19, 'str', 100),
(19, 'hitrtng', 66),
(19, 'exprtng', 51),
(19, 'hastertng', 48),
(19, 'critstrkrtng', 45),
(19, 'atkpwr', 34),
(19, 'armorpenrtng', 32),
(19, 'armor', 1),
(20, 'hitrtng', 100),
(20, 'splpwr', 60),
(20, 'hastertng', 56),
(20, 'critstrkrtng', 40),
(20, 'int', 11),
(21, 'mledps', 135),
(21, 'hitrtng', 100),
(21, 'exprtng', 84),
(21, 'agi', 55),
(21, 'int', 55),
(21, 'critstrkrtng', 55),
(21, 'hastertng', 42),
(21, 'str', 35),
(21, 'atkpwr', 32),
(21, 'splpwr', 29),
(21, 'armorpenrtng', 26),
(22, 'manargn', 100),
(22, 'int', 85),
(22, 'splpwr', 77),
(22, 'critstrkrtng', 62),
(22, 'hastertng', 35),
(23, 'hitrtng', 100),
(23, 'hastertng', 54),
(23, 'arcsplpwr', 49),
(23, 'splpwr', 49),
(23, 'critstrkrtng', 37),
(23, 'int', 34),
(23, 'frosplpwr', 24),
(23, 'firsplpwr', 24),
(23, 'spi', 14),
(24, 'hitrtng', 100),
(24, 'hastertng', 53),
(24, 'firsplpwr', 46),
(24, 'splpwr', 46),
(24, 'critstrkrtng', 43),
(24, 'frosplpwr', 23),
(24, 'arcsplpwr', 23),
(24, 'int', 13),
(25, 'hitrtng', 100),
(25, 'hastertng', 42),
(25, 'frosplpwr', 39),
(25, 'splpwr', 39),
(25, 'arcsplpwr', 19),
(25, 'firsplpwr', 19),
(25, 'critstrkrtng', 19),
(25, 'int', 6),
(26, 'hitrtng', 100),
(26, 'shasplpwr', 72),
(26, 'splpwr', 72),
(26, 'hastertng', 61),
(26, 'critstrkrtng', 38),
(26, 'firsplpwr', 36),
(26, 'spi', 34),
(26, 'int', 15),
(27, 'hitrtng', 100),
(27, 'hastertng', 50),
(27, 'firsplpwr', 45),
(27, 'shasplpwr', 45),
(27, 'splpwr', 45),
(27, 'critstrkrtng', 31),
(27, 'spi', 29),
(27, 'int', 13),
(28, 'hitrtng', 100),
(28, 'firsplpwr', 47),
(28, 'splpwr', 47),
(28, 'hastertng', 46),
(28, 'spi', 26),
(28, 'shasplpwr', 23),
(28, 'critstrkrtng', 16),
(28, 'int', 13),
(29, 'hitrtng', 100),
(29, 'splpwr', 66),
(29, 'hastertng', 54),
(29, 'critstrkrtng', 43),
(29, 'spi', 22),
(29, 'int', 22),
(30, 'agi', 100),
(30, 'sta', 75),
(30, 'dodgertng', 65),
(30, 'defrtng', 60),
(30, 'exprtng', 16),
(30, 'str', 10),
(30, 'armor', 10),
(30, 'hitrtng', 8),
(30, 'hastertng', 5),
(30, 'atkpwr', 4),
(30, 'feratkpwr', 4),
(30, 'critstrkrtng', 3),
(31, 'splpwr', 100),
(31, 'manargn', 73),
(31, 'hastertng', 57),
(31, 'int', 51),
(31, 'spi', 32),
(31, 'critstrkrtng', 11),
(32, 'agi', 100),
(32, 'armorpenrtng', 90),
(32, 'str', 80),
(32, 'critstrkrtng', 55),
(32, 'exprtng', 50),
(32, 'hitrtng', 50),
(32, 'feratkpwr', 40),
(32, 'atkpwr', 40),
(32, 'hastertng', 35);

View File

@@ -7,35 +7,35 @@ CREATE TABLE IF NOT EXISTS `playerbots_weightscales` (
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
INSERT INTO `playerbots_weightscales` (`id`, `name`, `class`) VALUES
(1, 'arms', 1),
(2, 'fury', 1),
(3, 'prot', 1),
(4, 'holy', 2),
(5, 'prot', 2),
(6, 'retrib', 2),
(7, 'beast', 3),
(8, 'marks', 3),
(9, 'surv', 3),
(10, 'assas', 4),
(11, 'combat', 4),
(12, 'subtle', 4),
(13, 'disc', 5),
(14, 'holy', 5),
(15, 'shadow', 5),
(16, 'blooddps', 6),
(17, 'frostdps', 6),
(18, 'frosttank', 6),
(19, 'unholydps', 6),
(20, 'elem', 7),
(21, 'enhance', 7),
(22, 'resto', 7),
(23, 'arcane', 8),
(24, 'fire', 8),
(25, 'frost', 8),
(26, 'afflic', 9),
(27, 'demo', 9),
(28, 'destro', 9),
(29, 'balance', 11),
(30, 'feraltank', 11),
(31, 'resto', 11),
(32, 'feraldps', 11);
(1, 'arms', 1),
(2, 'fury', 1),
(3, 'prot', 1),
(4, 'holy', 2),
(5, 'prot', 2),
(6, 'retrib', 2),
(7, 'beast', 3),
(8, 'marks', 3),
(9, 'surv', 3),
(10, 'assas', 4),
(11, 'combat', 4),
(12, 'subtle', 4),
(13, 'disc', 5),
(14, 'holy', 5),
(15, 'shadow', 5),
(16, 'blooddps', 6),
(17, 'frostdps', 6),
(18, 'frosttank', 6),
(19, 'unholydps', 6),
(20, 'elem', 7),
(21, 'enhance', 7),
(22, 'resto', 7),
(23, 'arcane', 8),
(24, 'fire', 8),
(25, 'frost', 8),
(26, 'afflic', 9),
(27, 'demo', 9),
(28, 'destro', 9),
(29, 'balance', 11),
(30, 'feraltank', 11),
(31, 'resto', 11),
(32, 'feraldps', 11);

View File

@@ -24,9 +24,9 @@ CREATE TABLE IF NOT EXISTS `updates_include` (
DELETE FROM `updates_include`;
/*!40000 ALTER TABLE `updates_include` DISABLE KEYS */;
INSERT INTO `updates_include` (`path`, `state`) VALUES
('$/data/sql/playerbots/updates', 'RELEASED'),
('$/data/sql/playerbots/custom', 'CUSTOM'),
('$/data/sql/playerbots/archive', 'ARCHIVED');
('$/data/sql/playerbots/updates', 'RELEASED'),
('$/data/sql/playerbots/custom', 'CUSTOM'),
('$/data/sql/playerbots/archive', 'ARCHIVED');
/*!40000 ALTER TABLE `updates_include` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;

View File

@@ -1285,7 +1285,7 @@ CREATE TABLE IF NOT EXISTS `ai_playerbot_texts_chance` (
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` DISABLE KEYS */;
INSERT INTO `ai_playerbot_texts_chance` (`id`, `name`, `probability`) VALUES
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
(1, 'taunt', 30),
(2, 'aoe', 75),
(3, 'loot', 20);
/*!40000 ALTER TABLE `ai_playerbot_texts_chance` ENABLE KEYS */;

View File

@@ -4,11 +4,11 @@ DELETE FROM `playerbots_travelnode_path` WHERE `node_id` = 3780;
DELETE FROM `playerbots_travelnode` WHERE `id` = 3780;
-- Insert new entries into playerbots_travelnode
INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`)
INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`)
VALUES (3780, 'Highlord Mograine', 533, 2524.32, -2951.28, 245.633, 1);
-- Insert new entries into playerbots_travelnode_path
INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`)
INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`)
VALUES
(3780, 472, 0, 533, 2524.32, -2951.28, 245.633),
(3780, 472, 1, 533, 2528.79, -2948.58, 245.633),
@@ -16,7 +16,7 @@ VALUES
(3780, 757, 1, 533, 2517.62, -2959.38, 245.636);
-- Insert new entries into playerbots_travelnode_link
INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, `extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`)
INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`, `type`, `object`, `distance`, `swim_distance`, `extra_cost`, `calculated`, `max_creature_0`, `max_creature_1`, `max_creature_2`)
VALUES
(3780, 472, 1, 0, 5.3221, 0, 0, 1, 83, 0, 0),
(3780, 757, 1, 0, 10.6118, 0, 0, 1, 83, 0, 0);

View File

@@ -0,0 +1,9 @@
-- Create playerbots_account_type table for tracking accounts assignments
DROP TABLE IF EXISTS `playerbots_account_type`;
CREATE TABLE `playerbots_account_type` (
`account_id` int unsigned NOT NULL,
`account_type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 = unassigned, 1 = RNDbot, 2 = AddClass',
`assignment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Playerbot account type assignments';

View File

@@ -0,0 +1,849 @@
UPDATE `ai_playerbot_texts` SET `text_loc8` = "посреди нигде" WHERE `id` = 1;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "в неопределенном месте" WHERE `id` = 2;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "где-то" WHERE `id` = 3;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что-то" WHERE `id` = 4;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "интересно, какой на вкус %item_link" WHERE `id` = 5;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "нет, мне выпал %item_link" WHERE `id` = 6;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "опять этот хлам %item_link" WHERE `id` = 7;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "похоже, я лутаю мусор %item_link" WHERE `id` = 8;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ну, лучше чем ничего, наверное %item_link" WHERE `id` = 9;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "не знаю, что делать с %item_link" WHERE `id` = 10;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "интересно, какой на вкус %item_link" WHERE `id` = 11;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "могу лутать %item_link весь день" WHERE `id` = 12;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще один день, еще один %item_link" WHERE `id` = 13;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "получил немного %item_link" WHERE `id` = 14;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "немного %item_link — это нормально" WHERE `id` = 15;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "неплохо, только что получил %item_link" WHERE `id` = 16;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "только что залутал %item_link в %zone_name" WHERE `id` = 17;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "могу найти применение %item_link" WHERE `id` = 18;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "деньги, деньги, деньги %item_link" WHERE `id` = 19;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "получил %item_link" WHERE `id` = 20;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_link — лучший для охотника" WHERE `id` = 21;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_link — лучший для %my_class" WHERE `id` = 22;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сегодня удача на моей стороне %item_link" WHERE `id` = 23;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "классный %item_link, только что залутал" WHERE `id` = 24;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "вау, только что получил %item_link" WHERE `id` = 25;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_link — лучший для охотника" WHERE `id` = 26;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_link — лучший для %my_class" WHERE `id` = 27;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сегодня удача на моей стороне %item_link" WHERE `id` = 28;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "классный %item_link, только что залутал" WHERE `id` = 29;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ОГО, посмотрите, что я только что получил %item_link!!!" WHERE `id` = 30;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нет @#$@%! Не может быть, я получил %item_link, это безумие" WHERE `id` = 31;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нет @#$@%! Не может быть, я получил %item_link, это безумие" WHERE `id` = 32;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я только что взял %quest_link" WHERE `id` = 33;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "только что принял %quest_link" WHERE `id` = 34;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%quest_link попробую выполнить это задание" WHERE `id` = 35;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "взял %quest_link в %zone_name" WHERE `id` = 36;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Наконец-то закончил %quest_obj_name для %quest_link" WHERE `id` = 37;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "наконец-то получил %quest_obj_available/%quest_obj_required %quest_obj_name для %quest_link" WHERE `id` = 38;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%quest_obj_full_formatted для %quest_link, наконец-то" WHERE `id` = 39;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ох, получил %quest_obj_available/%quest_obj_required %quest_obj_name для %quest_link" WHERE `id` = 40;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще нужно %quest_obj_missing %quest_obj_name для %quest_link" WHERE `id` = 41;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%quest_obj_full_formatted, все еще работаю над %quest_link" WHERE `id` = 42;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Наконец-то закончил с %item_link для %quest_link" WHERE `id` = 43;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "наконец-то получил %quest_obj_available/%quest_obj_required %item_link для %quest_link" WHERE `id` = 44;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%quest_obj_full_formatted для %quest_link, наконец-то" WHERE `id` = 45;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ох, получил %quest_obj_available/%quest_obj_required %item_link для %quest_link" WHERE `id` = 46;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще нужно %quest_obj_missing %item_link для %quest_link" WHERE `id` = 47;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%quest_obj_full_formatted, все еще работаю над %quest_link" WHERE `id` = 48;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не успел завершить %quest_link вовремя..." WHERE `id` = 49;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Время для %quest_link вышло :(" WHERE `id` = 50;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я выполнил все задачи для %quest_link" WHERE `id` = 51;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Выполнил все задачи для %quest_link" WHERE `id` = 52;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Скоро сдам %quest_link, только что закончил все задачи" WHERE `id` = 53;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Да, наконец-то сдал %quest_link" WHERE `id` = 54;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сдал %quest_link" WHERE `id` = 55;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "смог завершить %quest_link, только что сдал" WHERE `id` = 56;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "только что сдал %quest_link" WHERE `id` = 57;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "только что сдал %quest_link в %zone_name" WHERE `id` = 58;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще одна жертва — %victim_name" WHERE `id` = 59;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я продолжаю убивать %victim_name, нечего рассказывать" WHERE `id` = 60;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще один %victim_name пал" WHERE `id` = 61;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "минус один %victim_name в %zone_name" WHERE `id` = 62;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Убил этого элитного ублюдка %victim_name!" WHERE `id` = 63;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "убил элиту %victim_name в %zone_name" WHERE `id` = 64;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Фух, удалось завалить %victim_name!" WHERE `id` = 65;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это было круто! Только что убил %victim_name! Теперь есть что рассказать" WHERE `id` = 66;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Йо, я только что убил %victim_name!" WHERE `id` = 67;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "убил редкого %victim_name в %zone_name" WHERE `id` = 68;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Что я только что убил? %victim_name" WHERE `id` = 69;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Только что убил этого пета %victim_name" WHERE `id` = 70;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О да, только что убил %victim_name" WHERE `id` = 71;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "убил %victim_name в %zone_name" WHERE `id` = 72;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Динг!" WHERE `id` = 73;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Да, я теперь %my_level уровень!" WHERE `id` = 74;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я только что повысил уровень" WHERE `id` = 75;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я %my_level уровень!!!" WHERE `id` = 76;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Становлюсь сильнее, уже %my_level уровень!!!" WHERE `id` = 77;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Только что достиг %my_level уровня!!!" WHERE `id` = 78;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ОГО, наконец-то %my_level уровень!!!" WHERE `id` = 79;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_level!!! теперь могу заниматься эндгейм-контентом" WHERE `id` = 80;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "свежий новый уровень %my_level %my_class!!!" WHERE `id` = 81;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "еще один уровень %my_level %my_race %my_class!" WHERE `id` = 82;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Молодец %other_name. Ты это заслужил." WHERE `id` = 83;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это было ужасно %other_name. Не хотел этого делать, но..." WHERE `id` = 84;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %instance_name?" WHERE `id` = 85;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть группы в %instance_name?" WHERE `id` = 86;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь в %instance_name?" WHERE `id` = 87;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФД: %instance_name." WHERE `id` = 88;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %my_role для %instance_name?" WHERE `id` = 89;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не хватает %my_role для %instance_name?" WHERE `id` = 90;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Могу быть %my_role для %instance_name." WHERE `id` = 91;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь с %instance_name?" WHERE `id` = 92;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь %my_role с %instance_name?" WHERE `id` = 93;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен шмот из %instance_name?" WHERE `id` = 94;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Немного пофармить в %instance_name?" WHERE `id` = 95;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ищу %instance_name" WHERE `id` = 96;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь в %instance_name." WHERE `id` = 97;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу пройти %instance_name." WHERE `id` = 98;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_role ищет %instance_name." WHERE `id` = 99;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Как насчет %instance_name?" WHERE `id` = 100;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет пофармить %instance_name?" WHERE `id` = 101;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пойдем в %instance_name?" WHERE `id` = 102;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ищу %instance_name." WHERE `id` = 103;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь с квестами в %instance_name?" WHERE `id` = 104;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу квестить в %instance_name." WHERE `id` = 105;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-то с квестами в %instance_name?" WHERE `id` = 106;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Могу помочь с квестами в %instance_name." WHERE `id` = 107;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_role: есть место в группе для %instance_name?" WHERE `id` = 108;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь еще ходит в %instance_name в наши дни?" WHERE `id` = 109;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%instance_name: кто хочет взять %my_role?" WHERE `id` = 110;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть ли смысл быть %my_role в %instance_name?" WHERE `id` = 111;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Стоит ли идти в %instance_name?" WHERE `id` = 112;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужны еще люди для %instance_name?" WHERE `id` = 113;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "В %instance_name боссы дропают хороший шмот. Пойдем?" WHERE `id` = 114;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Как насчет %instance_name?" WHERE `id` = 115;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %my_role?" WHERE `id` = 116;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %my_role?" WHERE `id` = 117;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет %instance_name?" WHERE `id` = 118;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто может призвать меня в %instance_name?" WHERE `id` = 119;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Встретимся в %instance_name" WHERE `id` = 120;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу быструю пробежку %instance_name" WHERE `id` = 121;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу полный забег %instance_name" WHERE `id` = 122;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сколько раз ты был в %instance_name?" WHERE `id` = 123;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Еще один забег в %instance_name?" WHERE `id` = 124;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вайпнулись в %instance_name? Возьмите меня!" WHERE `id` = 125;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Возьмите меня в %instance_name, пожалуйста." WHERE `id` = 126;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Быстрый забег в %instance_name?" WHERE `id` = 127;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Полный забег в %instance_name?" WHERE `id` = 128;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто может взять %my_role в %instance_name?" WHERE `id` = 129;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФГ %instance_name, я %my_role" WHERE `id` = 130;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_role ЛФГ %instance_name" WHERE `id` = 131;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь с %quest_link?" WHERE `id` = 132;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет поделиться %quest_link?" WHERE `id` = 133;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто делает %quest_link?" WHERE `id` = 134;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу сделать %quest_link." WHERE `id` = 135;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет пофармить %category?" WHERE `id` = 136;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ищу помощь для фарма %category." WHERE `id` = 137;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Черт, %category такие дорогие!" WHERE `id` = 138;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу %category." WHERE `id` = 139;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь с %category." WHERE `id` = 140;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Куплю %category." WHERE `id` = 141;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому интересно %category?" WHERE `id` = 142;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Продам %category." WHERE `id` = 143;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Продаю %category дешевле, чем на Аукционе." WHERE `id` = 144;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет пофармить %category?" WHERE `id` = 145;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу пофармить %category." WHERE `id` = 146;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ищу пати после %category." WHERE `id` = 147;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Любые %category приветствуются." WHERE `id` = 148;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Куплю что угодно из %category." WHERE `id` = 149;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вау, кто-то фармит %category!" WHERE `id` = 150;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%category отлично продаются на аукционе." WHERE `id` = 151;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Аукцион горячий по %category." WHERE `id` = 152;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%category на рынке." WHERE `id` = 153;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу обменять немного %category." WHERE `id` = 154;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужно больше %category." WHERE `id` = 155;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь может поделиться %category?" WHERE `id` = 156;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет %category?" WHERE `id` = 157;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Немного %category, пожалуйста?" WHERE `id` = 158;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надо было прокачать навык для %category." WHERE `id` = 159;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Очень хочу %category." WHERE `id` = 160;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Люди убивают ради %category." WHERE `id` = 161;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%category — отличная сделка!" WHERE `id` = 162;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все сходят с ума по %category!" WHERE `id` = 163;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Где лучше всего фармить %category?" WHERE `id` = 164;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я полностью готов к %category." WHERE `id` = 165;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Выгодно ли продавать %category?" WHERE `id` = 166;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Наверное, я бы оставил все свои %category себе." WHERE `id` = 167;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна группа? Может, пофармить %category?" WHERE `id` = 168;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все еще думаю о %category." WHERE `id` = 169;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я уже слышал о %category, но карманы пусты." WHERE `id` = 170;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФГ для %category" WHERE `id` = 171;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сделает ли продажа %category меня богатым?" WHERE `id` = 172;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ОК. Завтра фармлю %category." WHERE `id` = 173;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все говорят о %category." WHERE `id` = 174;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Видел как минимум десять человек, фармящих %category." WHERE `id` = 175;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вчера продал все свои %category. Теперь я полностью на мели!" WHERE `id` = 176;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу вступить в гильдию, фармящую %category." WHERE `id` = 177;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто фармит репутацию %faction?" WHERE `id` = 178;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто поможет с %faction?" WHERE `id` = 179;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу квестить ради %faction." WHERE `id` = 180;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%faction — лучшая." WHERE `id` = 181;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужно чуть-чуть, чтобы стать %rep_level у %faction." WHERE `id` = 182;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь уже %rep_level у %faction?" WHERE `id` = 183;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет стать %rep_level у %faction?" WHERE `id` = 184;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я никогда не стану %rep_level у %faction." WHERE `id` = 185;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-то не хватает репы у %faction?" WHERE `id` = 186;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Могу помочь с фармом репы %faction." WHERE `id` = 187;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Чем больше репы, тем лучше. Особенно у %faction." WHERE `id` = 188;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%faction: нужно %rndK для %rep_level." WHERE `id` = 189;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто может поделиться квестами %faction?" WHERE `id` = 190;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть подземелья для %faction?" WHERE `id` = 191;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу фармить репу %faction." WHERE `id` = 192;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давайте фармить репу %faction!" WHERE `id` = 193;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Фармлю репу %faction." WHERE `id` = 194;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу пофармить %faction." WHERE `id` = 195;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна помощь с %faction." WHERE `id` = 196;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%faction что-то полезное продает?" WHERE `id` = 197;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть ли у %faction торговцы?" WHERE `id` = 198;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто фармит %faction?" WHERE `id` = 199;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Как лучше всего фармить %faction?" WHERE `id` = 200;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ненавижу фарм репы %faction." WHERE `id` = 201;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я так устал от %faction." WHERE `id` = 202;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пойдем за %faction?" WHERE `id` = 203;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кажется, все уже %rep_level у %faction. Только я, как обычно, опаздываю." WHERE `id` = 204;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФГ для фарма репы %faction?" WHERE `id` = 205;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь подскажет хорошее место для фарма репы %faction?" WHERE `id` = 206;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Будет ли польза от репы %faction?" WHERE `id` = 207;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто бы мог подумать, что репа %faction окажется полезной..." WHERE `id` = 208;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу быть превознесенным у всех фракций, начну с %faction." WHERE `id` = 209;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть ли смысл повышать репу с %faction?" WHERE `id` = 210;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Что лучше для %faction? Квесты или фарм мобов?" WHERE `id` = 211;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пофармлю репу %faction для тебя. Только дай немного золота." WHERE `id` = 212;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Думаю, фармить репу %faction — это навсегда." WHERE `id` = 213;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я убиваю ради %faction каждый день, но все еще далеко до %rep_level." WHERE `id` = 214;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "На %my_level депозиты на аукционе уменьшатся, да?" WHERE `id` = 215;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сколько у тебя превознесенных реп?" WHERE `id` = 216;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет быть %my_level у %faction?" WHERE `id` = 217;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Черт. Моя гильдия вчера хорошо пофармила %faction без меня." WHERE `id` = 218;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Никто не хочет мне помогать, потому что я %rep_level у %faction." WHERE `id` = 219;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пожалуйста, держись подальше от %faction." WHERE `id` = 220;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу в пати в %zone_name." WHERE `id` = 221;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто ищет %my_role?" WHERE `id` = 222;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_role ищет гильдию." WHERE `id` = 223;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ищу золото." WHERE `id` = 224;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%my_role хочет вступить в хорошую гильдию." WHERE `id` = 225;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужен друг." WHERE `id` = 226;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь чувствует себя одиноко?" WHERE `id` = 227;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Скучно..." WHERE `id` = 228;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет немного?" WHERE `id` = 229;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давай, поймай меня!" WHERE `id` = 230;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Может, дуэль в %zone_name?" WHERE `id` = 231;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь что-то делает?" WHERE `id` = 232;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name: кто-нибудь здесь есть?" WHERE `id` = 233;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name: где все?" WHERE `id` = 234;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Похоже, я один в %zone_name." WHERE `id` = 235;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Встретимся в %zone_name." WHERE `id` = 236;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давайте квестить в %zone_name!" WHERE `id` = 237;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name — лучшее место!" WHERE `id` = 238;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу в %zone_name. Кто со мной?" WHERE `id` = 239;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет пойти в %zone_name?" WHERE `id` = 240;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне не нравится %zone_name. Куда пойти?" WHERE `id` = 241;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть хорошие квесты в %zone_name?" WHERE `id` = 242;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Куда идти после %zone_name?" WHERE `id` = 243;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто в %zone_name?" WHERE `id` = 244;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФГ в %zone_name." WHERE `id` = 245;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name — худшее место." WHERE `id` = 246;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Лови меня в %zone_name!" WHERE `id` = 247;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пойдем в %zone_name!" WHERE `id` = 248;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу квестить в %zone_name" WHERE `id` = 249;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У кого есть квесты в %zone_name?" WHERE `id` = 250;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Приходите сюда, в %zone_name!" WHERE `id` = 251;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Похоже, в %zone_name нет Орды" WHERE `id` = 252;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Похоже, в %zone_name нет Альянса" WHERE `id` = 253;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я очень устал от %zone_name. Может, пойти куда-нибудь еще?" WHERE `id` = 254;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Удачи" WHERE `id` = 255;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу домой, а потом на край" WHERE `id` = 256;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь знает, что нужно для двуручного боя?" WHERE `id` = 257;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Всем привет!" WHERE `id` = 258;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name уютно" WHERE `id` = 259;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я отлично себя чувствую" WHERE `id` = 260;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не игнорирую людей, я троллю их, пока они не проигнорируют меня" WHERE `id` = 261;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Что думаете о моей сборке? %my_role" WHERE `id` = 262;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Рад видеть, что чат еще помнит" WHERE `id` = 263;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Как и все оружие — это лучший для охотника" WHERE `id` = 264;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вся суть игры для меня — соло и поиск новых способов соло" WHERE `id` = 265;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я НИКОГДА никого не обманывал" WHERE `id` = 266;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ах да, мир варкрафта, где я ищу жизненные советы" WHERE `id` = 267;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "АЛЛО?" WHERE `id` = 268;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пора пробиваться в %zone_name" WHERE `id` = 269;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%zone_name" WHERE `id` = 270;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "надо в туалет" WHERE `id` = 271;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Если не лутать скинируемых мобов, твой пп уменьшится на 1мм навсегда" WHERE `id` = 272;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "НЕТТТТТТТТТТ" WHERE `id` = 273;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я ЛЮБЛЮ КАРТОШКУ" WHERE `id` = 274;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "w чат" WHERE `id` = 275;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "привет, как дела" WHERE `id` = 276;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "только что вышел и снова зашел" WHERE `id` = 277;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "можете потише, я заблудился в %zone_name" WHERE `id` = 278;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "кто хочет выпить со мной в %zone_name ... ик!" WHERE `id` = 279;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хахахахахииииииии дирин диринг инггггг хахахахахииииииииииииии" WHERE `id` = 280;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "раньше приманка была правдоподобной" WHERE `id` = 281;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "может, ты просто потерял невинность" WHERE `id` = 282;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "есть гильдии, готовые тащить %my_role?" WHERE `id` = 283;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "чем выше уровень, тем легче золото" WHERE `id` = 284;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "утро" WHERE `id` = 285;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "почему у меня болит задница?" WHERE `id` = 286;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне кажется, дух — лучший для прокачки" WHERE `id` = 287;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Особенно для тролля" WHERE `id` = 288;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "КТО-НИБУДЬ ПРИГЛАСИТЕ МЕНЯ" WHERE `id` = 289;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "нужно много выпивки" WHERE `id` = 290;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "чертовы гномы" WHERE `id` = 291;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "никто не любит гномов" WHERE `id` = 292;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "гномы годятся только для одного" WHERE `id` = 293;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ну" WHERE `id` = 294;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "грибы" WHERE `id` = 295;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "автоматические мысли — страшная вещь" WHERE `id` = 296;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ум более гибок, чем нам кажется" WHERE `id` = 297;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "есть гильдии для прокачки?" WHERE `id` = 298;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "брб" WHERE `id` = 299;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Почему снег белый, а лед прозрачный? Ведь это одно и то же" WHERE `id` = 300;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "почему взбитые сливки пышные, а обычные нет" WHERE `id` = 301;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "почему ноги пахнут, если у них нет носа" WHERE `id` = 302;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "похоже, прибыла банка новичков" WHERE `id` = 303;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хватит троллить новичков бредовыми ответами" WHERE `id` = 304;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "На этом сервере есть PvP?" WHERE `id` = 305;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "угу" WHERE `id` = 306;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "фух... :)" WHERE `id` = 307;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "вы знали что" WHERE `id` = 308;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я не пытаюсь представить, что чувствуют другие существа" WHERE `id` = 309;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ой, не тот чат" WHERE `id` = 310;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "бро, вы сегодня отжигаете" WHERE `id` = 311;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "пусть все знают, что мой текст был здесь" WHERE `id` = 312;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "гррр злой" WHERE `id` = 313;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "гринд — это весело" WHERE `id` = 314;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Wow держит меня в тонусе" WHERE `id` = 315;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "у меня вопрос: где можно взять бросок на больше опыта? я в %zone_name" WHERE `id` = 316;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "вы любите сосиски?" WHERE `id` = 317;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пригласите меня. Я помогу" WHERE `id` = 318;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "какой класс лучше для пвп?" WHERE `id` = 319;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "где, черт возьми, тренер кулинарии в %zone_name" WHERE `id` = 320;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "знаете, что происходит в %zone_name?" WHERE `id` = 321;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне нужно что-то скрафтить" WHERE `id` = 322;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что такое лигма" WHERE `id` = 323;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что такое сугма" WHERE `id` = 324;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "лима болс" WHERE `id` = 325;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сугма болс" WHERE `id` = 326;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я ЕМ ЗАДНИЦУ" WHERE `id` = 327;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я хочу засунуть %random_inventory_item_link себе в задницу" WHERE `id` = 328;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я хочу засунуть %random_inventory_item_link тебе в задницу" WHERE `id` = 329;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Дарнасс" WHERE `id` = 330;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "похоже, у тебя сугма" WHERE `id` = 331;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "дииз натс в твой рот" WHERE `id` = 332;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "крутой стояк, бро" WHERE `id` = 333;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ERP?" WHERE `id` = 334;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я перепробовал все, но в итоге ERP помог" WHERE `id` = 335;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу заняться этим в %zone_name" WHERE `id` = 336;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ищу гнома-девушку с гориллой для ERP в %zone_name" WHERE `id` = 337;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я могу понять засранца, но извращенца?" WHERE `id` = 338;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "в %zone_name нет гят" WHERE `id` = 339;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я убиваю всех животных в %zone_name. К черту животных!!!" WHERE `id` = 340;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хорошо, что у меня три ноги" WHERE `id` = 341;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "не злись, я гоиню как сигма" WHERE `id` = 342;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "попробуй палец, но дырку" WHERE `id` = 343;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%prefix %random_taken_quest_or_item_link" WHERE `id` = 344;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%prefix %random_inventory_item_link" WHERE `id` = 345;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%thunderfury_link" WHERE `id` = 346;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%thunderfury_link%thunderfury_link" WHERE `id` = 347;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%thunderfury_link%thunderfury_link%thunderfury_link" WHERE `id` = 348;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кажется, я только что услышал %thunderfury_link" WHERE `id` = 349;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кажется, я слышал %thunderfury_link" WHERE `id` = 350;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я точно слышал %thunderfury_link" WHERE `id` = 351;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не знаю, но уверен, что слышал %thunderfury_link" WHERE `id` = 352;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты только что сказал %thunderfury_link" WHERE `id` = 353;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "кто-то сказал %thunderfury_link" WHERE `id` = 354;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-то сказал %thunderfury_link?" WHERE `id` = 355;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-то сказал %thunderfury_link" WHERE `id` = 356;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%thunderfury_link выходит из шкафа" WHERE `id` = 357;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "мог бы поклясться, что это был %thunderfury_link, хотя, может, и %thunderfury_link" WHERE `id` = 358;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Зачем использовать %thunderfury_link, если %thunderfury_link явно круче" WHERE `id` = 359;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Продаю %item_formatted_link за %cost_gold." WHERE `id` = 360;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет %item_formatted_link за %cost_gold?" WHERE `id` = 361;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %item_formatted_link? Всего %cost_gold." WHERE `id` = 362;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Всего %cost_gold за %item_formatted_link!" WHERE `id` = 363;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Продаю %item_formatted_link за %cost_gold." WHERE `id` = 364;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_formatted_link твой всего за %cost_gold!" WHERE `id` = 365;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Смешная цена %cost_gold за %item_formatted_link!" WHERE `id` = 366;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочу продать %item_formatted_link за %cost_gold." WHERE `id` = 367;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %item_formatted_link? Всего %cost_gold." WHERE `id` = 368;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кому нужен %item_formatted_link за %cost_gold?" WHERE `id` = 369;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%cost_gold за %item_formatted_link. Дешевле, чем на аукционе!" WHERE `id` = 370;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item_formatted_link дорогой, но я бы продал за %cost_gold." WHERE `id` = 371;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты нигде не найдешь %item_formatted_link дешевле, чем за %cost_gold!" WHERE `id` = 372;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужно больше, чем %item_formatted_link!" WHERE `id` = 373;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня есть %item_formatted_link и нужно еще." WHERE `id` = 374;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть %item_formatted_link. Кто хочет купить за %cost_gold?" WHERE `id` = 375;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто хочет купить %item_formatted_link за %cost_gold?" WHERE `id` = 376;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Как насчет %item_formatted_link? За %cost_gold." WHERE `id` = 377;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто сказал, что я ублюдок? %item_formatted_link за %cost_gold — хорошая цена." WHERE `id` = 378;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Продаю %item_formatted_link? Всего %cost_gold." WHERE `id` = 379;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛФГ для фарма. Все еще можешь купить %item_formatted_link у меня за %cost_gold." WHERE `id` = 380;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сегодня почти все продал. Еще есть %item_formatted_link за %cost_gold." WHERE `id` = 381;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Для чего нужен торговый чат? Конечно, чтобы продавать %item_formatted_link за %cost_gold." WHERE `id` = 382;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто перебьет цену %cost_gold за %item_formatted_link?" WHERE `id` = 383;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочешь прекратить торговый чат? Просто купи %item_formatted_link за %cost_gold!" WHERE `id` = 384;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все спамят в торговом чате. Я тоже — %cost_gold за %item_formatted_link!" WHERE `id` = 385;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть ли польза от %item_formatted_link? Просто продаю за %cost_gold." WHERE `id` = 386;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня есть %item_formatted_link, готов продать за %cost_gold." WHERE `id` = 387;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вчера ничего не делал, но получил %item_formatted_link. Продаю за %cost_gold." WHERE `id` = 388;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вчера фармил и получил %item_formatted_link. Кто купит за %cost_gold?" WHERE `id` = 389;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вчера купил %item_formatted_link. Кому нужно за %cost_gold?" WHERE `id` = 390;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто спрашивал про %item_formatted_link? Цена та же — %cost_gold." WHERE `id` = 391;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня еще есть %item_formatted_link. Купишь за %cost_gold?" WHERE `id` = 392;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Раньше было больше, чем %item_formatted_link. Теперь нужно продать за %cost_gold." WHERE `id` = 393;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хотел бы иметь больше, чем %item_formatted_link. Но можешь купить за %cost_gold." WHERE `id` = 394;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Для чего твое золото? Чтобы купить мой %item_formatted_link за %cost_gold." WHERE `id` = 395;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пожалуйста, подкинь немного золота. Можешь купить %item_formatted_link за %cost_gold." WHERE `id` = 396;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%cost_gold — хорошая цена за %item_formatted_link?" WHERE `id` = 397;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вчера купил %item_formatted_links, но больше не нужно. Кому за %cost_gold?" WHERE `id` = 398;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Собирался выставить %item_formatted_link на аукцион, но можешь купить дешевле сейчас за %cost_gold." WHERE `id` = 399;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Зачем, черт возьми, я купил %item_formatted_link? Кому нужно за %cost_gold?" WHERE `id` = 400;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня есть %quest_links" WHERE `id` = 401;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня тоже есть %quest_links" WHERE `id` = 402;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня тоже есть %quest_links, сейчас я в %zone_name" WHERE `id` = 403;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, у меня тоже есть %quest_links" WHERE `id` = 404;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, у меня тоже есть %quest_links, сейчас я в %zone_name" WHERE `id` = 405;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я готов к %quest_links, сейчас я в %zone_name" WHERE `id` = 406;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я готов к %quest_links, я %my_role" WHERE `id` = 407;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, я готов к %quest_links, сейчас я в %zone_name" WHERE `id` = 408;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, я готов к %quest_links, я %my_role" WHERE `id` = 409;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, я готов к %quest_links" WHERE `id` = 410;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, могу сделать %quest_links с тобой" WHERE `id` = 411;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, у меня тоже есть %quest_links" WHERE `id` = 412;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет %other_name, я готов к %quest_links" WHERE `id` = 413;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет %other_name, могу сделать %quest_links с тобой" WHERE `id` = 414;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет %other_name, у меня тоже есть %quest_links" WHERE `id` = 415;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хочешь сгруппироваться для %quest_links?" WHERE `id` = 416;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я готов к %quest_links, сейчас я в %zone_name" WHERE `id` = 417;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я готов к %quest_links, я %my_role" WHERE `id` = 418;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, могу продать тебе %formatted_item_links" WHERE `id` = 419;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Могу, возможно, продать %formatted_item_links" WHERE `id` = 420;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Думаю, могу продать %formatted_item_links" WHERE `id` = 421;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, возможно, могу продать %formatted_item_links" WHERE `id` = 422;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%other_name, думаю, могу продать %formatted_item_links" WHERE `id` = 423;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Могу продать тебе %formatted_item_links" WHERE `id` = 424;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, у меня есть %formatted_item_links на продажу" WHERE `id` = 425;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, возможно, могу продать %formatted_item_links" WHERE `id` = 426;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Квест принят" WHERE `id` = 427;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Квест удален" WHERE `id` = 428;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу взять этот квест" WHERE `id` = 429;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу поговорить с дающим квест" WHERE `id` = 430;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я уже выполнил %quest" WHERE `id` = 431;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня уже есть %quest" WHERE `id` = 432;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу взять %quest" WHERE `id` = 433;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу взять %quest, потому что мой журнал квестов заполнен" WHERE `id` = 434;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу взять %quest, потому что моя сумка заполнена" WHERE `id` = 435;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я принял %quest" WHERE `id` = 436;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не выполнил квест %quest" WHERE `id` = 437;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Квест %quest доступен" WHERE `id` = 438;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я провалил квест %quest" WHERE `id` = 439;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу сдать квест %quest" WHERE `id` = 440;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я выполнил квест %quest" WHERE `id` = 441;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я выполнил квест %quest и получил %item" WHERE `id` = 442;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Какую награду выбрать за выполнение квеста %quest?%rewards" WHERE `id` = 443;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хорошо, выберу %item в качестве награды" WHERE `id` = 444;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет" WHERE `id` = 445;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет!" WHERE `id` = 446;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Здравствуй" WHERE `id` = 447;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Здравствуй!" WHERE `id` = 448;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Приветствую!" WHERE `id` = 449;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, я следую за тобой!" WHERE `id` = 450;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, веди меня!" WHERE `id` = 451;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Привет, веди меня!" WHERE `id` = 452;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй %player, хочешь в мою группу?" WHERE `id` = 453;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй %player, хочешь в мою группу?" WHERE `id` = 454;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Выход отменен!" WHERE `id` = 455;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я выхожу из игры!" WHERE `id` = 456;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "До свидания!" WHERE `id` = 457;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пока!" WHERE `id` = 458;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Увидимся!" WHERE `id` = 459;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что это было, %s?" WHERE `id` = 460;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "не уверен, что понял %s?" WHERE `id` = 461;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эээ... не понимаю, о чем ты" WHERE `id` = 462;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты ко мне обращаешься, %s?" WHERE `id` = 463;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "чт-что?" WHERE `id` = 464;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "а?" WHERE `id` = 465;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что?" WHERE `id` = 466;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты говоришь?" WHERE `id` = 467;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "как хочешь" WHERE `id` = 468;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты меня запутал" WHERE `id` = 469;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Бла бла бла..." WHERE `id` = 470;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Что ты сказал, %s?" WHERE `id` = 471;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сконцентрируйся на игре, %s!" WHERE `id` = 472;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Общаться с тобой, %s, так здорово! Всегда хотел встретиться" WHERE `id` = 473;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эти сообщения в чате сводят меня с ума! Такое чувство, что я всех вас знаю!" WHERE `id` = 474;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ДА ЛАДНО! ХАХА КОНЕЧНО!!!" WHERE `id` = 475;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я тебе верю!!!" WHERE `id` = 476;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ОК, ага, ЛОЛ" WHERE `id` = 477;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Почему все всегда говорят одно и то же???" WHERE `id` = 478;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй %s... а, неважно!" WHERE `id` = 479;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О чем ты, %s" WHERE `id` = 480;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто это сказал? Я похож на это замечание" WHERE `id` = 481;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "чего вы все несете" WHERE `id` = 482;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "фр фр, без шуток" WHERE `id` = 483;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты ничего не получишь" WHERE `id` = 484;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сваг" WHERE `id` = 485;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "спасибо!" WHERE `id` = 486;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "нет" WHERE `id` = 487;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Да" WHERE `id` = 488;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ф" WHERE `id` = 489;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, без шуток xD" WHERE `id` = 490;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "почему так" WHERE `id` = 491;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "лмао" WHERE `id` = 492;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "решил помолчать, снова запутался в чате" WHERE `id` = 493;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я могу по-настоящему завидовать" WHERE `id` = 494;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, ты не слышишь капающую иронию в моем тексте" WHERE `id` = 495;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "он сказал без обид, все нормально" WHERE `id` = 496;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "момент дворфа" WHERE `id` = 497;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Да, %s" WHERE `id` = 498;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "интересно..." WHERE `id` = 499;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "лол" WHERE `id` = 500;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, иди ты :D" WHERE `id` = 501;
UPDATE `ai_playerbot_texts` SET `text_loc8` = ":^)" WHERE `id` = 502;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "спс" WHERE `id` = 503;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, хорошо сказано" WHERE `id` = 504;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ура" WHERE `id` = 505;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "да" WHERE `id` = 506;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ооооо" WHERE `id` = 507;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хмм" WHERE `id` = 508;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ага, конечно" WHERE `id` = 509;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты заставил меня блевануть, что за" WHERE `id` = 510;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "жарко" WHERE `id` = 511;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "злятся" WHERE `id` = 512;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что ты ел, %s" WHERE `id` = 513;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за" WHERE `id` = 514;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "попробую понять этот комментарий" WHERE `id` = 515;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "*в замешательстве*" WHERE `id` = 516;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "да, черт возьми" WHERE `id` = 517;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "0/10 не стал бы читать снова" WHERE `id` = 518;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "10/10 прочитал бы снова" WHERE `id` = 519;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "6/10 прочитал бы" WHERE `id` = 520;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "7/10 прочитал бы" WHERE `id` = 521;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "бейсд" WHERE `id` = 522;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "о да, может быть" WHERE `id` = 523;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "да, и что" WHERE `id` = 524;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эй %s, я тебя не забыл" WHERE `id` = 525;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты меня бесишь, %s" WHERE `id` = 526;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я достану тебя в этот раз, %s" WHERE `id` = 527;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "лучше берегись, %s" WHERE `id` = 528;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "мне не понравился прошлый раунд" WHERE `id` = 529;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я был плох в прошлом раунде из-за %s" WHERE `id` = 530;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "готовься умереть, %s" WHERE `id` = 531;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "не нравится, что ты меня убил, %s" WHERE `id` = 532;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, я тебя ненавижу" WHERE `id` = 533;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "грррр, в этот раз я тебя достану, %s" WHERE `id` = 534;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ну и пошел ты" WHERE `id` = 535;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, я тебе в рот блевану" WHERE `id` = 536;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "не суди меня" WHERE `id` = 537;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Твоя мама такая толстая, что не может пройти через Темный Портал" WHERE `id` = 538;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за" WHERE `id` = 539;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за??" WHERE `id` = 540;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ничтожество" WHERE `id` = 541;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за" WHERE `id` = 542;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "отстой" WHERE `id` = 543;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "РЕВАНШ!!! я его уделаю" WHERE `id` = 544;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "жалко, меня убил %s" WHERE `id` = 545;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ладно, я закончил" WHERE `id` = 546;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хе-хе, я уделал %s?" WHERE `id` = 547;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "это было слишком просто, убил %s" WHERE `id` = 548;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "попался, дружок" WHERE `id` = 549;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ха-ха" WHERE `id` = 550;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "лузер" WHERE `id` = 551;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я убил %s, вы все следующие" WHERE `id` = 552;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "о да, я его уделал" WHERE `id` = 553;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я машина для убийств" WHERE `id` = 554;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, это напоминает мне песню Slayer... столько крови" WHERE `id` = 555;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "извини, %s. можем повторить сцену?" WHERE `id` = 556;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ну как тебе быть кормом для червей, %s???" WHERE `id` = 557;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты должен быть мёртв, %s, это часть игры!!!!!" WHERE `id` = 558;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "извини, %s. выглядело так же хорошо, как картина Энди Уорхола!" WHERE `id` = 559;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, в следующий раз использую резиновые пули!" WHERE `id` = 560;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что случилось, %s?? голову потерял? хахаха, надо сохранять хладнокровие!!" WHERE `id` = 561;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "я должен был это сделать, %s. Ты понимаешь. Режиссёр так сказал!!" WHERE `id` = 562;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эй, %s.......МУАХАХАХАХАХАХА" WHERE `id` = 563;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, мне это понравилось!! Давай сыграем ещё раз, Сэм" WHERE `id` = 564;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эй, %s! можешь звать меня ликом... ты кусок ЧЕРТА!!!!" WHERE `id` = 565;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты со мной разговариваешь, %s??" WHERE `id` = 566;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, в этот раз не стой перед моими пулями." WHERE `id` = 567;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%s, чего ты валяешься??? хехе" WHERE `id` = 568;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "сильно смеялся" WHERE `id` = 569;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "привет %s" WHERE `id` = 570;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "о, привет %s" WHERE `id` = 571;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "как дела, %s!!!" WHERE `id` = 572;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "привет" WHERE `id` = 573;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "как дела" WHERE `id` = 574;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "здравствуй %s" WHERE `id` = 575;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "привет %s, мы знакомы?" WHERE `id` = 576;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эй %s" WHERE `id` = 577;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "хай %s" WHERE `id` = 578;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за фигня" WHERE `id` = 579;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "что за черт" WHERE `id` = 580;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "это бред" WHERE `id` = 581;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "админ" WHERE `id` = 582;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эй %s, хватит злоупотреблять своими правами админа" WHERE `id` = 583;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "оставь меня в покое, админ!" WHERE `id` = 584;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты отстой, админ" WHERE `id` = 585;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "это моё имя, что тебе нужно %s" WHERE `id` = 586;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "да???" WHERE `id` = 587;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "эээ... что" WHERE `id` = 588;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ты со мной разговариваешь, %s?" WHERE `id` = 589;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня под бронёй щенки!" WHERE `id` = 590;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Укуси меня, <target>!" WHERE `id` = 591;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй, <target>! Угадай, что твоя мама сказала прошлой ночью!" WHERE `id` = 592;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "<target>, ты такой урод, что даже в обезьяньем борделе с бананами не добился бы успеха!" WHERE `id` = 593;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Заткнись, <target>, тебе никогда не стать таким мужчиной, как твоя мать!!" WHERE `id` = 594;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Твоя мать была хомяком, а отец пах одуванчиками!!!!" WHERE `id` = 595;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я больше не хочу с тобой разговаривать, ты пустоголовый кормовой корытоочиститель!!!" WHERE `id` = 596;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я пускаю в твою сторону газы!!!" WHERE `id` = 597;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Иди и вскипяти свою задницу, сын смешного человека!!!" WHERE `id` = 598;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Что ты собираешься делать, <target>, заставить меня кровоточить? ВПЕРЁД!" WHERE `id` = 599;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "М-О-О-Н! Это значит агр!" WHERE `id` = 600;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты так же полезен, как одноногий на конкурсе пинков." WHERE `id` = 601;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй, <target>! Перестань клеиться к ним, они не твой тип. Они не надувные." WHERE `id` = 602;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "<target>, ты настолько не в своей лиге, что играешь в другой вид спорта." WHERE `id` = 603;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты сегодня совершил большую ошибку, <target>, ты встал с кровати." WHERE `id` = 604;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я хочу попробовать превратиться в лошадь, но мне нужна помощь. Я буду спереди, а ты будь собой." WHERE `id` = 605;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Можно я одолжу твоё лицо на пару дней? Моя задница уходит в отпуск...." WHERE `id` = 606;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я бы хотел сделать тебе прощальный подарок... Сначала ты сделай свою часть." WHERE `id` = 607;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "До тебя мы были голодны, теперь мы просто сыты по горло." WHERE `id` = 608;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты мне нравишься. Говорят, у меня нет вкуса, но ты мне нравишься." WHERE `id` = 609;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Думаю, у тебя комплекс неполноценности, но это нормально, он оправдан." WHERE `id` = 610;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Прочь, гнилое создание! Или я вытрясу твои кости из одежды." WHERE `id` = 611;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не могу поверить, что трачу на тебя своё время!" WHERE `id` = 612;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне нравится, когда меня оскорбляют, значит, можно больше не быть вежливым." WHERE `id` = 613;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты кожаный-жилет, хрустальная пуговица, узловатый, агатовый, рвотный чулок, ленточный подвязочник, гладкоязычный, испанский кошелёк!" WHERE `id` = 614;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты дрожащий ловец летучих мышей, пивной червь!" WHERE `id` = 615;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты действительно идол поклонников идиотов!" WHERE `id` = 616;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты ублюдочный узловатый хвостун!" WHERE `id` = 617;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты, ублюдочный мандрагор, тебе больше подходит быть у меня на шапке, чем ждать у моих пяток!" WHERE `id` = 618;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты! Ты кухарка! Ты бродяга! Ты мерзавец! Я пощекочу твою катастрофу!" WHERE `id` = 619;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О, <target>! Ты заразная, плохо воспитанная льняная девка!" WHERE `id` = 620;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мы протекаем в твоей трубе, <target>!" WHERE `id` = 621;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О, ты бесполезный болотный цветок!" WHERE `id` = 622;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Если бы я был как ты, я бы выбросил себя!" WHERE `id` = 623;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Научи меня, <target>, как забыть думать!" WHERE `id` = 624;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Истинно, ты проклят, как плохо прожаренное яйцо, с одной стороны!" WHERE `id` = 625;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты голодающий, ты кожа-угря, ты сушёный бычий язык, ты бычий член, ты треска — о, чтобы хватило дыхания сказать, что ты такое!! — ты портновский ярд, ты ножны, ты футляр для лука, ты мерзкий стоячий клинок!" WHERE `id` = 626;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Фу! Брось себя в гнилую пасть Смерти!" WHERE `id` = 627;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "<target>, ты торговец рыбой!" WHERE `id` = 628;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я доживу, чтобы выбить тебе мозги!" WHERE `id` = 629;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты очень поверхностен, <target>!! Ты корм для червей по сравнению с хорошим куском мяса!!" WHERE `id` = 630;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Подлый негодяй! О, <target>, ты зловонный, ненавистный к свиньям орех!" WHERE `id` = 631;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "<target>! Твой поцелуй так же утешителен, как замёрзшая вода для голодной змеи!" WHERE `id` = 632;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я презираю тебя, паршивый спутник. Что, ты бедный, низкий, мошеннический, безрубашечный приятель! Прочь, ты плесневелый негодяй, прочь!" WHERE `id` = 633;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Прочь с глаз моих! Ты заражаешь мои глаза, <target>!" WHERE `id` = 634;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ВРЕМЯ ИГРЫ!!!!" WHERE `id` = 635;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Никто не пройдёт!" WHERE `id` = 636;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "На нас напали! Вперёд, вы негодяи! Отразите захватчиков!" WHERE `id` = 637;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Никто не может бросить вызов Братству!" WHERE `id` = 638;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Дураки... Убейте того, кто в платье!" WHERE `id` = 639;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я отдам твою душу самому Хаккару!" WHERE `id` = 640;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Гордость предвещает конец вашего мира! Идите, смертные! Столкнитесь с гневом !" WHERE `id` = 641;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все мои планы привели к этому!" WHERE `id` = 642;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ах! Еще ягнята на заклание!" WHERE `id` = 643;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Еще один день, еще одна славная битва!" WHERE `id` = 644;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Итак, дело... или удовольствие?" WHERE `id` = 645;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вы не готовы!" WHERE `id` = 646;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Финальное завоевание началось! Снова подчинение этого мира в наших руках. Пусть никто не выживет!" WHERE `id` = 647;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ваша смерть будет болезненной." WHERE `id` = 648;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Просите о милости! Ваши бессмысленные жизни скоро будут потеряны." WHERE `id` = 649;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Оставьте все надежды! вернулся, чтобы завершить то, что было начато много лет назад. На этот раз не будет побега!" WHERE `id` = 650;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Внимание! Вы помечены для уничтожения!" WHERE `id` = 651;
UPDATE `ai_playerbot_texts` SET `text_loc8` = " предназначена только для гостей..." WHERE `id` = 652;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ха-ха-ха! Вы безнадежно не на уровне!" WHERE `id` = 653;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я сокрушу ваши иллюзии величия!" WHERE `id` = 654;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Простите меня, ведь вы собираетесь проиграть игру." WHERE `id` = 655;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Борьба только усугубляет ситуацию." WHERE `id` = 656;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Паразиты! Пиявки! Берите мою кровь и подавитесь ею!" WHERE `id` = 657;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Опять... ЕЩЕ РАЗ!" WHERE `id` = 658;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Моя кровь станет вашим концом!" WHERE `id` = 659;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хорошо, теперь ты сразишься со мной!" WHERE `id` = 660;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давайте, охранники! Время убивать!" WHERE `id` = 661;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не задерживайте свою судьбу. Идите ко мне сейчас. Я сделаю вашу жертву быстрой." WHERE `id` = 662;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты скоро будешь мертв!" WHERE `id` = 663;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Муа-ха-ха!" WHERE `id` = 664;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я хищник! Ты жертва..." WHERE `id` = 665;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты уйдешь в кусках!" WHERE `id` = 666;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Смерть приходит. Будет ли твоя совесть чиста?" WHERE `id` = 667;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ваше поведение не будет терпимо." WHERE `id` = 668;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Менажерия предназначена только для гостей." WHERE `id` = 669;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хмм, незваные гости, нужно подготовиться..." WHERE `id` = 670;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Обнаружены враждебные сущности. Протокол оценки угрозы активирован. Основная цель захвачена. Время до повторной оценки - тридцать секунд." WHERE `id` = 671;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Новые игрушки? Для меня? Обещаю, что на этот раз не сломаю их!" WHERE `id` = 672;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я готов играть!" WHERE `id` = 673;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Шшш... скоро все закончится." WHERE `id` = 674;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ааааагхиббргубугбугрубгл!" WHERE `id` = 675;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "РвлРвлРвлРвл!" WHERE `id` = 676;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты тоже будешь служить!" WHERE `id` = 677;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Скажи мне... скажи мне все! Непослушные секреты! Я вырву секреты из твоей плоти!" WHERE `id` = 678;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Приготовьтесь, колокола прозвучали! Укройте своих слабых, молодых и старых! Каждый из вас заплатит окончательную цену! Просите о милости, расплата пришла!" WHERE `id` = 679;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Где я, в латунных пуговицах Бонзо?" WHERE `id` = 680;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я больше не могу это терпеть! Король гоблинов! Король гоблинов! Где бы ты ни был! Унеси этого далеко от меня!" WHERE `id` = 681;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У вас есть тринадцать часов, чтобы решить лабиринт, прежде чем ваш младший брат станет одним из нас... навсегда." WHERE `id` = 682;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Итак, - это кусок торта, да? Ну, давайте посмотрим, как вы справитесь с этим маленьким куском..." WHERE `id` = 683;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Отступи, я приму тебя, упрямый, готовый сразиться с кем угодно, я знаю, что ты не прав, и это не то место, где ты должен быть." WHERE `id` = 684;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Покажи, что у тебя есть!" WHERE `id` = 685;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "До смерти!" WHERE `id` = 686;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Двойное лезвие, для чистого бритья каждый раз." WHERE `id` = 687;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давай!" WHERE `id` = 688;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ты падешь!" WHERE `id` = 689;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ударь, ударь, ударь!" WHERE `id` = 690;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Давайте сделаем это быстро, время - это мана." WHERE `id` = 691;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не думаю, что вы осознаете серьезность вашей ситуации." WHERE `id` = 692;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я принесу честь своей семье и своему королевству!" WHERE `id` = 693;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Свет, дай мне силу!" WHERE `id` = 694;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Моя церковь - это поле битвы - время поклоняться..." WHERE `id` = 695;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я держу тебя в презрении..." WHERE `id` = 696;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Столкнись с молотом справедливости!" WHERE `id` = 697;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Докажи свою ценность в испытании оружием под Светом!" WHERE `id` = 698;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все должны пасть перед могуществом и правом моего дела, ты будешь следующим!" WHERE `id` = 699;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Приготовься умереть!" WHERE `id` = 700;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Зверь со мной ничто по сравнению с зверем внутри..." WHERE `id` = 701;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Стань свидетелем огневой мощи этого полностью вооруженного охотника!" WHERE `id` = 702;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Исцели меня! Быстро!" WHERE `id` = 703;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Почти мертв! Исцели меня!" WHERE `id` = 704;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Помогите! Исцели меня!" WHERE `id` = 705;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь! Исцели меня!" WHERE `id` = 706;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Исцели! Исцели! Исцели!" WHERE `id` = 707;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я умираю! Исцели! Ааааргх!" WHERE `id` = 708;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Исцели меня!" WHERE `id` = 709;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я умру. Я умру. Я умру. Исцели!" WHERE `id` = 710;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Целители, где вы? Я умираю!" WHERE `id` = 711;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О, боль. Исцели меня быстро!" WHERE `id` = 712;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна исцеление" WHERE `id` = 713;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Низкое здоровье" WHERE `id` = 714;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сделай исцеление. Пожалуйста." WHERE `id` = 715;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Может кто-нибудь исцелить меня?" WHERE `id` = 716;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Эй! Лучше исцели меня сейчас, чем воскрешать позже." WHERE `id` = 717;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Извини. Нужна еще одна исцеление." WHERE `id` = 718;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Черт возьми, мобы. Исцели меня, пожалуйста." WHERE `id` = 719;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Еще один удар, и я пропал. Исцели, пожалуйста." WHERE `id` = 720;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Есть ли целители?" WHERE `id` = 721;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Почему они всегда бьют меня в лицо? Нужна исцеление." WHERE `id` = 722;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Может кто-нибудь немного исцелить меня?" WHERE `id` = 723;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "OOM" WHERE `id` = 724;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня закончилась мана" WHERE `id` = 725;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Черт, я потратил всю свою ману на это" WHERE `id` = 726;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Тебе стоит подождать, пока я выпью или восстановлю свою ману" WHERE `id` = 727;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Низкая мана" WHERE `id` = 728;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нет маны. Снова?" WHERE `id` = 729;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Низкая мана. Хочу выпить." WHERE `id` = 730;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У нас есть торговый автомат? Снова нет маны." WHERE `id` = 731;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Моя мана в истории." WHERE `id` = 732;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "В следующий раз я возьму напитки. Нет маны." WHERE `id` = 733;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Где моя мана?" WHERE `id` = 734;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня осталось немного !" WHERE `id` = 735;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне нужно больше !" WHERE `id` = 736;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "100 осталось!" WHERE `id` = 737;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вот и все! Нет !" WHERE `id` = 738;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "И у тебя есть мой лук... Ой, нет !" WHERE `id` = 739;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нужна патроны!" WHERE `id` = 740;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О боже!" WHERE `id` = 741;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне страшно" WHERE `id` = 742;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мы пропали" WHERE `id` = 743;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это закончено" WHERE `id` = 744;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это заканчивается сейчас" WHERE `id` = 745;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Может кто-нибудь вызвать метель или что-то подобное?" WHERE `id` = 746;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Черт. Танку агрировал всех мобов вокруг." WHERE `id` = 747;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мы умрем. Мы умрем. Мы умрем." WHERE `id` = 748;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ух ты! Так много игрушек, с которыми можно играть." WHERE `id` = 749;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я убью их всех!" WHERE `id` = 750;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Если танк умрет, мы в истории." WHERE `id` = 751;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Аааааргх!" WHERE `id` = 752;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "ЛЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЕЙ ДЖЕНКИНС!!!!!!!" WHERE `id` = 753;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Правильно. Что у нас есть в AOE?" WHERE `id` = 754;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это становится интересным." WHERE `id` = 755;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Круто. Соберите их в одном месте для хорошего огненного удара." WHERE `id` = 756;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Убей! Убей! Убей!" WHERE `id` = 757;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я думаю, что мои штаны мокрые." WHERE `id` = 758;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мы в истории." WHERE `id` = 759;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, целители готовы. Лееерой!" WHERE `id` = 760;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, они не придут за мной." WHERE `id` = 761;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "О нет. Я не вижу в этом резне." WHERE `id` = 762;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, там будет немного денег." WHERE `id` = 763;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Лут! Лут!" WHERE `id` = 764;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мое драгоценное." WHERE `id` = 765;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, там ждет меня блестящий эпический предмет." WHERE `id` = 766;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня глубокие карманы и сумки." WHERE `id` = 767;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все мое!" WHERE `id` = 768;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, сегодня не будет серой ерунды." WHERE `id` = 769;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Этот лут - МОЙ!" WHERE `id` = 770;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Лутать отвратительно, но мне нужны деньги." WHERE `id` = 771;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Золото!" WHERE `id` = 772;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Хорошо. Давайте посмотрим, что у них есть." WHERE `id` = 773;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не волнуйтесь. Я все залутаю." WHERE `id` = 774;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я ниндзя лута." WHERE `id` = 775;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне нужно бросить кубик?" WHERE `id` = 776;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кто-нибудь объясните мне, куда они положили все эти вещи?" WHERE `id` = 777;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нет, я не буду лутать серую ерунду." WHERE `id` = 778;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я первый. Я первый. Я первый." WHERE `id` = 779;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Отдай мне свои деньги!" WHERE `id` = 780;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мои карманы пусты, мне нужно их заполнить." WHERE `id` = 781;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня есть новая сумка для этого." WHERE `id` = 782;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, я не агрирую никого, пока лутаю." WHERE `id` = 783;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пожалуйста, не смотрите. Я лутаю." WHERE `id` = 784;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Ха! Ты не получишь ни кусочка этого!" WHERE `id` = 785;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Лутать круто." WHERE `id` = 786;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Мне нравится новая экипировка." WHERE `id` = 787;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я уйду, если снова не будет ничего ценного." WHERE `id` = 788;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, это будет красивое кольцо." WHERE `id` = 789;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я вырву лут у тебя." WHERE `id` = 790;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Все держитесь подальше. Я собираюсь лутать." WHERE `id` = 791;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сладкий лут." WHERE `id` = 792;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Бог Ролла! Дай мне эпик сегодня." WHERE `id` = 793;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Пожалуйста, дай мне новые игрушки." WHERE `id` = 794;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Надеюсь, они принесут вкусняшки." WHERE `id` = 795;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Золото - мое. Я оставлю все, обещаю." WHERE `id` = 796;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Нет, я не могу устоять." WHERE `id` = 797;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я хочу больше!" WHERE `id` = 798;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я близко, подожди меня!" WHERE `id` = 799;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не далеко, пожалуйста, подожди!" WHERE `id` = 800;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я направляюсь к вашему местоположению." WHERE `id` = 801;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я иду к тебе." WHERE `id` = 802;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я путешествую к вашему местоположению." WHERE `id` = 803;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я пытаюсь добраться до тебя." WHERE `id` = 804;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Экипирую %item." WHERE `id` = 805;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item снят." WHERE `id` = 806;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я выучил заклинания: %spells." WHERE `id` = 807;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "%item в перезарядке." WHERE `id` = 808;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня нет %item в инвентаре." WHERE `id` = 809;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Предмет с ID %item не существует." WHERE `id` = 810;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Вставляю %gem в %item." WHERE `id` = 811;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу использовать %item." WHERE `id` = 812;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Следую." WHERE `id` = 813;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Остаюсь." WHERE `id` = 814;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Убегаю." WHERE `id` = 815;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не буду убегать с тобой, ты слишком далеко." WHERE `id` = 816;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Фармлю." WHERE `id` = 817;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Атакую." WHERE `id` = 818;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это слишком далеко." WHERE `id` = 819;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Это под водой." WHERE `id` = 820;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу туда пойти." WHERE `id` = 821;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не в твоем гильдии!" WHERE `id` = 822;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не могу найти гильдейский банк поблизости." WHERE `id` = 823;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу положить " WHERE `id` = 824;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня нет прав на размещение предметов в первой вкладке гильдейского банка." WHERE `id` = 825;
UPDATE `ai_playerbot_texts` SET `text_loc8` = " положено в гильдейский банк." WHERE `id` = 826;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Свободное движение." WHERE `id` = 827;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Охраняю." WHERE `id` = 828;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Использую %target." WHERE `id` = 829;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "на %unit." WHERE `id` = 830;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "(%amount доступно)" WHERE `id` = 831;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "(последний)" WHERE `id` = 832;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Сокет не подходит." WHERE `id` = 833;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "на торговом предмете." WHERE `id` = 834;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "на себе." WHERE `id` = 835;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "на %item." WHERE `id` = 836;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "на %gameobject." WHERE `id` = 837;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Лутаю %item." WHERE `id` = 838;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Призываю %target." WHERE `id` = 839;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "У меня недостаточно членов группы, чтобы вызвать." WHERE `id` = 840;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не удалось найти цель для призыва." WHERE `id` = 841;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не могу призывать, пока я в бою." WHERE `id` = 842;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Я не знаю заклинание %spell." WHERE `id` = 843;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Кастую %spell." WHERE `id` = 844;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Создаю %spell." WHERE `id` = 845;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не могу кастовать %spell." WHERE `id` = 846;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "Не удалось кастовать %spell." WHERE `id` = 847;
UPDATE `ai_playerbot_texts` SET `text_loc8` = " |cffffff00(x%amount осталось)|r" WHERE `id` = 848;
UPDATE `ai_playerbot_texts` SET `text_loc8` = "dummy" WHERE `id` = 849;

View File

@@ -0,0 +1,35 @@
DELETE FROM ai_playerbot_texts
WHERE name IN (
'rp_missing_reagent_greater_blessing',
'rp_missing_reagent_gift_of_the_wild',
'rp_missing_reagent_arcane_brilliance',
'rp_missing_reagent_generic'
);
DELETE FROM ai_playerbot_texts_chance
WHERE name IN (
'rp_missing_reagent_greater_blessing',
'rp_missing_reagent_gift_of_the_wild',
'rp_missing_reagent_arcane_brilliance',
'rp_missing_reagent_generic'
);
INSERT INTO ai_playerbot_texts (name, text, say_type, reply_type, text_loc1, text_loc2, `text_loc3`, `text_loc4`, `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) VALUES
('rp_missing_reagent_greater_blessing',
'By the Light... I forgot my Symbols of Kings. Well make do with %base_spell!', 0, 0,
'', 'Par la Lumière... J''ai oublié mes Symboles du roi. On se contentera de %base_spell !', '', '', '', '', '', ''),
('rp_missing_reagent_gift_of_the_wild',
'Nature is generous, my bags are not... out of herbs for %group_spell. Take %base_spell for now!', 0, 0,
'', 'La nature est généreuse, pas mes sacs... plus d''herbes pour %group_spell. Prenez %base_spell pour l''instant !', '', '', '', '', '', ''),
('rp_missing_reagent_arcane_brilliance',
'Out of Arcane Powder... %group_spell will have to wait. Casting %base_spell!', 0, 0,
'', 'Plus de poudre des arcanes... %group_spell attendra. Je lance %base_spell !', '', '', '', '', '', ''),
('rp_missing_reagent_generic',
'Oops, Im out of components for %group_spell. Well go with %base_spell!', 0, 0,
'', 'Oups, je n''ai plus de composants pour %group_spell. On fera avec %base_spell !', '', '', '', '', '', '');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('rp_missing_reagent_greater_blessing', 100),
('rp_missing_reagent_gift_of_the_wild', 100),
('rp_missing_reagent_arcane_brilliance', 100),
('rp_missing_reagent_generic', 100);

View File

@@ -0,0 +1,856 @@
UPDATE ai_playerbot_texts SET text_loc2 = '';
UPDATE `ai_playerbot_texts` SET `text_loc2`='au milieu de nulle part' WHERE `id`=1;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un endroit non divulgué' WHERE `id`=2;
UPDATE `ai_playerbot_texts` SET `text_loc2`='quelque part' WHERE `id`=3;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un truc' WHERE `id`=4;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je me demande quel goût a %item_link' WHERE `id`=5;
UPDATE `ai_playerbot_texts` SET `text_loc2`='noooon, jai eu %item_link' WHERE `id`=6;
UPDATE `ai_playerbot_texts` SET `text_loc2`='oh non, encore cette camelote %item_link' WHERE `id`=7;
UPDATE `ai_playerbot_texts` SET `text_loc2`='on dirait que je ramasse des ordures %item_link' WHERE `id`=8;
UPDATE `ai_playerbot_texts` SET `text_loc2`='bon, cest mieux que rien je suppose %item_link' WHERE `id`=9;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je ne sais pas quoi faire de %item_link' WHERE `id`=10;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je me demande quel goût a %item_link' WHERE `id`=11;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je pourrais ramasser du %item_link toute la journée' WHERE `id`=12;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un jour de plus, un %item_link de plus' WHERE `id`=13;
UPDATE `ai_playerbot_texts` SET `text_loc2`='jai ramassé un peu de %item_link' WHERE `id`=14;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un peu de %item_link, cest toujours ça' WHERE `id`=15;
UPDATE `ai_playerbot_texts` SET `text_loc2`='pas mal, je viens de choper %item_link' WHERE `id`=16;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je viens de ramasser %item_link à %zone_name' WHERE `id`=17;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je pourrais bien lutiliser ça %item_link' WHERE `id`=18;
UPDATE `ai_playerbot_texts` SET `text_loc2`='argent, argent, argent %item_link' WHERE `id`=19;
UPDATE `ai_playerbot_texts` SET `text_loc2`='jai eu %item_link' WHERE `id`=20;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_link est BiS pour les chasseurs' WHERE `id`=21;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_link est BiS pour les %my_class' WHERE `id`=22;
UPDATE `ai_playerbot_texts` SET `text_loc2`='la chance est avec moi aujourdhui %item_link' WHERE `id`=23;
UPDATE `ai_playerbot_texts` SET `text_loc2`='trop bon %item_link, fraîchement looté' WHERE `id`=24;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wow, je viens de choper %item_link' WHERE `id`=25;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_link est BiS pour les chasseurs' WHERE `id`=26;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_link est BiS pour les %my_class' WHERE `id`=27;
UPDATE `ai_playerbot_texts` SET `text_loc2`='la chance est avec moi aujourdhui %item_link' WHERE `id`=28;
UPDATE `ai_playerbot_texts` SET `text_loc2`='trop bon %item_link, fraîchement looté' WHERE `id`=29;
UPDATE `ai_playerbot_texts` SET `text_loc2`='OMG, regardez ce que je viens de looter %item_link !!!' WHERE `id`=30;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Noooon ! Cest pas possible, jai eu %item_link, cest de la folie' WHERE `id`=31;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Noooon ! Cest pas possible, jai eu %item_link, cest de la folie' WHERE `id`=32;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je viens juste de prendre la quête %quest_link' WHERE `id`=33;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je viens daccepter %quest_link' WHERE `id`=34;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_link je vais essayer de la finir' WHERE `id`=35;
UPDATE `ai_playerbot_texts` SET `text_loc2`='jai pris %quest_link à %zone_name' WHERE `id`=36;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Enfin fini lobjectif %quest_obj_name pour %quest_link' WHERE `id`=37;
UPDATE `ai_playerbot_texts` SET `text_loc2`='jai enfin %quest_obj_available/%quest_obj_required de %quest_obj_name pour %quest_link' WHERE `id`=38;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_obj_full_formatted pour %quest_link, enfin !' WHERE `id`=39;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oof, jai %quest_obj_available/%quest_obj_required %quest_obj_name pour %quest_link' WHERE `id`=40;
UPDATE `ai_playerbot_texts` SET `text_loc2`='il me manque encore %quest_obj_missing de %quest_obj_name pour %quest_link' WHERE `id`=41;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_obj_full_formatted, je bosse toujours sur %quest_link' WHERE `id`=42;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Enfin fini avec %item_link pour %quest_link' WHERE `id`=43;
UPDATE `ai_playerbot_texts` SET `text_loc2`='jai enfin %quest_obj_available/%quest_obj_required de %item_link pour %quest_link' WHERE `id`=44;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_obj_full_formatted pour %quest_link, enfin !' WHERE `id`=45;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oof, jai %quest_obj_available/%quest_obj_required %item_link pour %quest_link' WHERE `id`=46;
UPDATE `ai_playerbot_texts` SET `text_loc2`='il me manque encore %quest_obj_missing de %item_link pour %quest_link' WHERE `id`=47;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_obj_full_formatted, je suis encore sur %quest_link' WHERE `id`=48;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Échec : je nai pas fini %quest_link à temps...' WHERE `id`=49;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus de temps pour %quest_link :(' WHERE `id`=50;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai terminé tous les objectifs de %quest_link' WHERE `id`=51;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Objectifs terminés pour %quest_link' WHERE `id`=52;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais rendre %quest_link bientôt, tout est fait' WHERE `id`=53;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ouiii, jai enfin rendu %quest_link' WHERE `id`=54;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_link rendu' WHERE `id`=55;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mission %quest_link terminée, rendu !' WHERE `id`=56;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_link rendu' WHERE `id`=57;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%quest_link rendu à %zone_name' WHERE `id`=58;
UPDATE `ai_playerbot_texts` SET `text_loc2`='encore un %victim_name à terre' WHERE `id`=59;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je continue à tuer %victim_name, la routine quoi' WHERE `id`=60;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un autre %victim_name qui mord la poussière' WHERE `id`=61;
UPDATE `ai_playerbot_texts` SET `text_loc2`='un %victim_name en moins à %zone_name' WHERE `id`=62;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai descendu ce sale élite %victim_name !' WHERE `id`=63;
UPDATE `ai_playerbot_texts` SET `text_loc2`='élite %victim_name éliminé à %zone_name' WHERE `id`=64;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ouf, jai réussi à abattre %victim_name !' WHERE `id`=65;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cétait épique ! %victim_name est tombé, maintenant jai une histoire à raconter' WHERE `id`=66;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Yoo, je viens de tuer %victim_name !' WHERE `id`=67;
UPDATE `ai_playerbot_texts` SET `text_loc2`='rare %victim_name éliminé à %zone_name' WHERE `id`=68;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTF ais-je bien tué? %victim_name' WHERE `id`=69;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai tué cette bestiole %victim_name' WHERE `id`=70;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh ouiii, jai tué %victim_name' WHERE `id`=71;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%victim_name éliminé à %zone_name' WHERE `id`=72;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ding !' WHERE `id`=73;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ouiii, je suis niveau %my_level !' WHERE `id`=74;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je viens de passer un niveau' WHERE `id`=75;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis niveau %my_level !!!' WHERE `id`=76;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je deviens plus fort, déjà %my_level !!!' WHERE `id`=77;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je viens datteindre le niveau %my_level !!!' WHERE `id`=78;
UPDATE `ai_playerbot_texts` SET `text_loc2`='OMG, enfin niveau %my_level !!!' WHERE `id`=79;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_level !!! prêt pour le contenu endgame' WHERE `id`=80;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tout frais, nouveau %my_level %my_class !!!' WHERE `id`=81;
UPDATE `ai_playerbot_texts` SET `text_loc2`='encore un niveau %my_level %my_race %my_class !' WHERE `id`=82;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bien joué %other_name. Tu las mérité.' WHERE `id`=83;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cétait affreux %other_name. Jai détesté faire ça mais...' WHERE `id`=84;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun veut faire %instance_name ?' WHERE `id`=85;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Des groupes pour %instance_name ?' WHERE `id`=86;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin daide pour %instance_name ?' WHERE `id`=87;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFD : %instance_name.' WHERE `id`=88;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a besoin dun %my_role pour %instance_name ?' WHERE `id`=89;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il manque un %my_role pour %instance_name ?' WHERE `id`=90;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux être %my_role pour %instance_name.' WHERE `id`=91;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin dun coup de main à %instance_name ?' WHERE `id`=92;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin dun %my_role pour %instance_name ?' WHERE `id`=93;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a besoin de loot à %instance_name ?' WHERE `id`=94;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Un petit farm à %instance_name ?' WHERE `id`=95;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTR %instance_name' WHERE `id`=96;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin daide pour %instance_name.' WHERE `id`=97;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie de faire %instance_name.' WHERE `id`=98;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_role cherche un groupe pour %instance_name.' WHERE `id`=99;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Et %instance_name, on y va ?' WHERE `id`=100;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut farmer %instance_name ?' WHERE `id`=101;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On entre dans %instance_name ?' WHERE `id`=102;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je cherche groupe pour %instance_name.' WHERE `id`=103;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Des quêtes à %instance_name ?' WHERE `id`=104;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie de faire des quêtes à %instance_name.' WHERE `id`=105;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun avec des quêtes à %instance_name ?' WHERE `id`=106;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux aider pour les quêtes à %instance_name.' WHERE `id`=107;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_role : une place dispo pour %instance_name ?' WHERE `id`=108;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Est-ce que quelquun fait encore %instance_name de nos jours ?' WHERE `id`=109;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%instance_name : quelquun cherche un %my_role ?' WHERE `id`=110;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ça sert encore à quelque chose dêtre %my_role à %instance_name ?' WHERE `id`=111;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ça vaut vraiment le coup daller à %instance_name ?' WHERE `id`=112;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a besoin de plus de joueurs pour %instance_name ?' WHERE `id`=113;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Les boss de %instance_name lootent du bon matos. On y va ?' WHERE `id`=114;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Alors, %instance_name ?' WHERE `id`=115;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a besoin dun %my_role ?' WHERE `id`=116;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a besoin dun %my_role ?' WHERE `id`=117;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut aller à %instance_name ?' WHERE `id`=118;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut me TP à %instance_name ?' WHERE `id`=119;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Retrouve-moi à %instance_name' WHERE `id`=120;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie dun petit run rapide à %instance_name ?' WHERE `id`=121;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On se fait un run complet à %instance_name ?' WHERE `id`=122;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tas été combien de fois à %instance_name ?' WHERE `id`=123;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Encore un run à %instance_name ?' WHERE `id`=124;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Wipe à %instance_name ? Prends-moi, je suis un porte-bonheur !' WHERE `id`=125;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Emmène-moi à %instance_name sil te plaît.' WHERE `id`=126;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Un petit %instance_name vite fait bien fait ?' WHERE `id`=127;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Full %instance_name jusquà la fin ?' WHERE `id`=128;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui peut prendre un %my_role à %instance_name ?' WHERE `id`=129;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFG %instance_name, je suis %my_role' WHERE `id`=130;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_role cherche groupe pour %instance_name' WHERE `id`=131;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin daide pour %quest_link ?' WHERE `id`=132;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun veut partager %quest_link ?' WHERE `id`=133;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun fait %quest_link ?' WHERE `id`=134;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On fait %quest_link ?' WHERE `id`=135;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun pour farmer du %category ?' WHERE `id`=136;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cherche aide pour farmer %category.' WHERE `id`=137;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ces %category coûtent un bras !' WHERE `id`=138;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin de %category.' WHERE `id`=139;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin daide pour %category.' WHERE `id`=140;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTB %category.' WHERE `id`=141;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun est intéressé par %category ?' WHERE `id`=142;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTS %category.' WHERE `id`=143;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vends %category moins cher que lHV.' WHERE `id`=144;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut farmer du %category ?' WHERE `id`=145;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On farme du %category ?' WHERE `id`=146;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je cherche un groupe, après ça on fait du %category ?' WHERE `id`=147;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tous les %category sont les bienvenus.' WHERE `id`=148;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jachète tout ce qui ressemble à du %category.' WHERE `id`=149;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Wow, ya des gens qui farment encore du %category ?' WHERE `id`=150;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Les %category partent comme des petits pains à lHV !' WHERE `id`=151;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LHV brûle à cause des %category !' WHERE `id`=152;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya des %category sur le marché.' WHERE `id`=153;
UPDATE `ai_playerbot_texts` SET `text_loc2`='J\'échange des %category ?' WHERE `id`=154;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il me faut plus de %category.' WHERE `id`=155;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a un peu de %category à donner ?' WHERE `id`=156;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut du %category ?' WHERE `id`=157;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Un peu de %category sil vous plaît ?' WHERE `id`=158;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jaurais monter une compétence pour les %category.' WHERE `id`=159;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je meurs denvie davoir des %category.' WHERE `id`=160;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Les gens se tuent pour les %category.' WHERE `id`=161;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%category, cest une affaire en or !' WHERE `id`=162;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tout le monde devient fou pour les %category !' WHERE `id`=163;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest le meilleur spot pour farmer des %category ?' WHERE `id`=164;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis prêt pour le farm de %category.' WHERE `id`=165;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ça se vend bien les %category ?' WHERE `id`=166;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais garder tous mes %category. Pour moi.' WHERE `id`=167;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin dun groupe ? On pourrait farm des %category ensemble.' WHERE `id`=168;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pense encore aux %category.' WHERE `id`=169;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai entendu parler des %category... mais mon porte-monnaie ne veut pas.' WHERE `id`=170;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFG pour %category' WHERE `id`=171;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vendre %category rend riche ?' WHERE `id`=172;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ok. Demain je farm les %category.' WHERE `id`=173;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tout le monde parle des %category.' WHERE `id`=174;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai vu au moins dix gars farmer du %category.' WHERE `id`=175;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai tout vendu mes %category hier. Maintenant je mange du pain sec!' WHERE `id`=176;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie de rejoindre une guilde qui farme du %category.' WHERE `id`=177;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun farm la réputation %faction ?' WHERE `id`=178;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut maider pour %faction ?' WHERE `id`=179;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On fait des quêtes pour %faction ?' WHERE `id`=180;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%faction est la meilleur' WHERE `id`=181;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il me manque juste un tout petit peu pour être %rep_level avec %faction.' WHERE `id`=182;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun est %rep_level avec %faction ?' WHERE `id`=183;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut devenir %rep_level avec %faction ?' WHERE `id`=184;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je serai jamais %rep_level avec %faction.' WHERE `id`=185;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a oublié de monter la réputation %faction ?' WHERE `id`=186;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux aider à farmer la réputation %faction.' WHERE `id`=187;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus on a de réputation, mieux cest. Surtout avec %faction.' WHERE `id`=188;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%faction : il me faut encore %rndK pour être %rep_level.' WHERE `id`=189;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui peut partager des quêtes %faction ?' WHERE `id`=190;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Des donjons pour la réputation %faction ?' WHERE `id`=191;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On farm la réput %faction ?' WHERE `id`=192;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allons-y pour %faction !' WHERE `id`=193;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je farm la réputation %faction.' WHERE `id`=194;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On farme pour %faction ?' WHERE `id`=195;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin daide pour %faction.' WHERE `id`=196;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%faction vend quelque chose dutile ?' WHERE `id`=197;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il existe des vendeurs %faction ?' WHERE `id`=198;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui farme %faction ?' WHERE `id`=199;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelle est la meilleure façon de farmer %faction ?' WHERE `id`=200;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je déteste farmer la réputation %faction.' WHERE `id`=201;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jen ai marre de %faction.' WHERE `id`=202;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On y va pour %faction ?' WHERE `id`=203;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tout le monde est %rep_level avec %faction. Et moi, je galère encore.' WHERE `id`=204;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFG pour farm de réputation %faction ?' WHERE `id`=205;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a un bon spot pour la réputation %faction ?' WHERE `id`=206;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Améliorer ma réput %faction, ça sert ?' WHERE `id`=207;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui aurait cru que la réputation %faction finirait par servir...' WHERE `id`=208;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je veux être exalté avec toutes les factions. En commençant par %faction.' WHERE `id`=209;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Est-ce que ça vaut le coup de monter %faction ?' WHERE `id`=210;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quest-ce qui marche le mieux pour %faction ? Les quêtes ou tuer des mobs ?' WHERE `id`=211;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je farm %faction pour toi, si tu me paies.' WHERE `id`=212;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Farmer la réputation %faction ? Ça prendra 3 vies au moins.' WHERE `id`=213;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je tue pour %faction tous les jours, mais je suis toujours pas %rep_level.' WHERE `id`=214;
UPDATE `ai_playerbot_texts` SET `text_loc2`='À %my_level, les dépôts à lHV vont baisser, non ?' WHERE `id`=215;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Combien de réputations exaltées tu as ?' WHERE `id`=216;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut être %my_level avec %faction ?' WHERE `id`=217;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ma guilde a farmé la réputation %faction hier. Sans moi...' WHERE `id`=218;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Personne ne veut maider, tout ça parce que je suis %rep_level avec %faction.' WHERE `id`=219;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Évitez la %faction.' WHERE `id`=220;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On se fait une soirée à %zone_name ?' WHERE `id`=221;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun cherche un %my_role ?' WHERE `id`=222;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_role cherche une guilde.' WHERE `id`=223;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je cherche de lor.' WHERE `id`=224;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%my_role cherche une bonne guilde.' WHERE `id`=225;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin dun ami.' WHERE `id`=226;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun dautre se sent seul ?' WHERE `id`=227;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je mennuie...' WHERE `id`=228;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui en veut ?' WHERE `id`=229;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allez viens te battre !' WHERE `id`=230;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Un petit duel à %zone_name ?' WHERE `id`=231;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun fait quelque chose ici ?' WHERE `id`=232;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name : ya une âme qui vive ici ?' WHERE `id`=233;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name : tout le monde est en furtif ou quoi ?' WHERE `id`=234;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On dirait que je suis seul à %zone_name.' WHERE `id`=235;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Rejoins-moi à %zone_name .' WHERE `id`=236;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allez, on fait des quêtes à %zone_name !' WHERE `id`=237;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name cest LE coin sympa du momment.' WHERE `id`=238;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jveux aller à %zone_name. Quelquun me suit ?' WHERE `id`=239;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut aller à %zone_name ?' WHERE `id`=240;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jaime pas %zone_name. doi-je aller ?' WHERE `id`=241;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya de bonnes quêtes à %zone_name ?' WHERE `id`=242;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On va après %zone_name ?' WHERE `id`=243;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui traîne à %zone_name ?' WHERE `id`=244;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFG pour %zone_name.' WHERE `id`=245;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name est lendroit le plus naze du monde.' WHERE `id`=246;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Attrape-moi à %zone_name si tu peux !' WHERE `id`=247;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Direction %zone_name !' WHERE `id`=248;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie de quêtes à %zone_name' WHERE `id`=249;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a des quêtes à %zone_name ?' WHERE `id`=250;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Venez à %zone_name !' WHERE `id`=251;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On dirait que la Horde a déserté %zone_name...' WHERE `id`=252;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On dirait que lAlliance a déserté %zone_name...' WHERE `id`=253;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jen peux plus de %zone_name. Quelquun me sort de ?' WHERE `id`=254;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonne chance !' WHERE `id`=255;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je veux rentrer chez moi puis pleurer au bord du vide' WHERE `id`=256;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun sait ce quil faut pour jouer double arme ?' WHERE `id`=257;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut tout le monde !' WHERE `id`=258;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name est cosy' WHERE `id`=259;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je me sens bien' WHERE `id`=260;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nignore pas les gens. Je les trolle jusquà ce quils mignorent.' WHERE `id`=261;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Que pensez-vous de mon build ? %my_role' WHERE `id`=262;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Heureux de voir que le chat ne ma pas oublié' WHERE `id`=263;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Comme toutes les armes, cest BiS pour chasseur' WHERE `id`=264;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Le but du jeu pour moi ? Solo tout ce qui bouge.' WHERE `id`=265;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai JAMAIS arnaqué personne.' WHERE `id`=266;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ah, World of Warcraft. je viens chercher des conseils de vie.' WHERE `id`=267;
UPDATE `ai_playerbot_texts` SET `text_loc2`='YA QUELQUUN ?!' WHERE `id`=268;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il est temps de me frayer un chemin dans %zone_name.' WHERE `id`=269;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%zone_name' WHERE `id`=270;
UPDATE `ai_playerbot_texts` SET `text_loc2`=' faut que jaille aux toilettes.' WHERE `id`=271;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Si tu loot pas tes mobs skinables, ton zizi perd 1mm. Cest la règle.' WHERE `id`=272;
UPDATE `ai_playerbot_texts` SET `text_loc2`='NOOOOOOOOOOOOO' WHERE `id`=273;
UPDATE `ai_playerbot_texts` SET `text_loc2`='JAIME LA PATATE' WHERE `id`=274;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La discussion est animée.' WHERE `id`=275;
UPDATE `ai_playerbot_texts` SET `text_loc2`='salut, comment ça va les gens ?' WHERE `id`=276;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je viens de me déco / reco.' WHERE `id`=277;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Faites moins de bruit, jsuis perdu dans %zone_name' WHERE `id`=278;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun veut boire un verre à %zone_name ? hic' WHERE `id`=279;
UPDATE `ai_playerbot_texts` SET `text_loc2`='hahahahaheeeee dirin diring inggggg hahahahaheeeeeeeeeeeeee' WHERE `id`=280;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Avant, les appâts étaient crédibles.' WHERE `id`=281;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Peut-être que tas juste perdu ton innocence.' WHERE `id`=282;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya une guilde qui veut carry un %my_role fragile ?' WHERE `id`=283;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus on monte en niveau, plus lor coule à flots' WHERE `id`=284;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour !' WHERE `id`=285;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi jai mal au cul ?' WHERE `id`=286;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pense que lesprit est BiS pour monter de niveau.' WHERE `id`=287;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Encore plus si tes troll.' WHERE `id`=288;
UPDATE `ai_playerbot_texts` SET `text_loc2`='QUELQUUN PEUT MINVITER ?' WHERE `id`=289;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin de beaucouuuup de boissons.' WHERE `id`=290;
UPDATE `ai_playerbot_texts` SET `text_loc2`='P*utain de gnomes' WHERE `id`=291;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Personne naime les gnomes.' WHERE `id`=292;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Les gnomes ne servent quà une chose' WHERE `id`=293;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bah' WHERE `id`=294;
UPDATE `ai_playerbot_texts` SET `text_loc2`=' des champignons.' WHERE `id`=295;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Les pensées automatiques, cest flippant.' WHERE `id`=296;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lesprit est plus malléable quon aimerait le croire.' WHERE `id`=297;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya des guildes pour leveling ?' WHERE `id`=298;
UPDATE `ai_playerbot_texts` SET `text_loc2`='brb' WHERE `id`=299;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi la neige est-elle blanche alors que la glace est transparente, alors quelles sont faites de la même chose ?' WHERE `id`=300;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi la crème fouettée est fluffy, mais pas la normale ?' WHERE `id`=301;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi les pieds sentent alors quils nont pas de nez ?' WHERE `id`=302;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On dirait que la boîte à noobs vient de souvrir.' WHERE `id`=303;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Arrêtez de troller les nouveaux avec vos réponses à la con.' WHERE `id`=304;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya du PvP sur ce serveur ?' WHERE `id`=305;
UPDATE `ai_playerbot_texts` SET `text_loc2`='évidemment...' WHERE `id`=306;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ouf :)' WHERE `id`=307;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vous saviez que' WHERE `id`=308;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne tente pas dimaginer ce que ressentent les autres créatures' WHERE `id`=309;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oups, mauvais canal.' WHERE `id`=310;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bruh, vous êtes déchaînés aujourdhui' WHERE `id`=311;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Juste pour que tout le monde sache : mon message est passé ici' WHERE `id`=312;
UPDATE `ai_playerbot_texts` SET `text_loc2`='grrr énervéééééé' WHERE `id`=313;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Le farm, cest marrant' WHERE `id`=314;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WoW me garde vif' WHERE `id`=315;
UPDATE `ai_playerbot_texts` SET `text_loc2`=', question : on prend le rôle pour plus dXP ? Je suis à %zone_name.' WHERE `id`=316;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vous aimez les saucisses ?' WHERE `id`=317;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Invitez-moi. Je peux aider.' WHERE `id`=318;
UPDATE `ai_playerbot_texts` SET `text_loc2`='À votre avis, quelle classe est la meilleure en PvP ?' WHERE `id`=319;
UPDATE `ai_playerbot_texts` SET `text_loc2`=' est ce foutu maître de cuisine à %zone_name ?!' WHERE `id`=320;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vous savez ce quil se passe à %zone_name ?' WHERE `id`=321;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin de crafter quelque chose' WHERE `id`=322;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest quoi lèchemes' WHERE `id`=323;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest quoi sucemes ' WHERE `id`=324;
UPDATE `ai_playerbot_texts` SET `text_loc2`='lèchemes couilles' WHERE `id`=325;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sucemes couilles' WHERE `id`=326;
UPDATE `ai_playerbot_texts` SET `text_loc2`='JE MANGE DES FESSES' WHERE `id`=327;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai envie de me fourrer %random_inventory_item_link le soleil ne brille pas' WHERE `id`=328;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai envie de te fourrer %random_inventory_item_link tu penses' WHERE `id`=329;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Darnasses' WHERE `id`=330;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On dirait que tas chopé le syndrôme de sucemes' WHERE `id`=331;
UPDATE `ai_playerbot_texts` SET `text_loc2`='cesnoix dans ta bouche' WHERE `id`=332;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Sympa ton os, frérot' WHERE `id`=333;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ERP ?' WHERE `id`=334;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai tout essayé, mais au final cest lERP qui a marché' WHERE `id`=335;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai envie de batifoler à %zone_name' WHERE `id`=336;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Recherche gnome femelle avec gorille pour ERP sauvage à %zone_name' WHERE `id`=337;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux comprendre un idiot, mais un gros pervers ?' WHERE `id`=338;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Aucun GYAT en vue à %zone_name' WHERE `id`=339;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je tue tous les animaux de %zone_name. Désolé WWF !' WHERE `id`=340;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Heureusement que jai trois jambes' WHERE `id`=341;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Sois pas vénère, je goon comme un sigma' WHERE `id`=342;
UPDATE `ai_playerbot_texts` SET `text_loc2`='essaye doigt, mais trou' WHERE `id`=343;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%prefix %random_taken_quest_or_item_link' WHERE `id`=344;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%prefix %random_inventory_item_link' WHERE `id`=345;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%thunderfury_link' WHERE `id`=346;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%thunderfury_link%thunderfury_link' WHERE `id`=347;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%thunderfury_link%thunderfury_link%thunderfury_link' WHERE `id`=348;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je crois que je viens dentendre %thunderfury_link' WHERE `id`=349;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai entendu %thunderfury_link' WHERE `id`=350;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai clairement entendu %thunderfury_link' WHERE `id`=351;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jsuis pas sûr, mais jcrois avoir entendu %thunderfury_link' WHERE `id`=352;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tas dit %thunderfury_link' WHERE `id`=353;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a dit %thunderfury_link' WHERE `id`=354;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a VRAIMENT dit %thunderfury_link' WHERE `id`=355;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun a parlé de %thunderfury_link' WHERE `id`=356;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%thunderfury_link sort du placard, les gars' WHERE `id`=357;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jaurais juré que cétait un %thunderfury_link ou peut-être un %thunderfury_link' WHERE `id`=358;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi utiliser %thunderfury_link alors que %thunderfury_link est bien plus OP' WHERE `id`=359;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTS %item_formatted_link pour %cost_gold.' WHERE `id`=360;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui veut %item_formatted_link pour %cost_gold ?' WHERE `id`=361;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun veut %item_formatted_link ? Seulement %cost_gold' WHERE `id`=362;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Juste %cost_gold pour %item_formatted_link!' WHERE `id`=363;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vends %item_formatted_link pour %cost_gold' WHERE `id`=364;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_formatted_link est à toi pour seulement %cost_gold !' WHERE `id`=365;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Prix ridicule : %cost_gold pour %item_formatted_link !' WHERE `id`=366;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je cherche à vendre %item_formatted_link pour %cost_gold' WHERE `id`=367;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui a besoin de %item_formatted_link ? Seulement %cost_gold' WHERE `id`=368;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun veut %item_formatted_link pour %cost_gold ?' WHERE `id`=369;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%cost_gold pour %item_formatted_link. Moins cher quà lHV !' WHERE `id`=370;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_formatted_link est cher, mais je te le fais à %cost_gold' WHERE `id`=371;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu trouveras jamais %item_formatted_link moins cher que %cost_gold!' WHERE `id`=372;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin de plus que %item_formatted_link, !' WHERE `id`=373;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai %item_formatted_link et jen veux encore' WHERE `id`=374;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai %item_formatted_link. Qui lachète pour %cost_gold ?' WHERE `id`=375;
UPDATE `ai_playerbot_texts` SET `text_loc2`='WTB %item_formatted_link pour %cost_gold, quelquun ?' WHERE `id`=376;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Et %item_formatted_link ? Pour %cost_gold.' WHERE `id`=377;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui ma traité d\'arnaqueur ? %item_formatted_link pour %cost_gold cest honnête !' WHERE `id`=378;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vends %item_formatted_link. Juste %cost_gold' WHERE `id`=379;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LFG pour du farm, et au passage %item_formatted_link à vendre %cost_gold' WHERE `id`=380;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Presque tout vendu aujourdhui. Me reste %item_formatted_link pour %cost_gold' WHERE `id`=381;
UPDATE `ai_playerbot_texts` SET `text_loc2`='À quoi sert le canal commerce ? A vendre %item_formatted_link pour %cost_gold, évidemment' WHERE `id`=382;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut battre %cost_gold pour %item_formatted_link ?' WHERE `id`=383;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Envie darrêter le spam commerce ? Achetez %item_formatted_link à %cost_gold !' WHERE `id`=384;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tout le monde spam, moi aussi : %cost_gold pour %item_formatted_link !' WHERE `id`=385;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item_formatted_link est utile ? Je sais pas, mais je le vends %cost_gold' WHERE `id`=386;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai %item_formatted_link prêt à vendre %cost_gold' WHERE `id`=387;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hier jai rien foutu, mais jai loot %item_formatted_link. À vendre %cost_gold' WHERE `id`=388;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai farmé hier, jai eu %item_formatted_link. WTB ? %cost_gold' WHERE `id`=389;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai acheté %item_formatted_link hier. Quelquun le veut ? %cost_gold' WHERE `id`=390;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun cherchait %item_formatted_link ? Cest %cost_gold toujours' WHERE `id`=391;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai encore %item_formatted_link. Achetez-le %cost_gold' WHERE `id`=392;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Avant jen avais plein %item_formatted_link, maintenant je dois vendre à %cost_gold' WHERE `id`=393;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jaimerais en avoir plus que %item_formatted_link. Mais achetez celui-là %cost_gold' WHERE `id`=394;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ton or te sert à quoi ? À acheter %item_formatted_link pour %cost_gold' WHERE `id`=395;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ayez pitié, donnez-moi de lor... ou achetez %item_formatted_link %cost_gold' WHERE `id`=396;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Est-ce que %cost_gold est un bon prix pour %item_formatted_link ?' WHERE `id`=397;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai acheté %item_formatted_links hier, mais jen veux plus. Quelqu\'un vends pour %cost_gold' WHERE `id`=398;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais le mettre à lHV %item_formatted_link, mais tu peux lavoir moins cher : %cost_gold' WHERE `id`=399;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi jai acheté %item_formatted_link bordel ? Quelquun le veut ? %cost_gold' WHERE `id`=400;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai %quest_links' WHERE `id`=401;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Moi aussi jai %quest_links' WHERE `id`=402;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Moi aussi jai %quest_links, je suis à %zone_name' WHERE `id`=403;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, moi aussi jai %quest_links' WHERE `id`=404;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, moi aussi jai %quest_links, et je suis à %zone_name' WHERE `id`=405;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis dispo pour %quest_links, je suis à %zone_name' WHERE `id`=406;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis dispo pour %quest_links, je suis %my_role' WHERE `id`=407;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, je suis dispo pour %quest_links à %zone_name' WHERE `id`=408;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, je suis dispo pour %quest_links, je suis %my_role' WHERE `id`=409;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey, je suis partant pour %quest_links' WHERE `id`=410;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey, je pourrais faire %quest_links avec toi' WHERE `id`=411;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey, moi aussi jai %quest_links' WHERE `id`=412;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %other_name, partant pour %quest_links' WHERE `id`=413;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %other_name, je peux faire %quest_links avec toi' WHERE `id`=414;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %other_name, moi aussi jai %quest_links' WHERE `id`=415;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On se groupe pour %quest_links ?' WHERE `id`=416;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis partant pour %quest_links, je suis à %zone_name' WHERE `id`=417;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis dispo pour %quest_links, je suis %my_role' WHERE `id`=418;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, je peux te vendre %formatted_item_links' WHERE `id`=419;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux peut-être vendre %formatted_item_links' WHERE `id`=420;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pense pouvoir vendre %formatted_item_links' WHERE `id`=421;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, je peux peut-être te vendre %formatted_item_links' WHERE `id`=422;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%other_name, tu penses que je peux vendre %formatted_item_links ?' WHERE `id`=423;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux te vendre %formatted_item_links' WHERE `id`=424;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey, jai %formatted_item_links à vendre' WHERE `id`=425;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey, je pourrais peut-être vendre %formatted_item_links' WHERE `id`=426;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quête acceptée' WHERE `id`=427;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quête abandonnée' WHERE `id`=428;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas prendre cette quête' WHERE `id`=429;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas parler au donneur de quête' WHERE `id`=430;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai déjà terminé %quest' WHERE `id`=431;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai déjà la quête %quest' WHERE `id`=432;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas prendre %quest' WHERE `id`=433;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas prendre %quest, mon journal de quêtes est plein' WHERE `id`=434;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas prendre %quest, mes sacs sont pleins' WHERE `id`=435;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai accepté la quête %quest' WHERE `id`=436;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nai pas encore terminé la quête %quest' WHERE `id`=437;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La quête %quest est dispo' WHERE `id`=438;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai échoué à la quête %quest' WHERE `id`=439;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas rendre la quête %quest' WHERE `id`=440;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai terminé la quête %quest' WHERE `id`=441;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai terminé la quête %quest et reçu %item' WHERE `id`=442;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelle récompense devrais-je choisir pour la quête %quest ? %rewards' WHERE `id`=443;
UPDATE `ai_playerbot_texts` SET `text_loc2`='OK, je vais prendre %item comme récompense' WHERE `id`=444;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour' WHERE `id`=445;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour !' WHERE `id`=446;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut' WHERE `id`=447;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut !' WHERE `id`=448;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut à toi !' WHERE `id`=449;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour, je vous suis !' WHERE `id`=450;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour, montrez-moi le chemin !' WHERE `id`=451;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut, montre-moi le chemin !' WHERE `id`=452;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %player, tu veux rejoindre mon groupe ?' WHERE `id`=453;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %player, tu veux rejoindre mon groupe ?' WHERE `id`=454;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Déconnexion annulée !' WHERE `id`=455;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je me déconnecte !' WHERE `id`=456;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Au revoir !' WHERE `id`=457;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bye bye !' WHERE `id`=458;
UPDATE `ai_playerbot_texts` SET `text_loc2`='À plus tard !' WHERE `id`=459;
UPDATE `ai_playerbot_texts` SET `text_loc2`='cétait quoi ce truc %s ?' WHERE `id`=460;
UPDATE `ai_playerbot_texts` SET `text_loc2`='pas sûr davoir compris %s ?' WHERE `id`=461;
UPDATE `ai_playerbot_texts` SET `text_loc2`='euh jai aucune idée de ce que tu racontes' WHERE `id`=462;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tu parles à moi, %s ?' WHERE `id`=463;
UPDATE `ai_playerbot_texts` SET `text_loc2`='whaaaa ?' WHERE `id`=464;
UPDATE `ai_playerbot_texts` SET `text_loc2`='hein ?' WHERE `id`=465;
UPDATE `ai_playerbot_texts` SET `text_loc2`='quoi ?' WHERE `id`=466;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tu parles ou tu râles ?' WHERE `id`=467;
UPDATE `ai_playerbot_texts` SET `text_loc2`='comme tu veux, mec' WHERE `id`=468;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tu mas perdu ' WHERE `id`=469;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bla bla bla' WHERE `id`=470;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tas dit quoi, %s ?' WHERE `id`=471;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Concentre-toi sur le jeu, %s !' WHERE `id`=472;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Parler avec toi %s, cest génial ! Jai toujours rêvé de te rencontrer' WHERE `id`=473;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ces messages me font flipper ! Jai limpression de tous vous connaître !' WHERE `id`=474;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ouais bien sûr ! HAHA ! Cest ça, allez !' WHERE `id`=475;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je te crois !!!' WHERE `id`=476;
UPDATE `ai_playerbot_texts` SET `text_loc2`='OK, uhuh LOL' WHERE `id`=477;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi tout le monde dit toujours les mêmes trucs ???' WHERE `id`=478;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %s euh, laisse tomber !' WHERE `id`=479;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu parles de quoi %s ? Sérieux ?' WHERE `id`=480;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Qui a dit ça ? Je me sens visé ' WHERE `id`=481;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wtf vous racontez tous ?' WHERE `id`=482;
UPDATE `ai_playerbot_texts` SET `text_loc2`='fr fr no cap on a stack' WHERE `id`=483;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tauras que dalle' WHERE `id`=484;
UPDATE `ai_playerbot_texts` SET `text_loc2`='swag' WHERE `id`=485;
UPDATE `ai_playerbot_texts` SET `text_loc2`='merci !' WHERE `id`=486;
UPDATE `ai_playerbot_texts` SET `text_loc2`='non' WHERE `id`=487;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Yep' WHERE `id`=488;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Paix à son ame.' WHERE `id`=489;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s sans déconner xD' WHERE `id`=490;
UPDATE `ai_playerbot_texts` SET `text_loc2`='pourquoi ça ?' WHERE `id`=491;
UPDATE `ai_playerbot_texts` SET `text_loc2`='mdr' WHERE `id`=492;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pensais fermer ma gueule, jai encore rien compris au chat' WHERE `id`=493;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux devenir vraiment jaloux... comme un elfe sans loot' WHERE `id`=494;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s tu captes pas le sarcasme qui dégouline de mon message ?' WHERE `id`=495;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il a dit "no homo", donc cest bon apparemment' WHERE `id`=496;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Instant nain' WHERE `id`=497;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oui %s' WHERE `id`=498;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Intéressant' WHERE `id`=499;
UPDATE `ai_playerbot_texts` SET `text_loc2`='lol' WHERE `id`=500;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s va te faire voir mec :D' WHERE `id`=501;
UPDATE `ai_playerbot_texts` SET `text_loc2`=':^)' WHERE `id`=502;
UPDATE `ai_playerbot_texts` SET `text_loc2`='merci' WHERE `id`=503;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s bien dit !' WHERE `id`=504;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ouiiiii' WHERE `id`=505;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ouais' WHERE `id`=506;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ooooooh' WHERE `id`=507;
UPDATE `ai_playerbot_texts` SET `text_loc2`='hmm' WHERE `id`=508;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ouais cest ça' WHERE `id`=509;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tas failli me faire vomir, wtf' WHERE `id`=510;
UPDATE `ai_playerbot_texts` SET `text_loc2`='chaud !' WHERE `id`=511;
UPDATE `ai_playerbot_texts` SET `text_loc2`='les rageux pleurent' WHERE `id`=512;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tas mangé quoi %s ?' WHERE `id`=513;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wtf' WHERE `id`=514;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais essayer de comprendre ce que tas dit' WHERE `id`=515;
UPDATE `ai_playerbot_texts` SET `text_loc2`='*confus*' WHERE `id`=516;
UPDATE `ai_playerbot_texts` SET `text_loc2`='putain ouais' WHERE `id`=517;
UPDATE `ai_playerbot_texts` SET `text_loc2`='0/10 ne lirait pas ça une deuxième fois' WHERE `id`=518;
UPDATE `ai_playerbot_texts` SET `text_loc2`='10/10 je relis direct' WHERE `id`=519;
UPDATE `ai_playerbot_texts` SET `text_loc2`='6/10 ouais, pourquoi pas' WHERE `id`=520;
UPDATE `ai_playerbot_texts` SET `text_loc2`='7/10 ça passe' WHERE `id`=521;
UPDATE `ai_playerbot_texts` SET `text_loc2`='basé' WHERE `id`=522;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ah ouais peut-être' WHERE `id`=523;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ouais, et alors ?' WHERE `id`=524;
UPDATE `ai_playerbot_texts` SET `text_loc2`='hey %s je tai pas oublié' WHERE `id`=525;
UPDATE `ai_playerbot_texts` SET `text_loc2`='tu ménerves %s' WHERE `id`=526;
UPDATE `ai_playerbot_texts` SET `text_loc2`='je vais tavoir cette fois %s' WHERE `id`=527;
UPDATE `ai_playerbot_texts` SET `text_loc2`='garde un œil derrière toi %s' WHERE `id`=528;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai pas trop aimé la manche davant' WHERE `id`=529;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai é nul au dernier round à cause de %s' WHERE `id`=530;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Prépare-toi à mourir %s' WHERE `id`=531;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai pas trop kiffé que tu me tues %s' WHERE `id`=532;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, je te hais' WHERE `id`=533;
UPDATE `ai_playerbot_texts` SET `text_loc2`='grrrrr je vais tavoir cette fois %s' WHERE `id`=534;
UPDATE `ai_playerbot_texts` SET `text_loc2`='eh bien va te faire foutre' WHERE `id`=535;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s je vais vomir dans ta putain de bouche' WHERE `id`=536;
UPDATE `ai_playerbot_texts` SET `text_loc2`='me juge pas bordel' WHERE `id`=537;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ta mère est tellement grosse quelle passe même pas le Portail des Ténèbres' WHERE `id`=538;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wtf' WHERE `id`=539;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wtf ??' WHERE `id`=540;
UPDATE `ai_playerbot_texts` SET `text_loc2`='vie misérable' WHERE `id`=541;
UPDATE `ai_playerbot_texts` SET `text_loc2`='quest-ce qui se passe ?' WHERE `id`=542;
UPDATE `ai_playerbot_texts` SET `text_loc2`='nul à chier' WHERE `id`=543;
UPDATE `ai_playerbot_texts` SET `text_loc2`='REVANCHE !!! Je vais léclater' WHERE `id`=544;
UPDATE `ai_playerbot_texts` SET `text_loc2`='pathétique, je me suis fait tuer par %s' WHERE `id`=545;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ok jen ai fini' WHERE `id`=546;
UPDATE `ai_playerbot_texts` SET `text_loc2`=' , jai explosé %s ?' WHERE `id`=547;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cétait trop facile, d\'exploser %s' WHERE `id`=548;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes grillé, clochard' WHERE `id`=549;
UPDATE `ai_playerbot_texts` SET `text_loc2`='ha ha' WHERE `id`=550;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Loser' WHERE `id`=551;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai tué %s et vous êtes tous les prochains, les gars' WHERE `id`=552;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh ouais, je lai éclaté' WHERE `id`=553;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis une machine à tuer' WHERE `id`=554;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, ça me rappelle un morceau de Slayer… tout ce sang, cest beau' WHERE `id`=555;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Désolé %s. On peut refaire la scène ?' WHERE `id`=556;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Alors %s… ça fait quoi de nourrir les vers ???' WHERE `id`=557;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tétais censé être mort %s ! Cest dans le script bon sang !' WHERE `id`=558;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Désolé %s. Franchement, cétait aussi beau quun Warhol !' WHERE `id`=559;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, je prendrai les balles en caoutchouc la prochaine fois, promis !' WHERE `id`=560;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quest-ce quil y a %s ?? Tas perdu la tête ? Hahaha, faut rester cool !' WHERE `id`=561;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Fallait que je le fasse %s… Le Réalisateur me la dit !' WHERE `id`=562;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %s… MUAHAHAHAHAHAHAHAHAHAHA' WHERE `id`=563;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, celle-là, je lai savourée !! Allez, on recommence Sam !' WHERE `id`=564;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %s ! Tu peux commencer à mappeler Scarface… espèce de M…erde !' WHERE `id`=565;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu me parles à moi %s ?? Tu me parles à MOI ?!' WHERE `id`=566;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, fais-le bien cette fois, évite MES balles.' WHERE `id`=567;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%s, pourquoi tu traînes par terre ? Allez bouge !' WHERE `id`=568;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai rigolé comme jamais' WHERE `id`=569;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut %s' WHERE `id`=570;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh, salut %s' WHERE `id`=571;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Wazzup %s !!!' WHERE `id`=572;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut' WHERE `id`=573;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Wazzup' WHERE `id`=574;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bonjour %s' WHERE `id`=575;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Salut %s, je te connais ?' WHERE `id`=576;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %s' WHERE `id`=577;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hai %s' WHERE `id`=578;
UPDATE `ai_playerbot_texts` SET `text_loc2`='cest quoi ce délire' WHERE `id`=579;
UPDATE `ai_playerbot_texts` SET `text_loc2`='wtf' WHERE `id`=580;
UPDATE `ai_playerbot_texts` SET `text_loc2`='cest du foutage de gueule' WHERE `id`=581;
UPDATE `ai_playerbot_texts` SET `text_loc2`='admin' WHERE `id`=582;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey %s, arrête dabuser de ton admin là' WHERE `id`=583;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lâche-moi admin !' WHERE `id`=584;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes nul admin' WHERE `id`=585;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest mon nom ça, tu veux quoi %s ?' WHERE `id`=586;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oui ???' WHERE `id`=587;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Euh… quoi ?' WHERE `id`=588;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu me parles à moi %s ?' WHERE `id`=589;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai des chiots sous mon armure !' WHERE `id`=590;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mords-moi, <target> !' WHERE `id`=591;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey <target> ! Devine ce que ta mère a dit hier soir !' WHERE `id`=592;
UPDATE `ai_playerbot_texts` SET `text_loc2`='<target>, tes tellement moche que tu pourrais même pas scorer dans un bordel de singes avec un sac de bananes !' WHERE `id`=593;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tais-toi <target>, tu seras jamais lhomme que ta mère est !!' WHERE `id`=594;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ta mère était un hamster et ton père sentait la surette !' WHERE `id`=595;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne veux plus te parler, espèce de vide-écuelle à bestiaux débiles !!!' WHERE `id`=596;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pète dans ta direction générale !!!' WHERE `id`=597;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Va faire bouillir ton postérieur, fils dun imbécile cosmique !!!' WHERE `id`=598;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu comptes faire quoi <target>, saigner sur moi ? ALLEZ, VIENS !' WHERE `id`=599;
UPDATE `ai_playerbot_texts` SET `text_loc2`='M-O-O-N ! Ça veut dire aggro !' WHERE `id`=600;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes aussi utile quun unijambiste dans un concours de coups de pied au cul' WHERE `id`=601;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey <target> ! Arrête de draguer, cest pas ton genre. Cest pas gonflable.' WHERE `id`=602;
UPDATE `ai_playerbot_texts` SET `text_loc2`='<target>, tes tellement hors catégorie que tu joues carrément à un autre sport' WHERE `id`=603;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu as fait une grosse erreur aujourdhui <target>… tes sorti du lit.' WHERE `id`=604;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je veux essayer de me transformer en cheval. Je prends lavant, et toi… tu restes toi.' WHERE `id`=605;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je peux emprunter ton visage ? Mon cul part en vacances.' WHERE `id`=606;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jaimerais toffrir un cadeau de départ… dabord, fais ta part.' WHERE `id`=607;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Avant que tarrives, on avait faim. Maintenant on en a juste marre.' WHERE `id`=608;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je taime bien. Les gens disent que jai mauvais goût, mais je taime bien.' WHERE `id`=609;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je pense que tas un complexe dinfériorité… mais tinquiète, il est mérité.' WHERE `id`=610;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Arrière, créature pourrie ! Ou je secoue tes os hors de ta tunique !' WHERE `id`=611;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je crois pas que je perds mon temps avec toi… et pourtant !' WHERE `id`=612;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jadore quand on minsulte : jai plus besoin dêtre poli.' WHERE `id`=613;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Espèce de veston de cuir, boutons en cristal, tête nouée, braillard à gerbe, jarretière de puce, langue mielleuse, bourse espagnole !' WHERE `id`=614;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Espèce de pleurnichard, chasseur de chauve-souris, ivrogne de malte !' WHERE `id`=615;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes vraiment une idole pour les adorateurs de lidiotie !' WHERE `id`=616;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Misérable piaf mal dégrossi !' WHERE `id`=617;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Fils de mandragore ! Tes plus utile en plumeau quen laquais !' WHERE `id`=618;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Toi ! Gueux ! Cloporte ! Tarlouze ! Je vais chatouiller ta catastrophe !' WHERE `id`=619;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh <target> ! Infâme fille de lin mal élevé !' WHERE `id`=620;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On fuit par ta cheminée, <target> !' WHERE `id`=621;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh toi, misérable fleur de cancre gorgée de marais !' WHERE `id`=622;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Si jétais comme toi, je me jetterais à la poubelle.' WHERE `id`=623;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Apprends-moi <target>… à ne plus penser du tout.' WHERE `id`=624;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes maudit comme un œuf mal rôti… tout cramé dun côté.' WHERE `id`=625;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Espèce de gringalet, de peau de hareng, de langue de bœuf séchée, de… souffle… taureau démembré, morceau de corde ! Queue dépée, boîte à rien, tige de tailleur !' WHERE `id`=626;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Fi ! Quon te jette dans la bouche pourrie de la Mort !' WHERE `id`=627;
UPDATE `ai_playerbot_texts` SET `text_loc2`='<target>, tes poissonnier, avoue !' WHERE `id`=628;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vivrai assez pour te défoncer le crâne !' WHERE `id`=629;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tes aussi profond quune flaque ! <target>, tes bonne pour les vers, pas pour la viande !' WHERE `id`=630;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vermine ! Ô <target>, espèce de noisette infernale puante !' WHERE `id`=631;
UPDATE `ai_playerbot_texts` SET `text_loc2`='<target> ! Ton baiser est aussi réconfortant quun glaçon pour un serpent affamé !' WHERE `id`=632;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je te méprise, compagnon galeux. Quoi, pauvre arnaqueur sans chemise ! Dégage, raclure moisie !' WHERE `id`=633;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hors de ma vue ! Tu infectes mes yeux <target> !' WHERE `id`=634;
UPDATE `ai_playerbot_texts` SET `text_loc2`='HEURE DE JEU !!!!' WHERE `id`=635;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Personne ne passera !' WHERE `id`=636;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On est attaqués ! Hissez les voiles ! Repoussez les intrus !' WHERE `id`=637;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Personne ne défie la Confrérie !' WHERE `id`=638;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Imbéciles… tuez celui en robe !' WHERE `id`=639;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais offrir ton âme à Hakkar lui-même !' WHERE `id`=640;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lorgueil annonce la fin de ton monde ! Venez, mortels ! Affrontez la colère de la %randomfaction !' WHERE `id`=641;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tous mes plans menaient à CE moment !' WHERE `id`=642;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ahh ! Encore des agneaux pour labattoir !' WHERE `id`=643;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Encore une journée, encore une glorieuse bataille !' WHERE `id`=644;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Alors, affaires… ou plaisir ?' WHERE `id`=645;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vous nêtes pas préparés !' WHERE `id`=646;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La conquête finale de la %randomfaction a commencé ! Cette fois, aucun ne survivra !' WHERE `id`=647;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ta mort sera douloureuse.' WHERE `id`=648;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Implore pitié ! Tes vies inutiles vont être sacrifiées.' WHERE `id`=649;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Abandonne tout espoir ! La %randomfaction est revenue pour finir ce qui a commencé… et cette fois, pas déchappatoire !' WHERE `id`=650;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Alerte ! Tu es marqué pour lEXTERMINATION !' WHERE `id`=651;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Le %subzone est réservé aux invités seulement…' WHERE `id`=652;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ha ha ha ! Tu es totalement dépassé !' WHERE `id`=653;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais écraser tes illusions de grandeur !' WHERE `id`=654;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pardonne-moi, mais tu vas perdre cette partie.' WHERE `id`=655;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Résister ne fait quempirer les choses.' WHERE `id`=656;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vermine ! Sangsues ! Prends mon sang et étouffe-toi avec !' WHERE `id`=657;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pas encore… PAS ENCORE !' WHERE `id`=658;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mon sang sera ta perte !' WHERE `id`=659;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Parfait. Maintenant bats-toi contre moi !' WHERE `id`=660;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allez les gardes ! Cest lheure de tuer !' WHERE `id`=661;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Nattends pas la mort, viens à moi. Je rendrai ton sacrifice rapide.' WHERE `id`=662;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu seras mort très bientôt !' WHERE `id`=663;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mouahaha !' WHERE `id`=664;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest moi le prédateur ! Toi, la proie...' WHERE `id`=665;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu vas repartir en morceaux !' WHERE `id`=666;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La mort approche... As-tu la conscience tranquille ?' WHERE `id`=667;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ton comportement ne sera pas toléré.' WHERE `id`=668;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La Ménagerie est réservée aux invités.' WHERE `id`=669;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hmm, des visiteurs non annoncés… Il faut se préparer…' WHERE `id`=670;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Entités hostiles détectées. Évaluation de menace en cours. Cible principale verrouillée. Réévaluation dans trente secondes.' WHERE `id`=671;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Des nouveaux jouets ? Pour moi ? Promis, je les casse pas… cette fois !' WHERE `id`=672;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis prêt à jouer !' WHERE `id`=673;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Chut… tout sera fini bientôt.' WHERE `id`=674;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Aaaaaughibbrgubugbugrguburgle !' WHERE `id`=675;
UPDATE `ai_playerbot_texts` SET `text_loc2`='RwlRwlRwlRwl !' WHERE `id`=676;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Toi aussi, tu serviras !' WHERE `id`=677;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Dis-moi... dis-moi tout ! Tes vilains petits secrets ! Je vais les arracher de ta chair !' WHERE `id`=678;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Préparez-vous, les cloches ont sonné ! Protégez vos faibles, vos jeunes et vos vieux ! Chacun paiera le prix final ! Implorerez-vous pitié ? Le Jugement est arrivé !' WHERE `id`=679;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mais où suis-je, par les boutons en laiton de Bonzo ?' WHERE `id`=680;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nen peux plus ! Roi Gobelin ! Roi Gobelin ! Où que tu sois ! Emporte ce <target> loin de moi !' WHERE `id`=681;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu as treize heures pour résoudre le labyrinthe... sinon ton petit frère deviendra lun des nôtres... pour toujours.' WHERE `id`=682;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Alors, le <subzone> cest du gâteau, hein ? Voyons comment tu gères ce petit bout-là…' WHERE `id`=683;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Recule, jvais taffronter. Déterminé, prêt à affronter nimporte qui. Jsais que tas tort, tas rien à faire ici !' WHERE `id`=684;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Montre-moi cque tas dans le ventre !' WHERE `id`=685;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jusquà la mort !' WHERE `id`=686;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Double lame, pour un rasage net à chaque fois !' WHERE `id`=687;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allez, viens !' WHERE `id`=688;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tu vas tomber !' WHERE `id`=689;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Piou piou, coup de couteau !' WHERE `id`=690;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Finissons-en vite, le temps cest du mana.' WHERE `id`=691;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne crois pas que tu réalises dans quelle merde tu es.' WHERE `id`=692;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais faire honneur à ma famille et à mon royaume !' WHERE `id`=693;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lumière, donne-moi la force !' WHERE `id`=694;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mon église, cest le champ de bataille lheure de la messe a sonné !' WHERE `id`=695;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je te tiens en mépris total…' WHERE `id`=696;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Affronte le marteau de la justice !' WHERE `id`=697;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Prouve ta valeur dans lépreuve des armes, sous la Lumière !' WHERE `id`=698;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tous doivent tomber devant la puissance de ma cause tu es le prochain !' WHERE `id`=699;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Prépare-toi à mourir !' WHERE `id`=700;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La bête en moi est bien pire que celle à mes côtés…' WHERE `id`=701;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Admire la puissance de feu dun chasseur totalement équipé !' WHERE `id`=702;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Soigne-moi ! Vite !' WHERE `id`=703;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis presque mort ! Soignez-moi !' WHERE `id`=704;
UPDATE `ai_playerbot_texts` SET `text_loc2`='À laide ! Soignez-moi !' WHERE `id`=705;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun ! Un soin vite !' WHERE `id`=706;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Heal ! Heal ! Heal !' WHERE `id`=707;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je meurs ! Soin ! Aaaaarhg !' WHERE `id`=708;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Soignez-moi !' WHERE `id`=709;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais mourir. Je vais mourir. Je vais mourir. Soignez-moi !' WHERE `id`=710;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Healers, vous êtes où ? Je crève !' WHERE `id`=711;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ouille, la douleur ! Soignez vite !' WHERE `id`=712;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin de soin' WHERE `id`=713;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Vie basse !' WHERE `id`=714;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lâche un heal. Sil te plaît.' WHERE `id`=715;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut me balancer un soin ?' WHERE `id`=716;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Hey ! Mieux vaut me soigner maintenant que me rez plus tard !' WHERE `id`=717;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Désolé… mais encore un heal, please.' WHERE `id`=718;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Saletés de mobs… heal moi vite !' WHERE `id`=719;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Encore un coup et je suis mort. Un petit heal ?' WHERE `id`=720;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ya des soigneurs dans cette galère ?' WHERE `id`=721;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourquoi cest toujours ma tête quils frappent ? Jai besoin dun soin !' WHERE `id`=722;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun pour me soigner un chouïa ?' WHERE `id`=723;
UPDATE `ai_playerbot_texts` SET `text_loc2`='OOM' WHERE `id`=724;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus de mana !' WHERE `id`=725;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai cramé tout mon mana pour ça, sérieux...' WHERE `id`=726;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Faudrait attendre que je boive ou que je régène, là…' WHERE `id`=727;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mana faible… très faible…' WHERE `id`=728;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Encore à sec ? Pas de mana, encore.' WHERE `id`=729;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mana bas. Je veux une boisson !' WHERE `id`=730;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On a une machine à boissons ? Jai encore plus rien !' WHERE `id`=731;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mon mana ? Parti dans les limbes.' WHERE `id`=732;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jachèterai des boissons la prochaine fois. Là jai que dalle.' WHERE `id`=733;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Où est passé mon mana ?' WHERE `id`=734;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Il me reste quelques <ammo> !' WHERE `id`=735;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Besoin de plus de <ammo> !' WHERE `id`=736;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus que 100 <ammo> !' WHERE `id`=737;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest fini ! Plus un seul <ammo> !' WHERE `id`=738;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Et tu as mon arc… oups, plus de <ammo> !' WHERE `id`=739;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai besoin de munitions !' WHERE `id`=740;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh mon dieu !' WHERE `id`=741;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai peur… là… vraiment.' WHERE `id`=742;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On est foutus !' WHERE `id`=743;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest fini. F.I.N.I.' WHERE `id`=744;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ça se termine maintenant.' WHERE `id`=745;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut balancer un blizzard ou un truc ?!' WHERE `id`=746;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mince. Le tank a aggro TOUT le monde…' WHERE `id`=747;
UPDATE `ai_playerbot_texts` SET `text_loc2`='On va mourir. On va mourir. On VA MOURIR !' WHERE `id`=748;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Wow ! Tant de jouets à casser !' WHERE `id`=749;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais tous les buter ! TOUS !' WHERE `id`=750;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Si le tank meurt, cest foutu pour nous tous…' WHERE `id`=751;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Aaaaaargh !' WHERE `id`=752;
UPDATE `ai_playerbot_texts` SET `text_loc2`='LEEEEERROOOYYYYYYYYYYYY JENNKINNNSSSSSS !!!!!!!!' WHERE `id`=753;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Bon. Quest-ce quon a en AOE là ?' WHERE `id`=754;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ça devient intéressant…' WHERE `id`=755;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cool. Regroupez-les bien pour une jolie boule de feu !' WHERE `id`=756;
UPDATE `ai_playerbot_texts` SET `text_loc2`='TUEZ ! TUEZ ! TUEZ !' WHERE `id`=757;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je crois que jai mouillé mon pantalon…' WHERE `id`=758;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest notre fin. Cétait sympa.' WHERE `id`=759;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère que les healers sont prêts… LEEEEROYYYY !' WHERE `id`=760;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pourvu quils ne me ciblent pas moi…' WHERE `id`=761;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oh non. Je peux pas regarder ce massacre…' WHERE `id`=762;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère quil y aura de la thune.' WHERE `id`=763;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Loot ! LOOT !' WHERE `id`=764;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mon précieux…' WHERE `id`=765;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère quun bel objet épique mattend là-dedans' WHERE `id`=766;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai des poches profondes et des sacs encore vides.' WHERE `id`=767;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tout est à moi !' WHERE `id`=768;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Pas de gris moisi aujourdhui, pitié…' WHERE `id`=769;
UPDATE `ai_playerbot_texts` SET `text_loc2`='CE loot est À MOI !' WHERE `id`=770;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Looter cest sale… mais jai besoin de thunes.' WHERE `id`=771;
UPDATE `ai_playerbot_texts` SET `text_loc2`='De lor !' WHERE `id`=772;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ok. Voyons ce quils ont laissé…' WHERE `id`=773;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Tinquiète, je vais tout looter. Tout.' WHERE `id`=774;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis un ninja du loot.' WHERE `id`=775;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je dois lancer les dés ?' WHERE `id`=776;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Quelquun peut mexpliquer où ils ont rangé tout ça ?' WHERE `id`=777;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Non, je loot pas cette merde grise.' WHERE `id`=778;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest moi dabord ! Cest moi ! MOI !' WHERE `id`=779;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Donne-moi ton fric !' WHERE `id`=780;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Mes poches sont vides, il faut les remplir.' WHERE `id`=781;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai un nouveau sac, il est fait pour ça.' WHERE `id`=782;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère que je vais pas aggro en lootant…' WHERE `id`=783;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ne regardez pas… je loot discret…' WHERE `id`=784;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Ha ! Vous naurez rien de tout ça !' WHERE `id`=785;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Le loot, cest stylé.' WHERE `id`=786;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jadore les nouveaux équipements.' WHERE `id`=787;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je me casse si ya encore rien de valeur…' WHERE `id`=788;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère que ce sera une jolie bague !' WHERE `id`=789;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je vais tarracher le loot des mains !' WHERE `id`=790;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Personne ne touche à rien. Cest MOI qui loot.' WHERE `id`=791;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Loot sucré :D' WHERE `id`=792;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Dieu du lancer, donne-moi un épique aujourdhui…' WHERE `id`=793;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Allez, de nouveaux jouets sil vous plaît !' WHERE `id`=794;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jespère quils ont des trucs savoureux…' WHERE `id`=795;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lor est à moi. Je laisse tout le reste… promis…' WHERE `id`=796;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Non, je peux pas résister.' WHERE `id`=797;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jen veux ENCORE !' WHERE `id`=798;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis presque là, attendez-moi !' WHERE `id`=799;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je suis pas loin, attendez-moi !' WHERE `id`=800;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jarrive vers ta position' WHERE `id`=801;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jarrive vers toi' WHERE `id`=802;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je me dirige vers ta position' WHERE `id`=803;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jessaie de te rejoindre' WHERE `id`=804;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Équipement de %item' WHERE `id`=805;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item retiré' WHERE `id`=806;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Jai appris les sorts : %spells' WHERE `id`=807;
UPDATE `ai_playerbot_texts` SET `text_loc2`='%item est en recharge' WHERE `id`=808;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nai pas %item dans mon inventaire' WHERE `id`=809;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lobjet avec lID %item nexiste pas' WHERE `id`=810;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Insertion de %gem dans %item' WHERE `id`=811;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas utiliser %item' WHERE `id`=812;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Suivi' WHERE `id`=813;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je reste ici' WHERE `id`=814;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je fuis' WHERE `id`=815;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je fuis pas avec toi, tes trop loin !' WHERE `id`=816;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Farm en cours' WHERE `id`=817;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Attaque en cours' WHERE `id`=818;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest trop loin' WHERE `id`=819;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Cest sous leau' WHERE `id`=820;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas y aller' WHERE `id`=821;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne suis pas dans ta guilde !' WHERE `id`=822;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Impossible de trouver une banque de guilde à proximité' WHERE `id`=823;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas déposer' WHERE `id`=824;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nai pas les droits pour déposer dans le premier onglet de la banque de guilde' WHERE `id`=825;
UPDATE `ai_playerbot_texts` SET `text_loc2`='déposé dans la banque de guilde' WHERE `id`=826;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Déplacement libre' WHERE `id`=827;
UPDATE `ai_playerbot_texts` SET `text_loc2`='En garde' WHERE `id`=828;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Utilisation de %target' WHERE `id`=829;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sur %unit' WHERE `id`=830;
UPDATE `ai_playerbot_texts` SET `text_loc2`='(%amount disponible)' WHERE `id`=831;
UPDATE `ai_playerbot_texts` SET `text_loc2`='(le dernier)' WHERE `id`=832;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La châsse ne correspond pas' WHERE `id`=833;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sur objet déchange' WHERE `id`=834;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sur moi-même' WHERE `id`=835;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sur %item' WHERE `id`=836;
UPDATE `ai_playerbot_texts` SET `text_loc2`='sur %gameobject' WHERE `id`=837;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Loot de %item' WHERE `id`=838;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Invocation de %target' WHERE `id`=839;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je nai pas assez de membres du groupe à proximité pour invoquer' WHERE `id`=840;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Impossible de trouver la cible dinvocation' WHERE `id`=841;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne peux pas invoquer en combat' WHERE `id`=842;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Je ne connais pas le sort %spell' WHERE `id`=843;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Lancement du sort %spell' WHERE `id`=844;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Fabrication de %spell' WHERE `id`=845;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Impossible de lancer %spell' WHERE `id`=846;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Échec du lancement de %spell' WHERE `id`=847;
UPDATE `ai_playerbot_texts` SET `text_loc2`='|cffffff00(x%amount restant)|r' WHERE `id`=848;
UPDATE `ai_playerbot_texts` SET `text_loc2`='dummy' WHERE `id`=849;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Par la Lumière... J\'ai oublié mes Symboles du roi. On se contentera de %base_spell !' WHERE `id`=934;
UPDATE `ai_playerbot_texts` SET `text_loc2`='La nature est généreuse, pas mes sacs... plus d\'herbes pour %group_spell. Prenez %base_spell pour l\'instant !' WHERE `id`=935;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Plus de poudre des arcanes... %group_spell attendra. Je lance %base_spell !' WHERE `id`=936;
UPDATE `ai_playerbot_texts` SET `text_loc2`='Oups, je n\'ai plus de composants pour %group_spell. On fera avec %base_spell !' WHERE `id`=937;

View File

@@ -0,0 +1,29 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'netherspite_beam_blocking_red',
'netherspite_beam_blocking_blue',
'netherspite_beam_blocking_green',
'netherspite_beam_leaving_blue',
'netherspite_beam_leaving_green'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'netherspite_beam_blocking_red',
'netherspite_beam_blocking_blue',
'netherspite_beam_blocking_green',
'netherspite_beam_leaving_blue',
'netherspite_beam_leaving_green'
);
INSERT INTO ai_playerbot_texts (name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
('netherspite_beam_blocking_red', '%player is moving to block the red beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_blocking_blue', '%player is moving to block the blue beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_blocking_green', '%player is moving to block the green beam!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_leaving_blue', '%player is leaving the blue beam--next blocker up!', 0, 0, '', '', '', '', '', '', '', ''),
('netherspite_beam_leaving_green', '%player is leaving the green beam--next blocker up!', 0, 0, '', '', '', '', '', '', '', '');
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('netherspite_beam_blocking_red', 100),
('netherspite_beam_blocking_blue', 100),
('netherspite_beam_blocking_green', 100),
('netherspite_beam_leaving_blue', 100),
('netherspite_beam_leaving_green', 100);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
DELETE FROM ai_playerbot_texts WHERE name IN (
'pet_usage_error',
'pet_no_pet_error',
'pet_stance_report',
'pet_no_target_error',
'pet_target_dead_error',
'pet_invalid_target_error',
'pet_pvp_prohibited_error',
'pet_attack_success',
'pet_attack_failed',
'pet_follow_success',
'pet_stay_success',
'pet_unknown_command_error',
'pet_stance_set_success',
'pet_type_pet',
'pet_type_guardian',
'pet_stance_aggressive',
'pet_stance_defensive',
'pet_stance_passive',
'pet_stance_unknown'
);
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
'pet_usage_error',
'pet_no_pet_error',
'pet_stance_report',
'pet_no_target_error',
'pet_target_dead_error',
'pet_invalid_target_error',
'pet_pvp_prohibited_error',
'pet_attack_success',
'pet_attack_failed',
'pet_follow_success',
'pet_stay_success',
'pet_unknown_command_error',
'pet_stance_set_success',
'pet_type_pet',
'pet_type_guardian',
'pet_stance_aggressive',
'pet_stance_defensive',
'pet_stance_passive',
'pet_stance_unknown'
);
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
(1717, 'pet_usage_error', "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
"사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
(1718, 'pet_no_pet_error', "You have no pet or guardian pet.", 0, 0,
"펫이나 수호자 펫이 없습니다.",
"Vous n'avez pas de familier ou gardien.",
"Du hast kein Tier oder Wächter.",
"你没有宠物或守护者宠物。",
"你沒有寵物或守護者寵物。",
"No tienes mascota o mascota guardián.",
"No tienes mascota o mascota guardián.",
"У вас нет питомца или защитника."),
(1719, 'pet_stance_report', "Current stance of %type \"%name\": %stance.", 0, 0,
"%type \"%name\"의 현재 태세: %stance.",
"Position actuelle du %type \"%name\": %stance.",
"Aktuelle Haltung des %type \"%name\": %stance.",
"%type \"%name\" 的当前姿态: %stance。",
"%type \"%name\" 的當前姿態: %stance。",
"Postura actual del %type \"%name\": %stance.",
"Postura actual del %type \"%name\": %stance.",
"Текущая позиция %type \"%name\": %stance."),
(1720, 'pet_no_target_error', "No valid target selected by master.", 0, 0,
"주인이 유효한 대상을 선택하지 않았습니다.",
"Aucune cible valide sélectionnée par le maître.",
"Kein gültiges Ziel vom Meister ausgewählt.",
"主人未选择有效目标。",
"主人未選擇有效目標。",
"No hay objetivo válido seleccionado por el maestro.",
"No hay objetivo válido seleccionado por el maestro.",
"Хозяин не выбрал действительную цель."),
(1721, 'pet_target_dead_error', "Target is not alive.", 0, 0,
"대상이 살아있지 않습니다.",
"La cible n'est pas vivante.",
"Das Ziel ist nicht am Leben.",
"目标未存活。",
"目標未存活。",
"El objetivo no está vivo.",
"El objetivo no está vivo.",
"Цель не жива."),
(1722, 'pet_invalid_target_error', "Target is not a valid attack target for the bot.", 0, 0,
"대상이 봇에게 유효한 공격 대상이 아닙니다.",
"La cible n'est pas une cible d'attaque valide pour le bot.",
"Das Ziel ist kein gültiges Angriffsziel für den Bot.",
"目标不是机器人的有效攻击目标。",
"目標不是機器人的有效攻擊目標。",
"El objetivo no es un objetivo de ataque válido para el bot.",
"El objetivo no es un objetivo de ataque válido para el bot.",
"Цель не является допустимой целью атаки для бота."),
(1723, 'pet_pvp_prohibited_error', "I cannot command my pet to attack players in PvP prohibited areas.", 0, 0,
"PvP 금지 지역에서는 펫에게 플레이어 공격 명령을 내릴 수 없습니다.",
"Je ne peux pas commander à mon familier d'attaquer des joueurs dans les zones où le PvP est interdit.",
"Ich kann meinem Tier nicht befehlen, Spieler in PvP-verbotenen Gebieten anzugreifen.",
"我不能命令我的宠物在禁止PvP的区域攻击玩家。",
"我不能命令我的寵物在禁止PvP的區域攻擊玩家。",
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
"Я не могу приказать своему питомцу атаковать игроков в зонах, где PvP запрещено."),
(1724, 'pet_attack_success', "Pet commanded to attack your target.", 0, 0,
"펫이 당신의 대상을 공격하도록 명령했습니다.",
"Le familier a reçu l'ordre d'attaquer votre cible.",
"Tier wurde befohlen, dein Ziel anzugreifen.",
"宠物已命令攻击你的目标。",
"寵物已命令攻擊你的目標。",
"Mascota ordenada a atacar tu objetivo.",
"Mascota ordenada a atacar tu objetivo.",
"Питомцу приказано атаковать вашу цель."),
(1725, 'pet_attack_failed', "Pet did not attack. (Already attacking or unable to attack target)", 0, 0,
"펫이 공격하지 않았습니다. (이미 공격 중이거나 대상 공격 불가)",
"Le familier n'a pas attaqué. (Attaque déjà en cours ou impossible d'attaquer la cible)",
"Tier hat nicht angegriffen. (Greift bereits an oder kann Ziel nicht angreifen)",
"宠物未攻击。(已在攻击或无法攻击目标)",
"寵物未攻擊。(已在攻擊或無法攻擊目標)",
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
"Питомец не атаковал. (Уже атакует или не может атаковать цель)"),
(1726, 'pet_follow_success', "Pet commanded to follow.", 0, 0,
"펫이 따라오도록 명령했습니다.",
"Le familier a reçu l'ordre de suivre.",
"Tier wurde befohlen zu folgen.",
"宠物已命令跟随。",
"寵物已命令跟隨。",
"Mascota ordenada a seguir.",
"Mascota ordenada a seguir.",
"Питомцу приказано следовать."),
(1727, 'pet_stay_success', "Pet commanded to stay.", 0, 0,
"펫이 머물도록 명령했습니다.",
"Le familier a reçu l'ordre de rester.",
"Tier wurde befohlen zu bleiben.",
"宠物已命令停留。",
"寵物已命令停留。",
"Mascota ordenada a quedarse.",
"Mascota ordenada a quedarse.",
"Питомцу приказано остаться."),
(1728, 'pet_unknown_command_error', "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
"알 수 없는 펫 명령: %param. 사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Commande de familier inconnue: %param. Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Unbekannter Tierbefehl: %param. Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"未知宠物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"未知寵物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
"Неизвестная команда питомца: %param. Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
(1729, 'pet_stance_set_success', "Pet stance set to %stance.", 0, 0,
"펫 태세가 %stance(으)로 설정되었습니다.",
"Position du familier définie sur %stance.",
"Tierhaltung auf %stance gesetzt.",
"宠物姿态设置为 %stance。",
"寵物姿態設置為 %stance。",
"Postura de mascota establecida en %stance.",
"Postura de mascota establecida en %stance.",
"Позиция питомца установлена на %stance."),
(1730, 'pet_type_pet', "pet", 0, 0,
"",
"familier",
"Tier",
"宠物",
"寵物",
"mascota",
"mascota",
"питомец"),
(1731, 'pet_type_guardian', "guardian", 0, 0,
"수호자",
"gardien",
"Wächter",
"守护者",
"守護者",
"guardián",
"guardián",
"защитник"),
(1732, 'pet_stance_aggressive', "aggressive", 0, 0,
"공격적",
"agressif",
"aggressiv",
"进攻",
"進攻",
"agresivo",
"agresivo",
"агрессивная"),
(1733, 'pet_stance_defensive', "defensive", 0, 0,
"방어적",
"défensif",
"defensiv",
"防御",
"防禦",
"defensivo",
"defensivo",
"защитная"),
(1734, 'pet_stance_passive', "passive", 0, 0,
"수동적",
"passif",
"passiv",
"被动",
"被動",
"pasivo",
"pasivo",
"пассивная"),
(1735, 'pet_stance_unknown', "unknown", 0, 0,
"알 수 없음",
"inconnu",
"unbekannt",
"未知",
"未知",
"desconocido",
"desconocido",
"неизвестная");
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
('pet_usage_error', 100),
('pet_no_pet_error', 100),
('pet_stance_report', 100),
('pet_no_target_error', 100),
('pet_target_dead_error', 100),
('pet_invalid_target_error', 100),
('pet_pvp_prohibited_error', 100),
('pet_attack_success', 100),
('pet_attack_failed', 100),
('pet_follow_success', 100),
('pet_stay_success', 100),
('pet_unknown_command_error', 100),
('pet_stance_set_success', 100),
('pet_type_pet', 100),
('pet_type_guardian', 100),
('pet_stance_aggressive', 100),
('pet_stance_defensive', 100),
('pet_stance_passive', 100),
('pet_stance_unknown', 100);

View File

@@ -1,5 +1,5 @@
DROP TABLE IF EXISTS `playerbots_rpg_races`;
CREATE TABLE `playerbots_rpg_races`
CREATE TABLE `playerbots_rpg_races`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`entry` int(11),

View File

@@ -0,0 +1,3 @@
DELETE FROM spell_dbc WHERE ID = 30758;
INSERT INTO spell_dbc (`ID`,`Category`,`DispelType`,`Mechanic`,`Attributes`,`AttributesEx`,`AttributesEx2`,`AttributesEx3`,`AttributesEx4`,`AttributesEx5`,`AttributesEx6`,`AttributesEx7`,`ShapeshiftMask`,`unk_320_2`,`ShapeshiftExclude`,`unk_320_3`,`Targets`,`TargetCreatureType`,`RequiresSpellFocus`,`FacingCasterFlags`,`CasterAuraState`,`TargetAuraState`,`ExcludeCasterAuraState`,`ExcludeTargetAuraState`,`CasterAuraSpell`,`TargetAuraSpell`,`ExcludeCasterAuraSpell`,`ExcludeTargetAuraSpell`,`CastingTimeIndex`,`RecoveryTime`,`CategoryRecoveryTime`,`InterruptFlags`,`AuraInterruptFlags`,`ChannelInterruptFlags`,`ProcTypeMask`,`ProcChance`,`ProcCharges`,`MaxLevel`,`BaseLevel`,`SpellLevel`,`DurationIndex`,`PowerType`,`ManaCost`,`ManaCostPerLevel`,`ManaPerSecond`,`ManaPerSecondPerLevel`,`RangeIndex`,`Speed`,`ModalNextSpell`,`CumulativeAura`,`Totem_1`,`Totem_2`,`Reagent_1`,`Reagent_2`,`Reagent_3`,`Reagent_4`,`Reagent_5`,`Reagent_6`,`Reagent_7`,`Reagent_8`,`ReagentCount_1`,`ReagentCount_2`,`ReagentCount_3`,`ReagentCount_4`,`ReagentCount_5`,`ReagentCount_6`,`ReagentCount_7`,`ReagentCount_8`,`EquippedItemClass`,`EquippedItemSubclass`,`EquippedItemInvTypes`,`Effect_1`,`Effect_2`,`Effect_3`,`EffectDieSides_1`,`EffectDieSides_2`,`EffectDieSides_3`,`EffectRealPointsPerLevel_1`,`EffectRealPointsPerLevel_2`,`EffectRealPointsPerLevel_3`,`EffectBasePoints_1`,`EffectBasePoints_2`,`EffectBasePoints_3`,`EffectMechanic_1`,`EffectMechanic_2`,`EffectMechanic_3`,`ImplicitTargetA_1`,`ImplicitTargetA_2`,`ImplicitTargetA_3`,`ImplicitTargetB_1`,`ImplicitTargetB_2`,`ImplicitTargetB_3`,`EffectRadiusIndex_1`,`EffectRadiusIndex_2`,`EffectRadiusIndex_3`,`EffectAura_1`,`EffectAura_2`,`EffectAura_3`,`EffectAuraPeriod_1`,`EffectAuraPeriod_2`,`EffectAuraPeriod_3`,`EffectMultipleValue_1`,`EffectMultipleValue_2`,`EffectMultipleValue_3`,`EffectChainTargets_1`,`EffectChainTargets_2`,`EffectChainTargets_3`,`EffectItemType_1`,`EffectItemType_2`,`EffectItemType_3`,`EffectMiscValue_1`,`EffectMiscValue_2`,`EffectMiscValue_3`,`EffectMiscValueB_1`,`EffectMiscValueB_2`,`EffectMiscValueB_3`,`EffectTriggerSpell_1`,`EffectTriggerSpell_2`,`EffectTriggerSpell_3`,`EffectPointsPerCombo_1`,`EffectPointsPerCombo_2`,`EffectPointsPerCombo_3`,`EffectSpellClassMaskA_1`,`EffectSpellClassMaskA_2`,`EffectSpellClassMaskA_3`,`EffectSpellClassMaskB_1`,`EffectSpellClassMaskB_2`,`EffectSpellClassMaskB_3`,`EffectSpellClassMaskC_1`,`EffectSpellClassMaskC_2`,`EffectSpellClassMaskC_3`,`SpellVisualID_1`,`SpellVisualID_2`,`SpellIconID`,`ActiveIconID`,`SpellPriority`,`Name_Lang_enUS`,`Name_Lang_enGB`,`Name_Lang_koKR`,`Name_Lang_frFR`,`Name_Lang_deDE`,`Name_Lang_enCN`,`Name_Lang_zhCN`,`Name_Lang_enTW`,`Name_Lang_zhTW`,`Name_Lang_esES`,`Name_Lang_esMX`,`Name_Lang_ruRU`,`Name_Lang_ptPT`,`Name_Lang_ptBR`,`Name_Lang_itIT`,`Name_Lang_Unk`,`Name_Lang_Mask`,`NameSubtext_Lang_enUS`,`NameSubtext_Lang_enGB`,`NameSubtext_Lang_koKR`,`NameSubtext_Lang_frFR`,`NameSubtext_Lang_deDE`,`NameSubtext_Lang_enCN`,`NameSubtext_Lang_zhCN`,`NameSubtext_Lang_enTW`,`NameSubtext_Lang_zhTW`,`NameSubtext_Lang_esES`,`NameSubtext_Lang_esMX`,`NameSubtext_Lang_ruRU`,`NameSubtext_Lang_ptPT`,`NameSubtext_Lang_ptBR`,`NameSubtext_Lang_itIT`,`NameSubtext_Lang_Unk`,`NameSubtext_Lang_Mask`,`Description_Lang_enUS`,`Description_Lang_enGB`,`Description_Lang_koKR`,`Description_Lang_frFR`,`Description_Lang_deDE`,`Description_Lang_enCN`,`Description_Lang_zhCN`,`Description_Lang_enTW`,`Description_Lang_zhTW`,`Description_Lang_esES`,`Description_Lang_esMX`,`Description_Lang_ruRU`,`Description_Lang_ptPT`,`Description_Lang_ptBR`,`Description_Lang_itIT`,`Description_Lang_Unk`,`Description_Lang_Mask`,`AuraDescription_Lang_enUS`,`AuraDescription_Lang_enGB`,`AuraDescription_Lang_koKR`,`AuraDescription_Lang_frFR`,`AuraDescription_Lang_deDE`,`AuraDescription_Lang_enCN`,`AuraDescription_Lang_zhCN`,`AuraDescription_Lang_enTW`,`AuraDescription_Lang_zhTW`,`AuraDescription_Lang_esES`,`AuraDescription_Lang_esMX`,`AuraDescription_Lang_ruRU`,`AuraDescription_Lang_ptPT`,`AuraDescription_Lang_ptBR`,`AuraDescription_Lang_itIT`,`AuraDescription_Lang_Unk`,`AuraDescription_Lang_Mask`,`ManaCostPct`,`StartRecoveryCategory`,`StartRecoveryTime`,`MaxTargetLevel`,`SpellClassSet`,`SpellClassMask_1`,`SpellClassMask_2`,`SpellClassMask_3`,`MaxTargets`,`DefenseType`,`PreventionType`,`StanceBarOrder`,`EffectChainAmplitude_1`,`EffectChainAmplitude_2`,`EffectChainAmplitude_3`,`MinFactionID`,`MinReputation`,`RequiredAuraVision`,`RequiredTotemCategoryID_1`,`RequiredTotemCategoryID_2`,`RequiredAreasID`,`SchoolMask`,`RuneCostID`,`SpellMissileID`,`PowerDisplayID`,`EffectBonusMultiplier_1`,`EffectBonusMultiplier_2`,`EffectBonusMultiplier_3`,`SpellDescriptionVariableID`,`SpellDifficultyID`)
VALUES (30758,0,0,0,696254720,132128,268976133,269680640,8388736,393224,4100,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,101,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,52,0,0,0,0,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,'aedm','','','','','','','','','','','','','','','',16712190,'','','','','','','','','','','','','','','','',16712172,'','','','','','','','','','','','','','','','',16712188,'','','','','','','','','','','','','','','','',16712188,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "AiFactory.h"
@@ -80,13 +80,19 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot)
switch (bot->getClass())
{
case CLASS_MAGE:
tab = 1;
tab = MAGE_TAB_FROST;
break;
case CLASS_PALADIN:
tab = 2;
tab = PALADIN_TAB_RETRIBUTION;
break;
case CLASS_PRIEST:
tab = 1;
tab = PRIEST_TAB_HOLY;
break;
case CLASS_WARLOCK:
tab = WARLOCK_TAB_DEMONOLOGY;
break;
case CLASS_SHAMAN:
tab = SHAMAN_TAB_ELEMENTAL;
break;
}
@@ -289,7 +295,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
{
engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr);
}
else if (tab == PRIEST_TAB_DISIPLINE)
else if (tab == PRIEST_TAB_DISCIPLINE)
{
engine->addStrategiesNoInit("heal", nullptr);
}
@@ -301,23 +307,23 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
engine->addStrategiesNoInit("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategiesNoInit("arcane", "arcane aoe", nullptr);
else if (tab == 1)
if (tab == 0) // Arcane
engine->addStrategiesNoInit("arcane", nullptr);
else if (tab == 1) // Fire
{
if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/)
{
engine->addStrategiesNoInit("frostfire", "frostfire aoe", nullptr);
engine->addStrategiesNoInit("frostfire", nullptr);
}
else
{
engine->addStrategiesNoInit("fire", "fire aoe", nullptr);
engine->addStrategiesNoInit("fire", nullptr);
}
}
else
engine->addStrategiesNoInit("frost", "frost aoe", nullptr);
}
else // Frost
engine->addStrategiesNoInit("frost", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", nullptr);
engine->addStrategiesNoInit("dps", "dps assist", "cure", "aoe", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
@@ -328,14 +334,14 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
engine->addStrategiesNoInit("fury", "aoe", "dps assist", /*"behind",*/ nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0)
engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr);
else if (tab == 2)
engine->addStrategiesNoInit("heal", "bmana", nullptr);
else
engine->addStrategiesNoInit("melee", "melee aoe", "bdps", nullptr);
if (tab == 0) // Elemental
engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr);
else if (tab == 2) // Restoration
engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr);
else // Enhancement
engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", "totems", nullptr);
engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1)
@@ -367,12 +373,14 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
break;
case CLASS_HUNTER:
engine->addStrategiesNoInit("dps", "aoe", "bdps", "dps assist", nullptr);
engine->addStrategy("dps debuff", false);
// if (tab == HUNTER_TAB_SURVIVAL)
// {
// engine->addStrategy("trap weave", false);
// }
if (tab == 0) // Beast Mastery
engine->addStrategiesNoInit("bm", nullptr);
else if (tab == 1) // Marksmanship
engine->addStrategiesNoInit("mm", nullptr);
else if (tab == 2) // Survival
engine->addStrategiesNoInit("surv", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY)
@@ -386,13 +394,13 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
break;
case CLASS_WARLOCK:
if (tab == 0) // Affliction
engine->addStrategiesNoInit("affli", "affli aoe", nullptr);
engine->addStrategiesNoInit("affli", "curse of agony", nullptr);
else if (tab == 1) // Demonology
engine->addStrategiesNoInit("demo", "demo aoe", "meta melee", nullptr);
engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr);
else if (tab == 2) // Destruction
engine->addStrategiesNoInit("destro", "destro aoe", "curse of elements", nullptr);
engine->addStrategiesNoInit("destro", "curse of elements", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", nullptr);
engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr);
break;
case CLASS_DEATH_KNIGHT:
@@ -405,17 +413,20 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
break;
}
if (PlayerbotAI::IsTank(player, true)) {
if (PlayerbotAI::IsTank(player, true))
{
engine->addStrategy("tank face", false);
}
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) {
if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true))
{
engine->addStrategy("behind", false);
}
if (PlayerbotAI::IsHeal(player, true))
{
if (sPlayerbotAIConfig->autoSaveMana)
engine->addStrategy("save mana", false);
engine->addStrategy("healer dps", false);
if (!sPlayerbotAIConfig->IsRestrictedHealerDPSMap(player->GetMapId()))
engine->addStrategy("healer dps", false);
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
@@ -466,6 +477,10 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
}
}
}
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
}
else
{
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
@@ -594,19 +609,19 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
nonCombatEngine->addStrategy("dps assist", false);
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICATION)
if (tab == WARLOCK_TAB_AFFLICTION)
{
nonCombatEngine->addStrategiesNoInit("felhunter", nullptr);
nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DEMONOLOGY)
{
nonCombatEngine->addStrategiesNoInit("felguard", nullptr);
nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr);
}
else if (tab == WARLOCK_TAB_DESTRUCTION)
{
nonCombatEngine->addStrategiesNoInit("imp", nullptr);
nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr);
}
nonCombatEngine->addStrategiesNoInit("dps assist", nullptr);
nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
@@ -693,7 +708,9 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const
// {
// // nonCombatEngine->addStrategy("travel");
// nonCombatEngine->addStrategy("rpg");
// } else {
// }
// else
// {
// nonCombatEngine->addStrategy("move random");
// }

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AIFACTORY_H

View File

@@ -70,7 +70,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s
return false;
}
for (const auto& pair : toChannels)
for (auto const& pair : toChannels)
{
uint32 roll = urand(1, 100);
uint32 chance = pair.second;
@@ -166,7 +166,7 @@ bool BroadcastHelper::BroadcastToChannelWithGlobalChance(PlayerbotAI* ai, std::s
return false;
}
bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, const ItemTemplate *proto)
bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTemplate const* proto)
{
if (!sPlayerbotAIConfig->enableBroadcasts)
return false;
@@ -410,7 +410,6 @@ bool BroadcastHelper::BroadcastQuestUpdateComplete(PlayerbotAI* ai, Player* bot,
placeholders["%my_race"] = ai->GetChatHelper()->FormatRace(bot->getRace());
placeholders["%my_level"] = std::to_string(bot->GetLevel());
return BroadcastToChannelWithGlobalChance(
ai,
BOT_TEXT2("broadcast_quest_update_complete", placeholders),

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ChatFilter.h"
@@ -8,6 +8,18 @@
#include "Group.h"
#include "Playerbots.h"
#include "RtiTargetValue.h"
#include "AiFactory.h"
#include <algorithm>
#include <cctype>
#include <string>
static std::string ToLower(const std::string& str)
{
std::string out = str;
std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c){ return std::tolower(c); });
return out;
}
std::string const ChatFilter::Filter(std::string& message)
{
@@ -25,27 +37,36 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool tank = message.find("@tank") == 0;
bool tank = msgLower.find("@tank") == 0;
if (tank && !botAI->IsTank(bot))
return "";
bool dps = message.find("@dps") == 0;
bool dps = msgLower.find("@dps") == 0;
if (dps && (botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
bool heal = message.find("@heal") == 0;
bool heal = msgLower.find("@heal") == 0;
if (heal && !botAI->IsHeal(bot))
return "";
bool ranged = message.find("@ranged") == 0;
bool ranged = msgLower.find("@ranged") == 0;
if (ranged && !botAI->IsRanged(bot))
return "";
bool melee = message.find("@melee") == 0;
bool melee = msgLower.find("@melee") == 0;
if (melee && botAI->IsRanged(bot))
return "";
bool rangeddps = msgLower.find("@rangeddps") == 0;
if (rangeddps && (!botAI->IsRanged(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
bool meleedps = msgLower.find("@meleedps") == 0;
if (meleedps && (!botAI->IsMelee(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
if (tank || dps || heal || ranged || melee)
return ChatFilter::Filter(message);
@@ -61,11 +82,11 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
if (message[0] != '@')
std::string msgLower = ToLower(message);
if (msgLower[0] != '@')
return message;
if (message.find("-") != std::string::npos)
if (msgLower.find("-") != std::string::npos)
{
uint32 fromLevel = atoi(message.substr(message.find("@") + 1, message.find("-")).c_str());
uint32 toLevel = atoi(message.substr(message.find("-") + 1, message.find(" ")).c_str());
@@ -92,9 +113,10 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool melee = message.find("@melee") == 0;
bool ranged = message.find("@ranged") == 0;
bool melee = msgLower.find("@melee") == 0;
bool ranged = msgLower.find("@ranged") == 0;
if (!melee && !ranged)
return message;
@@ -151,17 +173,17 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
Group* group = bot->GetGroup();
if (!group)
return message;
bool found = false;
//bool isRti = false; //not used, shadowed by the next declaration, line marked for removal.
for (std::vector<std::string>::iterator i = rtis.begin(); i != rtis.end(); i++)
{
std::string const rti = *i;
bool isRti = message.find(rti) == 0;
std::string rtiLower = ToLower(rti);
bool isRti = msgLower.find(rtiLower) == 0;
if (!isRti)
continue;
@@ -196,7 +218,7 @@ class ClassChatFilter : public ChatFilter
public:
ClassChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
classNames["@death_knight"] = CLASS_DEATH_KNIGHT;
classNames["@dk"] = CLASS_DEATH_KNIGHT;
classNames["@druid"] = CLASS_DRUID;
classNames["@hunter"] = CLASS_HUNTER;
classNames["@mage"] = CLASS_MAGE;
@@ -211,12 +233,12 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
bool found = false;
//bool isClass = false; //not used, shadowed by the next declaration, line marked for removal.
for (std::map<std::string, uint8>::iterator i = classNames.begin(); i != classNames.end(); i++)
{
bool isClass = message.find(i->first) == 0;
bool isClass = msgLower.find(ToLower(i->first)) == 0;
if (isClass && bot->getClass() != i->second)
return "";
@@ -243,30 +265,321 @@ public:
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
if (message.find("@group") == 0)
std::string msgLower = ToLower(message);
if (msgLower.find("@group") == 0)
{
std::string const pnum = message.substr(6, message.find(" "));
uint32 from = atoi(pnum.c_str());
uint32 to = from;
if (pnum.find("-") != std::string::npos)
size_t spacePos = message.find(" ");
if (spacePos == std::string::npos)
return message;
std::string pnum = message.substr(6, spacePos - 6);
std::string actualMessage = message.substr(spacePos + 1);
std::set<uint32> targets;
std::istringstream ss(pnum);
std::string token;
while (std::getline(ss, token, ','))
{
from = atoi(pnum.substr(pnum.find("@") + 1, pnum.find("-")).c_str());
to = atoi(pnum.substr(pnum.find("-") + 1, pnum.find(" ")).c_str());
size_t dashPos = token.find("-");
if (dashPos != std::string::npos)
{
uint32 from = atoi(token.substr(0, dashPos).c_str());
uint32 to = atoi(token.substr(dashPos + 1).c_str());
if (from > to) std::swap(from, to);
for (uint32 i = from; i <= to; ++i)
targets.insert(i);
}
else
{
uint32 index = atoi(token.c_str());
targets.insert(index);
}
}
if (!bot->GetGroup())
return message;
uint32 sg = bot->GetSubGroup() + 1;
if (sg >= from && sg <= to)
return ChatFilter::Filter(message);
if (targets.find(sg) != targets.end())
return ChatFilter::Filter(actualMessage);
}
return message;
}
};
class SpecChatFilter : public ChatFilter
{
public:
SpecChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
// Map (class, specTab) to spec+class string
specTabNames[{CLASS_PALADIN, 0}] = "hpal";
specTabNames[{CLASS_PALADIN, 1}] = "ppal";
specTabNames[{CLASS_PALADIN, 2}] = "rpal";
specTabNames[{CLASS_PRIEST, 0}] = "disc";
specTabNames[{CLASS_PRIEST, 1}] = "hpr";
specTabNames[{CLASS_PRIEST, 2}] = "spr";
specTabNames[{CLASS_MAGE, 0}] = "arc";
specTabNames[{CLASS_MAGE, 1}] = "frost";
specTabNames[{CLASS_MAGE, 2}] = "fire";
specTabNames[{CLASS_WARRIOR, 0}] = "arms";
specTabNames[{CLASS_WARRIOR, 1}] = "fury";
specTabNames[{CLASS_WARRIOR, 2}] = "pwar";
specTabNames[{CLASS_WARLOCK, 0}] = "affl";
specTabNames[{CLASS_WARLOCK, 1}] = "demo";
specTabNames[{CLASS_WARLOCK, 2}] = "dest";
specTabNames[{CLASS_SHAMAN, 0}] = "ele";
specTabNames[{CLASS_SHAMAN, 1}] = "enh";
specTabNames[{CLASS_SHAMAN, 2}] = "rsha";
specTabNames[{CLASS_DRUID, 0}] = "bal";
// See below for feral druid
specTabNames[{CLASS_DRUID, 2}] = "rdru";
specTabNames[{CLASS_HUNTER, 0}] = "bmh";
specTabNames[{CLASS_HUNTER, 1}] = "mmh";
specTabNames[{CLASS_HUNTER, 2}] = "svh";
specTabNames[{CLASS_ROGUE, 0}] = "mut";
specTabNames[{CLASS_ROGUE, 1}] = "comb";
specTabNames[{CLASS_ROGUE, 2}] = "sub";
// See below for blood death knight
specTabNames[{CLASS_DEATH_KNIGHT, 1}] = "fdk";
specTabNames[{CLASS_DEATH_KNIGHT, 2}] = "udk";
}
std::string const Filter(std::string& message) override
{
std::string msgLower = ToLower(message);
std::string specPrefix;
std::string rest;
if (!ParseSpecPrefix(message, specPrefix, rest))
{
return message;
}
Player* bot = botAI->GetBot();
if (!MatchesSpec(bot, specPrefix))
{
return "";
}
std::string result = ChatFilter::Filter(rest);
return result;
}
private:
std::map<std::pair<uint8, int>, std::string> specTabNames;
bool ParseSpecPrefix(const std::string& message, std::string& specPrefix, std::string& rest)
{
std::string msgLower = ToLower(message);
for (auto const& entry : specTabNames)
{
std::string prefix = "@" + entry.second;
if (msgLower.find(ToLower(prefix)) == 0)
{
specPrefix = entry.second;
size_t spacePos = message.find(' ');
rest = (spacePos != std::string::npos) ? message.substr(spacePos + 1) : "";
return true;
}
}
return false;
}
bool MatchesSpec(Player* bot, const std::string& specPrefix)
{
uint8 cls = bot->getClass();
int specTab = AiFactory::GetPlayerSpecTab(bot);
std::string botSpecClass;
// For druids, specTab==1 is always feral; distinguish bear/cat at runtime by role
if (cls == CLASS_DRUID && specTab == 1)
{
botSpecClass = botAI->IsTank(bot) ? "bear" : "cat";
}
// For death knights, specTab==0 is always blood; distinguish tank/dps at runtime by role
else if (cls == CLASS_DEATH_KNIGHT && specTab == 0)
{
botSpecClass = botAI->IsTank(bot) ? "bdkt" : "bdkd";
}
else
{
auto it = specTabNames.find({cls, specTab});
if (it != specTabNames.end())
botSpecClass = it->second;
}
return ToLower(botSpecClass) == ToLower(specPrefix);
}
};
class AuraChatFilter : public ChatFilter
{
public:
AuraChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
const std::string auraPrefix = "@aura";
const std::string noAuraPrefix = "@noaura";
size_t prefixLen = 0;
bool isNoAura = false;
if (msgLower.find(auraPrefix) == 0)
{
prefixLen = auraPrefix.length();
isNoAura = false;
}
else if (msgLower.find(noAuraPrefix) == 0)
{
prefixLen = noAuraPrefix.length();
isNoAura = true;
}
else
{
return message;
}
// Trim any leading spaces after @aura or @noaura (can use space between prefix and spell ID if desired, but not required)
std::string auraIdOrName = message.substr(prefixLen);
auraIdOrName.erase(0, auraIdOrName.find_first_not_of(' '));
if (auraIdOrName.empty())
{
return message;
}
uint32 auraId = 0;
size_t spacePos = auraIdOrName.find(' ');
std::string idStr = (spacePos != std::string::npos) ? auraIdOrName.substr(0, spacePos) : auraIdOrName;
std::string rest = (spacePos != std::string::npos) ? auraIdOrName.substr(spacePos + 1) : "";
if (!idStr.empty())
{
bool isNumeric = std::all_of(idStr.begin(), idStr.end(), ::isdigit);
if (isNumeric)
{
auraId = atoi(idStr.c_str());
}
}
if (auraId == 0)
return message;
bool hasAura = bot->HasAura(auraId);
bool match = isNoAura ? !hasAura : hasAura;
std::string result = match ? ChatFilter::Filter(rest) : "";
return result;
}
};
class AggroByChatFilter : public ChatFilter
{
public:
AggroByChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) {}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
std::string msgLower = ToLower(message);
const std::string prefix = "@aggroby";
size_t prefixLen = prefix.length();
if (msgLower.find(prefix) != 0)
{
return message;
}
// Trim any leading spaces after @aggroby (can use space between prefix and entry ID/creature name if desired, but not required)
std::string enemyStr = message.substr(prefixLen);
enemyStr.erase(0, enemyStr.find_first_not_of(' '));
if (enemyStr.empty())
{
return message;
}
// If creature name is more than one word, it must be enclosed in quotes, e.g. @aggroby "Scarlet Commander Mograine" flee
std::string rest = "";
std::string enemyName = "";
bool isName = false;
uint32 entryId = 0;
if (enemyStr[0] == '"')
{
size_t endQuote = enemyStr.find('"', 1);
if (endQuote != std::string::npos)
{
enemyName = enemyStr.substr(1, endQuote - 1);
isName = true;
size_t spacePos = enemyStr.find(' ', endQuote + 1);
if (spacePos != std::string::npos)
{
rest = enemyStr.substr(spacePos + 1);
}
else
{
rest = "";
}
}
else
{
enemyName = enemyStr.substr(1);
isName = true;
rest = "";
}
}
else
{
size_t splitPos = enemyStr.find_first_of(" ");
std::string idOrName = (splitPos != std::string::npos) ? enemyStr.substr(0, splitPos) : enemyStr;
if (splitPos != std::string::npos)
{
rest = enemyStr.substr(splitPos + 1);
}
else
{
rest = "";
}
if (!idOrName.empty())
{
bool isNumeric = std::all_of(idOrName.begin(), idOrName.end(), ::isdigit);
if (isNumeric)
{
entryId = atoi(idOrName.c_str());
}
else
{
enemyName = idOrName;
isName = true;
}
}
}
const float radius = 100.0f;
GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool match = false;
for (auto const& guid : npcs)
{
Creature* c = botAI->GetCreature(guid);
if (!c)
{
continue;
}
bool nameMatch = isName && ToLower(c->GetName()) == ToLower(enemyName);
bool idMatch = (entryId != 0) && c->GetEntry() == entryId;
if ((nameMatch || idMatch) && c->GetDistance2d(bot) <= radius)
{
Unit* victim = c->GetVictim();
if (victim && victim->GetGUID() == bot->GetGUID())
{
match = true;
break;
}
}
}
std::string result = match ? ChatFilter::Filter(rest) : "";
return result;
}
};
CompositeChatFilter::CompositeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
filters.push_back(new StrategyChatFilter(botAI));
@@ -275,6 +588,9 @@ CompositeChatFilter::CompositeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
filters.push_back(new CombatTypeChatFilter(botAI));
filters.push_back(new LevelChatFilter(botAI));
filters.push_back(new SubGroupChatFilter(botAI));
filters.push_back(new SpecChatFilter(botAI));
filters.push_back(new AuraChatFilter(botAI));
filters.push_back(new AggroByChatFilter(botAI));
}
CompositeChatFilter::~CompositeChatFilter()
@@ -285,14 +601,11 @@ CompositeChatFilter::~CompositeChatFilter()
std::string const CompositeChatFilter::Filter(std::string& message)
{
for (uint32 j = 0; j < filters.size(); ++j)
for (auto* filter : filters)
{
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
{
message = (*i)->Filter(message);
if (message.empty())
break;
}
message = filter->Filter(message);
if (message.empty())
break;
}
return message;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATFILTER_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ChatHelper.h"
@@ -308,43 +308,46 @@ ItemIds ChatHelper::parseItems(std::string const text)
ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text)
{
ItemWithRandomProperty res;
size_t itemStart = text.find("Hitem:");
if (itemStart == std::string::npos)
return res;
itemStart += 6;
if (itemStart >= text.length())
return res;
size_t colonPos = text.find(':', itemStart);
if (colonPos == std::string::npos)
return res;
std::string itemIdStr = text.substr(itemStart, colonPos - itemStart);
res.itemId = atoi(itemIdStr.c_str());
std::vector<std::string> params;
size_t currentPos = colonPos + 1;
while (currentPos < text.length()) {
size_t nextColon = text.find(':', currentPos);
if (nextColon == std::string::npos) {
if (nextColon == std::string::npos)
{
size_t hTag = text.find("|h", currentPos);
if (hTag != std::string::npos) {
if (hTag != std::string::npos)
{
params.push_back(text.substr(currentPos, hTag - currentPos));
}
break;
}
params.push_back(text.substr(currentPos, nextColon - currentPos));
currentPos = nextColon + 1;
}
if (params.size() >= 6) {
if (params.size() >= 6)
{
res.randomPropertyId = atoi(params[5].c_str());
}
return res;
}
@@ -364,7 +367,7 @@ std::string const ChatHelper::FormatQuest(Quest const* quest)
if (questTitle.empty())
questTitle = quest->GetTitle();
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
return out.str();
}
@@ -432,7 +435,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count
if (locale && locale->Name.size() > sWorld->GetDefaultDbcLocale())
itemName = locale->Name[sWorld->GetDefaultDbcLocale()];
if (itemName.empty())
itemName = proto->Name1;
@@ -677,7 +680,7 @@ std::set<uint32> extractGeneric(std::string_view text, std::string_view prefix)
std::string_view number_str = text_view.substr(pos, end_pos - pos);
uint32 number = 0;
auto [ptr, ec] = std::from_chars(number_str.data(), number_str.data() + number_str.size(), number);
if (ec == std::errc())

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CHATHELPER_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "FleeManager.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_FLEEMANAGER_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "GuildTaskMgr.h"
@@ -590,7 +590,7 @@ uint32 GuildTaskMgr::GetTaskValue(uint32 owner, uint32 guildId, std::string cons
{
return 0;
}
uint32 value = 0;
PlayerbotsDatabasePreparedStatement* stmt =
@@ -1067,7 +1067,7 @@ void GuildTaskMgr::SendCompletionMessage(Player* player, std::string const verb)
void GuildTaskMgr::CheckKillTaskInternal(Player* player, Unit* victim)
{
ObjectGuid::LowType owner = player->GetGUID().GetCounter();
if (victim->GetTypeId() != TYPEID_UNIT)
if (!victim->IsCreature())
return;
Creature* creature = reinterpret_cast<Creature*>(victim);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_GUILDTASKMGR_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Helpers.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_HELPERS_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_LAZYCALCULATEDVALUE_H

View File

@@ -1,11 +1,13 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "LootObjectStack.h"
#include "LootMgr.h"
#include "Object.h"
#include "ObjectAccessor.h"
#include "Playerbots.h"
#include "Unit.h"
@@ -287,7 +289,7 @@ bool LootObject::IsLootPossible(Player* bot)
if (reqItem && !bot->HasItemCount(reqItem, 1))
return false;
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE -2.0f)
if (abs(worldObj->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE - 2.0f)
return false;
Creature* creature = botAI->GetCreature(guid);
@@ -297,9 +299,10 @@ bool LootObject::IsLootPossible(Player* bot)
return false;
}
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event)
// Prevent bot from running to chests that are unlootable (e.g. Gunship Armory before completing the event) or on
// respawn time
GameObject* go = botAI->GetGameObject(guid);
if (go && go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE))
if (go && (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE) || !go->isSpawned()))
return false;
if (skillId == SKILL_NONE)
@@ -317,29 +320,17 @@ bool LootObject::IsLootPossible(Player* bot)
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
if (reqSkillValue > skillValue)
return false;
if (skillId == SKILL_MINING &&
!bot->HasItemCount(756, 1) &&
!bot->HasItemCount(778, 1) &&
!bot->HasItemCount(1819, 1) &&
!bot->HasItemCount(1893, 1) &&
!bot->HasItemCount(1959, 1) &&
!bot->HasItemCount(2901, 1) &&
!bot->HasItemCount(9465, 1) &&
!bot->HasItemCount(20723, 1) &&
!bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40892, 1) &&
!bot->HasItemCount(40893, 1))
if (skillId == SKILL_MINING && !bot->HasItemCount(756, 1) && !bot->HasItemCount(778, 1) &&
!bot->HasItemCount(1819, 1) && !bot->HasItemCount(1893, 1) && !bot->HasItemCount(1959, 1) &&
!bot->HasItemCount(2901, 1) && !bot->HasItemCount(9465, 1) && !bot->HasItemCount(20723, 1) &&
!bot->HasItemCount(40772, 1) && !bot->HasItemCount(40892, 1) && !bot->HasItemCount(40893, 1))
{
return false; // Bot is missing a mining pick
}
if (skillId == SKILL_SKINNING &&
!bot->HasItemCount(7005, 1) &&
!bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40893, 1) &&
!bot->HasItemCount(12709, 1) &&
!bot->HasItemCount(19901, 1))
if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1) && !bot->HasItemCount(40772, 1) &&
!bot->HasItemCount(40893, 1) && !bot->HasItemCount(12709, 1) && !bot->HasItemCount(19901, 1))
{
return false; // Bot is missing a skinning knife
}
@@ -376,42 +367,45 @@ void LootObjectStack::Clear() { availableLoot.clear(); }
bool LootObjectStack::CanLoot(float maxDistance)
{
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return !ordered.empty();
LootObject nearest = GetNearest(maxDistance);
return !nearest.IsEmpty();
}
LootObject LootObjectStack::GetLoot(float maxDistance)
{
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return ordered.empty() ? LootObject() : *ordered.begin();
LootObject nearest = GetNearest(maxDistance);
return nearest.IsEmpty() ? LootObject() : nearest;
}
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
LootObject LootObjectStack::GetNearest(float maxDistance)
{
availableLoot.shrink(time(nullptr) - 30);
std::map<float, LootObject> sortedMap;
LootObject nearest;
float nearestDistance = std::numeric_limits<float>::max();
LootTargetList safeCopy(availableLoot);
for (LootTargetList::iterator i = safeCopy.begin(); i != safeCopy.end(); i++)
{
ObjectGuid guid = i->guid;
LootObject lootObject(bot, guid);
if (!lootObject.IsLootPossible(bot)) // Ensure loot object is valid
continue;
WorldObject* worldObj = lootObject.GetWorldObject(bot);
if (!worldObj) // Prevent null pointer dereference
{
WorldObject* worldObj = ObjectAccessor::GetWorldObject(*bot, guid);
if (!worldObj)
continue;
}
float distance = bot->GetDistance(worldObj);
if (!maxDistance || distance <= maxDistance)
sortedMap[distance] = lootObject;
if (distance >= nearestDistance || (maxDistance && distance > maxDistance))
continue;
LootObject lootObject(bot, guid);
if (!lootObject.IsLootPossible(bot))
continue;
nearestDistance = distance;
nearest = lootObject;
}
std::vector<LootObject> result;
for (auto& [_, lootObject] : sortedMap)
result.push_back(lootObject);
return result;
return nearest;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_LOOTOBJECTSTACK_H
@@ -78,7 +78,7 @@ public:
LootObject GetLoot(float maxDistance = 0);
private:
std::vector<LootObject> OrderByDistance(float maxDistance = 0);
LootObject GetNearest(float maxDistance = 0);
Player* bot;
LootTargetList availableLoot;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PerformanceMonitor.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PERFORMANCEMONITOR_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlaceholderHelper.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLACEHOLDERHELPER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERbotAI_H
@@ -46,27 +46,27 @@ struct GameObjectData;
enum StrategyType : uint32;
enum HealingItemDisplayId
enum HealingItemId
{
HEALTHSTONE_DISPLAYID = 8026,
MAJOR_HEALING_POTION = 24152,
WHIPPER_ROOT_TUBER = 21974,
NIGHT_DRAGON_BREATH = 21975,
LIMITED_INVULNERABILITY_POTION = 24213,
GREATER_DREAMLESS_SLEEP_POTION = 17403,
SUPERIOR_HEALING_POTION = 15714,
CRYSTAL_RESTORE = 2516,
DREAMLESS_SLEEP_POTION = 17403,
GREATER_HEALING_POTION = 15713,
HEALING_POTION = 15712,
LESSER_HEALING_POTION = 15711,
DISCOLORED_HEALING_POTION = 15736,
MINOR_HEALING_POTION = 15710,
VOLATILE_HEALING_POTION = 24212,
SUPER_HEALING_POTION = 37807,
CRYSTAL_HEALING_POTION = 47132,
FEL_REGENERATION_POTION = 37864,
MAJOR_DREAMLESS_SLEEP_POTION = 37845
HEALTHSTONE = 5512,
MAJOR_HEALING_POTION = 13446,
WHIPPER_ROOT_TUBER = 11951,
NIGHT_DRAGON_BREATH = 11952,
LIMITED_INVULNERABILITY_POTION = 3387,
GREATER_DREAMLESS_SLEEP_POTION = 22886,
SUPERIOR_HEALING_POTION = 3928,
CRYSTAL_RESTORE = 11564,
DREAMLESS_SLEEP_POTION = 12190,
GREATER_HEALING_POTION = 1710,
HEALING_POTION = 929,
LESSER_HEALING_POTION = 858,
DISCOLORED_HEALING_POTION = 3391,
MINOR_HEALING_POTION = 118,
VOLATILE_HEALING_POTION = 28100,
SUPER_HEALING_POTION = 22829,
CRYSTAL_HEALING_POTION = 13462,
FEL_REGENERATION_POTION = 28101,
MAJOR_DREAMLESS_SLEEP_POTION = 20002
};
enum BotState
@@ -152,6 +152,7 @@ static std::map<ChatChannelSource, std::string> ChatChannelSourceStr = {
{SRC_RAID, "SRC_RAID"},
{SRC_UNDEFINED, "SRC_UNDEFINED"}};
enum ChatChannelId
{
GENERAL = 1,
@@ -162,60 +163,66 @@ enum ChatChannelId
GUILD_RECRUITMENT = 25,
};
enum RoguePoisonDisplayId
enum RoguePoisonId
{
DEADLY_POISON_DISPLAYID = 13707,
INSTANT_POISON_DISPLAYID = 13710,
WOUND_POISON_DISPLAYID = 37278
INSTANT_POISON = 6947,
INSTANT_POISON_II = 6949,
INSTANT_POISON_III = 6950,
INSTANT_POISON_IV = 8926,
INSTANT_POISON_V = 8927,
INSTANT_POISON_VI = 8928,
INSTANT_POISON_VII = 21927,
INSTANT_POISON_VIII = 43230,
INSTANT_POISON_IX = 43231,
DEADLY_POISON = 2892,
DEADLY_POISON_II = 2893,
DEADLY_POISON_III = 8984,
DEADLY_POISON_IV = 8985,
DEADLY_POISON_V = 20844,
DEADLY_POISON_VI = 22053,
DEADLY_POISON_VII = 22054,
DEADLY_POISON_VIII = 43232,
DEADLY_POISON_IX = 43233
};
enum SharpeningStoneDisplayId
enum SharpeningStoneId
{
ROUGH_SHARPENING_DISPLAYID = 24673,
COARSE_SHARPENING_DISPLAYID = 24674,
HEAVY_SHARPENING_DISPLAYID = 24675,
SOLID_SHARPENING_DISPLAYID = 24676,
DENSE_SHARPENING_DISPLAYID = 24677,
CONSECRATED_SHARPENING_DISPLAYID =
24674, // will not be used because bot can not know if it will face undead targets
ELEMENTAL_SHARPENING_DISPLAYID = 21072,
FEL_SHARPENING_DISPLAYID = 39192,
ADAMANTITE_SHARPENING_DISPLAYID = 39193
ROUGH_SHARPENING_STONE = 2862,
COARSE_SHARPENING_STONE = 2863,
HEAVY_SHARPENING_STONE = 2871,
SOLID_SHARPENING_STONE = 7964,
DENSE_SHARPENING_STONE = 12404,
ELEMENTAL_SHARPENING_STONE = 18262,
FEL_SHARPENING_STONE = 23528,
ADAMANTITE_SHARPENING_STONE = 23529
};
enum WeightStoneDisplayId
enum WeightstoneId
{
ROUGH_WEIGHTSTONE_DISPLAYID = 24683,
COARSE_WEIGHTSTONE_DISPLAYID = 24684,
HEAVY_WEIGHTSTONE_DISPLAYID = 24685,
SOLID_WEIGHTSTONE_DISPLAYID = 24686,
DENSE_WEIGHTSTONE_DISPLAYID = 24687,
FEL_WEIGHTSTONE_DISPLAYID = 39548,
ADAMANTITE_WEIGHTSTONE_DISPLAYID = 39549
ROUGH_WEIGHTSTONE = 3239,
COARSE_WEIGHTSTONE = 3240,
HEAVY_WEIGHTSTONE = 3241,
SOLID_WEIGHTSTONE = 7965,
DENSE_WEIGHTSTONE = 12643,
FEL_WEIGHTSTONE = 28420,
ADAMANTITE_WEIGHTSTONE = 28421
};
enum WizardOilDisplayId
enum WizardOilId
{
MINOR_WIZARD_OIL = 9731,
LESSER_WIZARD_OIL = 47903,
BRILLIANT_WIZARD_OIL = 47901,
WIZARD_OIL = 47905,
SUPERIOR_WIZARD_OIL = 47904,
/// Blessed Wizard Oil = 26865 //scourge inv
MINOR_WIZARD_OIL = 20744,
LESSER_WIZARD_OIL = 20746,
WIZARD_OIL = 20750,
BRILLIANT_WIZARD_OIL = 20749,
SUPERIOR_WIZARD_OIL = 22522
};
enum ManaOilDisplayId
enum ManaOilId
{
MINOR_MANA_OIL = 34492,
LESSER_MANA_OIL = 47902,
BRILLIANT_MANA_OIL = 41488,
SUPERIOR_MANA_OIL = 36862
};
enum ShieldWardDisplayId
{
LESSER_WARD_OFSHIELDING = 38759,
GREATER_WARD_OFSHIELDING = 38760
MINOR_MANA_OIL = 20745,
LESSER_MANA_OIL = 20747,
BRILLIANT_MANA_OIL = 20748,
SUPERIOR_MANA_OIL = 22521
};
enum class BotTypeNumber : uint8
@@ -269,7 +276,7 @@ enum BotRoles : uint8
enum HUNTER_TABS
{
HUNTER_TAB_BEASTMASTER,
HUNTER_TAB_BEASTMASTERY,
HUNTER_TAB_MARKSMANSHIP,
HUNTER_TAB_SURVIVAL,
};
@@ -278,12 +285,12 @@ enum ROGUE_TABS
{
ROGUE_TAB_ASSASSINATION,
ROGUE_TAB_COMBAT,
ROGUE_TAB_SUBTLETY
ROGUE_TAB_SUBTLETY,
};
enum PRIEST_TABS
{
PRIEST_TAB_DISIPLINE,
PRIEST_TAB_DISCIPLINE,
PRIEST_TAB_HOLY,
PRIEST_TAB_SHADOW,
};
@@ -325,7 +332,7 @@ enum PALADIN_TABS
enum WARLOCK_TABS
{
WARLOCK_TAB_AFFLICATION,
WARLOCK_TAB_AFFLICTION,
WARLOCK_TAB_DEMONOLOGY,
WARLOCK_TAB_DESTRUCTION,
};
@@ -401,12 +408,14 @@ public:
void ClearStrategies(BotState type);
std::vector<std::string> GetStrategies(BotState type);
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
void EvaluateHealerDpsStrategy();
bool ContainsStrategy(StrategyType type);
bool HasStrategy(std::string const name, BotState type);
BotState GetState() { return currentState; };
void ResetStrategies(bool load = false);
void ReInitCurrentEngine();
void Reset(bool full = false);
void LeaveOrDisbandGroup();
static bool IsTank(Player* player, bool bySpec = false);
static bool IsHeal(Player* player, bool bySpec = false);
static bool IsDps(Player* player, bool bySpec = false);
@@ -415,13 +424,15 @@ public:
static bool IsCaster(Player* player, bool bySpec = false);
static bool IsRangedDps(Player* player, bool bySpec = false);
static bool IsCombo(Player* player);
static bool IsBotMainTank(Player* player);
static bool IsMainTank(Player* player);
static uint32 GetGroupTankNum(Player* player);
bool IsAssistTank(Player* player);
bool IsAssistTankOfIndex(Player* player, int index);
bool IsHealAssistantOfIndex(Player* player, int index);
bool IsRangedDpsAssistantOfIndex(Player* player, int index);
static bool IsAssistTank(Player* player);
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
static bool IsHealAssistantOfIndex(Player* player, int index);
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
bool HasAggro(Unit* unit);
static int32 GetAssistTankIndex(Player* player);
int32 GetGroupSlotIndex(Player* player);
int32 GetRangedIndex(Player* player);
int32 GetClassIndex(Player* player, uint8 cls);
@@ -471,7 +482,7 @@ public:
Item* FindBandage() const;
Item* FindOpenableItem() const;
Item* FindLockedItem() const;
Item* FindConsumable(uint32 displayId) const;
Item* FindConsumable(uint32 itemId) const;
Item* FindStoneFor(Item* weapon) const;
Item* FindOilFor(Item* weapon) const;
void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID);
@@ -518,6 +529,7 @@ public:
Player* GetBot() { return bot; }
Player* GetMaster() { return master; }
Player* FindNewMaster();
// Checks if the bot is really a player. Players always have themselves as master.
bool IsRealPlayer() { return master ? (master == bot) : false; }
@@ -528,7 +540,7 @@ public:
// Get the group leader or the master of the bot.
// Checks if the bot is summoned as alt of a player
bool IsAlt();
Player* GetGroupMaster();
Player* GetGroupLeader();
// Returns a semi-random (cycling) number that is fixed for each bot.
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
GrouperType GetGrouperType();
@@ -545,6 +557,8 @@ public:
bool IsSafe(WorldObject* obj);
ChatChannelSource GetChatChannelSource(Player* bot, uint32 type, std::string channelName);
bool CheckLocationDistanceByLevel(Player* player, const WorldLocation &loc, bool fromStartUp = false);
bool HasCheat(BotCheatMask mask)
{
return ((uint32)mask & (uint32)cheatMask) != 0 ||
@@ -589,6 +603,7 @@ public:
NewRpgInfo rpgInfo;
NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest;
time_t bgReleaseAttemptTime = 0;
// Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
@@ -597,7 +612,7 @@ private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
void UpdateAIGroupMembership();
void UpdateAIGroupMaster();
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
void HandleCommands();
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERbotAIAWARE_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotAIBase.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTAIBASE_H

View File

@@ -1,13 +1,12 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotAIConfig.h"
#include <iostream>
#include "Config.h"
#include "NewRpgInfo.h"
#include "PlayerbotDungeonSuggestionMgr.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
@@ -58,52 +57,54 @@ void LoadListString(std::string const value, T& list)
bool PlayerbotAIConfig::Initialize()
{
LOG_INFO("server.loading", "Initializing AI Playerbots by ike3, based on the original Playerbots by blueboy");
LOG_INFO("server.loading", "Initializing mod-playerbots, based on AI Playerbots by ike3 and the original Playerbots by blueboy");
enabled = sConfigMgr->GetOption<bool>("AiPlayerbot.Enabled", true);
if (!enabled)
{
LOG_INFO("server.loading", "AI Playerbots is Disabled in aiplayerbot.conf");
LOG_INFO("server.loading", "Playerbots Module is disabled in playerbots.conf");
return false;
}
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 1500);
globalCoolDown = sConfigMgr->GetOption<int32>("AiPlayerbot.GlobalCooldown", 500);
maxWaitForMove = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxWaitForMove", 5000);
disableMoveSplinePath = sConfigMgr->GetOption<int32>("AiPlayerbot.DisableMoveSplinePath", 0);
maxMovementSearchTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxMovementSearchTime", 3);
expireActionTime = sConfigMgr->GetOption<int32>("AiPlayerbot.ExpireActionTime", 5000);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 7000);
dispelAuraDuration = sConfigMgr->GetOption<int32>("AiPlayerbot.DispelAuraDuration", 700);
reactDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReactDelay", 100);
dynamicReactDelay = sConfigMgr->GetOption<bool>("AiPlayerbot.DynamicReactDelay", true);
passiveDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.PassiveDelay", 10000);
repeatDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RepeatDelay", 2000);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 5000);
errorDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ErrorDelay", 100);
rpgDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgDelay", 10000);
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 20000);
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 2000);
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
minBotsForGreaterBuff = sConfigMgr->GetOption<int32>("AiPlayerbot.MinBotsForGreaterBuff", 3);
rpWarningCooldown = sConfigMgr->GetOption<int32>("AiPlayerbot.RPWarningCooldown", 30);
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
farDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 75.0f);
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 25.0f);
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 25.0f);
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 25.0f);
sightDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SightDistance", 100.0f);
spellDistance = sConfigMgr->GetOption<float>("AiPlayerbot.SpellDistance", 28.5f);
shootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ShootDistance", 5.0f);
healDistance = sConfigMgr->GetOption<float>("AiPlayerbot.HealDistance", 38.5f);
lootDistance = sConfigMgr->GetOption<float>("AiPlayerbot.LootDistance", 15.0f);
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 7.5f);
fleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FleeDistance", 5.0f);
aggroDistance = sConfigMgr->GetOption<float>("AiPlayerbot.AggroDistance", 22.0f);
tooCloseDistance = sConfigMgr->GetOption<float>("AiPlayerbot.TooCloseDistance", 5.0f);
meleeDistance = sConfigMgr->GetOption<float>("AiPlayerbot.MeleeDistance", 0.75f);
followDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FollowDistance", 1.5f);
whisperDistance = sConfigMgr->GetOption<float>("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.5f);
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 5.0f);
contactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ContactDistance", 0.45f);
aoeRadius = sConfigMgr->GetOption<float>("AiPlayerbot.AoeRadius", 10.0f);
rpgDistance = sConfigMgr->GetOption<float>("AiPlayerbot.RpgDistance", 200.0f);
grindDistance = sConfigMgr->GetOption<float>("AiPlayerbot.GrindDistance", 75.0f);
reactDistance = sConfigMgr->GetOption<float>("AiPlayerbot.ReactDistance", 150.0f);
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 20);
criticalHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.CriticalHealth", 25);
lowHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.LowHealth", 45);
mediumHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumHealth", 65);
almostFullHealth = sConfigMgr->GetOption<int32>("AiPlayerbot.AlmostFullHealth", 85);
@@ -114,11 +115,12 @@ bool PlayerbotAIConfig::Initialize()
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
maxAoeAvoidRadius = sConfigMgr->GetOption<float>("AiPlayerbot.MaxAoeAvoidRadius", 15.0f);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810"),
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AoeAvoidSpellWhitelist", "50759,57491,13810,29946"),
aoeAvoidSpellWhitelist);
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", false);
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.0f);
incrementalGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.IncrementalGearInit", true);
randomGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearQualityLimit", 3);
randomGearScoreLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomGearScoreLimit", 0);
@@ -126,7 +128,7 @@ bool PlayerbotAIConfig::Initialize()
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.1f);
randomBotRpgChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotRpgChance", 0.20f);
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);
iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 10);
allowAccountBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowAccountBots", true);
allowGuildBots = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowGuildBots", true);
@@ -139,30 +141,47 @@ bool PlayerbotAIConfig::Initialize()
randomBotMapsAsString = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotMaps", "0,1,530,571");
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
probTeleToBankers = sConfigMgr->GetOption<float>("AiPlayerbot.ProbTeleToBankers", 0.25f);
enableWeightTeleToCityBankers = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableWeightTeleToCityBankers", false);
weightTeleToStormwind = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToStormwindWeight", 2);
weightTeleToIronforge = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToIronforgeWeight", 1);
weightTeleToDarnassus = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDarnassusWeight", 1);
weightTeleToExodar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToExodarWeight", 1);
weightTeleToOrgrimmar = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToOrgrimmarWeight", 2);
weightTeleToUndercity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToUndercityWeight", 1);
weightTeleToThunderBluff = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToThunderBluffWeight", 1);
weightTeleToSilvermoonCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToSilvermoonCityWeight", 1);
weightTeleToShattrathCity = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToShattrathCityWeight", 1);
weightTeleToDalaran = sConfigMgr->GetOption<int>("AiPlayerbot.TeleToDalaranWeight", 1);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestItems",
"6948,5175,5176,5177,5178,16309,12382,13704,11000"),
"5175,5176,5177,5178,6948,11000,12382,13704,16309"),
randomBotQuestItems);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotSpellIds", "54197"),
randomBotSpellIds);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedZoneIds",
"2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,"
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395"),
"3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"),
pvpProhibitedZoneIds);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds", "976,35"),
pvpProhibitedAreaIds);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"),
pvpProhibitedAreaIds);
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"),
sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotQuestIds", "3802,5505,6502,7761,7848,10277,10285,11492,13188,13189,24499,24511,24710,24712"),
randomBotQuestIds);
LoadSet<std::set<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects", "176213,17155"),
disallowedGameObjects);
LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.DisallowedGameObjects",
"176213,17155,2656,74448,19020,3719,3658,3705,3706,105579,75293,2857,"
"179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,"
"165739,165738,175245,175970,176325,176327,123329,2560"),
disallowedGameObjects);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 50);
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 50);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
maxRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBots", 500);
randomBotUpdateInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotUpdateInterval", 20);
randomBotCountChangeMinInterval =
sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE);
@@ -180,8 +199,8 @@ bool PlayerbotAIConfig::Initialize()
maxRandomBotReviveTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotReviveTime", 5 * MINUTE);
minRandomBotTeleportInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBotTeleportInterval", 1 * HOUR);
maxRandomBotTeleportInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotTeleportInterval", 5 * HOUR);
permanantlyInWorldTime =
sConfigMgr->GetOption<int32>("AiPlayerbot.PermanantlyInWorldTime", 1 * YEAR);
permanentlyInWorldTime =
sConfigMgr->GetOption<int32>("AiPlayerbot.PermanentlyInWorldTime", 1 * YEAR);
randomBotTeleportDistance = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotTeleportDistance", 100);
randomBotsPerInterval = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotsPerInterval", 60);
minRandomBotsPriceChangeInterval =
@@ -189,10 +208,19 @@ bool PlayerbotAIConfig::Initialize()
maxRandomBotsPriceChangeInterval =
sConfigMgr->GetOption<int32>("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
randomBotJoinLfg = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotJoinLfg", true);
//////////////////////////// ICC
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
restrictHealerDPS = sConfigMgr->GetOption<bool>("AiPlayerbot.HealerDPSMapRestriction", false);
LoadList<std::vector<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.RestrictedHealerDPSMaps",
"33,34,36,43,47,48,70,90,109,129,209,229,230,329,349,389,429,1001,1004,"
"1007,269,540,542,543,545,546,547,552,553,554,555,556,557,558,560,585,574,"
"575,576,578,595,599,600,601,602,604,608,619,632,650,658,668,409,469,509,"
"531,532,534,544,548,550,564,565,580,249,533,603,615,616,624,631,649,724"),
restrictedHealerDPSMaps);
//////////////////////////// ICC
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
//////////////////////////// CHAT
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
@@ -345,7 +373,7 @@ bool PlayerbotAIConfig::Initialize()
{
std::string setting = "AiPlayerbot.ZoneBracket." + std::to_string(zoneId);
std::string value = sConfigMgr->GetOption<std::string>(setting, "");
if (!value.empty())
{
size_t commaPos = value.find(',');
@@ -360,10 +388,10 @@ bool PlayerbotAIConfig::Initialize()
randomChangeMultiplier = sConfigMgr->GetOption<float>("AiPlayerbot.RandomChangeMultiplier", 1.0);
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "-threat");
randomBotCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotCombatStrategies", "");
randomBotNonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotNonCombatStrategies", "");
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "+custom::say");
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
combatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.CombatStrategies", "");
nonCombatStrategies = sConfigMgr->GetOption<std::string>("AiPlayerbot.NonCombatStrategies", "");
applyInstanceStrategies = sConfigMgr->GetOption<bool>("AiPlayerbot.ApplyInstanceStrategies", true);
commandPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.CommandPrefix", "");
@@ -447,11 +475,13 @@ bool PlayerbotAIConfig::Initialize()
}
botCheats.clear();
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "taxi"),
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.BotCheats", "food,taxi,raid"),
botCheats);
botCheatMask = 0;
if (std::find(botCheats.begin(), botCheats.end(), "food") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::food;
if (std::find(botCheats.begin(), botCheats.end(), "taxi") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::taxi;
if (std::find(botCheats.begin(), botCheats.end(), "gold") != botCheats.end())
@@ -462,38 +492,26 @@ bool PlayerbotAIConfig::Initialize()
botCheatMask |= (uint32)BotCheatMask::mana;
if (std::find(botCheats.begin(), botCheats.end(), "power") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::power;
if (std::find(botCheats.begin(), botCheats.end(), "raid") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::raid;
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AllowedLogFiles", ""),
allowedLogFiles);
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.TradeActionExcludedPrefixes", ""),
tradeActionExcludedPrefixes);
worldBuffs.clear();
LOG_INFO("playerbots", "Loading Worldbuff...");
for (uint32 factionId = 0; factionId < 3; factionId++)
{
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
{
for (uint32 specId = 0; specId <= MAX_WORLDBUFF_SPECNO; specId++)
{
for (uint32 minLevel = 0; minLevel <= randomBotMaxLevel; minLevel++)
{
for (uint32 maxLevel = minLevel; maxLevel <= randomBotMaxLevel; maxLevel++)
{
loadWorldBuff(factionId, classId, specId, minLevel, maxLevel);
}
loadWorldBuff(factionId, classId, specId, minLevel, 0);
}
}
}
}
loadWorldBuff();
LOG_INFO("playerbots", "Loading World Buff Feature...");
randomBotAccountPrefix = sConfigMgr->GetOption<std::string>("AiPlayerbot.RandomBotAccountPrefix", "rndbot");
randomBotAccountCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotAccountCount", 0);
deleteRandomBotAccounts = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotAccounts", false);
randomBotGuildCount = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildCount", 20);
randomBotGuildSizeMax = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotGuildSizeMax", 15);
deleteRandomBotGuilds = sConfigMgr->GetOption<bool>("AiPlayerbot.DeleteRandomBotGuilds", false);
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", true);
guildTaskEnabled = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGuildTasks", false);
minGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskChangeTime", 3 * 24 * 3600);
maxGuildTaskChangeTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MaxGuildTaskChangeTime", 4 * 24 * 3600);
minGuildTaskAdvertisementTime = sConfigMgr->GetOption<int32>("AiPlayerbot.MinGuildTaskAdvertisementTime", 300);
@@ -531,6 +549,27 @@ bool PlayerbotAIConfig::Initialize()
addClassCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassCommand", 1);
addClassAccountPoolSize = sConfigMgr->GetOption<int32>("AiPlayerbot.AddClassAccountPoolSize", 50);
maintenanceCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.MaintenanceCommand", 1);
altMaintenanceAttunementQs = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceAttunementQuests", true);
altMaintenanceBags = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceBags", true);
altMaintenanceAmmo = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceAmmo", true);
altMaintenanceFood = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceFood", true);
altMaintenanceReagents = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceReagents", true);
altMaintenanceConsumables = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceConsumables", true);
altMaintenancePotions = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenancePotions", true);
altMaintenanceTalentTree = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceTalentTree", true);
altMaintenancePet = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenancePet", true);
altMaintenancePetTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenancePetTalents", true);
altMaintenanceClassSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceClassSpells", true);
altMaintenanceAvailableSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceAvailableSpells", true);
altMaintenanceSkills = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceSkills", true);
altMaintenanceReputation = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceReputation", true);
altMaintenanceSpecialSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceSpecialSpells", true);
altMaintenanceMounts = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceMounts", true);
altMaintenanceGlyphs = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceGlyphs", true);
altMaintenanceKeyring = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceKeyring", true);
altMaintenanceGemsEnchants = sConfigMgr->GetOption<bool>("AiPlayerbot.AltMaintenanceGemsEnchants", true);
autoGearCommand = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommand", 1);
autoGearCommandAltBots = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearCommandAltBots", 1);
autoGearQualityLimit = sConfigMgr->GetOption<int32>("AiPlayerbot.AutoGearQualityLimit", 3);
@@ -558,12 +597,12 @@ bool PlayerbotAIConfig::Initialize()
minEnchantingBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.MinEnchantingBotLevel", 60);
limitEnchantExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitEnchantExpansion", 1);
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 1);
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 5);
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 1);
enablePeriodicOnlineOffline = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePeriodicOnlineOffline", false);
enableRandomBotTrading = sConfigMgr->GetOption<int32>("AiPlayerbot.EnableRandomBotTrading", 1);
periodicOnlineOfflineRatio = sConfigMgr->GetOption<float>("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0);
gearscorecheck = sConfigMgr->GetOption<bool>("AiPlayerbot.GearScoreCheck", false);
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", true);
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", false);
// SPP automation
freeMethodLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeMethodLoot", false);
@@ -579,14 +618,24 @@ bool PlayerbotAIConfig::Initialize()
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);
defaultPetStance = sConfigMgr->GetOption<int32>("AiPlayerbot.DefaultPetStance", 1);
petChatCommandDebug = sConfigMgr->GetOption<bool>("AiPlayerbot.PetChatCommandDebug", 0);
autoLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnTrainerSpells", true);
autoLearnQuestSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoLearnQuestSpells", false);
autoTeleportForLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoTeleportForLevel", false);
autoDoQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoDoQuests", true);
enableNewRpgStrategy = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableNewRpgStrategy", false);
enableNewRpgStrategy = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableNewRpgStrategy", true);
RpgStatusProbWeight[RPG_WANDER_RANDOM] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.WanderRandom", 15);
RpgStatusProbWeight[RPG_WANDER_NPC] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.WanderNpc", 20);
RpgStatusProbWeight[RPG_GO_GRIND] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.GoGrind", 15);
RpgStatusProbWeight[RPG_GO_CAMP] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.GoCamp", 10);
RpgStatusProbWeight[RPG_DO_QUEST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.DoQuest", 60);
RpgStatusProbWeight[RPG_TRAVEL_FLIGHT] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.TravelFlight", 15);
RpgStatusProbWeight[RPG_REST] = sConfigMgr->GetOption<int32>("AiPlayerbot.RpgStatusProbWeight.Rest", 5);
syncLevelWithPlayers = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncLevelWithPlayers", false);
freeFood = sConfigMgr->GetOption<bool>("AiPlayerbot.FreeFood", true);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", true);
randomBotGroupNearby = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotGroupNearby", false);
// arena
randomBotArenaTeam2v2Count = sConfigMgr->GetOption<int32>("AiPlayerbot.RandomBotArenaTeam2v2Count", 10);
@@ -604,6 +653,9 @@ bool PlayerbotAIConfig::Initialize()
return true;
}
// Assign account types after accounts are created
sRandomPlayerbotMgr->AssignAccountTypes();
if (sPlayerbotAIConfig->enabled)
{
sRandomPlayerbotMgr->Init();
@@ -615,20 +667,18 @@ bool PlayerbotAIConfig::Initialize()
sPlayerbotTextMgr->LoadBotTextChance();
PlayerbotFactory::Init();
if (!sPlayerbotAIConfig->autoDoQuests)
{
LOG_INFO("server.loading", "Loading Quest Detail Data...");
sTravelMgr->LoadQuestTravelTable();
}
AiObjectContext::BuildAllSharedContexts();
if (sPlayerbotAIConfig->randomBotSuggestDungeons)
{
sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions();
}
excludedHunterPetFamilies.clear();
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.ExcludedHunterPetFamilies", ""), excludedHunterPetFamilies);
LOG_INFO("server.loading", "---------------------------------------");
LOG_INFO("server.loading", " AI Playerbots initialized ");
LOG_INFO("server.loading", " mod-playerbots initialized ");
LOG_INFO("server.loading", "---------------------------------------");
return true;
@@ -659,6 +709,12 @@ bool PlayerbotAIConfig::IsInPvpProhibitedArea(uint32 id)
return find(pvpProhibitedAreaIds.begin(), pvpProhibitedAreaIds.end(), id) != pvpProhibitedAreaIds.end();
}
bool PlayerbotAIConfig::IsRestrictedHealerDPSMap(uint32 mapId) const
{
return restrictHealerDPS &&
std::find(restrictedHealerDPSMaps.begin(), restrictedHealerDPSMaps.end(), mapId) != restrictedHealerDPSMaps.end();
}
std::string const PlayerbotAIConfig::GetTimestampStr()
{
time_t t = time(nullptr);
@@ -669,8 +725,8 @@ std::string const PlayerbotAIConfig::GetTimestampStr()
// HH hour (2 digits 00-23)
// MM minutes (2 digits 00-59)
// SS seconds (2 digits 00-59)
char buf[20];
snprintf(buf, 20, "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour,
char buf[32];
snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour,
aTm->tm_min, aTm->tm_sec);
return std::string(buf);
}
@@ -731,88 +787,62 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
fflush(stdout);
}
void PlayerbotAIConfig::loadWorldBuff(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1)
void PlayerbotAIConfig::loadWorldBuff()
{
std::vector<uint32> buffs;
std::string matrix = sConfigMgr->GetOption<std::string>("AiPlayerbot.WorldBuffMatrix", "", true);
if (matrix.empty())
return;
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1 << "." << maxLevel1;
std::istringstream entryStream(matrix);
std::string entry;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
while (std::getline(entryStream, entry, ';'))
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
if (maxLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1;
entry.erase(0, entry.find_first_not_of(" \t\r\n"));
entry.erase(entry.find_last_not_of(" \t\r\n") + 1);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
size_t firstColon = entry.find(':');
size_t secondColon = entry.find(':', firstColon + 1);
for (auto buff : buffs)
if (firstColon == std::string::npos || secondColon == std::string::npos)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
LOG_ERROR("playerbots", "Malformed entry: [{}]", entry);
continue;
}
}
if (maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1 << "." << specId1;
std::string metaPart = entry.substr(firstColon + 1, secondColon - firstColon - 1);
std::string spellPart = entry.substr(secondColon + 1);
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
std::vector<uint32> ids;
std::istringstream metaStream(metaPart);
std::string token;
while (std::getline(metaStream, token, ','))
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
try {
ids.push_back(static_cast<uint32>(std::stoi(token)));
} catch (...) {
LOG_ERROR("playerbots", "Invalid meta token in [{}]", entry);
break;
}
}
}
if (maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
if (ids.size() != 5)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
LOG_ERROR("playerbots", "Entry [{}] has incomplete meta block", entry);
continue;
}
}
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
std::istringstream spellStream(spellPart);
while (std::getline(spellStream, token, ','))
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
}
}
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff";
LoadList<std::vector<uint32>>(sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1};
worldBuffs.push_back(wb);
try {
uint32 spellId = static_cast<uint32>(std::stoi(token));
worldBuff wb = { spellId, ids[0], ids[1], ids[2], ids[3], ids[4] };
worldBuffs.push_back(wb);
} catch (...) {
LOG_ERROR("playerbots", "Invalid spell ID in [{}]", entry);
}
}
}
}

View File

@@ -1,12 +1,13 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERbotAICONFIG_H
#define _PLAYERBOT_PLAYERbotAICONFIG_H
#include <mutex>
#include <unordered_map>
#include "Common.h"
#include "DBCEnums.h"
@@ -21,7 +22,9 @@ enum class BotCheatMask : uint32
health = 4,
mana = 8,
power = 16,
maxMask = 32
raid = 32,
food = 64,
maxMask = 128
};
enum class HealingManaEfficiency : uint8
@@ -34,8 +37,27 @@ enum class HealingManaEfficiency : uint8
SUPERIOR = 32
};
enum NewRpgStatus : int
{
RPG_STATUS_START = 0,
// Going to far away place
RPG_GO_GRIND = 0,
RPG_GO_CAMP = 1,
// Exploring nearby
RPG_WANDER_RANDOM = 2,
RPG_WANDER_NPC = 3,
// Do Quest (based on quest status)
RPG_DO_QUEST = 4,
// Travel
RPG_TRAVEL_FLIGHT = 5,
// Taking a break
RPG_REST = 6,
// Initial status
RPG_IDLE = 7,
RPG_STATUS_END = 8
};
#define MAX_SPECNO 20
#define MAX_WORLDBUFF_SPECNO 3
class PlayerbotAIConfig
{
@@ -80,6 +102,17 @@ public:
bool botAutologin;
std::string randomBotMapsAsString;
float probTeleToBankers;
bool enableWeightTeleToCityBankers;
int weightTeleToStormwind;
int weightTeleToIronforge;
int weightTeleToDarnassus;
int weightTeleToExodar;
int weightTeleToOrgrimmar;
int weightTeleToUndercity;
int weightTeleToThunderBluff;
int weightTeleToSilvermoonCity;
int weightTeleToShattrathCity;
int weightTeleToDalaran;
std::vector<uint32> randomBotMaps;
std::vector<uint32> randomBotQuestItems;
std::vector<uint32> randomBotAccounts;
@@ -87,6 +120,7 @@ public:
std::vector<uint32> randomBotQuestIds;
uint32 randomBotTeleportDistance;
float randomGearLoweringChance;
bool incrementalGearInit;
int32 randomGearQualityLimit;
int32 randomGearScoreLimit;
float randomBotMinLevelChance, randomBotMaxLevelChance;
@@ -98,13 +132,19 @@ public:
uint32 minRandomBotChangeStrategyTime, maxRandomBotChangeStrategyTime;
uint32 minRandomBotReviveTime, maxRandomBotReviveTime;
uint32 minRandomBotTeleportInterval, maxRandomBotTeleportInterval;
uint32 permanantlyInWorldTime;
uint32 permanentlyInWorldTime;
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
bool randomBotJoinLfg;
// Buff system
// Min group size to use Greater buffs (Paladin, Mage, Druid). Default: 3
int32 minBotsForGreaterBuff;
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
int32 rpWarningCooldown;
// chat
bool randomBotTalk;
bool randomBotEmote;
@@ -227,7 +267,7 @@ public:
uint32 randomBotAccountCount;
bool randomBotRandomPassword;
bool deleteRandomBotAccounts;
uint32 randomBotGuildCount;
uint32 randomBotGuildCount, randomBotGuildSizeMax;
bool deleteRandomBotGuilds;
std::vector<uint32> randomBotGuilds;
std::vector<uint32> pvpProhibitedZoneIds;
@@ -255,6 +295,7 @@ public:
uint32 iterationsPerTick;
std::mutex m_logMtx;
std::vector<std::string> tradeActionExcludedPrefixes;
std::vector<std::string> allowedLogFiles;
std::unordered_map<std::string, std::pair<FILE*, bool>> logFiles;
@@ -264,11 +305,11 @@ public:
struct worldBuff
{
uint32 spellId;
uint32 factionId = 0;
uint32 classId = 0;
uint32 specId = 0;
uint32 minLevel = 0;
uint32 maxLevel = 0;
uint32 factionId;
uint32 classId;
uint32 specId;
uint32 minLevel;
uint32 maxLevel;
};
std::vector<worldBuff> worldBuffs;
@@ -310,11 +351,13 @@ public:
bool autoPickTalents;
bool autoUpgradeEquip;
int32 hunterWolfPet;
int32 defaultPetStance;
int32 petChatCommandDebug;
bool autoLearnTrainerSpells;
bool autoDoQuests;
bool enableNewRpgStrategy;
std::unordered_map<NewRpgStatus, uint32> RpgStatusProbWeight;
bool syncLevelWithPlayers;
bool freeFood;
bool autoLearnQuestSpells;
bool autoTeleportForLevel;
bool randomBotGroupNearby;
@@ -348,6 +391,25 @@ public:
int32 addClassCommand;
int32 addClassAccountPoolSize;
int32 maintenanceCommand;
bool altMaintenanceAttunementQs,
altMaintenanceBags,
altMaintenanceAmmo,
altMaintenanceFood,
altMaintenanceReagents,
altMaintenanceConsumables,
altMaintenancePotions,
altMaintenanceTalentTree,
altMaintenancePet,
altMaintenancePetTalents,
altMaintenanceClassSpells,
altMaintenanceAvailableSpells,
altMaintenanceSkills,
altMaintenanceReputation,
altMaintenanceSpecialSpells,
altMaintenanceMounts,
altMaintenanceGlyphs,
altMaintenanceKeyring,
altMaintenanceGemsEnchants;
int32 autoGearCommand, autoGearCommandAltBots, autoGearQualityLimit, autoGearScoreLimit;
uint32 useGroundMountAtMinLevel;
@@ -374,9 +436,16 @@ public:
}
void log(std::string const fileName, const char* str, ...);
void loadWorldBuff(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel);
void loadWorldBuff();
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
bool restrictHealerDPS = false;
std::vector<uint32> restrictedHealerDPSMaps;
bool IsRestrictedHealerDPSMap(uint32 mapId) const;
std::vector<uint32> excludedHunterPetFamilies;
};
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotCommandServer.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTCOMMANDSERVER_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotDbStore.h"
@@ -82,7 +82,7 @@ void PlayerbotDbStore::Reset(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_CUSTOM_STRATEGY);
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_DB_STORE);
stmt->SetData(0, guid);
PlayerbotsDatabase.Execute(stmt);
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTDBSTORE_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotDungeonSuggestionMgr.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTDUNGEONSUGGESTIONMGR_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotMgr.h"
@@ -27,15 +27,19 @@
#include "PlayerbotAIConfig.h"
#include "PlayerbotDbStore.h"
#include "PlayerbotFactory.h"
#include "PlayerbotOperations.h"
#include "PlayerbotSecurity.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "SharedDefines.h"
#include "WorldSession.h"
// #include "ChannelMgr.h" Double include
#include "ChannelMgr.h"
#include "BroadcastHelper.h"
#include "PlayerbotDbStore.h"
#include "WorldSessionMgr.h"
#include "DatabaseEnv.h" // Added for gender choice
#include <algorithm> // Added for gender choice
class BotInitGuard
{
@@ -83,7 +87,6 @@ public:
void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId)
{
// bot is loading
if (botLoading.find(playerGuid) != botLoading.end())
return;
@@ -166,7 +169,7 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
uint32 botAccountId = holder.GetAccountId();
// At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this
// allows channels to work as intended)
WorldSession* botSession = new WorldSession(botAccountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), sWorld->GetDefaultDbcLocale(), 0, false, false, 0, true);
botSession->HandlePlayerLoginFromDB(holder); // will delete lqh
@@ -193,7 +196,9 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con
}
sRandomPlayerbotMgr->OnPlayerLogin(bot);
OnBotLogin(bot);
auto op = std::make_unique<OnBotLoginOperation>(bot->GetGUID(), this);
sPlayerbotWorldProcessor->QueueOperation(std::move(op));
botLoading.erase(holder.GetGuid());
}
@@ -225,6 +230,12 @@ void PlayerbotHolder::HandleBotPackets(WorldSession* session)
{
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
if (!opHandle)
{
LOG_ERROR("playerbots", "Unhandled opcode {} queued for bot session {}. Packet dropped.", static_cast<uint32>(opcode), session->GetAccountId());
delete packet;
continue;
}
opHandle->Call(session, *packet);
delete packet;
}
@@ -308,11 +319,9 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid)
if (!botAI)
return;
Group* group = bot->GetGroup();
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
{
sPlayerbotDbStore->Save(botAI);
}
// Queue group cleanup operation for world thread
auto cleanupOp = std::make_unique<BotLogoutGroupCleanupOperation>(guid);
sPlayerbotWorldProcessor->QueueOperation(std::move(cleanupOp));
LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str());
bot->SaveToDB(false, false);
@@ -473,7 +482,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
Player* master = botAI->GetMaster();
if (master)
if (master)
{
ObjectGuid masterGuid = master->GetGUID();
if (master->GetGroup() && !master->GetGroup()->IsLeader(masterGuid))
@@ -497,7 +506,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
}
// Don't disband alt groups when master goes away
// Don't disband alt groups when master goes away
// Controlled by config
if (sPlayerbotAIConfig->KeepAltsInGroup())
{
@@ -512,7 +521,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (!groupValid)
{
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
}
}
@@ -541,6 +550,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
botAI->TellMaster("Hello!", PLAYERBOT_SECURITY_TALK);
// Queue group operations for world thread
if (master && master->GetGroup() && !group)
{
Group* mgroup = master->GetGroup();
@@ -548,24 +558,29 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
{
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
{
mgroup->ConvertToRaid();
// Queue ConvertToRaid operation
auto convertOp = std::make_unique<GroupConvertToRaidOperation>(master->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp));
}
if (mgroup->isRaidGroup())
{
mgroup->AddMember(bot);
// Queue AddMember operation
auto addOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(addOp));
}
}
else
{
mgroup->AddMember(bot);
// Queue AddMember operation
auto addOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(addOp));
}
}
else if (master && !group)
{
Group* newGroup = new Group();
newGroup->Create(master);
sGroupMgr->AddGroup(newGroup);
newGroup->AddMember(bot);
// Queue group creation and AddMember operation
auto inviteOp = std::make_unique<GroupInviteOperation>(master->GetGUID(), bot->GetGUID());
sPlayerbotWorldProcessor->QueueOperation(std::move(inviteOp));
}
// if (master)
// {
@@ -584,8 +599,8 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
}
bot->SaveToDB(false, false);
bool addClassBot = sRandomPlayerbotMgr->IsAddclassBot(bot->GetGUID().GetCounter());
if (addClassBot && master && isRandomAccount && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
bool addClassBot = sRandomPlayerbotMgr->IsAccountType(accountId, 2);
if (addClassBot && master && abs((int)master->GetLevel() - (int)bot->GetLevel()) > 3)
{
// PlayerbotFactory factory(bot, master->GetLevel());
// factory.Randomize(false);
@@ -837,6 +852,18 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
return "unknown command";
}
// Added for gender choice : Returns the gender of an offline character: 0 = male, 1 = female.
static uint8 GetOfflinePlayerGender(ObjectGuid guid)
{
QueryResult result = CharacterDatabase.Query(
"SELECT gender FROM characters WHERE guid = {}", guid.GetCounter());
if (result)
return (*result)[0].Get<uint8>(); // 0 = male, 1 = female
return GENDER_MALE; // fallback value
}
bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* args)
{
if (!sPlayerbotAIConfig->enabled)
@@ -879,15 +906,17 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
if (!*args)
{
messages.push_back("usage: list/reload/tweak/self or add/addaccount/init/remove PLAYERNAME\n");
messages.push_back("usage: addclass CLASSNAME");
messages.push_back("usage: addclass CLASSNAME [male|female|0|1]");
return messages;
}
char* cmd = strtok((char*)args, " ");
char* charname = strtok(nullptr, " ");
char* genderArg = strtok(nullptr, " "); // Added for gender choice [male|female|0|1] optionnel
if (!cmd)
{
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME");
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME [male|female]");
return messages;
}
@@ -1110,6 +1139,24 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
messages.push_back("Error: Invalid Class. Try again.");
return messages;
}
// Added for gender choice : Parsing gender
int8 gender = -1; // -1 = gender will be random
if (genderArg)
{
std::string g = genderArg;
std::transform(g.begin(), g.end(), g.begin(), ::tolower);
if (g == "male" || g == "0")
gender = GENDER_MALE; // 0
else if (g == "female" || g == "1")
gender = GENDER_FEMALE; // 1
else
{
messages.push_back("Unknown gender : " + g + " (male/female/0/1)");
return messages;
}
} //end
if (claz == 6 && master->GetLevel() < sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL))
{
messages.push_back("Your level is too low to summon Deathknight");
@@ -1119,6 +1166,9 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
const std::unordered_set<ObjectGuid> &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)];
for (const ObjectGuid &guid: guidCache)
{
// If the user requested a specific gender, skip any character that doesn't match.
if (gender != -1 && GetOfflinePlayerGender(guid) != gender)
continue;
if (botLoading.find(guid) != botLoading.end())
continue;
if (ObjectAccessor::FindConnectedPlayer(guid))
@@ -1559,8 +1609,26 @@ void PlayerbotMgr::OnBotLoginInternal(Player* const bot)
void PlayerbotMgr::OnPlayerLogin(Player* player)
{
if (!player)
return;
WorldSession* session = player->GetSession();
if (!session)
{
LOG_WARN("playerbots", "Unable to register locale priority for player {} because the session is missing", player->GetName());
return;
}
// DB locale (source of bot text translation)
LocaleConstant const databaseLocale = session->GetSessionDbLocaleIndex();
// For bot texts (DB-driven), prefer the database locale with a safe fallback.
LocaleConstant usedLocale = databaseLocale;
if (usedLocale >= MAX_LOCALES)
usedLocale = LOCALE_enUS; // fallback
// set locale priority for bot texts
sPlayerbotTextMgr->AddLocalePriority(player->GetSession()->GetSessionDbcLocale());
sPlayerbotTextMgr->AddLocalePriority(usedLocale);
if (sPlayerbotAIConfig->selfBotLevel > 2)
HandlePlayerbotCommand("self", player);
@@ -1568,7 +1636,7 @@ void PlayerbotMgr::OnPlayerLogin(Player* player)
if (!sPlayerbotAIConfig->botAutologin)
return;
uint32 accountId = player->GetSession()->GetAccountId();
uint32 accountId = session->GetAccountId();
QueryResult results = CharacterDatabase.Query("SELECT name FROM characters WHERE account = {}", accountId);
if (results)
{
@@ -1693,7 +1761,8 @@ PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player)
{
return nullptr;
}
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) {
// if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld())
// {
// return nullptr;
// }
auto itr = _playerbotsAIMap.find(player->GetGUID());

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTMGR_H

93
src/PlayerbotOperation.h Normal file
View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_OPERATION_H
#define _PLAYERBOT_OPERATION_H
#include "Common.h"
#include "ObjectGuid.h"
#include <memory>
/**
* @brief Base class for thread-unsafe operations that must be executed in the world thread
*
* PlayerbotOperation represents an operation that needs to be deferred from a map thread
* to the world thread for safe execution. Examples include group modifications, LFG operations,
* guild operations, etc.
*
* Thread Safety:
* - The constructor and data members must be thread-safe (use copies, not pointers)
* - Execute() is called in the world thread and can safely perform thread-unsafe operations
* - Subclasses must not store raw pointers to (core/world thread) game object (use ObjectGuid instead)
*/
class PlayerbotOperation
{
public:
virtual ~PlayerbotOperation() = default;
/**
* @brief Execute this operation in the world thread
*
* This method is called by PlayerbotWorldThreadProcessor::Update() which runs in the world thread.
* It's safe to perform any thread-unsafe operation here (Group, LFG, Guild, etc.)
*
* @return true if operation succeeded, false if it failed
*/
virtual bool Execute() = 0;
/**
* @brief Get the bot GUID this operation is for (optional)
*
* Used for logging and debugging purposes.
*
* @return ObjectGuid of the bot, or ObjectGuid::Empty if not applicable
*/
virtual ObjectGuid GetBotGuid() const { return ObjectGuid::Empty; }
/**
* @brief Get the operation priority (higher = more urgent)
*
* Priority levels:
* - 100: Critical (crash prevention, cleanup operations)
* - 50: High (player-facing operations like group invites)
* - 10: Normal (background operations)
* - 0: Low (statistics, logging)
*
* @return Priority value (0-100)
*/
virtual uint32 GetPriority() const { return 10; }
/**
* @brief Get a human-readable name for this operation
*
* Used for logging and debugging.
*
* @return Operation name
*/
virtual std::string GetName() const { return "Unknown Operation"; }
/**
* @brief Check if this operation is still valid
*
* Called before Execute() to check if the operation should still be executed.
* For example, if a bot logged out, group invite operations for that bot can be skipped.
*
* @return true if operation should be executed, false to skip
*/
virtual bool IsValid() const { return true; }
};
/**
* @brief Comparison operator for priority queue (higher priority first)
*/
struct PlayerbotOperationComparator
{
bool operator()(const std::unique_ptr<PlayerbotOperation>& a, const std::unique_ptr<PlayerbotOperation>& b) const
{
return a->GetPriority() < b->GetPriority(); // Lower priority goes to back of queue
}
};
#endif

500
src/PlayerbotOperations.h Normal file
View File

@@ -0,0 +1,500 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_OPERATIONS_H
#define _PLAYERBOT_OPERATIONS_H
#include "Group.h"
#include "GroupMgr.h"
#include "GuildMgr.h"
#include "ObjectAccessor.h"
#include "PlayerbotOperation.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "PlayerbotMgr.h"
#include "PlayerbotDbStore.h"
#include "RandomPlayerbotMgr.h"
// Group invite operation
class GroupInviteOperation : public PlayerbotOperation
{
public:
GroupInviteOperation(ObjectGuid botGuid, ObjectGuid targetGuid)
: m_botGuid(botGuid), m_targetGuid(targetGuid)
{
}
bool Execute() override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
Player* target = ObjectAccessor::FindPlayer(m_targetGuid);
if (!bot || !target)
{
LOG_DEBUG("playerbots", "GroupInviteOperation: Bot or target not found");
return false;
}
// Check if target is already in a group
if (target->GetGroup())
{
LOG_DEBUG("playerbots", "GroupInviteOperation: Target {} is already in a group", target->GetName());
return false;
}
Group* group = bot->GetGroup();
// Create group if bot doesn't have one
if (!group)
{
group = new Group;
if (!group->Create(bot))
{
delete group;
LOG_ERROR("playerbots", "GroupInviteOperation: Failed to create group for bot {}", bot->GetName());
return false;
}
sGroupMgr->AddGroup(group);
LOG_DEBUG("playerbots", "GroupInviteOperation: Created new group for bot {}", bot->GetName());
}
// Convert to raid if needed (more than 5 members)
if (!group->isRaidGroup() && group->GetMembersCount() >= 5)
{
group->ConvertToRaid();
LOG_DEBUG("playerbots", "GroupInviteOperation: Converted group to raid");
}
// Add member to group
if (group->AddMember(target))
{
LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName());
return true;
}
else
{
LOG_ERROR("playerbots", "GroupInviteOperation: Failed to add {} to group", target->GetName());
return false;
}
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 50; } // High priority (player-facing)
std::string GetName() const override { return "GroupInvite"; }
bool IsValid() const override
{
// Check if bot still exists and is online
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
Player* target = ObjectAccessor::FindPlayer(m_targetGuid);
return bot && target;
}
private:
ObjectGuid m_botGuid;
ObjectGuid m_targetGuid;
};
// Remove member from group
class GroupRemoveMemberOperation : public PlayerbotOperation
{
public:
GroupRemoveMemberOperation(ObjectGuid botGuid, ObjectGuid targetGuid)
: m_botGuid(botGuid), m_targetGuid(targetGuid)
{
}
bool Execute() override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
Player* target = ObjectAccessor::FindPlayer(m_targetGuid);
if (!bot || !target)
return false;
Group* group = bot->GetGroup();
if (!group)
{
LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Bot is not in a group");
return false;
}
if (!group->IsMember(target->GetGUID()))
{
LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Target is not in bot's group");
return false;
}
group->RemoveMember(target->GetGUID());
LOG_DEBUG("playerbots", "GroupRemoveMemberOperation: Removed {} from group", target->GetName());
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 50; }
std::string GetName() const override { return "GroupRemoveMember"; }
bool IsValid() const override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
return bot != nullptr;
}
private:
ObjectGuid m_botGuid;
ObjectGuid m_targetGuid;
};
// Convert group to raid
class GroupConvertToRaidOperation : public PlayerbotOperation
{
public:
GroupConvertToRaidOperation(ObjectGuid botGuid) : m_botGuid(botGuid) {}
bool Execute() override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
if (!bot)
return false;
Group* group = bot->GetGroup();
if (!group)
{
LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Bot is not in a group");
return false;
}
if (group->isRaidGroup())
{
LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Group is already a raid");
return true; // Success - already in desired state
}
group->ConvertToRaid();
LOG_DEBUG("playerbots", "GroupConvertToRaidOperation: Converted group to raid");
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 50; }
std::string GetName() const override { return "GroupConvertToRaid"; }
bool IsValid() const override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
return bot != nullptr;
}
private:
ObjectGuid m_botGuid;
};
// Set group leader
class GroupSetLeaderOperation : public PlayerbotOperation
{
public:
GroupSetLeaderOperation(ObjectGuid botGuid, ObjectGuid newLeaderGuid)
: m_botGuid(botGuid), m_newLeaderGuid(newLeaderGuid)
{
}
bool Execute() override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
Player* newLeader = ObjectAccessor::FindPlayer(m_newLeaderGuid);
if (!bot || !newLeader)
return false;
Group* group = bot->GetGroup();
if (!group)
{
LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Bot is not in a group");
return false;
}
if (!group->IsMember(newLeader->GetGUID()))
{
LOG_DEBUG("playerbots", "GroupSetLeaderOperation: New leader is not in the group");
return false;
}
group->ChangeLeader(newLeader->GetGUID());
LOG_DEBUG("playerbots", "GroupSetLeaderOperation: Changed leader to {}", newLeader->GetName());
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 50; }
std::string GetName() const override { return "GroupSetLeader"; }
bool IsValid() const override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
Player* newLeader = ObjectAccessor::FindPlayer(m_newLeaderGuid);
return bot && newLeader;
}
private:
ObjectGuid m_botGuid;
ObjectGuid m_newLeaderGuid;
};
// Form arena group
class ArenaGroupFormationOperation : public PlayerbotOperation
{
public:
ArenaGroupFormationOperation(ObjectGuid leaderGuid, std::vector<ObjectGuid> memberGuids,
uint32 requiredSize, uint32 arenaTeamId, std::string arenaTeamName)
: m_leaderGuid(leaderGuid), m_memberGuids(memberGuids),
m_requiredSize(requiredSize), m_arenaTeamId(arenaTeamId), m_arenaTeamName(arenaTeamName)
{
}
bool Execute() override
{
Player* leader = ObjectAccessor::FindPlayer(m_leaderGuid);
if (!leader)
{
LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Leader not found");
return false;
}
// Step 1: Remove all members from their existing groups
for (const ObjectGuid& memberGuid : m_memberGuids)
{
Player* member = ObjectAccessor::FindPlayer(memberGuid);
if (!member)
continue;
Group* memberGroup = member->GetGroup();
if (memberGroup)
{
memberGroup->RemoveMember(memberGuid);
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Removed {} from their existing group",
member->GetName());
}
}
// Step 2: Disband leader's existing group
Group* leaderGroup = leader->GetGroup();
if (leaderGroup)
{
leaderGroup->Disband(true);
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Disbanded leader's existing group");
}
// Step 3: Create new group with leader
Group* newGroup = new Group();
if (!newGroup->Create(leader))
{
delete newGroup;
LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Failed to create arena group for leader {}",
leader->GetName());
return false;
}
sGroupMgr->AddGroup(newGroup);
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Created new arena group with leader {}",
leader->GetName());
// Step 4: Add members to the new group
uint32 addedMembers = 0;
for (const ObjectGuid& memberGuid : m_memberGuids)
{
Player* member = ObjectAccessor::FindPlayer(memberGuid);
if (!member)
{
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Member {} not found, skipping",
memberGuid.ToString());
continue;
}
if (member->GetLevel() < 70)
{
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Member {} is below level 70, skipping",
member->GetName());
continue;
}
if (newGroup->AddMember(member))
{
addedMembers++;
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Added {} to arena group",
member->GetName());
}
else
LOG_ERROR("playerbots", "ArenaGroupFormationOperation: Failed to add {} to arena group",
member->GetName());
}
if (addedMembers == 0)
{
LOG_ERROR("playerbots", "ArenaGroupFormationOperation: No members were added to the arena group");
newGroup->Disband();
return false;
}
// Step 5: Teleport members to leader and reset AI
for (const ObjectGuid& memberGuid : m_memberGuids)
{
Player* member = ObjectAccessor::FindPlayer(memberGuid);
if (!member || !newGroup->IsMember(memberGuid))
continue;
PlayerbotAI* memberBotAI = sPlayerbotsMgr->GetPlayerbotAI(member);
if (memberBotAI)
memberBotAI->Reset();
member->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
member->TeleportTo(leader->GetMapId(), leader->GetPositionX(), leader->GetPositionY(),
leader->GetPositionZ(), 0);
LOG_DEBUG("playerbots", "ArenaGroupFormationOperation: Teleported {} to leader", member->GetName());
}
// Check if we have enough members
if (newGroup->GetMembersCount() < m_requiredSize)
{
LOG_INFO("playerbots", "Team #{} <{}> Group is not ready for match (not enough members: {}/{})",
m_arenaTeamId, m_arenaTeamName, newGroup->GetMembersCount(), m_requiredSize);
newGroup->Disband();
return false;
}
LOG_INFO("playerbots", "Team #{} <{}> Group is ready for match with {} members",
m_arenaTeamId, m_arenaTeamName, newGroup->GetMembersCount());
return true;
}
ObjectGuid GetBotGuid() const override { return m_leaderGuid; }
uint32 GetPriority() const override { return 60; } // Very high priority (arena/BG operations)
std::string GetName() const override { return "ArenaGroupFormation"; }
bool IsValid() const override
{
Player* leader = ObjectAccessor::FindPlayer(m_leaderGuid);
return leader != nullptr;
}
private:
ObjectGuid m_leaderGuid;
std::vector<ObjectGuid> m_memberGuids;
uint32 m_requiredSize;
uint32 m_arenaTeamId;
std::string m_arenaTeamName;
};
// Bot logout group cleanup operation
class BotLogoutGroupCleanupOperation : public PlayerbotOperation
{
public:
BotLogoutGroupCleanupOperation(ObjectGuid botGuid) : m_botGuid(botGuid) {}
bool Execute() override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
if (!bot)
return false;
PlayerbotAI* botAI = sPlayerbotsMgr->GetPlayerbotAI(bot);
if (!botAI)
return false;
Group* group = bot->GetGroup();
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
sPlayerbotDbStore->Save(botAI);
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 70; }
std::string GetName() const override { return "BotLogoutGroupCleanup"; }
bool IsValid() const override
{
Player* bot = ObjectAccessor::FindPlayer(m_botGuid);
return bot != nullptr;
}
private:
ObjectGuid m_botGuid;
};
// Add player bot operation (for logging in bots from map threads)
class AddPlayerBotOperation : public PlayerbotOperation
{
public:
AddPlayerBotOperation(ObjectGuid botGuid, uint32 masterAccountId)
: m_botGuid(botGuid), m_masterAccountId(masterAccountId)
{
}
bool Execute() override
{
sRandomPlayerbotMgr->AddPlayerBot(m_botGuid, m_masterAccountId);
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 50; } // High priority
std::string GetName() const override { return "AddPlayerBot"; }
bool IsValid() const override
{
return !ObjectAccessor::FindConnectedPlayer(m_botGuid);
}
private:
ObjectGuid m_botGuid;
uint32 m_masterAccountId;
};
class OnBotLoginOperation : public PlayerbotOperation
{
public:
OnBotLoginOperation(ObjectGuid botGuid, PlayerbotHolder* holder)
: m_botGuid(botGuid), m_holder(holder)
{
}
bool Execute() override
{
Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid);
if (!bot || !m_holder)
return false;
m_holder->OnBotLogin(bot);
return true;
}
ObjectGuid GetBotGuid() const override { return m_botGuid; }
uint32 GetPriority() const override { return 100; }
std::string GetName() const override { return "OnBotLogin"; }
bool IsValid() const override
{
return ObjectAccessor::FindConnectedPlayer(m_botGuid) != nullptr;
}
private:
ObjectGuid m_botGuid;
PlayerbotHolder* m_holder;
};
#endif

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotSecurity.h"
@@ -164,7 +164,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea
if (reason)
*reason = PLAYERBOT_DENY_INVITE;
return PLAYERBOT_SECURITY_INVITE;
}
@@ -251,9 +251,9 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
out << "I am currently leading a group. I can invite you if you want.";
break;
case PLAYERBOT_DENY_NOT_LEADER:
if (botAI->GetGroupMaster())
if (botAI->GetGroupLeader())
{
out << "I am in a group with " << botAI->GetGroupMaster()->GetName()
out << "I am in a group with " << botAI->GetGroupLeader()->GetName()
<< ". You can ask him for invite.";
}
else

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTSECURITY_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotTextMgr.h"
@@ -101,6 +101,22 @@ std::string PlayerbotTextMgr::GetBotText(std::string name, std::map<std::string,
return botText;
}
std::string PlayerbotTextMgr::GetBotTextOrDefault(std::string name, std::string defaultText,
std::map<std::string, std::string> placeholders)
{
std::string botText = GetBotText(name, placeholders);
if (botText.empty())
{
for (std::map<std::string, std::string>::iterator i = placeholders.begin(); i != placeholders.end(); ++i)
{
replaceAll(defaultText, i->first, i->second);
}
return defaultText;
}
return botText;
}
// chat replies
std::string PlayerbotTextMgr::GetBotText(ChatReplyType replyType, std::map<std::string, std::string> placeholders)
@@ -174,26 +190,29 @@ bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map<
void PlayerbotTextMgr::AddLocalePriority(uint32 locale)
{
if (!locale)
if (locale >= MAX_LOCALES)
{
LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds MAX_LOCALES ({})", locale, MAX_LOCALES - 1);
return;
}
botTextLocalePriority[locale]++;
}
uint32 PlayerbotTextMgr::GetLocalePriority()
{
uint32 topLocale = 0;
// if no real players online, reset top locale
if (!sWorldSessionMgr->GetActiveSessionCount())
uint32 const activeSessions = sWorldSessionMgr->GetActiveSessionCount();
if (!activeSessions)
{
ResetLocalePriority();
return 0;
}
uint32 topLocale = 0;
for (uint8 i = 0; i < MAX_LOCALES; ++i)
{
if (botTextLocalePriority[i] > topLocale)
if (botTextLocalePriority[i] > botTextLocalePriority[topLocale])
topLocale = i;
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_PLAYERBOTTEXTMGR_H
@@ -83,6 +83,8 @@ public:
std::string GetBotText(ChatReplyType replyType, std::string name);
bool GetBotText(std::string name, std::string& text);
bool GetBotText(std::string name, std::string& text, std::map<std::string, std::string> placeholders);
std::string GetBotTextOrDefault(std::string name, std::string defaultText,
std::map<std::string, std::string> placeholders);
void LoadBotTexts();
void LoadBotTextChance();
static void replaceAll(std::string& str, const std::string& from, const std::string& to);

View File

@@ -0,0 +1,217 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotWorldThreadProcessor.h"
#include "Log.h"
#include "PlayerbotAIConfig.h"
#include <algorithm>
PlayerbotWorldThreadProcessor::PlayerbotWorldThreadProcessor()
: m_enabled(true), m_maxQueueSize(10000), m_batchSize(100), m_queueWarningThreshold(80),
m_timeSinceLastUpdate(0), m_updateInterval(50) // Process at least every 50ms
{
LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized");
}
PlayerbotWorldThreadProcessor::~PlayerbotWorldThreadProcessor() { ClearQueue(); }
PlayerbotWorldThreadProcessor* PlayerbotWorldThreadProcessor::instance()
{
static PlayerbotWorldThreadProcessor instance;
return &instance;
}
void PlayerbotWorldThreadProcessor::Update(uint32 diff)
{
if (!m_enabled)
return;
// Accumulate time
m_timeSinceLastUpdate += diff;
// Don't process too frequently to reduce overhead
if (m_timeSinceLastUpdate < m_updateInterval)
return;
m_timeSinceLastUpdate = 0;
// Check queue health (warn if getting full)
CheckQueueHealth();
// Process a batch of operations
ProcessBatch();
}
bool PlayerbotWorldThreadProcessor::QueueOperation(std::unique_ptr<PlayerbotOperation> operation)
{
if (!operation)
{
LOG_ERROR("playerbots", "Attempted to queue null operation");
return false;
}
std::lock_guard<std::mutex> lock(m_queueMutex);
// Check if queue is full
if (m_operationQueue.size() >= m_maxQueueSize)
{
LOG_ERROR("playerbots",
"PlayerbotWorldThreadProcessor queue is full ({} operations). Dropping operation: {}",
m_maxQueueSize, operation->GetName());
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.totalOperationsSkipped++;
return false;
}
// Queue the operation
m_operationQueue.push(std::move(operation));
// Update statistics
{
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.currentQueueSize = static_cast<uint32>(m_operationQueue.size());
m_stats.maxQueueSize = std::max(m_stats.maxQueueSize, m_stats.currentQueueSize);
}
return true;
}
void PlayerbotWorldThreadProcessor::ProcessBatch()
{
// Extract a batch of operations from the queue
std::vector<std::unique_ptr<PlayerbotOperation>> batch;
batch.reserve(m_batchSize);
{
std::lock_guard<std::mutex> lock(m_queueMutex);
// Extract up to batchSize operations
while (!m_operationQueue.empty() && batch.size() < m_batchSize)
{
batch.push_back(std::move(m_operationQueue.front()));
m_operationQueue.pop();
}
// Update current queue size stat
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.currentQueueSize = static_cast<uint32>(m_operationQueue.size());
}
// Execute operations outside of lock to avoid blocking queue
uint32 totalExecutionTime = 0;
for (auto& operation : batch)
{
if (!operation)
continue;
try
{
// Check if operation is still valid
if (!operation->IsValid())
{
LOG_DEBUG("playerbots", "Skipping invalid operation: {}", operation->GetName());
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.totalOperationsSkipped++;
continue;
}
// Time the execution
uint32 startTime = getMSTime();
// Execute the operation
bool success = operation->Execute();
uint32 executionTime = GetMSTimeDiffToNow(startTime);
totalExecutionTime += executionTime;
// Log slow operations
if (executionTime > 100)
LOG_WARN("playerbots", "Slow operation: {} took {}ms", operation->GetName(), executionTime);
// Update statistics
std::lock_guard<std::mutex> statsLock(m_statsMutex);
if (success)
m_stats.totalOperationsProcessed++;
else
{
m_stats.totalOperationsFailed++;
LOG_DEBUG("playerbots", "Operation failed: {}", operation->GetName());
}
}
catch (std::exception const& e)
{
LOG_ERROR("playerbots", "Exception in operation {}: {}", operation->GetName(), e.what());
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.totalOperationsFailed++;
}
catch (...)
{
LOG_ERROR("playerbots", "Unknown exception in operation {}", operation->GetName());
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.totalOperationsFailed++;
}
}
// Update average execution time
if (!batch.empty())
{
std::lock_guard<std::mutex> statsLock(m_statsMutex);
uint32 avgTime = totalExecutionTime / static_cast<uint32>(batch.size());
// Exponential moving average
m_stats.averageExecutionTimeMs =
(m_stats.averageExecutionTimeMs * 9 + avgTime) / 10; // 90% old, 10% new
}
}
void PlayerbotWorldThreadProcessor::CheckQueueHealth()
{
uint32 queueSize = GetQueueSize();
uint32 threshold = (m_maxQueueSize * m_queueWarningThreshold) / 100;
if (queueSize >= threshold)
{
LOG_WARN("playerbots",
"PlayerbotWorldThreadProcessor queue is {}% full ({}/{}). "
"Consider increasing update frequency or batch size.",
(queueSize * 100) / m_maxQueueSize, queueSize, m_maxQueueSize);
}
}
uint32 PlayerbotWorldThreadProcessor::GetQueueSize() const
{
std::lock_guard<std::mutex> lock(m_queueMutex);
return static_cast<uint32>(m_operationQueue.size());
}
void PlayerbotWorldThreadProcessor::ClearQueue()
{
std::lock_guard<std::mutex> lock(m_queueMutex);
uint32 cleared = static_cast<uint32>(m_operationQueue.size());
if (cleared > 0)
LOG_INFO("playerbots", "Clearing {} queued operations", cleared);
// Clear the queue
while (!m_operationQueue.empty())
{
m_operationQueue.pop();
}
// Reset queue size stat
std::lock_guard<std::mutex> statsLock(m_statsMutex);
m_stats.currentQueueSize = 0;
}
PlayerbotWorldThreadProcessor::Statistics PlayerbotWorldThreadProcessor::GetStatistics() const
{
std::lock_guard<std::mutex> statsLock(m_statsMutex);
return m_stats; // Return a copy
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
#define _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
#include "Common.h"
#include "PlayerbotOperation.h"
#include <memory>
#include <mutex>
#include <queue>
/**
* @brief Processes thread-unsafe bot operations in the world thread
*
* The PlayerbotWorldThreadProcessor manages a queue of operations that must be executed
* in the world thread rather than map threads. This ensures thread safety for operations
* like group modifications, LFG, guilds, battlegrounds, etc.
*
* Architecture:
* - Map threads queue operations via QueueOperation()
* - World thread processes operations via Update() (called from WorldScript::OnUpdate)
* - Operations are processed in priority order
* - Thread-safe queue protected by mutex
*
* Usage:
* auto op = std::make_unique<MyOperation>(botGuid, params);
* sPlayerbotWorldProcessor->QueueOperation(std::move(op));
*/
class PlayerbotWorldThreadProcessor
{
public:
PlayerbotWorldThreadProcessor();
~PlayerbotWorldThreadProcessor();
static PlayerbotWorldThreadProcessor* instance();
/**
* @brief Update and process queued operations (called from world thread)
*
* This method should be called from WorldScript::OnUpdate hook, which runs in the world thread.
* It processes a batch of queued operations.
*
* @param diff Time since last update in milliseconds
*/
void Update(uint32 diff);
/**
* @brief Queue an operation for execution in the world thread
*
* Thread-safe method that can be called from any thread (typically map threads).
* The operation will be executed later during Update().
*
* @param operation Unique pointer to the operation (ownership is transferred)
* @return true if operation was queued, false if queue is full
*/
bool QueueOperation(std::unique_ptr<PlayerbotOperation> operation);
/**
* @brief Get current queue size
*
* Thread-safe method for monitoring queue size.
*
* @return Number of operations waiting to be processed
*/
uint32 GetQueueSize() const;
/**
* @brief Clear all queued operations
*
* Used during shutdown or emergency situations.
*/
void ClearQueue();
/**
* @brief Get statistics about operation processing
*/
struct Statistics
{
uint64 totalOperationsProcessed = 0;
uint64 totalOperationsFailed = 0;
uint64 totalOperationsSkipped = 0;
uint32 currentQueueSize = 0;
uint32 maxQueueSize = 0;
uint32 averageExecutionTimeMs = 0;
};
Statistics GetStatistics() const;
/**
* @brief Enable/disable operation processing
*
* When disabled, operations are still queued but not processed.
* Useful for testing or temporary suspension.
*
* @param enabled true to enable processing, false to disable
*/
void SetEnabled(bool enabled) { m_enabled = enabled; }
bool IsEnabled() const { return m_enabled; }
private:
/**
* @brief Process a single batch of operations
*
* Extracts operations from queue and executes them.
* Called internally by Update().
*/
void ProcessBatch();
/**
* @brief Check if queue is approaching capacity
*
* Logs warning if queue is getting full.
*/
void CheckQueueHealth();
// Thread-safe queue
mutable std::mutex m_queueMutex;
std::queue<std::unique_ptr<PlayerbotOperation>> m_operationQueue;
// Configuration
bool m_enabled;
uint32 m_maxQueueSize; // Maximum operations in queue
uint32 m_batchSize; // Operations to process per Update()
uint32 m_queueWarningThreshold; // Warn when queue reaches this percentage
// Statistics
mutable std::mutex m_statsMutex;
Statistics m_stats;
// Timing
uint32 m_timeSinceLastUpdate;
uint32 m_updateInterval; // Minimum ms between updates
};
#define sPlayerbotWorldProcessor PlayerbotWorldThreadProcessor::instance()
#endif

View File

@@ -25,10 +25,13 @@
#include "Metric.h"
#include "PlayerScript.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotSpellCache.h"
#include "PlayerbotWorldThreadProcessor.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
#include "cs_playerbots.h"
#include "cmath"
#include "BattleGroundTactics.h"
class PlayerbotsDatabaseScript : public DatabaseScript
{
@@ -80,13 +83,14 @@ public:
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
PLAYERHOOK_ON_LOGIN,
PLAYERHOOK_ON_AFTER_UPDATE,
PLAYERHOOK_ON_CHAT,
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
PLAYERHOOK_ON_CHAT_WITH_GROUP,
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
PLAYERHOOK_ON_GIVE_EXP
PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT,
PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT,
PLAYERHOOK_ON_GIVE_EXP,
PLAYERHOOK_ON_BEFORE_TELEPORT
}) {}
void OnPlayerLogin(Player* player) override
@@ -104,7 +108,7 @@ public:
{
ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
"|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r");
}
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
@@ -114,12 +118,32 @@ public:
roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2);
ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
+ roundedTime + "' minutes.");
}
}
}
bool OnPlayerBeforeTeleport(Player* player, uint32 mapid, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
{
// Only apply to bots to prevent affecting real players
if (!player || !player->GetSession()->IsBot())
return true;
// If changing maps, proactively clean visibility references to prevent
// stale pointers in other players' visibility maps during the teleport.
// This fixes a race condition where:
// 1. Bot A teleports and its visible objects start getting cleaned up
// 2. Bot B is simultaneously updating visibility and tries to access objects in Bot A's old visibility map
// 3. Those objects may already be freed, causing a segmentation fault
if (player->GetMapId() != mapid && player->IsInWorld())
{
player->GetObjectVisibilityContainer().CleanVisibilityReferences();
}
return true; // Allow teleport to continue
}
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
@@ -141,14 +165,17 @@ public:
{
botAI->HandleCommand(type, msg, player);
return false;
// hotfix; otherwise the server will crash when whispering logout
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
// TODO: find the root cause and solve it. (does not happen in party chat)
if (msg == "logout")
return false;
}
}
return true;
}
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
@@ -160,9 +187,10 @@ public:
}
}
}
return true;
}
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
{
if (type == CHAT_MSG_GUILD)
{
@@ -181,9 +209,10 @@ public:
}
}
}
return true;
}
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
@@ -194,38 +223,55 @@ public:
}
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
}
bool OnPlayerBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* /*criteria*/) override
{
if (sRandomPlayerbotMgr->IsRandomBot(player))
{
return false;
}
return true;
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* /*achievement*/) override
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
{
if (sRandomPlayerbotMgr->IsRandomBot(player))
if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) &&
(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)))
{
return false;
}
return true;
}
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
{
if (!player->GetSession()->IsBot())
return;
if (!sRandomPlayerbotMgr->IsRandomBot(player))
if (sPlayerbotAIConfig->randomBotXPRate == 1.0f || !player || !player->IsInWorld())
return;
if (sPlayerbotAIConfig->randomBotXPRate != 1.0)
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || !sRandomPlayerbotMgr->IsRandomBot(player))
return;
// No XP gain if master is a real player with XP gain disabled
if (const Player* master = botAI->GetMaster())
{
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
if (WorldSession* masterSession = master->GetSession();
masterSession && !masterSession->IsBot() && master->HasPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN))
{
amount = 0; // disable XP multiplier
return;
}
}
// No XP multiplier if bot is in a group with at least one real player
if (Group* group = player->GetGroup())
{
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
if (Player* member = gref->GetSource())
{
if (!member->GetSession()->IsBot())
return;
}
}
}
// Otherwise apply XP multiplier
amount = static_cast<uint32>(std::round(amount * sPlayerbotAIConfig->randomBotXPRate));
}
};
@@ -267,7 +313,8 @@ class PlayerbotsWorldScript : public WorldScript
{
public:
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript", {
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED
WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED,
WORLDHOOK_ON_UPDATE
}) {}
void OnBeforeWorldInitialized() override
@@ -284,11 +331,11 @@ public:
LOG_INFO("server.loading", "║ mod-playerbots is a community-driven open-source ║");
LOG_INFO("server.loading", "║ project based on AzerothCore, licensed under AGPLv3.0 ║");
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
LOG_INFO("server.loading", "║ https://github.com/liyunfan1223/mod-playerbots ");
LOG_INFO("server.loading", "║ https://github.com/mod-playerbots/mod-playerbots ║");
LOG_INFO("server.loading", "╚══════════════════════════════════════════════════════════╝");
uint32 oldMSTime = getMSTime();
LOG_INFO("server.loading", " ");
LOG_INFO("server.loading", "Load Playerbots Config...");
@@ -296,6 +343,16 @@ public:
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
sPlayerbotSpellCache->Initialize();
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
}
void OnUpdate(uint32 diff) override
{
sPlayerbotWorldProcessor->Update(diff);
sRandomPlayerbotMgr->UpdateAI(diff); // World thread only
}
};
@@ -357,8 +414,7 @@ public:
void OnPlayerbotUpdate(uint32 diff) override
{
sRandomPlayerbotMgr->UpdateAI(diff);
sRandomPlayerbotMgr->UpdateSessions();
sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only
}
void OnPlayerbotUpdateSessions(Player* player) override
@@ -389,6 +445,43 @@ public:
}
};
class PlayerBotsBGScript : public BGScript
{
public:
PlayerBotsBGScript() : BGScript("PlayerBotsBGScript") {}
void OnBattlegroundStart(Battleground* bg) override
{
BGStrategyData data;
switch (bg->GetBgTypeID())
{
case BATTLEGROUND_WS:
data.allianceStrategy = urand(0, WS_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, WS_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_AB:
data.allianceStrategy = urand(0, AB_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, AB_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_AV:
data.allianceStrategy = urand(0, AV_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, AV_STRATEGY_MAX - 1);
break;
case BATTLEGROUND_EY:
data.allianceStrategy = urand(0, EY_STRATEGY_MAX - 1);
data.hordeStrategy = urand(0, EY_STRATEGY_MAX - 1);
break;
default:
break;
}
bgStrategies[bg->GetInstanceID()] = data;
}
void OnBattlegroundEnd(Battleground* bg, TeamId /*winnerTeam*/) override { bgStrategies.erase(bg->GetInstanceID()); }
};
void AddPlayerbotsScripts()
{
new PlayerbotsDatabaseScript();
@@ -397,6 +490,7 @@ void AddPlayerbotsScripts()
new PlayerbotsServerScript();
new PlayerbotsWorldScript();
new PlayerbotsScript();
new PlayerBotsBGScript();
AddSC_playerbots_commandscript();
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RandomItemMgr.h"
@@ -982,7 +982,7 @@ void RandomItemMgr::BuildItemInfoCache()
continue;
}
if (proto->Flags & ITEM_FLAG_DEPRECATED)
if (proto->HasFlag(ITEM_FLAG_DEPRECATED))
{
itemForTest.insert(proto->ItemId);
continue;
@@ -1072,10 +1072,10 @@ void RandomItemMgr::BuildItemInfoCache()
// cacheInfo.team = TEAM_NEUTRAL;
// // check faction
// if (proto->Flags2 & ITEM_FLAG2_FACTION_HORDE)
// if (proto->HasFlag2(ITEM_FLAG2_FACTION_HORDE))
// cacheInfo.team = TEAM_HORDE;
// if (proto->Flags2 & ITEM_FLAG2_FACTION_ALLIANCE)
// if (proto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE))
// cacheInfo.team = TEAM_ALLIANCE;
// if (cacheInfo.team == TEAM_NEUTRAL && proto->AllowableRace > 1 && proto->AllowableRace < 8388607)
@@ -1099,7 +1099,7 @@ void RandomItemMgr::BuildItemInfoCache()
// // check item source
// if (proto->Flags & ITEM_FLAG_NO_DISENCHANT)
// if (proto->HasFlag(ITEM_FLAG_NO_DISENCHANT))
// {
// cacheInfo.source = ITEM_SOURCE_PVP;
// LOG_DEBUG("playerbots", "Item: {}, source: PvP Reward", proto->ItemId);
@@ -1367,7 +1367,7 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTem
}
// check item spells
for (const auto& spellData : proto->Spells)
for (auto const& spellData : proto->Spells)
{
// no spell
if (!spellData.SpellId)
@@ -2360,7 +2360,7 @@ void RandomItemMgr::BuildPotionCache()
(proto->SubClass != ITEM_SUBCLASS_POTION && proto->SubClass != ITEM_SUBCLASS_FLASK) ||
proto->Bonding != NO_BIND)
continue;
uint32 requiredLevel = proto->RequiredLevel;
if (requiredLevel > level || (level > 13 && requiredLevel < level - 13))
continue;
@@ -2374,10 +2374,9 @@ void RandomItemMgr::BuildPotionCache()
if (proto->Duration & 0x80000000)
continue;
if (proto->AllowableClass != -1)
continue;
bool hybrid = false;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[0].SpellId);
if (!spellInfo)

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RANDOMITEMMGR_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "RandomPlayerbotFactory.h"
@@ -15,189 +15,103 @@
#include "SharedDefines.h"
#include "SocialMgr.h"
#include "Timer.h"
#include "Guild.h" // EmblemInfo::SaveToDB
#include "Log.h"
#include "GuildMgr.h"
std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces;
constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 gender,
uint8 race)
constexpr RandomPlayerbotFactory::NameRaceAndGender RandomPlayerbotFactory::CombineRaceAndGender(uint8 race,
uint8 gender)
{
NameRaceAndGender baseIndex;
switch (race)
{
case RACE_ORC: baseIndex = NameRaceAndGender::OrcMale; break;
case RACE_DWARF: baseIndex = NameRaceAndGender::DwarfMale; break;
case RACE_NIGHTELF: baseIndex = NameRaceAndGender::NightelfMale; break;
case RACE_TAUREN: baseIndex = NameRaceAndGender::TaurenMale; break;
case RACE_GNOME: baseIndex = NameRaceAndGender::GnomeMale; break;
case RACE_TROLL: baseIndex = NameRaceAndGender::TrollMale; break;
case RACE_BLOODELF: baseIndex = NameRaceAndGender::BloodelfMale; break;
case RACE_DRAENEI: baseIndex = NameRaceAndGender::DraeneiMale; break;
case RACE_HUMAN:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
case RACE_ORC:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::OrcMale) + gender);
case RACE_DWARF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::DwarfMale) + gender);
case RACE_NIGHTELF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::NightelfMale) + gender);
case RACE_UNDEAD_PLAYER:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
case RACE_TAUREN:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::TaurenMale) + gender);
case RACE_GNOME:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GnomeMale) + gender);
case RACE_TROLL:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::TrollMale) + gender);
case RACE_DRAENEI:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::DraeneiMale) + gender);
case RACE_BLOODELF:
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::BloodelfMale) + gender);
default:
LOG_ERROR("playerbots", "The race with ID %d does not have a naming category", race);
return static_cast<NameRaceAndGender>(static_cast<uint8>(NameRaceAndGender::GenericMale) + gender);
baseIndex = NameRaceAndGender::GenericMale;
break;
}
return static_cast<NameRaceAndGender>(static_cast<uint8>(baseIndex) + ((gender >= GENDER_NONE) ? GENDER_MALE : gender));
}
RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(accountId)
bool RandomPlayerbotFactory::IsValidRaceClassCombination(uint8 race, uint8 cls, uint32 expansion)
{
uint32 const expansion = sWorld->getIntConfig(CONFIG_EXPANSION);
// skip expansion races if not playing with expansion
if (expansion < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
return false;
availableRaces[CLASS_WARRIOR].push_back(RACE_HUMAN);
availableRaces[CLASS_WARRIOR].push_back(RACE_NIGHTELF);
availableRaces[CLASS_WARRIOR].push_back(RACE_GNOME);
availableRaces[CLASS_WARRIOR].push_back(RACE_DWARF);
availableRaces[CLASS_WARRIOR].push_back(RACE_ORC);
availableRaces[CLASS_WARRIOR].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARRIOR].push_back(RACE_TAUREN);
availableRaces[CLASS_WARRIOR].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_WARRIOR].push_back(RACE_DRAENEI);
}
// skip expansion classes if not playing with expansion
if (expansion < EXPANSION_WRATH_OF_THE_LICH_KING && cls == CLASS_DEATH_KNIGHT)
return false;
availableRaces[CLASS_PALADIN].push_back(RACE_HUMAN);
availableRaces[CLASS_PALADIN].push_back(RACE_DWARF);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_PALADIN].push_back(RACE_DRAENEI);
availableRaces[CLASS_PALADIN].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_ROGUE].push_back(RACE_HUMAN);
availableRaces[CLASS_ROGUE].push_back(RACE_DWARF);
availableRaces[CLASS_ROGUE].push_back(RACE_NIGHTELF);
availableRaces[CLASS_ROGUE].push_back(RACE_GNOME);
availableRaces[CLASS_ROGUE].push_back(RACE_ORC);
availableRaces[CLASS_ROGUE].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_ROGUE].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_ROGUE].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_PRIEST].push_back(RACE_HUMAN);
availableRaces[CLASS_PRIEST].push_back(RACE_DWARF);
availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF);
availableRaces[CLASS_PRIEST].push_back(RACE_TROLL);
availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI);
availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_MAGE].push_back(RACE_HUMAN);
availableRaces[CLASS_MAGE].push_back(RACE_GNOME);
availableRaces[CLASS_MAGE].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_MAGE].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_MAGE].push_back(RACE_DRAENEI);
availableRaces[CLASS_MAGE].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_WARLOCK].push_back(RACE_HUMAN);
availableRaces[CLASS_WARLOCK].push_back(RACE_GNOME);
availableRaces[CLASS_WARLOCK].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARLOCK].push_back(RACE_ORC);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_WARLOCK].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_SHAMAN].push_back(RACE_ORC);
availableRaces[CLASS_SHAMAN].push_back(RACE_TAUREN);
availableRaces[CLASS_SHAMAN].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_SHAMAN].push_back(RACE_DRAENEI);
}
availableRaces[CLASS_HUNTER].push_back(RACE_DWARF);
availableRaces[CLASS_HUNTER].push_back(RACE_NIGHTELF);
availableRaces[CLASS_HUNTER].push_back(RACE_ORC);
availableRaces[CLASS_HUNTER].push_back(RACE_TAUREN);
availableRaces[CLASS_HUNTER].push_back(RACE_TROLL);
if (expansion >= EXPANSION_THE_BURNING_CRUSADE)
{
availableRaces[CLASS_HUNTER].push_back(RACE_DRAENEI);
availableRaces[CLASS_HUNTER].push_back(RACE_BLOODELF);
}
availableRaces[CLASS_DRUID].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DRUID].push_back(RACE_TAUREN);
if (expansion == EXPANSION_WRATH_OF_THE_LICH_KING)
{
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TAUREN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_HUMAN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_ORC);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TROLL);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_BLOODELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DRAENEI);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_GNOME);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DWARF);
}
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, cls);
return info != nullptr;
}
Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& nameCache)
{
LOG_DEBUG("playerbots", "Creating new random bot for class {}", cls);
LOG_DEBUG("playerbots", "Creating a new random bot for class: {}", cls);
const bool alliance = static_cast<bool>(urand(0, 1));
uint8 gender = rand() % 2 ? GENDER_MALE : GENDER_FEMALE;
bool alliance = rand() % 2 ? true : false;
std::vector<uint8> raceOptions;
for (const auto& race : availableRaces[cls])
for (uint8 race = RACE_HUMAN; race < MAX_RACES; ++race)
{
// skip disabled with config races
if ((1 << (race - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK))
continue;
// Try to get 50/50 faction distribution for random bot population balance.
// Without this check, races from the faction with more class options would dominate.
if (alliance == IsAlliance(race))
{
raceOptions.push_back(race);
if (IsValidRaceClassCombination(race, cls, sWorld->getIntConfig(CONFIG_EXPANSION)))
raceOptions.push_back(race);
}
}
if (raceOptions.empty())
{
LOG_ERROR("playerbots", "No races available for class: {}", cls);
LOG_ERROR("playerbots", "No races are available for class: {}", cls);
return nullptr;
}
uint8 race = raceOptions[urand(0, raceOptions.size() - 1)];
const auto raceAndGender = CombineRaceAndGender(gender, race);
const uint8 race = raceOptions[urand(0, raceOptions.size() - 1)];
const uint8 gender = urand(0, 1) ? GENDER_MALE : GENDER_FEMALE;
const auto raceAndGender = CombineRaceAndGender(race, gender);
std::string name;
if (nameCache.empty())
{
name = CreateRandomBotName(raceAndGender);
}
else
if (!nameCache.empty())
{
if (nameCache[raceAndGender].empty())
{
LOG_ERROR("playerbots", "No name found for race and gender: {}", raceAndGender);
LOG_ERROR("playerbots", "No names found for the specified race: {} and gender: {}",
race, gender);
return nullptr;
}
uint32 i = urand(0, nameCache[raceAndGender].size() - 1);
name = nameCache[raceAndGender][i];
swap(nameCache[raceAndGender][i], nameCache[raceAndGender].back());
nameCache[raceAndGender].pop_back();
}
else
{
name = CreateRandomBotName(raceAndGender);
}
if (name.empty())
{
LOG_ERROR("playerbots", "Unable to get random bot name!");
LOG_ERROR("playerbots", "Failed to get a valid random bot name");
return nullptr;
}
@@ -243,19 +157,20 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
player->CleanupsBeforeDelete();
delete player;
LOG_ERROR("playerbots", "Unable to create random bot for account {} - name: \"{}\"; race: {}; class: {}",
accountId, name.c_str(), race, cls);
LOG_ERROR("playerbots", "Unable to create random bot - name: \"{}\", race: {}, class: {}",
name.c_str(), race, cls);
return nullptr;
}
player->setCinematic(2);
player->SetAtLoginFlag(AT_LOGIN_NONE);
if (player->getClass() == CLASS_DEATH_KNIGHT)
if (cls == CLASS_DEATH_KNIGHT)
{
player->learnSpell(50977, false);
}
LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId,
LOG_DEBUG("playerbots", "Random bot created - name: \"{}\", race: {}, class: {}",
name.c_str(), race, cls);
return player;
@@ -393,37 +308,118 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender
return std::move(botName);
}
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
// or determining it dynamically based on MaxRandomBots, EnablePeriodicOnlineOffline and its ratio,
// and AddClassAccountPoolSize. The system also factors in the types of existing account, as assigned by
// AssignAccountTypes()
uint32 RandomPlayerbotFactory::CalculateTotalAccountCount()
{
// Calculates the total number of required accounts, either using the specified randomBotAccountCount
// or determining it dynamically based on the WOTLK condition, max random bots, rotation pool size,
// and additional class account pool size.
// Reset account types if features are disabled
// Reset is done here to precede needed accounts calculations
if (sPlayerbotAIConfig->maxRandomBots == 0 || sPlayerbotAIConfig->addClassAccountPoolSize == 0)
{
if (sPlayerbotAIConfig->maxRandomBots == 0)
{
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 1");
LOG_INFO("playerbots", "MaxRandomBots set to 0, any RNDbot accounts (type 1) will be unassigned (type 0)");
}
if (sPlayerbotAIConfig->addClassAccountPoolSize == 0)
{
PlayerbotsDatabase.Execute("UPDATE playerbots_account_type SET account_type = 0 WHERE account_type = 2");
LOG_INFO("playerbots", "AddClassAccountPoolSize set to 0, any AddClass accounts (type 2) will be unassigned (type 0)");
}
// Wait for DB to reflect the change, up to 1 second max. This is needed to make sure other logs don't show wrong info
for (int waited = 0; waited < 1000; waited += 50)
{
QueryResult res = PlayerbotsDatabase.Query("SELECT COUNT(*) FROM playerbots_account_type WHERE account_type IN ({}, {})",
sPlayerbotAIConfig->maxRandomBots == 0 ? 1 : -1,
sPlayerbotAIConfig->addClassAccountPoolSize == 0 ? 2 : -1);
if (!res || res->Fetch()[0].Get<uint64>() == 0)
{
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Extra 50ms fixed delay for safety.
}
}
// Checks if randomBotAccountCount is set, otherwise calculate it dynamically.
if (sPlayerbotAIConfig->randomBotAccountCount > 0)
return sPlayerbotAIConfig->randomBotAccountCount;
// Avoid creating accounts if both maxRandom & ClassBots are set to zero.
if (sPlayerbotAIConfig->maxRandomBots == 0 &&
sPlayerbotAIConfig->addClassAccountPoolSize == 0)
return 0;
// Check existing account types
uint32 existingRndBotAccounts = 0;
uint32 existingAddClassAccounts = 0;
uint32 existingUnassignedAccounts = 0;
//bool isWOTLK = sWorld->getIntConfig(CONFIG_EXPANSION) == EXPANSION_WRATH_OF_THE_LICH_KING; //not used, line marked for removal.
QueryResult typeCheck = PlayerbotsDatabase.Query("SELECT account_type, COUNT(*) FROM playerbots_account_type GROUP BY account_type");
if (typeCheck)
{
do
{
Field* fields = typeCheck->Fetch();
uint8 accountType = fields[0].Get<uint8>();
uint32 count = fields[1].Get<uint32>();
// Determine divisor based on WOTLK condition
if (accountType == 0) existingUnassignedAccounts = count;
else if (accountType == 1) existingRndBotAccounts = count;
else if (accountType == 2) existingAddClassAccounts = count;
} while (typeCheck->NextRow());
}
// Determine divisor based on Death Knight login eligibility and requested A&H faction ratio
int divisor = CalculateAvailableCharsPerAccount();
// Calculate max bots
int maxBots = sPlayerbotAIConfig->maxRandomBots;
// Take perodic online - offline into account
// Take periodic online - offline into account
if (sPlayerbotAIConfig->enablePeriodicOnlineOffline)
{
maxBots *= sPlayerbotAIConfig->periodicOnlineOfflineRatio;
}
// Calculate base accounts, add class account pool size, and add 1 as a fixed offset
uint32 baseAccounts = maxBots / divisor;
return baseAccounts + sPlayerbotAIConfig->addClassAccountPoolSize + 1;
// Calculate number of accounts needed for RNDbots
// Result is rounded up for maxBots not cleanly divisible by the divisor
uint32 neededRndBotAccounts = (maxBots + divisor - 1) / divisor;
uint32 neededAddClassAccounts = sPlayerbotAIConfig->addClassAccountPoolSize;
// Start with existing total
uint32 existingTotal = existingRndBotAccounts + existingAddClassAccounts + existingUnassignedAccounts;
// Calculate shortfalls after using unassigned accounts
uint32 availableUnassigned = existingUnassignedAccounts;
uint32 additionalAccountsNeeded = 0;
// Check RNDbot needs
if (neededRndBotAccounts > existingRndBotAccounts)
{
uint32 rndBotShortfall = neededRndBotAccounts - existingRndBotAccounts;
if (rndBotShortfall <= availableUnassigned)
availableUnassigned -= rndBotShortfall;
else
{
additionalAccountsNeeded += (rndBotShortfall - availableUnassigned);
availableUnassigned = 0;
}
}
// Check AddClass needs
if (neededAddClassAccounts > existingAddClassAccounts)
{
uint32 addClassShortfall = neededAddClassAccounts - existingAddClassAccounts;
if (addClassShortfall <= availableUnassigned)
availableUnassigned -= addClassShortfall;
else
{
additionalAccountsNeeded += (addClassShortfall - availableUnassigned);
availableUnassigned = 0;
}
}
// Return existing total plus any additional accounts needed
return existingTotal + additionalAccountsNeeded;
}
uint32 RandomPlayerbotFactory::CalculateAvailableCharsPerAccount()
@@ -475,8 +471,9 @@ void RandomPlayerbotFactory::CreateRandomBots()
LOG_INFO("playerbots", "Deleting all random bot characters and accounts...");
// First execute all the cleanup SQL commands
// Clear playerbots_random_bots table
// Clear playerbots_random_bots and playerbots_account_type
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots");
PlayerbotsDatabase.Execute("DELETE FROM playerbots_account_type");
// Get the database names dynamically
std::string loginDBName = LoginDatabase.GetConnectionInfo()->database;
@@ -486,6 +483,13 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')",
sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
// Wait for the characters to be deleted before proceeding to dependent deletes
while (CharacterDatabase.QueueSize())
{
std::this_thread::sleep_for(1s);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Clean up orphaned entries in playerbots_guild_tasks
PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)");
@@ -500,11 +504,13 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_arena_stats WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_entry_point WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
// Clean up pet data
CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)");
@@ -559,11 +565,23 @@ void RandomPlayerbotFactory::CreateRandomBots()
uint32 timer = getMSTime();
// After ALL deletions, make sure data is commited to DB
LoginDatabase.Execute("COMMIT");
CharacterDatabase.Execute("COMMIT");
PlayerbotsDatabase.Execute("COMMIT");
// Wait for all pending database operations to complete
while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize())
{
std::this_thread::sleep_for(1s);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Flush tables to ensure all data in memory are written to disk
LoginDatabase.Execute("FLUSH TABLES");
CharacterDatabase.Execute("FLUSH TABLES");
PlayerbotsDatabase.Execute("FLUSH TABLES");
LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer));
LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server...");
World::StopNow(SHUTDOWN_EXIT_CODE);
@@ -680,9 +698,9 @@ void RandomPlayerbotFactory::CreateRandomBots()
}
LOG_DEBUG("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, totalAccountCount);
RandomPlayerbotFactory factory(accountId);
RandomPlayerbotFactory factory;
WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
WorldSession* session = new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING,
time_t(0), LOCALE_enUS, 0, false, false, 0, true);
sessionBots.push_back(session);
@@ -692,29 +710,24 @@ void RandomPlayerbotFactory::CreateRandomBots()
if (!((1 << (cls - 1)) & CLASSMASK_ALL_PLAYABLE) || !sChrClassesStore.LookupEntry(cls))
continue;
if (bool const isClassDeathKnight = cls == CLASS_DEATH_KNIGHT;
isClassDeathKnight && sWorld->getIntConfig(CONFIG_EXPANSION) != EXPANSION_WRATH_OF_THE_LICH_KING)
// skip disabled with config classes
if ((1 << (cls - 1)) & sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK))
continue;
Player* playerBot = factory.CreateRandomBot(session, cls, nameCache);
if (!playerBot)
{
LOG_ERROR("playerbots", "Fail to create character for account {}", accountId);
continue;
}
if (cls != 10)
{
if (Player* playerBot = factory.CreateRandomBot(session, cls, nameCache))
{
playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(),
playerBot->getClass(), playerBot->GetLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
bot_creation++;
}
else
{
LOG_ERROR("playerbots", "Fail to create character for account {}", accountId);
}
}
playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(),
playerBot->getClass(), playerBot->GetLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
bot_creation++;
}
}
@@ -769,51 +782,105 @@ void RandomPlayerbotFactory::CreateRandomGuilds()
LOG_INFO("playerbots", "Random bot guilds deleted");
}
std::unordered_set<uint32> botAccounts;
botAccounts.reserve(sPlayerbotAIConfig->randomBotAccounts.size());
for (uint32 acc : sPlayerbotAIConfig->randomBotAccounts)
botAccounts.insert(acc);
// Recount bot guilds directly from the database (does not depend on connected bots)
uint32 guildNumber = 0;
GuidVector availableLeaders;
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
sPlayerbotAIConfig->randomBotGuilds.clear();
sPlayerbotAIConfig->randomBotGuilds.shrink_to_fit(); // avoids accumulating old capacity
if (!botAccounts.empty())
{
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(*i);
if (Guild* guild = sGuildMgr->GetGuildByLeader(leader))
if (QueryResult res = CharacterDatabase.Query(
// We only retrieve what is necessary (guildid, leader account)
"SELECT g.guildid, c.account "
"FROM guild g JOIN characters c ON g.leaderguid = c.guid"))
{
++guildNumber;
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
}
else
{
Player* player = ObjectAccessor::FindPlayer(leader);
if (player && !player->GetGuildId())
availableLeaders.push_back(leader);
do
{
Field* f = res->Fetch();
const uint32 guildId = f[0].Get<uint32>();
const uint32 accountId = f[1].Get<uint32>();
// Determine if guild leader's account is a bot account.
if (botAccounts.find(accountId) != botAccounts.end())
{
++guildNumber;
sPlayerbotAIConfig->randomBotGuilds.push_back(guildId);
}
} while (res->NextRow());
}
}
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; ++guildNumber)
LOG_INFO("playerbots", "{}/{} random bot guilds exist in guild table",guildNumber, sPlayerbotAIConfig->randomBotGuildCount);
if (guildNumber >= sPlayerbotAIConfig->randomBotGuildCount)
{
LOG_DEBUG("playerbots", "No new random guilds required");
return;
}
// We list the available leaders (online bots, not in guilds)
GuidVector availableLeaders;
availableLeaders.reserve(randomBots.size()); // limit reallocs
for (const uint32 botLowGuid : randomBots)
{
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(botLowGuid);
if (sGuildMgr->GetGuildByLeader(leader))
{
// already GuildLeader -> ignored
continue;
}
else
{
if (Player* player = ObjectAccessor::FindPlayer(leader))
{
if (!player->GetGuildId())
availableLeaders.push_back(leader);
}
}
}
LOG_DEBUG("playerbots", "{} available leaders for new guilds found", availableLeaders.size());
// Create up to randomBotGuildCount by counting only EFFECTIVE creations
uint32 createdThisRun = 0;
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; /* ++guildNumber -> done only if creation */)
{
std::string const guildName = CreateRandomGuildName();
if (guildName.empty())
continue;
break; // no more names available in playerbots_guild_names
if (sGuildMgr->GetGuildByName(guildName))
continue;
continue; // name already taken, skip
if (availableLeaders.empty())
{
LOG_ERROR("playerbots", "No leaders for random guilds available");
continue;
break; // no more leaders: we can no longer progress without distorting the counter
}
uint32 index = urand(0, availableLeaders.size() - 1);
ObjectGuid leader = availableLeaders[index];
availableLeaders.erase(availableLeaders.begin() + index); // Removes the chosen leader to avoid re-selecting it repeatedly
Player* player = ObjectAccessor::FindPlayer(leader);
if (!player)
{
LOG_ERROR("playerbots", "ObjectAccessor Cannot find player to set leader for guild {} . Skipped...",
guildName.c_str());
// we will try with other leaders in the next round (guildNumber is not incremented)
continue;
}
if (player->GetGuildId())
{
// leader already in guild -> we don't advance the counter, we move on to the next one
continue;
}
LOG_DEBUG("playerbots", "Creating guild name='{}' leader='{}'...", guildName.c_str(), player->GetName().c_str());
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
@@ -826,6 +893,8 @@ void RandomPlayerbotFactory::CreateRandomGuilds()
sGuildMgr->AddGuild(guild);
LOG_DEBUG("playerbots", "Guild created: id={} name='{}'", guild->GetId(), guildName.c_str());
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
@@ -833,13 +902,37 @@ void RandomPlayerbotFactory::CreateRandomGuilds()
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
EmblemInfo emblemInfo(st, cl, br, bc, bg);
guild->HandleSetEmblem(emblemInfo);
LOG_DEBUG("playerbots",
"[TABARD] new guild id={} random -> style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), st, cl, br, bc, bg);
// populate guild table with a random tabard design
CharacterDatabase.Execute(
"UPDATE guild SET EmblemStyle={}, EmblemColor={}, BorderStyle={}, BorderColor={}, BackgroundColor={} "
"WHERE guildid={}",
st, cl, br, bc, bg, guild->GetId());
LOG_DEBUG("playerbots", "[TABARD] UPDATE done for guild id={}", guild->GetId());
// Immediate reading for log
if (QueryResult qr = CharacterDatabase.Query(
"SELECT EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor FROM guild WHERE guildid={}",
guild->GetId()))
{
Field* f = qr->Fetch();
LOG_DEBUG("playerbots",
"[TABARD] DB check guild id={} => style={}, color={}, borderStyle={}, borderColor={}, bgColor={}",
guild->GetId(), f[0].Get<uint8>(), f[1].Get<uint8>(), f[2].Get<uint8>(), f[3].Get<uint8>(), f[4].Get<uint8>());
}
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
// The guild is only counted if it is actually created
++guildNumber;
++createdThisRun;
}
LOG_INFO("playerbots", "{} random bot guilds available", guildNumber);
// Shows the true total and how many were created during this run
LOG_INFO("playerbots", "{} random bot guilds created this run)", createdThisRun);
}
std::string const RandomPlayerbotFactory::CreateRandomGuildName()

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RANDOMPLAYERBOTFACTORY_H
@@ -44,9 +44,9 @@ public:
BloodelfFemale
};
static constexpr NameRaceAndGender CombineRaceAndGender(uint8 gender, uint8 race);
static constexpr NameRaceAndGender CombineRaceAndGender(uint8 race, uint8 gender);
RandomPlayerbotFactory(uint32 accountId);
RandomPlayerbotFactory() {};
virtual ~RandomPlayerbotFactory() {}
Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<NameRaceAndGender, std::vector<std::string>>& names);
@@ -58,11 +58,9 @@ public:
static uint32 CalculateAvailableCharsPerAccount();
private:
static bool IsValidRaceClassCombination(uint8 race, uint8 class_, uint32 expansion);
std::string const CreateRandomBotName(NameRaceAndGender raceAndGender);
static std::string const CreateRandomArenaTeamName();
uint32 accountId;
static std::map<uint8, std::vector<uint8>> availableRaces;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
@@ -60,7 +60,6 @@ public:
bool IsEmpty() { return !lastChangeTime; }
public:
uint32 value;
uint32 lastChangeTime;
uint32 validIn;
@@ -104,10 +103,6 @@ public:
void LogPlayerLocation();
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
private:
//void ScaleBotActivity();
public:
uint32 activeBots = 0;
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
bool IsRandomBot(Player* bot);
@@ -180,13 +175,24 @@ public:
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
std::vector<uint32> allianceFlightMasterCache;
std::vector<uint32> hordeFlightMasterCache;
struct LevelBracket {
uint32 low;
uint32 high;
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
};
std::map<uint32, LevelBracket> zone2LevelBracket;
std::map<uint8, std::vector<WorldLocation>> bankerLocsPerLevelCache;
struct BankerLocation {
WorldLocation loc;
uint32 entry;
};
std::map<uint8, std::vector<BankerLocation>> bankerLocsPerLevelCache;
// Account type management
void AssignAccountTypes();
bool IsAccountType(uint32 accountId, uint8 accountType);
protected:
void OnBotLoginInternal(Player* const bot) override;
@@ -216,10 +222,8 @@ private:
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
std::vector<Player*> players;
uint32 processTicks;
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
@@ -228,6 +232,12 @@ private:
std::list<uint32> currentBots;
uint32 bgBotsCount;
uint32 playersLevel;
// Account lists
std::vector<uint32> rndBotTypeAccounts; // Accounts marked as RNDbot (type 1)
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
//void ScaleBotActivity(); // Deprecated function
};
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "ServerFacade.h"
@@ -56,7 +56,7 @@ Unit* ServerFacade::GetChaseTarget(Unit* target)
MovementGenerator* movementGen = target->GetMotionMaster()->top();
if (movementGen && movementGen->GetMovementGeneratorType() == CHASE_MOTION_TYPE)
{
if (target->GetTypeId() == TYPEID_PLAYER)
if (target->IsPlayer())
{
return static_cast<ChaseMovementGenerator<Player> const*>(movementGen)->GetTarget();
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_SERVERFACADE_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "Talentspec.h"

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_TALENTSPEC_H

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "TravelMgr.h"
@@ -480,7 +480,7 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName)
std::set<Transport*> WorldPosition::getTransports(uint32 entry)
{
/*
if(!entry)
if (!entry)
return getMap()->m_transports;
else
{
@@ -488,7 +488,7 @@ std::set<Transport*> WorldPosition::getTransports(uint32 entry)
std::set<Transport*> transports;
/*
for (auto transport : getMap()->m_transports)
if(transport->GetEntry() == entry)
if (transport->GetEntry() == entry)
transports.insert(transport);
return transports;
@@ -1272,7 +1272,7 @@ std::string const RpgTravelDestination::getTitle()
{
std::ostringstream out;
if(entry > 0)
if (entry > 0)
out << "rpg npc ";
out << " " << ChatHelper::FormatWorldEntry(entry);
@@ -2076,7 +2076,6 @@ void TravelMgr::LoadQuestTravelTable()
continue;
}
if (r.role == 0)
{
container->questGivers.push_back(loc);
@@ -2591,7 +2590,7 @@ void TravelMgr::LoadQuestTravelTable()
//if (data->displayId == 3015)
// pos.setZ(pos.getZ() + 6.0f);
//else if(data->displayId == 3031)
//else if (data->displayId == 3031)
// pos.setZ(pos.getZ() - 17.0f);
if (prevNode)
@@ -3336,7 +3335,7 @@ void TravelMgr::LoadQuestTravelTable()
uint32 accountId = fields[0].Get<uint32>();
WorldSession* session =
new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
new WorldSession(accountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0),
LOCALE_enUS, 0, false, false, 0, true);
std::vector<std::pair<std::pair<uint32, uint32>, uint32>> classSpecLevel;

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_TRAVELMGR_H

Some files were not shown because too many files have changed in this diff Show More