Mercenaires : le mercenaire sort du portail, et sait enfin qui il est
Quatre corrections nees d une soiree d essais en jeu. Le mercenaire etait teleporte sur son employeur et non sur la structure qui l invoque : en reculant de quelques pas avant de payer, on le voyait apparaitre loin du portail. Le contrat retient desormais la position de la structure ; le mercenaire en sort a un ou trois metres, l angle tire au hasard pour que quatre recrues ne s empilent pas, face au portail qu il vient de franchir, la hauteur recalee sur le terrain. BotGroupAI expose pour cela un teleport vers un point precis - un bot n a pas de client pour accuser reception, seul BotAITeleport simule cet echange. Il jurait ensuite selon sa faction : un demoniste de l Alliance invoquait donc la Lumiere, qui est precisement ce qui le brule. Les serments viennent maintenant de la CLASSE et l origine de la RACE. Nuance importante : demonistes et chevaliers de la mort ne se reclament jamais de la Lumiere, mais peuvent la maudire et s en moquer - c est leur role. Enfin, les refus passagers des fournisseurs (503, 429) sont retentes deux fois avant d abandonner, dans le fil dedie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1219,6 +1219,12 @@ bool BotGroupAI::UpdateMountState()
|
||||
return TryUpMount();
|
||||
}
|
||||
|
||||
void BotGroupAI::TeleportToPoint(uint32 mapId, Position const& pos)
|
||||
{
|
||||
Position target = pos; // SetTeleport prend une reference modifiable
|
||||
m_Teleporting.SetTeleport(mapId, target);
|
||||
}
|
||||
|
||||
void BotGroupAI::Dismount()
|
||||
{
|
||||
if (!me->HasAura(m_UseMountID))
|
||||
|
||||
@@ -86,6 +86,8 @@ public:
|
||||
bool CastingSummonRite() { return m_MovetoUseGO.CastingSummonRite(); }
|
||||
bool SetMovetoUseGOTarget(ObjectGuid target) { return m_MovetoUseGO.SetNeedMovetoUseGO(target); }
|
||||
void Dismount();
|
||||
// Teleporte le bot a un point precis. Module Mercenaires : sortie du portail.
|
||||
void TeleportToPoint(uint32 mapId, Position const& pos);
|
||||
|
||||
void SetVehicle3DNextMoveGap(float gap);
|
||||
void SetVehicle3DMoveTarget(Unit* pTarget, float offset);
|
||||
|
||||
@@ -421,6 +421,46 @@ namespace
|
||||
default: return "aventurier";
|
||||
}
|
||||
}
|
||||
|
||||
// D ou il vient : peuple, terre natale, blessure de son histoire.
|
||||
char const* RaceOrigin(uint8 race)
|
||||
{
|
||||
switch (race)
|
||||
{
|
||||
case RACE_HUMAN: return "humain de Hurlevent, eleve dans les Royaumes de l Est ; ton peuple a survecu a la peste et aux orcs";
|
||||
case RACE_ORC: return "orc de Durotar, venu d un autre monde par le Portail des tenebres ; ton peuple s est arrache a la corruption demoniaque";
|
||||
case RACE_DWARF: return "nain de Forgefer, fils de la pierre et de la forge, buveur devant l eternel";
|
||||
case RACE_NIGHTELF: return "elfe de la nuit de Darnassus, vieux de plusieurs siecles, sylvestre et devoue a Elune";
|
||||
case RACE_UNDEAD_PLAYER: return "Reprouve de Fossoyeuse, mort puis releve, affranchi du Fleau ; ta chair tombe et tu t en moques";
|
||||
case RACE_TAUREN: return "tauren des Pitons du Tonnerre, fils de la Terre-Mere, lent a la colere mais terrible quand elle vient";
|
||||
case RACE_GNOME: return "gnome chasse de Gnomeregan par la radiation, bricoleur et bavard";
|
||||
case RACE_TROLL: return "troll sombrelance, exile des iles Echo, superstitieux et rancunier";
|
||||
case RACE_GOBLIN: return "gobelin de la Baie-du-Butin, marchand jusqu au bout des griffes : tout se vend, tout s achete";
|
||||
case RACE_BLOODELF: return "elfe de sang de Lune-d Argent, hautain, ronge par la soif de magie depuis la chute du Puits";
|
||||
case RACE_DRAENEI: return "draenei de l Exodar, exile d Argus, en fuite devant la Legion depuis des millenaires";
|
||||
case RACE_WORGEN: return "worgen de Gilneas, homme au-dehors, bete au-dedans, ronge par la malediction";
|
||||
default: return "vagabond sans terre connue";
|
||||
}
|
||||
}
|
||||
|
||||
// Ce par quoi il jure - et ce par quoi il ne jurerait pour rien au monde.
|
||||
char const* ClassOaths(uint8 playerClass)
|
||||
{
|
||||
switch (playerClass)
|
||||
{
|
||||
case CLASS_WARRIOR: return "Tu jures par le fer, par le sang et par la sueur. La magie t inspire de la mefiance.";
|
||||
case CLASS_PALADIN: return "La Lumiere te guide : \"Par la Lumiere !\", \"Que la Lumiere te garde !\". Tu meprises la magie du vil.";
|
||||
case CLASS_HUNTER: return "Tu jures par la meute, par la traque et par la corde de ton arc. Ta bete compte plus que bien des hommes.";
|
||||
case CLASS_ROGUE: return "Tu jures bas et vulgairement, par les ombres et par les bourses bien pleines. Les serments nobles te font rire.";
|
||||
case CLASS_PRIEST: return "Tu invoques la Lumiere quand elle sert, et le Vide quand elle ne suffit plus. Tu parles d ames et de peches.";
|
||||
case CLASS_DEATH_KNIGHT: return "Tu jures par le froid, par la tombe et par le Fleau qui t a releve. Tu ne te reclames JAMAIS de la Lumiere comme le ferait un paladin - mais tu la railles volontiers, elle te repugne et tu le dis.";
|
||||
case CLASS_SHAMAN: return "Les Elements te parlent : \"Par les esprits !\", \"Que les ancetres me foudroient !\". Tu evoques le vent, la terre et la flamme.";
|
||||
case CLASS_MAGE: return "Tu jures par les Arcanes et par les portails. Tu tiens les brutes en piteuse estime.";
|
||||
case CLASS_WARLOCK: return "Tu asservis des demons et le vil coule dans tes veines. Tu jures par les flammes tordues, par le Vide, par les seigneurs gueux. Tu ne jures JAMAIS par la Lumiere comme le ferait un paladin - en revanche tu peux la maudire, t en moquer, dire qu elle te brule.";
|
||||
case CLASS_DRUID: return "Tu jures par Elune, par le Reve d Emeraude et par la nature. Les cites de pierre t etouffent.";
|
||||
default: return "Tu jures comme jurent les soudards, sans facon.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string MercenaryChatMgr::BuildSystemPrompt(Player* owner, Player* bot) const
|
||||
@@ -447,17 +487,17 @@ std::string MercenaryChatMgr::BuildSystemPrompt(Player* owner, Player* bot) cons
|
||||
prompt << "Tu es " << bot->GetName() << ", " << RaceName(bot->getRace()) << " " << className
|
||||
<< " de niveau " << uint32(bot->getLevel()) << ", mercenaire "
|
||||
<< (alliance ? "de l'Alliance" : "de la Horde")
|
||||
<< " au service de " << owner->GetName() << " en Azeroth. "
|
||||
<< " au service de " << owner->GetName() << " en Azeroth.\n"
|
||||
<< "QUI TU ES : " << RaceOrigin(bot->getRace()) << ". "
|
||||
<< "Tu connais ton peuple, ta terre et ton metier ; tu en parles quand on te questionne. "
|
||||
<< "Un portail magique t'a arrache a tes affaires contre cent pieces d'or : tu sers cet employeur "
|
||||
<< "tant que dure le contrat, ni plus, ni moins.\n"
|
||||
|
||||
<< "PARLER : francais, une a trois phrases, jamais davantage. Registre medieval-fantastique, "
|
||||
<< "rude et image, celui d'un soudard qui a vu trop de champs de bataille. "
|
||||
<< "Tu tutoies ton employeur ; tu l'appelles parfois « patron », « chef » ou par son nom. "
|
||||
<< "Tu jures par ce que jure ton peuple : "
|
||||
<< (alliance ? "« Par la Lumiere ! », « Par la barbe de mes ancetres ! », « Par Elune ! »"
|
||||
: "« Par les esprits ! », « Lok'tar ! », « Que les ancetres me foudroient ! »")
|
||||
<< ". Tu parles d'or, d'acier, de sang, de gnognote et de bonne biere.\n"
|
||||
<< ClassOaths(bot->getClass()) << " "
|
||||
<< "Tu parles d'or, d'acier, de sang, de gnognote et de bonne biere.\n"
|
||||
|
||||
<< "INTERDIT : tout mot du monde moderne (d'accord, ok, super, cool, probleme technique, "
|
||||
<< "systeme, application, internet, robot, intelligence artificielle, modele, invite). "
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
#include "MercenaryChat.h"
|
||||
#include "Config.h"
|
||||
#include "DB2Stores.h"
|
||||
#include "Creature.h"
|
||||
#include "Group.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "Map.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Player.h"
|
||||
@@ -341,7 +343,7 @@ bool MercenaryMgr::FindCandidate(Player* owner, uint8 role, uint32& accountId, u
|
||||
return false;
|
||||
}
|
||||
|
||||
MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role)
|
||||
MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return MERC_ERR_DISABLED;
|
||||
@@ -431,6 +433,12 @@ MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role)
|
||||
contract.ownerGuid = owner->GetGUID();
|
||||
contract.role = role;
|
||||
contract.stage = MERC_STAGE_SUMMONING;
|
||||
if (portal)
|
||||
{
|
||||
contract.hasPortal = true;
|
||||
contract.portalMap = portal->GetMapId();
|
||||
contract.portalPos = portal->GetPosition();
|
||||
}
|
||||
m_contracts.push_back(contract);
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Mercenaires: %s a paye %u po pour un %s (compte bot %u).",
|
||||
@@ -632,7 +640,25 @@ void MercenaryMgr::Update(uint32 diff)
|
||||
{
|
||||
if (BotGroupAI* groupAI = dynamic_cast<BotGroupAI*>(bot->GetAI()))
|
||||
{
|
||||
groupAI->ProcessBotCommand(owner, "summon");
|
||||
if (it->hasPortal)
|
||||
{
|
||||
// Il franchit le seuil : quelques pas devant la structure,
|
||||
// l angle varie pour que quatre mercenaires ne se marchent
|
||||
// pas dessus, et la hauteur est recalee sur le terrain.
|
||||
float const angle = it->portalPos.GetOrientation() + frand(-1.0f, 1.0f);
|
||||
float const distance = frand(1.5f, 3.0f);
|
||||
float x = it->portalPos.GetPositionX() + distance * std::cos(angle);
|
||||
float y = it->portalPos.GetPositionY() + distance * std::sin(angle);
|
||||
float z = it->portalPos.GetPositionZ();
|
||||
if (Map* map = bot->GetMap())
|
||||
map->GetHeight(bot->GetPhaseShift(), x, y, z);
|
||||
|
||||
// Il fait face au portail dont il sort.
|
||||
Position spot(x, y, z, angle + float(M_PI));
|
||||
groupAI->TeleportToPoint(it->portalMap, spot);
|
||||
}
|
||||
else
|
||||
groupAI->ProcessBotCommand(owner, "!summon");
|
||||
it->summonPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Position.h"
|
||||
#include <vector>
|
||||
|
||||
class Creature;
|
||||
class Group;
|
||||
class Player;
|
||||
|
||||
@@ -64,7 +66,7 @@ enum MercenaryResult
|
||||
struct MercenaryContract
|
||||
{
|
||||
MercenaryContract() : accountId(0), role(0), stage(MERC_STAGE_SUMMONING), waitSeconds(0),
|
||||
pendingRelease(false), summonPending(false) { }
|
||||
pendingRelease(false), summonPending(false), hasPortal(false), portalMap(0) { }
|
||||
|
||||
uint32 accountId; // compte bot reserve
|
||||
ObjectGuid ownerGuid; // joueur qui a paye
|
||||
@@ -81,6 +83,12 @@ struct MercenaryContract
|
||||
// aupres de son employeur : l ordre part au tick suivant, quand l IA de
|
||||
// groupe aura reconnu son maitre.
|
||||
bool summonPending;
|
||||
|
||||
// Le portail d ou part l invocation : le mercenaire doit en sortir, pas
|
||||
// se materialiser aux pieds de son employeur reste en retrait.
|
||||
bool hasPortal;
|
||||
uint32 portalMap;
|
||||
Position portalPos;
|
||||
};
|
||||
|
||||
class TC_GAME_API MercenaryMgr
|
||||
@@ -101,7 +109,8 @@ class TC_GAME_API MercenaryMgr
|
||||
|
||||
// Verifie, encaisse et lance l invocation. Le prelevement n a lieu que
|
||||
// si un mercenaire du role demande a effectivement ete reserve.
|
||||
MercenaryResult Summon(Player* owner, uint8 role);
|
||||
// "portal" est la structure invoquante : le mercenaire en sortira.
|
||||
MercenaryResult Summon(Player* owner, uint8 role, Creature* portal = nullptr);
|
||||
|
||||
void Update(uint32 diff);
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ class npc_mercenary_portal : public CreatureScript
|
||||
|
||||
CloseGossipMenuFor(player);
|
||||
|
||||
MercenaryResult const result = sMercenaryMgr->Summon(player, role);
|
||||
MercenaryResult const result = sMercenaryMgr->Summon(player, role, creature);
|
||||
if (result != MERC_OK)
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("|cffff0000[Portail]|r %s",
|
||||
|
||||
Reference in New Issue
Block a user