diff --git a/ryzom/server/src/ags_test/command_event_manager.h b/ryzom/server/src/ags_test/command_event_manager.h index d66361e9f..06d97c726 100644 --- a/ryzom/server/src/ags_test/command_event_manager.h +++ b/ryzom/server/src/ags_test/command_event_manager.h @@ -70,7 +70,7 @@ public: /// Set the value of a var const std::string &set(const std::string &var, const std::string &value) { - std::pair res = _Map.insert(std::make_pair(var, value)); + std::pair res = _Map.insert(std::make_pair(var, value)); if (!res.second) (*(res.first)).second = value; return (*(res.first)).second; diff --git a/ryzom/server/src/ags_test/move_manager.h b/ryzom/server/src/ags_test/move_manager.h index ab78cecd4..9987b0169 100644 --- a/ryzom/server/src/ags_test/move_manager.h +++ b/ryzom/server/src/ags_test/move_manager.h @@ -95,7 +95,7 @@ public: { if (PrimZoneMap.find(primZone.getName()) == PrimZoneMap.end()) { - PrimZoneMap.insert(std::make_pair(primZone.getName(), primZone)); + PrimZoneMap.insert(std::make_pair(primZone.getName(), primZone)); nlinfo("Using PrimZone '%s'", primZone.getName().c_str()); } else diff --git a/ryzom/server/src/ai_service/ai_bot.cpp b/ryzom/server/src/ai_service/ai_bot.cpp index 3d9764241..acce67cf3 100644 --- a/ryzom/server/src/ai_service/ai_bot.cpp +++ b/ryzom/server/src/ai_service/ai_bot.cpp @@ -85,7 +85,8 @@ void CSpawnBot::setVisualPropertiesName() // In ringshard we use npc with fauna sheet but we want to be enable to change theire name - if (! botRef.getFaunaBotUseBotName()) //false by default + // Ulu : Ring is dead but i keep this code below to prevent side effects. Just add a condition : always set the custom name if exists + if (! botRef.getFaunaBotUseBotName() && botRef.getCustomName().empty()) //getFaunaBotUseBotName is false by default { if (botRef.getSheet()->ForceDisplayCreatureName()) return; diff --git a/ryzom/server/src/ai_service/ai_bot_npc.cpp b/ryzom/server/src/ai_service/ai_bot_npc.cpp index c2f6941f6..f6389faea 100644 --- a/ryzom/server/src/ai_service/ai_bot_npc.cpp +++ b/ryzom/server/src/ai_service/ai_bot_npc.cpp @@ -597,6 +597,9 @@ void CBotNpc::fillDescriptionMsg(RYMSG::TGenNpcDescMsg& msg) const msg.getLootList().push_back(sheetRef); } + if (_PrimAlias >= 900 && _PrimAlias <= 999) //Spawned bots + msg.getOptionalProperties().push_back("Name:"+getName()); + CGroupNpc::TFactionAttackableSet const& factionAttackableAbove = grp().getFactionAttackableAbove(); FOREACHC(itFaction, CGroupNpc::TFactionAttackableSet, factionAttackableAbove) msg.getOptionalProperties().push_back("FactionAttackableAbove:" + itFaction->first + ":" + NLMISC::toString(itFaction->second)); diff --git a/ryzom/server/src/ai_service/ai_grp.cpp b/ryzom/server/src/ai_service/ai_grp.cpp index e77618731..cb0b1bee5 100644 --- a/ryzom/server/src/ai_service/ai_grp.cpp +++ b/ryzom/server/src/ai_service/ai_grp.cpp @@ -376,6 +376,11 @@ void CSpawnGroup::checkRespawn() // respawn if there is not too much dead .. (no more than one at each tick). if (_BotsToRespawn.size()<=0) return; + + // Send url to EGS + std::string url = getUrl(); + std::string actionName = getActionName(); + CCreatureSetUrlMsg msg; //FOREACH_NOINC(it, std::vector, _BotsToRespawn) for(uint32 i = 0; i < _BotsToRespawn.size();) @@ -397,6 +402,11 @@ void CSpawnGroup::checkRespawn() } if (botPt->isSpawned() || botPt->reSpawn(false)) { + CSpawnBot* pbot = botPt->getSpawnObj(); + if (pbot!=NULL) + { + msg.Entities.push_back(pbot->dataSetRow()); + } continue; // directly test the same it (the next in fact). } else @@ -406,6 +416,13 @@ void CSpawnGroup::checkRespawn() } ++i; } + + if(url != "") + { + msg.ActionName = actionName; + msg.Url = url; + msg.send(egsString); + } } diff --git a/ryzom/server/src/ai_service/ai_grp.h b/ryzom/server/src/ai_service/ai_grp.h index 2866f3c04..ab92c8254 100644 --- a/ryzom/server/src/ai_service/ai_grp.h +++ b/ryzom/server/src/ai_service/ai_grp.h @@ -153,6 +153,13 @@ public: void setAggroMinimumFor(TDataSetRow const& bot, float aggro, bool forceReturnAggro, NLMISC::CSmartPtr place = NLMISC::CSmartPtr(NULL)); bool haveAggro() const; bool haveAggroOrReturnPlace() const; + + + const std::string getActionName() const { return _ActionName; } + void setActionName(const std::string &name) { _ActionName = name; } + + const std::string getUrl() const { return _Url; } + void setUrl(const std::string &url) { _Url = url; } protected: CProfilePtr _PunctualHoldMovingProfile; @@ -170,6 +177,9 @@ private: CProfilePtr _MovingProfile; CProfilePtr _ActivityProfile; CProfilePtr _FightProfile; + + std::string _ActionName; + std::string _Url; }; ////////////////////////////////////////////////////////////////////////////// diff --git a/ryzom/server/src/ai_service/ai_grp_fauna.cpp b/ryzom/server/src/ai_service/ai_grp_fauna.cpp index 5738af614..b84276b42 100644 --- a/ryzom/server/src/ai_service/ai_grp_fauna.cpp +++ b/ryzom/server/src/ai_service/ai_grp_fauna.cpp @@ -1088,6 +1088,10 @@ void CGrpFauna::setType(TFaunaType type) faction().removeProperties(); if (type==AITYPES::FaunaTypePredator) faction().addProperty(AITYPES::CPropertyId("Predator")); + else if (type==AITYPES::FaunaTypeHerbivore) + faction().addProperty(AITYPES::CPropertyId("Herbivore")); + else if (type==AITYPES::FaunaTypePlant) + faction().addProperty(AITYPES::CPropertyId("Plant")); _Type = type; } diff --git a/ryzom/server/src/ai_service/ai_grp_npc.cpp b/ryzom/server/src/ai_service/ai_grp_npc.cpp index 3e78d6ee2..e6b16c9a0 100644 --- a/ryzom/server/src/ai_service/ai_grp_npc.cpp +++ b/ryzom/server/src/ai_service/ai_grp_npc.cpp @@ -1096,6 +1096,41 @@ void CGroupNpc::firstBotSpawned() setFirstBotSpawned(); } +void CGroupNpc::setStateAlias(const std::string &state, uint32 alias) +{ + TStatesToAlias::iterator it=_StatesToAlias.find(state); + if (it != _StatesToAlias.end()) + it->second = alias; + else + _StatesToAlias.insert(make_pair(state, alias)); +} + +uint32 CGroupNpc::getStateAlias(const std::string &state) +{ + TStatesToAlias::iterator it=_StatesToAlias.find(state); + if (it != _StatesToAlias.end()) + return it->second; + return 0; +} + +void CGroupNpc::setStateEventAlias(const std::string &stateEvent, uint32 alias) +{ + TStatesToAlias::iterator it=_StateEventToAlias.find(stateEvent); + if (it != _StateEventToAlias.end()) + it->second = alias; + else + _StateEventToAlias.insert(make_pair(stateEvent, alias)); +} + +uint32 CGroupNpc::getStateEventAlias(const std::string &state) +{ + TStatesToAlias::iterator it=_StateEventToAlias.find(state); + if (it != _StateEventToAlias.end()) + return it->second; + return 0; +} + + void CGroupNpc::setColour(uint8 colour) { FOREACH(itBot, CCont, bots()) @@ -1116,26 +1151,26 @@ void CGroupNpc::setOutpostSide(OUTPOSTENUMS::TPVPSide side) } } -void CGroupNpc::setOutpostFactions(OUTPOSTENUMS::TPVPSide side) +void CGroupNpc::setOutpostFactions(const std::string &alias, OUTPOSTENUMS::TPVPSide side) { // Attack only the declared ennemies of the outpost if (side == OUTPOSTENUMS::OutpostOwner) { // Bots factions - faction ().addProperty(NLMISC::toString("outpost:%s:bot_defender", getAliasString().c_str())); - friendFaction().addProperty(NLMISC::toString("outpost:%s:bot_defender", getAliasString().c_str())); - ennemyFaction().addProperty(NLMISC::toString("outpost:%s:bot_attacker", getAliasString().c_str())); + faction ().addProperty(NLMISC::toString("outpost:%s:bot_defender", alias.c_str())); + friendFaction().addProperty(NLMISC::toString("outpost:%s:bot_defender", alias.c_str())); + ennemyFaction().addProperty(NLMISC::toString("outpost:%s:bot_attacker", alias.c_str())); // Players faction - ennemyFaction().addProperty(NLMISC::toString("outpost:%s:attacker", getAliasString().c_str())); + ennemyFaction().addProperty(NLMISC::toString("outpost:%s:attacker", alias.c_str())); } if (side == OUTPOSTENUMS::OutpostAttacker) { // Bots factions - faction ().addProperty(NLMISC::toString("outpost:%s:bot_attacker", getAliasString().c_str())); - friendFaction().addProperty(NLMISC::toString("outpost:%s:bot_attacker", getAliasString().c_str())); - ennemyFaction().addProperty(NLMISC::toString("outpost:%s:bot_defender", getAliasString().c_str())); + faction ().addProperty(NLMISC::toString("outpost:%s:bot_attacker", alias.c_str())); + friendFaction().addProperty(NLMISC::toString("outpost:%s:bot_attacker", alias.c_str())); + ennemyFaction().addProperty(NLMISC::toString("outpost:%s:bot_defender", alias.c_str())); // Players faction - ennemyFaction().addProperty(NLMISC::toString("outpost:%s:defender", getAliasString().c_str())); + ennemyFaction().addProperty(NLMISC::toString("outpost:%s:defender", alias.c_str())); } } diff --git a/ryzom/server/src/ai_service/ai_grp_npc.h b/ryzom/server/src/ai_service/ai_grp_npc.h index 1f838584d..518662e1a 100644 --- a/ryzom/server/src/ai_service/ai_grp_npc.h +++ b/ryzom/server/src/ai_service/ai_grp_npc.h @@ -167,6 +167,15 @@ public: bool botsAreNamed() { return _BotsAreNamed; } void setBotsAreNamedFlag() { _BotsAreNamed = true; } void clrBotsAreNamedFlag() { _BotsAreNamed = false; } + + // States to Alias + void setStateAlias(const std::string &state, uint32 alias); + uint32 getStateAlias(const std::string &state); + + // States Events to Alias + void setStateEventAlias(const std::string &stateEvent, uint32 alias); + uint32 getStateEventAlias(const std::string &stateEvent); + // Parameter management ------------------------------------- void clearParameters(); @@ -227,11 +236,12 @@ public: void setSpawnZone(const CNpcZone *zone) { _SpawnZone = zone; } const CNpcZone *getSpawnZone() const { return _SpawnZone; } + void setColour(uint8 colour); void setOutpostSide(OUTPOSTENUMS::TPVPSide side); - void setOutpostFactions(OUTPOSTENUMS::TPVPSide side); + void setOutpostFactions(std::string const& alias, OUTPOSTENUMS::TPVPSide side); bool isRingGrp() const { return _RingGrp;} private: @@ -251,7 +261,7 @@ private: uint32 _RespawnTime; /// Despawn time in ticks uint32 _DespawnTime; - + AITYPES::CPropertySetWithExtraList _faction; @@ -269,6 +279,9 @@ private: TNamedEntityListenerList _namedEntityListeners; typedef std::multimap, std::string> TNamedEntityListenerList2; TNamedEntityListenerList2 _namedEntityListeners2; + typedef std::map TStatesToAlias; + TStatesToAlias _StatesToAlias; + TStatesToAlias _StateEventToAlias; struct SHandle { diff --git a/ryzom/server/src/ai_service/ai_instance.cpp b/ryzom/server/src/ai_service/ai_instance.cpp index 3e76bf6a8..8e81fa143 100644 --- a/ryzom/server/src/ai_service/ai_instance.cpp +++ b/ryzom/server/src/ai_service/ai_instance.cpp @@ -1,9 +1,6 @@ // Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // -// This source file has been modified by the following contributors: -// Copyright (C) 2018-2020 Jan BOON (Kaetemi) -// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the @@ -20,6 +17,7 @@ #include "stdpch.h" #include "server_share/r2_vision.h" #include "ai_instance.h" +#include "ais_actions.h" #include "ai_player.h" #include "ai_grp_npc.h" @@ -29,6 +27,8 @@ #include "ai_instance_inline.h" +#include "nel/ligo/primitive_utils.h" + #include "commands.h" #include "messages.h" @@ -36,6 +36,10 @@ using namespace std; using namespace NLMISC; using namespace NLNET; using namespace MULTI_LINE_FORMATER; +using namespace NLLIGO; + +NLLIGO::CLigoConfig CAIInstance::_LigoConfig; + CAIInstance::CAIInstance(CAIS* owner) : CChild(owner) @@ -212,6 +216,9 @@ void CAIInstance::initInstance(string const& continentName, uint32 instanceNumbe _ContinentName = continentName; _InstanceNumber = instanceNumber; + _LastSpawnAlias = (900 + _InstanceNumber) << LigoConfig.getDynamicAliasSize(); + _LastStateAlias = 0; + sendInstanceInfoToEGS(); if (!EGSHasMirrorReady) @@ -288,13 +295,21 @@ void CAIInstance::addGroupInfo(CGroup* grp) { string const& name = grp->aliasTreeOwner()->getName(); uint32 alias = grp->aliasTreeOwner()->getAlias(); + if (!name.empty()) + _GroupFromNames[name].push_back(grp); + if (alias) + _GroupFromAlias[alias] = grp; +} +void CAIInstance::addGroupInfo(CGroup* grp, const string &name, uint32 alias) +{ if (!name.empty()) _GroupFromNames[name].push_back(grp); if (alias) _GroupFromAlias[alias] = grp; } + void CAIInstance::removeGroupInfo(CGroup* grp, CAliasTreeOwner* grpAliasTreeOwner) { string const& name = grpAliasTreeOwner->getName(); @@ -639,7 +654,7 @@ static CAIVector randomPos(double dispersionRadius) { return CAIVector(0., 0.); } - static const uint32 maxLimit = std::numeric_limits::max() >> 1; + const uint32 maxLimit = std::numeric_limits::max() >> 1; double rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[ double r = dispersionRadius*sqrt(rval); rval = (double)CAIS::rand32(maxLimit)/(double)maxLimit; // [0-1[ @@ -675,7 +690,10 @@ CGroupNpc* CAIInstance::eventCreateNpcGroup(uint nbBots, NLMISC::CSheetId const& _EventNpcManager->groups().addAliasChild(grp); // Set the group parameters grp->setAutoSpawn(false); - grp->setName(NLMISC::toString("event_group_%u", grp->getChildIndex())); + + string name = botsName.empty() ? NLMISC::toString("event_group_%u", grp->getChildIndex()):botsName; + + grp->setName(name); grp->clearParameters(); grp->setPlayerAttackable(true); grp->setBotAttackable(true); @@ -684,15 +702,21 @@ CGroupNpc* CAIInstance::eventCreateNpcGroup(uint nbBots, NLMISC::CSheetId const& grp->clrBotsAreNamedFlag(); + addGroupInfo(grp, name, grp->getAlias()); + + { // build unnamed bot - for (uint i=0; ibots().addChild(new CBotNpc(grp, 0, botsName.empty() ? grp->getName():botsName), i); // Doub: 0 instead of getAlias()+i otherwise aliases are wrong + _LastSpawnAlias++; + nlinfo("Spawn with alias : %d (%s)", _LastSpawnAlias, _LigoConfig.aliasToString(_LastSpawnAlias).c_str()); + grp->bots().addChild(new CBotNpc(grp, _LastSpawnAlias, name), i); // Doub: 0 instead of getAlias()+i otherwise aliases are wrong CBotNpc* const bot = NLMISC::safe_cast(grp->bots()[i]); bot->setSheet(sheet); + bot->setPrimAlias(900+_InstanceNumber); if (!look.empty()) bot->setClientSheet(look); bot->equipmentInit(); @@ -744,6 +768,25 @@ CGroupNpc* CAIInstance::eventCreateNpcGroup(uint nbBots, NLMISC::CSheetId const& destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(pos, 0, 0), (uint32)(dispersionRadius*1000.)); spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone)); + if (!botsName.empty()) + { + CStateMachine* sm = _EventNpcManager->getStateMachine(); + uint32 stateAlias = grp->getStateAlias("start"); + CAIStatePositional* statePositional; + if (stateAlias == 0) + { + _LastStateAlias++; + statePositional = new CAIStatePositional(sm, _LastStateAlias, "start"); + grp->setStateAlias("start", statePositional->getAlias()); + sm->states().addChild(statePositional); + } + else + { + statePositional = safe_cast(sm->states().getChildByAlias(stateAlias)); + } + grp->setNextState(statePositional); + } + if (spawnBots) grp->getSpawnObj()->spawnBots(); @@ -942,28 +985,27 @@ void cbEventNpcGroupScript( NLNET::CMessage& msgin, const std::string &serviceNa if (firstCommand[0] == '(') // Old eventNpcGroupScript command : (boteid, commands...) { - nlinfo("Event group script with %d strings :", nbString); strings.resize(nbString); strings[0] = eid; - nlinfo(" %d '%s'", 0, strings[0].c_str()); strings[1] = firstCommand; - nlinfo(" %d '%s'", 1, strings[1].c_str()); for (uint32 i=2; igetEntity(botEId); CSpawnBotNpc* bot = dynamic_cast(entity); @@ -976,13 +1018,14 @@ void cbEventNpcGroupScript( NLNET::CMessage& msgin, const std::string &serviceNa } else { - strings[0] = (string)groupname.replace("#last", _PlayersLastCreatedNpcGroup[playerId].c_str()); + if (!eid.empty()) + strings[0] = (string)groupname.replace("#last", _PlayersLastCreatedNpcGroup[playerId].c_str()); + else + strings[0] = (string)groupname; } - nlinfo(" %d '%s'", 0, strings[0].c_str()); for (uint32 i=1; i // Copyright (C) 2010 Winch Gate Property Limited // -// This source file has been modified by the following contributors: -// Copyright (C) 2020 Jan BOON (Kaetemi) -// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the @@ -168,6 +165,7 @@ public: //------------------------------------------------------------------- // group name/alias retreiver void addGroupInfo(CGroup* grp); + void addGroupInfo(CGroup* grp, const std::string &name, uint32 alias); void removeGroupInfo(CGroup* grp, CAliasTreeOwner* grpAliasTreeOwner); CGroup* findGroup(uint32 alias); void findGroup(std::vector& result, std::string const& name); @@ -190,7 +188,7 @@ public: /// Store mapping 'name:variant' -> squad to be used later by getSquadByVariantName() void registerSquadVariant(const std::string& nameAndVariant, CGroupDesc *squad ) { - if ( ! _SquadVariantNameToGroupDesc.insert( std::make_pair( NLMISC::toLowerAscii( nameAndVariant ), squad ) ).second ) + if ( ! _SquadVariantNameToGroupDesc.insert( std::make_pair( NLMISC::toLower( nameAndVariant ), squad ) ).second ) nlwarning( "Duplicate squad template / squad variant '%s'", nameAndVariant.c_str() ); } @@ -203,7 +201,7 @@ public: /// Get a squad by name:variant (works only during primitive parsing), or NULL if not found. Not case-sensitive. CGroupDesc *getSquadByVariantName(const std::string& nameAndVariant) { - std::map > >::iterator it = _SquadVariantNameToGroupDesc.find( NLMISC::toLowerAscii( nameAndVariant ) ); + std::map > >::iterator it = _SquadVariantNameToGroupDesc.find( NLMISC::toLower( nameAndVariant ) ); if ( it != _SquadVariantNameToGroupDesc.end() ) return (*it).second; else @@ -231,6 +229,8 @@ private: CAliasCont _Managers; //@} + static NLLIGO::CLigoConfig _LigoConfig; + /// The ai instance continent name (multi ai system) std::string _ContinentName; /// The ai instance number (multi ai system) @@ -245,6 +245,10 @@ private: CManagerPlayer* _PlayerManager; /// event npc Manager. CMgrNpc* _EventNpcManager; + + uint32 _LastSpawnAlias; + uint32 _LastStateAlias; + /// easter egg manager NLMISC::CRefPtr _EasterEggManager; diff --git a/ryzom/server/src/ai_service/ai_mgr_pet.cpp b/ryzom/server/src/ai_service/ai_mgr_pet.cpp index ebf18bbd6..f272d6069 100644 --- a/ryzom/server/src/ai_service/ai_mgr_pet.cpp +++ b/ryzom/server/src/ai_service/ai_mgr_pet.cpp @@ -233,10 +233,11 @@ void CPetSpawnMsgImp::callback(std::string const& name, NLNET::TServiceId id) #endif return; } - + + CAIPos position; + // calc a valid spawn position. { - CAIPos position; CAIEntityPhysical const* const phys = CAIS::instance().getEntityPhysical(CMirrors::DataSet->getDataSetRow(petOwnerId)); // TSpawnMode @@ -294,12 +295,10 @@ void CPetSpawnMsgImp::callback(std::string const& name, NLNET::TServiceId id) botPet->setSheet(sheet); -#ifdef RYZOM_FORGE_PET_NAME if (!CustomName.empty()) { botPet->setCustomName(CustomName); } -#endif if (!botPet->spawn()) { @@ -325,6 +324,26 @@ void CPetSpawnMsgImp::callback(std::string const& name, NLNET::TServiceId id) return; } + CEntityId id = botPet->getSpawn()->getEntityId(); + float t = 0; + uint8 cont = 0; + uint8 one = 1; + sint32 x = position.x(); + sint32 y = position.y(); + sint32 z = position.h(); + NLMISC::TGameCycle tick = CTickEventHandler::getGameCycle() + 1; + CMessage msgout2("ENTITY_TELEPORTATION"); + msgout2.serial( id ); + msgout2.serial( x ); + msgout2.serial( y ); + msgout2.serial( z ); + msgout2.serial( t ); + msgout2.serial( tick ); + msgout2.serial( cont ); + msgout2.serial( Cell ); + msgout2.serial( one ); + sendMessageViaMirror("GPMS", msgout2); + botPet->getSpawn()->setAIProfile(new CAIPetProfileStand(botPet->getSpawn())); confirmMsg.PetMirrorRow = botPet->getSpawn()->dataSetRow(); diff --git a/ryzom/server/src/ai_service/ai_outpost.cpp b/ryzom/server/src/ai_service/ai_outpost.cpp index 99dcbcee1..4a30a09b9 100644 --- a/ryzom/server/src/ai_service/ai_outpost.cpp +++ b/ryzom/server/src/ai_service/ai_outpost.cpp @@ -877,13 +877,12 @@ void COutpost::createSquad(CGroupDesc const* groupDesc, COu }*/ grp->setOutpostSide(side); - grp->setOutpostFactions(side); + grp->setOutpostFactions(getAliasString(), side); grp->_AggroRange = 25; grp->_UpdateNbTicks = 10; grp->respawnTime() = respawTimeGC; - grp->getDebugHistory()->setRecording(true); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152 grp->updateStateInstance(); // Directly call his first state (to retrieve associated params). if (createOrder!=0) @@ -903,6 +902,10 @@ void COutpost::createSquad(CGroupDesc const* groupDesc, COu void COutpost::spawnSquad(uint32 groupId) { OUTPOST_DBG( "Outpost %s: Spawning squad 0x%08x", getName().c_str(), groupId ); + + setAttackerAlliance(_AttackerAllianceId); + + FOREACH(itManager, CAliasCont, _Managers) { COutpostManager* manager = *itManager; @@ -1157,7 +1160,7 @@ NLMISC_COMMAND(displayOutposts, "list the available outpost", "") if (args.size() > 0) return false; - static const uint32 instanceNumber = std::numeric_limits::max(); + uint32 instanceNumber = std::numeric_limits::max(); for (uint i=0; isetTimer(uint32(waitMin+CAIS::rand32(uint32(waitMax-waitMin)))); } + pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedFirst); + pgrp.processStateEvent(pgrp.mgr().EventDestinationReachedAll); } continue; } diff --git a/ryzom/server/src/ai_service/ai_profile_pet.cpp b/ryzom/server/src/ai_service/ai_profile_pet.cpp index 87bce52cc..b4d7efe76 100644 --- a/ryzom/server/src/ai_service/ai_profile_pet.cpp +++ b/ryzom/server/src/ai_service/ai_profile_pet.cpp @@ -88,7 +88,8 @@ void CAIPetProfileFollowPlayer::updateProfile(uint ticksSinceLastUpdate) return; CPathCont& pathCont = _Bot->spawnGrp().getPathCont(); - if ((pathCont.getDestination()-_Bot->wpos().toAIVector()).quickNorm()>6.f) // follow only if > 6 meters. + + if (_Bot->getPersistent().getSheet()->Scale() <= .5f || (pathCont.getDestination()-_Bot->wpos().toAIVector()).quickNorm() > 6.f) // follow only if > 6 meters or it's a tiny creature. { // Handle the hunger of the animal CSpeedLimit speedLimit( TheDataset, _Bot->dataSetRow() ); diff --git a/ryzom/server/src/ai_service/ai_script_comp.cpp b/ryzom/server/src/ai_service/ai_script_comp.cpp index 595a2a8d1..4993163ba 100644 --- a/ryzom/server/src/ai_service/ai_script_comp.cpp +++ b/ryzom/server/src/ai_service/ai_script_comp.cpp @@ -1066,7 +1066,7 @@ void CFightScript::add(CFightScriptCompReader *reader) } -CFightScriptCompReader *CFightScriptCompReader::getScriptReader (const string &str) +CFightScriptCompReader* CFightScriptCompReader::getScriptReader(const string &str) { CFightScript::TFightScriptMap::iterator it=CFightScript::_ScriptCompList.find(str); if (it==CFightScript::_ScriptCompList.end()) diff --git a/ryzom/server/src/ai_service/ai_script_data_manager.cpp b/ryzom/server/src/ai_service/ai_script_data_manager.cpp index 7a94283c0..1d1796980 100644 --- a/ryzom/server/src/ai_service/ai_script_data_manager.cpp +++ b/ryzom/server/src/ai_service/ai_script_data_manager.cpp @@ -128,12 +128,15 @@ string CAIScriptDataManager::makePdrFileName() { string aisName; // get the AIS local path - CConfigFile::CVar *var = IService::getInstance()->ConfigFile.getVarPtr("AESAliasName"); - if (var) - aisName = var->asString(0); - else + //CConfigFile::CVar *var = IService::getInstance()->ConfigFile.getVarPtr("AESAliasName"); + // aisName must be get from getServiceAliasName to manage "-N AIS_NAME" command line arg + + aisName = IService::getInstance()->getServiceAliasName(); + + if (aisName.empty()) aisName = "unamed_ais"; - + + return string("ai_script_data/")+aisName+"_pdr.bin"; } diff --git a/ryzom/server/src/ai_service/ai_vision.h b/ryzom/server/src/ai_service/ai_vision.h index 8ea8e3fc5..916c16218 100644 --- a/ryzom/server/src/ai_service/ai_vision.h +++ b/ryzom/server/src/ai_service/ai_vision.h @@ -239,6 +239,8 @@ private: { CAIEntityPhysical const* phys = const_cast(&*it)->getSpawnObj(); + CMirrorPropValueRO cell( TheDataset, phys->dataSetRow(), DSPropertyCELL ); + if (phys && phys->aipos().quickDistTo(aiVectorXy) < playerRadiusInMeters) { _players.push_back(const_cast(&*it)); diff --git a/ryzom/server/src/ai_service/ais_user_models.cpp b/ryzom/server/src/ai_service/ais_user_models.cpp index de0063598..06871dd87 100644 --- a/ryzom/server/src/ai_service/ais_user_models.cpp +++ b/ryzom/server/src/ai_service/ais_user_models.cpp @@ -91,7 +91,8 @@ DEFINE_ACTION(ContextGlobal,USR_MDL) std::vector::const_iterator it = args.begin(); uint32 primAlias; - + string modelId; + for (it = args.begin(); it != args.end();++it) { if (it == args.begin()) @@ -102,9 +103,9 @@ DEFINE_ACTION(ContextGlobal,USR_MDL) } continue; } - std::string modelId(it->toString()); - std::string sheetId((++it)->toString()); - std::string script((++it)->toString()); + modelId = it->toString(); + string sheetId((++it)->toString()); + string script((++it)->toString()); //will contain all script info and the base sheet id TScriptContent scriptContent; @@ -134,7 +135,10 @@ DEFINE_ACTION(ContextGlobal,USR_MDL) //send msg to EGS if EGS up if (EGSHasMirrorReady) { - CAIUserModelManager::getInstance()->sendUserModels(); + if (modelId.size() > 4 && modelId[0] == 'A' && modelId[1] == 'R' && modelId[2] == 'K' && modelId[3] == '_') // Ark user model (only one) + CAIUserModelManager::getInstance()->sendUserModel(primAlias, modelId); + else + CAIUserModelManager::getInstance()->sendUserModels(); } } @@ -303,6 +307,14 @@ void CAIUserModelManager::addToUserModels(uint32 primAlias, const std::string &userModelId, const TScriptContent &userModel) { + if (userModelId.size() > 4 && userModelId[0] == 'A' && userModelId[1] == 'R' && userModelId[2] == 'K' && userModelId[3] == '_') // Ark user model (can be updated) + { + CCustomElementId id(primAlias, userModelId); + TScripts::iterator it = _UserModels.Scripts.find(id); + if (it != _UserModels.Scripts.end()) + _UserModels.Scripts.erase(it); + } + bool inserted = _UserModels.Scripts.insert(make_pair(CCustomElementId(primAlias, userModelId), userModel)).second; if (!inserted) { @@ -312,6 +324,23 @@ void CAIUserModelManager::addToUserModels(uint32 primAlias, nldebug(" Added usermodel '%s' with alias '%u'", userModelId.c_str(), primAlias); } +void CAIUserModelManager::sendUserModel(uint32 primAlias, const std::string &userModelId) +{ + NLNET::CMessage msgout("USER_MODEL"); + + CCustomElementId id(primAlias, userModelId); + TScripts::iterator it = _UserModels.Scripts.find(id); + if (it == _UserModels.Scripts.end()) + return; + + CScriptData scriptData; + scriptData.Scripts.insert(make_pair(id, it->second)); + msgout.serial(const_cast(scriptData)); + + nlinfo("Sending %u user models to EGS", scriptData.Scripts.size()); + sendMessageViaMirror("EGS", msgout); +} + void CAIUserModelManager::sendUserModels() { NLNET::CMessage msgout("USER_MODELS"); diff --git a/ryzom/server/src/ai_service/ais_user_models.h b/ryzom/server/src/ai_service/ais_user_models.h index 5e6d3591a..8fbecfcf7 100644 --- a/ryzom/server/src/ai_service/ais_user_models.h +++ b/ryzom/server/src/ai_service/ais_user_models.h @@ -41,6 +41,7 @@ public: /** send user models to EGS for script parsing and modified CStaticCreatures instanciation */ void sendUserModels(); + void sendUserModel(uint32 primAlias, const std::string &userModelId); /** send custom loot table script and info to EGS for script parsing and custom CStaticLootTable instanciation */ diff --git a/ryzom/server/src/ai_service/bot_chat_interface.cpp b/ryzom/server/src/ai_service/bot_chat_interface.cpp new file mode 100644 index 000000000..36dc9aa42 --- /dev/null +++ b/ryzom/server/src/ai_service/bot_chat_interface.cpp @@ -0,0 +1,1160 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#include "stdpch.h" +#if 0 +#error "Deprecated" +//#include "bot_chat_interface.h" +#include "game_share/synchronised_message.h" +#include "game_share/bot_chat_types.h" + +/* +// Nel Misc +#include "nel/net/unified_network.h" + +// Game share +#include "game_share/news_types.h" +#include "game_share/bot_chat_types.h" + +// Local includes +#include "bot_chat_interface.h" +*/ + +using namespace NLMISC; +using namespace NLNET; +using namespace std; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// the parent class for bot chat page type classes + +class CBotChatPageType +{ +public: + // virtual interface ---------------------------------------------------- + virtual bool open(const CEntityId &player, const CEntityId &bot)=0; + virtual bool close(const CEntityId &player, const CEntityId &bot)=0; +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for bot chat pages + +struct SBotChatPage +{ + // ctor ----------------------------------------------------------------- + SBotChatPage( + BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId, + CBotChatPageType * chatPageType, + uint numOptions + ) + { + ClientInterfaceId= clientInterfaceId; + PageType= chatPageType; + NumOptions= numOptions; + } + + // data ----------------------------------------------------------------- + BOTCHATTYPE::TBotChatInterfaceId ClientInterfaceId; // id of interface to display on client + CBotChatPageType * PageType; // type of chat page + uint NumOptions; // number of options for player to click on +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for a state for bot chat automatons + +struct SBotChatAutomatonState +{ + // public data ---------------------------------------------------------- + SBotChatPage *Page; + uint On[5]; // value to return on player click of slot 0..4 + + // ctor ----------------------------------------------------------------- + SBotChatAutomatonState(SBotChatPage *page,uint on0=std::numeric_limits::max(),uint on1=std::numeric_limits::max(),uint on2=std::numeric_limits::max(),uint on3=std::numeric_limits::max(),uint on4=std::numeric_limits::max()) + { + Page=page; + On[0]=on0; + On[1]=on1; + On[2]=on2; + On[3]=on3; + On[4]=on4; + + // make sure the number of arguments supplied corresponds to the + // number of options prresent on the user interfac page + nlassert(page->NumOptions>=0 && page->NumOptions<=4); + nlassert(page->NumOptions==0 || On[page->NumOptions-1]!=std::numeric_limits::max()); + nlassert(page->NumOptions==4 || On[page->NumOptions]==std::numeric_limits::max()); + } +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for a bot chat automatons & a singleton for indexing +// automatons by name + +struct SBotChatAutomaton +{ + // public data ---------------------------------------------------------- + string Name; + SBotChatAutomatonState *States; + uint Size; + + // ctor ----------------------------------------------------------------- + SBotChatAutomaton(string name, SBotChatAutomatonState *states,uint size) + { + Name=name; + States=states; + Size=size; + + if (NameMap.find(name)!=NameMap.end()) + { + nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name.c_str()); + return; + } + NameMap[name]=this; + } + + // dtor ----------------------------------------------------------------- + ~SBotChatAutomaton() + { + map ::iterator it=NameMap.find(Name); + if (it!=NameMap.end() && (*it).second==this) + NameMap.erase(it); + // don't try to display a warning in a dtor as the warning system is + // probably already down + } + + + // singleton methods ---------------------------------------------------- + static SBotChatAutomaton *getAutomatonByName(string name) + { + map ::iterator it=NameMap.find(name); + if (it==NameMap.end()) + return NULL; + return (*it).second; + } + + // singleton data ------------------------------------------------------- + static map NameMap; +}; +map SBotChatAutomaton::NameMap; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Implementation of different code modules for handling different bot +// chat page types + +////////////////////////////////////////////////////////////////////////////// +// this is a dummy page used to terminate chats + +class CBotChatPageTypeDone: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return false; // stop the bot chat! + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return false; + } +} +BotChatPageTypeDone; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that contains static text and buttons for +// player to click on/ select + +class CBotChatPageTypeTextOnly: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return true; + } +} +BotChatPageTypeTextOnly; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays NEWS as well as other text + +class CBotChatPageTypeNews: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return true; + } +} +BotChatPageTypeNews; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays a SHOP interface + +class CBotChatPageTypeShop: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("player %s entered trade page", player.toString().c_str()); + CMessage msgout( "TRADE_BEGIN" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("end of trade with player %s", player.toString().c_str()); + CMessage msgout( "TRADE_END" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + sendMessageViaMirror( "EGS", msgout ); + return true; + } +} +BotChatPageTypeShop; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays a MISSION SHOP interface + +class CBotChatPageTypeMissionShop: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("player %s entered mission page", player.toString().c_str()); + CMessage msgout( "MISSION_LIST_BEGIN" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("end of mission page with player %s", player.toString().c_str()); + CMessage msgout( "MISSION_LIST_END" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "EGS", msgout ); + return true; + } +} +BotChatPageTypeMissionShop; + + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Definitions of bot chat pages and automatons + +// define the usable bot chat pages ------------------------------------------ +SBotChatPage BotChatPageIntro (BOTCHATTYPE::Intro, &BotChatPageTypeTextOnly, 4); +SBotChatPage BotChatPageFriendly (BOTCHATTYPE::FriendlyMainPage, &BotChatPageTypeNews, 4); +SBotChatPage BotChatPageNeutral (BOTCHATTYPE::NeutralMainPage, &BotChatPageTypeNews, 3); +SBotChatPage BotChatPageHostile (BOTCHATTYPE::NastyMainPage, &BotChatPageTypeTextOnly, 1); +SBotChatPage BotChatPageMoreNews (BOTCHATTYPE::MoreNewsPage, &BotChatPageTypeNews, 2); +SBotChatPage BotChatPageShop (BOTCHATTYPE::BuySellPage, &BotChatPageTypeShop, 2); +SBotChatPage BotChatPageMissionShop (BOTCHATTYPE::MissionsPage, &BotChatPageTypeMissionShop, 2); +SBotChatPage BotChatPageDone (BOTCHATTYPE::Done, &BotChatPageTypeDone, 0); + +// the default automaton ----------------------------------------------------- +SBotChatAutomatonState BotChatStatesDefault[]= +{ + SBotChatAutomatonState(&BotChatPageIntro,2,3,4,1), // 0 - friendly/ neutral/ hostile/ done + SBotChatAutomatonState(&BotChatPageDone), // 1 + SBotChatAutomatonState(&BotChatPageFriendly,5,6,7,1), // 2 - more news/ buy sell/ mission/ done + SBotChatAutomatonState(&BotChatPageNeutral,6,7,1), // 3 - buy sell/ mission/ done + SBotChatAutomatonState(&BotChatPageHostile,1), // 4 - done + SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 5 - friendly/ done + SBotChatAutomatonState(&BotChatPageShop,3,1), // 6 - neutral/ done + SBotChatAutomatonState(&BotChatPageMissionShop,3,1), // 7 - neutral/ done +}; +SBotChatAutomaton BotChatDefault("default",BotChatStatesDefault,sizeof(BotChatStatesDefault)/sizeof(BotChatStatesDefault[0])); + +// the automaton for merchants ----------------------------------------------- +SBotChatAutomatonState BotChatStatesMerchant[]= +{ + SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 0 - shop/ done + SBotChatAutomatonState(&BotChatPageDone), // 1 + SBotChatAutomatonState(&BotChatPageShop,0,1), // 2 - news/ done +}; +SBotChatAutomaton BotChatMerchant("merchant",BotChatStatesMerchant,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0])); + +// the automaton for walkers and talkers ------------------------------------- +SBotChatAutomatonState BotChatStatesWalkerTalker[]= +{ + SBotChatAutomatonState(&BotChatPageHostile,1), // 0 + SBotChatAutomatonState(&BotChatPageDone) // 1 +}; +SBotChatAutomaton BotChatWalkerTalker("walker talker",BotChatStatesWalkerTalker,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0])); + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Represetnation of a conversation between a player and a bot +// includes conversation automaton state data + +struct CBotChat +{ + CBotChat () : Player(CEntityId::Unknown), Bot(CEntityId::Unknown) { } + CBotChat (CEntityId player, CEntityId bot, SBotChatAutomaton *automaton) + { + Player=player; + Bot=bot; + setAutomaton(automaton); + } + + void setState(uint32 state) + { + if (state>=Automaton->Size && state!=std::numeric_limits::max()) + { + nlwarning("CBotChatEntry()::setState: Invalid state: %d",state); + return; + } + + // if there is already a page open close it + if (CurrentStateSize) + Automaton->States[CurrentState].Page->PageType->close(Player,Bot); + + // open the new page + CurrentState=state; + if (state==std::numeric_limits::max()) + Done=true; + else + Done=!Automaton->States[CurrentState].Page->PageType->open(Player,Bot); + + // transmit the new page id to the client + uint32 happyness=10; + BOTCHATTYPE::TBotChatInterfaceId interfaceId=Done?BOTCHATTYPE::Done:Automaton->States[CurrentState].Page->ClientInterfaceId; + NEWSTYPE::TNewsType newsType=NEWSTYPE::Unknown; + + CMessage msgout("BOT_CHAT_SELECT_INTERFACE"); + msgout.serial (Player); + msgout.serial (happyness); + msgout.serialEnum (interfaceId); + msgout.serialEnum (newsType); + sendMessageViaMirror("IOS", msgout); + } + + void setAutomaton(SBotChatAutomaton *automaton) + { + Automaton=automaton; + CurrentState=std::numeric_limits::max(); // set this to a ~0 so that setState doesn't try to clse existing page + setState(0); + } + + void selectEntry (sint8 userInput) + { + // select the new page + if ((unsigned)userInput >= Automaton->States[CurrentState].Page->NumOptions) + { + nlwarning ("CBotChatEntry::selectEntry: For player %s: input out of bounds: %d", Player.toString().c_str(), userInput); + return; + } + + // advance through the state table + setState(Automaton->States[CurrentState].On[userInput]); + } + + void endChat () + { + setState(std::numeric_limits::max()); + } + + CEntityId Player; + CEntityId Bot; + SBotChatAutomaton *Automaton; + uint32 CurrentState; + bool Done; +}; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Singleton manager class central to the bot chat system + +class CBotChatManager +{ +public: + + static void newChat(CEntityId player, CEntityId bot) + { + // make sure the player isn't already chatting + map::iterator it = BotChatMap.find (player); + if (it != BotChatMap.end()) + return; + + // a bit of logging + nlinfo ("new chat between player %s and bot %s", player.toString().c_str(), bot.toString().c_str()); + + // call the CbBegin() callback to get the name of the automaton to use + string automatonName; + if (CbBegin!=NULL) + automatonName=CbBegin(player,bot); + else + automatonName="default"; + SBotChatAutomaton *automaton=SBotChatAutomaton::getAutomatonByName(automatonName); + if (automaton==NULL) + { + nlwarning("- ignoring bot chat request as automaton '%s' not found",automatonName.c_str()); + return; + } + + // setup the new chat + BotChatMap[player] = CBotChat(player, bot, automaton); + } + + static void endChat(CEntityId player) + { + CEntityId bot; // for use in callback ... at end of routine + + map::iterator it = BotChatMap.find (player); + if (it != BotChatMap.end()) + { + bot=(*it).second.Bot; + + nlinfo ("end of bot chat between player %s and bot %s", player.toString().c_str(),bot.toString().c_str()); + + // if the chat is still active then stop it + if ((*it).second.Done) + (*it).second.endChat(); + + // remove the map entry + BotChatMap.erase (it); + + // **** this code may be dodgy 'cos its in a dtor + // **** if it is dodgy then we need to migrate from an STL map + // **** to some kind of custom structure that we can guarantee OK + } + + if (CbEnd!=NULL) + CbEnd(player,bot); + } + + static void treatInput(CEntityId player, sint8 userInput) + { + // locate the bot chat for the given player + map::iterator it = BotChatMap.find (player); + if (it == BotChatMap.end()) + { + nlwarning ("No bot chat with the player %s", player.toString().c_str()); + return; + } + + // pass the player input to the bot chat handler + (*it).second.selectEntry(userInput); + + // check whether the bot chat is finished + if ((*it).second.Done) + endChat(player); + } + + + // static data for the singleton ----------------------------------------- + static CBotChatInterface::TCallbackBegin CbBegin; + static CBotChatInterface::TCallbackEnd CbEnd; + static map BotChatMap; +}; +CBotChatInterface::TCallbackBegin CBotChatManager::CbBegin; +CBotChatInterface::TCallbackEnd CBotChatManager::CbEnd; +map CBotChatManager::BotChatMap; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// message callbacks and callback table + +static void cbBotChatStart (CMessage& msgin, const string &serviceName, uint16 serviceId ) +{ + CEntityId player, bot; + + msgin.serial( player ); + msgin.serial( bot ); + + CBotChatManager::newChat (player, bot); +} + +static void cbBotChatSelectAnswer (CMessage& msgin, const string &serviceName, uint16 serviceId ) +{ + CEntityId player, bot; + sint8 answer; + + msgin.serial( player ); + msgin.serial( answer ); + + CBotChatManager::treatInput (player, answer); +} + + +static TUnifiedCallbackItem CbArray[]= +{ + { "BOT_CHAT_START", cbBotChatStart }, + { "BOT_CHAT_SELECT_ANSWER", cbBotChatSelectAnswer }, +}; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Interface class providing API for bot chat system + +void CBotChatInterface::init(CBotChatInterface::TCallbackBegin cbBegin,CBotChatInterface::TCallbackEnd cbEnd) +{ + CBotChatManager::CbBegin=cbBegin; + CBotChatManager::CbEnd=cbEnd; + CUnifiedNetwork::getInstance()->addCallbackArray(CbArray, sizeof (CbArray) / sizeof (CbArray[0])); +} + +void CBotChatInterface::release() +{ +} + +void CBotChatInterface::getBotChatPartners(CEntityId bot,vector &result) +{ + map::iterator it; + for (it=CBotChatManager::BotChatMap.begin();it!=CBotChatManager::BotChatMap.end();++it) + if ((*it).second.Bot==bot) + result.push_back((*it).first); +} + +void CBotChatInterface::endChatForPlayer(CEntityId player) +{ + CBotChatManager::endChat(player); +} + +void CBotChatInterface::endAllChatForBot(CEntityId bot) +{ + map::iterator it=CBotChatManager::BotChatMap.begin(); + while (it!=CBotChatManager::BotChatMap.end()) + { + map::iterator next=it; + if ((*it).second.Bot==bot) + CBotChatManager::endChat((*it).first); + it=next; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +////////////////////// +/// The following is probably out of date but I'm copying it here just in case... + + + +#if 0 + + + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// the parent class for bot chat page type classes + +class CBotChatPageType +{ +public: + // virtual interface ---------------------------------------------------- + virtual bool open(const CEntityId &player, const CEntityId &bot)=0; + virtual bool close(const CEntityId &player, const CEntityId &bot)=0; +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for bot chat pages + +struct SBotChatPage +{ + // ctor ----------------------------------------------------------------- + SBotChatPage( + BOTCHATTYPE::TBotChatInterfaceId clientInterfaceId, + CBotChatPageType * chatPageType, + uint numOptions + ) + { + ClientInterfaceId= clientInterfaceId; + PageType= chatPageType; + NumOptions= numOptions; + } + + // data ----------------------------------------------------------------- + BOTCHATTYPE::TBotChatInterfaceId ClientInterfaceId; // id of interface to display on client + CBotChatPageType * PageType; // type of chat page + uint NumOptions; // number of options for player to click on +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for a state for bot chat automatons + +struct SBotChatAutomatonState +{ + // public data ---------------------------------------------------------- + SBotChatPage *Page; + uint On[5]; // value to return on player click of slot 0..4 + + // ctor ----------------------------------------------------------------- + SBotChatAutomatonState(SBotChatPage *page,uint on0=std::numeric_limits::max(),uint on1=std::numeric_limits::max(),uint on2=std::numeric_limits::max(),uint on3=std::numeric_limits::max(),uint on4=std::numeric_limits::max()) + { + Page=page; + On[0]=on0; + On[1]=on1; + On[2]=on2; + On[3]=on3; + On[4]=on4; + + // make sure the number of arguments supplied corresponds to the + // number of options prresent on the user interfac page + nlassert(page->NumOptions>=0 && page->NumOptions<=4); + nlassert(page->NumOptions==0 || On[page->NumOptions-1]!=std::numeric_limits::max()); + nlassert(page->NumOptions==4 || On[page->NumOptions]==std::numeric_limits::max()); + } +}; + + +////////////////////////////////////////////////////////////////////////////// +// the structure for a bot chat automatons & a singleton for indexing +// automatons by name + +struct SBotChatAutomaton +{ + // public data ---------------------------------------------------------- + string Name; + SBotChatAutomatonState *States; + uint Size; + + // ctor ----------------------------------------------------------------- + SBotChatAutomaton(string name, SBotChatAutomatonState *states,uint size) + { + Name=name; + States=states; + Size=size; + + if (NameMap.find(name)!=NameMap.end()) + { + nlwarning("SBotChatAutomaton::SBotChatAutomaton(): More than one instance with name: %s",name.c_str()); + return; + } + NameMap[name]=this; + } + + // dtor ----------------------------------------------------------------- + ~SBotChatAutomaton() + { + map ::iterator it=NameMap.find(Name); + if (it!=NameMap.end() && (*it).second==this) + NameMap.erase(it); + // don't try to display a warning in a dtor as the warning system is + // probably already down + } + + + // singleton methods ---------------------------------------------------- + static SBotChatAutomaton *getAutomatonByName(string name) + { + map ::iterator it=NameMap.find(name); + if (it==NameMap.end()) + return NULL; + return (*it).second; + } + + // singleton data ------------------------------------------------------- + static map NameMap; +}; +map SBotChatAutomaton::NameMap; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Implementation of different code modules for handling different bot +// chat page types + +////////////////////////////////////////////////////////////////////////////// +// this is a dummy page used to terminate chats + +class CBotChatPageTypeDone: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return false; // stop the bot chat! + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return false; + } +} +BotChatPageTypeDone; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that contains static text and buttons for +// player to click on/ select + +class CBotChatPageTypeTextOnly: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return true; + } +} +BotChatPageTypeTextOnly; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays NEWS as well as other text + +class CBotChatPageTypeNews: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + return true; + } +} +BotChatPageTypeNews; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays a SHOP interface + +class CBotChatPageTypeShop: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("player %s entered trade page", player.toString().c_str()); + CMessage msgout( "TRADE_BEGIN" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("end of trade with player %s", player.toString().c_str()); + CMessage msgout( "TRADE_END" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + sendMessageViaMirror( "EGS", msgout ); + return true; + } +} +BotChatPageTypeShop; + + +////////////////////////////////////////////////////////////////////////////// +// definition for a chat page that displays a MISSION SHOP interface + +class CBotChatPageTypeMissionShop: public CBotChatPageType +{ +public: + virtual bool open(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("player %s entered mission page", player.toString().c_str()); + CMessage msgout( "MISSION_LIST_BEGIN" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "WOS", msgout ); + return true; + } + + virtual bool close(const CEntityId &player, const CEntityId &bot) + { + nlinfo ("end of mission page with player %s", player.toString().c_str()); + CMessage msgout( "MISSION_LIST_END" ); + msgout.serial( const_cast(player) ); + msgout.serial( const_cast(bot) ); + sendMessageViaMirror( "EGS", msgout ); + return true; + } +} +BotChatPageTypeMissionShop; + + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Definitions of bot chat pages and automatons + +// define the usable bot chat pages ------------------------------------------ +SBotChatPage BotChatPageIntro (BOTCHATTYPE::Intro, &BotChatPageTypeTextOnly, 4); +SBotChatPage BotChatPageFriendly (BOTCHATTYPE::FriendlyMainPage, &BotChatPageTypeNews, 4); +SBotChatPage BotChatPageNeutral (BOTCHATTYPE::NeutralMainPage, &BotChatPageTypeNews, 3); +SBotChatPage BotChatPageHostile (BOTCHATTYPE::NastyMainPage, &BotChatPageTypeTextOnly, 1); +SBotChatPage BotChatPageMoreNews (BOTCHATTYPE::MoreNewsPage, &BotChatPageTypeNews, 2); +SBotChatPage BotChatPageShop (BOTCHATTYPE::BuySellPage, &BotChatPageTypeShop, 2); +SBotChatPage BotChatPageMissionShop (BOTCHATTYPE::MissionsPage, &BotChatPageTypeMissionShop, 2); +SBotChatPage BotChatPageDone (BOTCHATTYPE::Done, &BotChatPageTypeDone, 0); + +// the default automaton ----------------------------------------------------- +SBotChatAutomatonState BotChatStatesDefault[]= +{ + SBotChatAutomatonState(&BotChatPageIntro,2,3,4,1), // 0 - friendly/ neutral/ hostile/ done + SBotChatAutomatonState(&BotChatPageDone), // 1 + SBotChatAutomatonState(&BotChatPageFriendly,5,6,7,1), // 2 - more news/ buy sell/ mission/ done + SBotChatAutomatonState(&BotChatPageNeutral,6,7,1), // 3 - buy sell/ mission/ done + SBotChatAutomatonState(&BotChatPageHostile,1), // 4 - done + SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 5 - friendly/ done + SBotChatAutomatonState(&BotChatPageShop,3,1), // 6 - neutral/ done + SBotChatAutomatonState(&BotChatPageMissionShop,3,1), // 7 - neutral/ done +}; +SBotChatAutomaton BotChatDefault("default",BotChatStatesDefault,sizeof(BotChatStatesDefault)/sizeof(BotChatStatesDefault[0])); + +// the automaton for merchants ----------------------------------------------- +SBotChatAutomatonState BotChatStatesMerchant[]= +{ + SBotChatAutomatonState(&BotChatPageMoreNews,2,1), // 0 - shop/ done + SBotChatAutomatonState(&BotChatPageDone), // 1 + SBotChatAutomatonState(&BotChatPageShop,0,1), // 2 - news/ done +}; +SBotChatAutomaton BotChatMerchant("merchant",BotChatStatesMerchant,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0])); + +// the automaton for walkers and talkers ------------------------------------- +SBotChatAutomatonState BotChatStatesWalkerTalker[]= +{ + SBotChatAutomatonState(&BotChatPageHostile,1), // 0 + SBotChatAutomatonState(&BotChatPageDone) // 1 +}; +SBotChatAutomaton BotChatWalkerTalker("walker talker",BotChatStatesWalkerTalker,sizeof(BotChatStatesMerchant)/sizeof(BotChatStatesMerchant[0])); + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Represetnation of a conversation between a player and a bot +// includes conversation automaton state data + +struct CBotChat +{ + CBotChat () : Player(CEntityId::Unknown), Bot(CEntityId::Unknown) { } + CBotChat (CEntityId player, CEntityId bot, SBotChatAutomaton *automaton) + { + Player=player; + Bot=bot; + setAutomaton(automaton); + } + + void setState(uint32 state) + { + if (state>=Automaton->Size && state!=std::numeric_limits::max()) + { + nlwarning("CBotChatEntry()::setState: Invalid state: %d",state); + return; + } + + // if there is already a page open close it + if (CurrentStateSize) + Automaton->States[CurrentState].Page->PageType->close(Player,Bot); + + // open the new page + CurrentState=state; + if (state==std::numeric_limits::max()) + Done=true; + else + Done=!Automaton->States[CurrentState].Page->PageType->open(Player,Bot); + + // transmit the new page id to the client + uint32 happyness=10; + BOTCHATTYPE::TBotChatInterfaceId interfaceId=Done?BOTCHATTYPE::Done:Automaton->States[CurrentState].Page->ClientInterfaceId; + NEWSTYPE::TNewsType newsType=NEWSTYPE::Unknown; + + CMessage msgout("BOT_CHAT_SELECT_INTERFACE"); + msgout.serial (Player); + msgout.serial (happyness); + msgout.serialEnum (interfaceId); + msgout.serialEnum (newsType); + sendMessageViaMirror("IOS", msgout); + } + + void setAutomaton(SBotChatAutomaton *automaton) + { + Automaton=automaton; + CurrentState=std::numeric_limits::max(); // set this to a std::numeric_limits::max() so that setState doesn't try to clse existing page + setState(0); + } + + void selectEntry (sint8 userInput) + { + // select the new page + if ((unsigned)userInput >= Automaton->States[CurrentState].Page->NumOptions) + { + nlwarning ("CBotChatEntry::selectEntry: For player %s: input out of bounds: %d", Player.toString().c_str(), userInput); + return; + } + + // advance through the state table + setState(Automaton->States[CurrentState].On[userInput]); + } + + void endChat () + { + setState(std::numeric_limits::max()); + } + + CEntityId Player; + CEntityId Bot; + SBotChatAutomaton *Automaton; + uint32 CurrentState; + bool Done; +}; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Singleton manager class central to the bot chat system + +class CBotChatManager +{ +public: + + static void newChat(CEntityId player, CEntityId bot) + { + // make sure the player isn't already chatting + map::iterator it = BotChatMap.find (player); + if (it != BotChatMap.end()) + return; + + // a bit of logging + nlinfo ("new chat between player %s and bot %s", player.toString().c_str(), bot.toString().c_str()); + + // call the CbBegin() callback to get the name of the automaton to use + string automatonName; + if (CbBegin!=NULL) + automatonName=CbBegin(player,bot); + else + automatonName="default"; + SBotChatAutomaton *automaton=SBotChatAutomaton::getAutomatonByName(automatonName); + if (automaton==NULL) + { + nlwarning("- ignoring bot chat request as automaton '%s' not found",automatonName.c_str()); + return; + } + + // setup the new chat + BotChatMap[player] = CBotChat(player, bot, automaton); + } + + static void endChat(CEntityId player) + { + CEntityId bot; // for use in callback ... at end of routine + + map::iterator it = BotChatMap.find (player); + if (it != BotChatMap.end()) + { + bot=(*it).second.Bot; + + nlinfo ("end of bot chat between player %s and bot %s", player.toString().c_str(),bot.toString().c_str()); + + // if the chat is still active then stop it + if ((*it).second.Done) + (*it).second.endChat(); + + // remove the map entry + BotChatMap.erase (it); + + // **** this code may be dodgy 'cos its in a dtor + // **** if it is dodgy then we need to migrate from an STL map + // **** to some kind of custom structure that we can guarantee OK + } + + if (CbEnd!=NULL) + CbEnd(player,bot); + } + + static void treatInput(CEntityId player, sint8 userInput) + { + // locate the bot chat for the given player + map::iterator it = BotChatMap.find (player); + if (it == BotChatMap.end()) + { + nlwarning ("No bot chat with the player %s", player.toString().c_str()); + return; + } + + // pass the player input to the bot chat handler + (*it).second.selectEntry(userInput); + + // check whether the bot chat is finished + if ((*it).second.Done) + endChat(player); + } + + + // static data for the singleton ----------------------------------------- + static CBotChatInterface::TCallbackBegin CbBegin; + static CBotChatInterface::TCallbackEnd CbEnd; + static map BotChatMap; +}; +CBotChatInterface::TCallbackBegin CBotChatManager::CbBegin; +CBotChatInterface::TCallbackEnd CBotChatManager::CbEnd; +map CBotChatManager::BotChatMap; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// message callbacks and callback table + +static void cbBotChatStart (CMessage& msgin, const string &serviceName, uint16 serviceId ) +{ + CEntityId player, bot; + + msgin.serial( player ); + msgin.serial( bot ); + + CBotChatManager::newChat (player, bot); +} + +static void cbBotChatSelectAnswer (CMessage& msgin, const string &serviceName, uint16 serviceId ) +{ + CEntityId player, bot; + sint8 answer; + + msgin.serial( player ); + msgin.serial( answer ); + + CBotChatManager::treatInput (player, answer); +} + + +static TUnifiedCallbackItem CbArray[]= +{ + { "BOT_CHAT_START", cbBotChatStart }, + { "BOT_CHAT_SELECT_ANSWER", cbBotChatSelectAnswer }, +}; + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Interface class providing API for bot chat system + +void CBotChatInterface::init(CBotChatInterface::TCallbackBegin cbBegin,CBotChatInterface::TCallbackEnd cbEnd) +{ + CBotChatManager::CbBegin=cbBegin; + CBotChatManager::CbEnd=cbEnd; + CUnifiedNetwork::getInstance()->addCallbackArray(CbArray, sizeof (CbArray) / sizeof (CbArray[0])); +} + +void CBotChatInterface::release() +{ +} + +void CBotChatInterface::getBotChatPartners(CEntityId bot,vector &result) +{ + map::iterator it; + for (it=CBotChatManager::BotChatMap.begin();it!=CBotChatManager::BotChatMap.end();++it) + if ((*it).second.Bot==bot) + result.push_back((*it).first); +} + +void CBotChatInterface::endChatForPlayer(CEntityId player) +{ + CBotChatManager::endChat(player); +} + +void CBotChatInterface::endAllChatForBot(CEntityId bot) +{ + map::iterator it=CBotChatManager::BotChatMap.begin(); + while (it!=CBotChatManager::BotChatMap.end()) + { + map::iterator next=it; + if ((*it).second.Bot==bot) + CBotChatManager::endChat((*it).first); + it=next; + } +} + +#endif + +#endif diff --git a/ryzom/server/src/ai_service/bot_chat_interface.h b/ryzom/server/src/ai_service/bot_chat_interface.h new file mode 100644 index 000000000..3586b737f --- /dev/null +++ b/ryzom/server/src/ai_service/bot_chat_interface.h @@ -0,0 +1,58 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#error "Deprecated" + +#ifndef RYAI_BOT_CHAT_INTERFACE_H +#define RYAI_BOT_CHAT_INTERFACE_H + +// Nel Misc +#include "nel/misc/types_nl.h" +#include "nel/misc/entity_id.h" +#include "game_share/news_types.h" +#include "game_share/bot_chat_types.h" + +// the class +class CBotChatInterface +{ +public: + // the callback type for user callbacks for start of bot chat + // returns the name of the bot chat automaton to use + // if an empty string is returned no bot chat is launched + typedef std::string (*TCallbackBegin)(NLMISC::CEntityId player,NLMISC::CEntityId bot); + + // the callback types for user callback for end of bot chat + // called just after the chat session has been closed + typedef void (*TCallbackEnd)(NLMISC::CEntityId player,NLMISC::CEntityId bot); + +public: + // classic init() and release() + static void init(TCallbackBegin cbBegin=NULL,TCallbackEnd cbEnd=NULL); + static void release(); + + // build a vector of the players currently chatting with a given bot + // this routine may not be very fast as the entire bot chat map is + // parsed in order to build the vector + static void getBotChatPartners(NLMISC::CEntityId bot,std::vector &result); + + // routines to force the end of a bot chat + static void endChatForPlayer(NLMISC::CEntityId player); + static void endAllChatForBot(NLMISC::CEntityId bot); +}; + +#endif diff --git a/ryzom/server/src/ai_service/combat_interface.cpp b/ryzom/server/src/ai_service/combat_interface.cpp index 94d874bd8..bbdf61f52 100644 --- a/ryzom/server/src/ai_service/combat_interface.cpp +++ b/ryzom/server/src/ai_service/combat_interface.cpp @@ -82,8 +82,8 @@ void CBSAIEventReportMsg::callback(const std::string &name, NLNET::TServiceId id { uint8 actionType=ActionType[i]; if ( ( actionType!=ACTNATURE::FIGHT - && actionType!=ACTNATURE::OFFENSIVE_MAGIC - && actionType!=ACTNATURE::CURATIVE_MAGIC ) + || actionType!=ACTNATURE::OFFENSIVE_MAGIC + || actionType!=ACTNATURE::CURATIVE_MAGIC ) && AggroAdd[i]==0 ) continue; diff --git a/ryzom/server/src/ai_service/commands.cpp b/ryzom/server/src/ai_service/commands.cpp index 0cea6f93c..6e92be533 100644 --- a/ryzom/server/src/ai_service/commands.cpp +++ b/ryzom/server/src/ai_service/commands.cpp @@ -1710,19 +1710,6 @@ NLMISC_COMMAND(script2,"execute a script for groups containing a bot matching th return true; } -static std::string scriptHex_decode(std::string str) -{ - std::string output; - for (size_t i=0; i<(str.length()-1); i+=2) - { - char c1 = str[i], c2 = str[i+1]; - char buffer[3] = { c1, c2, '\0' }; - char c = (char)strtol(buffer, NULL, 16); - output.push_back(c); - } - return output; -} - NLMISC_COMMAND(scriptHex,"execute a hex-encoded script for a group in the given aIInstance [buffered]"," ") { vector _args = args; diff --git a/ryzom/server/src/ai_service/event_reaction.cpp b/ryzom/server/src/ai_service/event_reaction.cpp index a34d0cba9..54e62d789 100644 --- a/ryzom/server/src/ai_service/event_reaction.cpp +++ b/ryzom/server/src/ai_service/event_reaction.cpp @@ -66,6 +66,14 @@ void CAIEventReaction::setState(uint32 alias) _eventMgr->addReaction(this); } +uint32 CAIEventReaction::getState() +{ + if (_states.empty()) + return 0; + + return _states[0]; +} + void CAIEventReaction::setGroup(uint32 alias) { if (!_eventMgr.isNULL()) diff --git a/ryzom/server/src/ai_service/event_reaction.h b/ryzom/server/src/ai_service/event_reaction.h index 31689f613..1338503a6 100644 --- a/ryzom/server/src/ai_service/event_reaction.h +++ b/ryzom/server/src/ai_service/event_reaction.h @@ -85,12 +85,13 @@ public: void setEvent (const std::string &eventName,CStateMachine *container);// this causes the event reaction to be linked to an event manager void setState (uint32 alias); // used for events that apply to a specific state + uint32 getState (); // used for events that apply to a specific state void setGroup (uint32 alias); // used for events that apply to a specific group // the following routine shouldn't be needed it should be superceded by a better alternative // in the event manager template - bool testCompatibility(CStateInstance *const stateInstance, const TState *const state) const; + bool testCompatibility(CStateInstance *const stateInstance, const TState *const state) const; protected: // protected data --------------------------------------------------- diff --git a/ryzom/server/src/ai_service/event_reaction_container.h b/ryzom/server/src/ai_service/event_reaction_container.h index 92032338c..e754877da 100644 --- a/ryzom/server/src/ai_service/event_reaction_container.h +++ b/ryzom/server/src/ai_service/event_reaction_container.h @@ -27,7 +27,10 @@ class CStateMachine #endif { public: - CStateMachine() { } + CStateMachine() + { + _LastStateEventAlias = 0; + } virtual ~CStateMachine() { clearEventContainerContent (); @@ -171,7 +174,10 @@ public: CAliasCont& states() { return _states; } CAliasCont const& cstStates() const { return _states; } + uint32 getLastStateEventAlias() { return ++_LastStateEventAlias; } + protected: + uint32 _LastStateEventAlias; CAliasCont _states; CAliasCont _eventReactions; std::map > _eventNameMap; diff --git a/ryzom/server/src/ai_service/messages.cpp b/ryzom/server/src/ai_service/messages.cpp index ae22cee48..74f229945 100644 --- a/ryzom/server/src/ai_service/messages.cpp +++ b/ryzom/server/src/ai_service/messages.cpp @@ -38,6 +38,7 @@ #include "ai_profile_fauna.h" // for CCorpseFaunaProfile #include "dyn_mission.h" #include "mirrors.h" +#include "commands.h" #include "game_share/tick_event_handler.h" @@ -684,6 +685,8 @@ void CMessages::init() TRANSPORT_CLASS_REGISTER (CAILostAggroMsg); TRANSPORT_CLASS_REGISTER (CAIGainAggroMsg); + TRANSPORT_CLASS_REGISTER (CAINotifyDeathMsg); + TRANSPORT_CLASS_REGISTER (CAIPlayerRespawnMsgImp); TRANSPORT_CLASS_REGISTER (CAIAskForInfosOnEntityImp); @@ -1022,7 +1025,7 @@ void CBSAIDeathReport::callback(const std::string &name, NLNET::TServiceId id) continue; CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(aggroIt->first); if (!ep) - continue; + continue; CBotPlayer const* const player = NLMISC::safe_cast(ep); if (!player) continue; @@ -1031,7 +1034,6 @@ void CBSAIDeathReport::callback(const std::string &name, NLNET::TServiceId id) playerAggroable.push_back(aggroIt->first); } -#ifdef RYZOM_FORGE NLNET::CMessage msgout("TRIGGER_WEBIG"); if (!eventBotKilled.empty()) msgout.serial(eventBotKilled); @@ -1045,7 +1047,6 @@ void CBSAIDeathReport::callback(const std::string &name, NLNET::TServiceId id) msgout.serial(playerAggroable[i]); } sendMessageViaMirror("EGS", msgout); -#endif } } break; @@ -1149,9 +1150,37 @@ void sAggroGain(TDataSetRow playerBot, TDataSetRow targetBot) || CMirrors::getEntityId(playerBot).getType()!=RYZOMID::player) return; - CAIGainAggroMsg msg(targetBot, playerBot); + CAIGainAggroMsg msg(targetBot, playerBot, false); msg.send("EGS"); + CAIEntityPhysical *botEntity = CAIEntityPhysicalLocator::getInstance()->getEntity(targetBot); + CSpawnBotNpc* bot = dynamic_cast(botEntity); + if (bot && bot->getPersistent().getOwner()) { + std::string groupName = bot->getPersistent().getOwner()->getName(); + if (groupName.substr(0, 6) == "group_") { + if (groupName.find("_guardian_") != std::string::npos) { + strFindReplace(groupName, "_guardian_", "_boss_"); + std::vector bots; + /// try to find the bot name + buildFilteredBotList(bots, groupName); + if (!bots.empty()) + { + FOREACH(itBot, std::vector, bots) + { + CBot* bot = *itBot; + CSpawnBot *const sp = bot->getSpawnObj(); + if (sp && sp->isAlive()) + { + CAIGainAggroMsg msg(sp->dataSetRow(), playerBot, true); + msg.send("EGS"); + } + } + } + } + } + } + + CBotPlayer *player=NLMISC::safe_cast(entity); if (!player) return; diff --git a/ryzom/server/src/ai_service/nf_grp.cpp b/ryzom/server/src/ai_service/nf_grp.cpp index fde5a557c..b37432457 100644 --- a/ryzom/server/src/ai_service/nf_grp.cpp +++ b/ryzom/server/src/ai_service/nf_grp.cpp @@ -1630,11 +1630,13 @@ Arguments: s(actionName),s(url) -> // CGroup void setUrl_ss_(CStateInstance* entity, CScriptStack& stack) { + CGroup* group = entity->getGroup(); + std::string url = (std::string)stack.top();stack.pop(); std::string actionName = (std::string)stack.top();stack.pop(); CCreatureSetUrlMsg msg; - FOREACH(botIt, CCont, entity->getGroup()->bots()) + FOREACH(botIt, CCont, group->bots()) { CSpawnBot* pbot = botIt->getSpawnObj(); if (pbot!=NULL) @@ -1642,7 +1644,9 @@ void setUrl_ss_(CStateInstance* entity, CScriptStack& stack) msg.Entities.push_back(pbot->dataSetRow()); } } - + CSpawnGroup* spawnGroup = group->getSpawnObj(); + spawnGroup->setActionName(actionName); + spawnGroup->setUrl(url); msg.ActionName = actionName; msg.Url = url; msg.send(egsString); diff --git a/ryzom/server/src/ai_service/nf_grp_npc.cpp b/ryzom/server/src/ai_service/nf_grp_npc.cpp index 12e085b76..5444a3e3b 100644 --- a/ryzom/server/src/ai_service/nf_grp_npc.cpp +++ b/ryzom/server/src/ai_service/nf_grp_npc.cpp @@ -153,7 +153,7 @@ void setOupostMode_ss_(CStateInstance* entity, CScriptStack& stack) } npcGroup->setOutpostSide(side); - npcGroup->setOutpostFactions(side); + npcGroup->setOutpostFactions(aliasStr, side); FOREACH(botIt, CCont, npcGroup->bots()) { CBot* bot = *botIt; @@ -1610,7 +1610,6 @@ Then user events are triggered on the group to inform it about what happens: - user_event_3: triggered after the player has given the mission items to the npc. Warning: this function can only be called after the event "player_target_npc". -Warning: only works on an R2 shard for R2 plot items. Arguments: s(missionItems), s(missionText), c(groupToNotify) -> @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...". @@ -1713,6 +1712,36 @@ void receiveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack) DEBUG_STOP; return; } + // if LD use this the function outside a ring shard + if (IsRingShard) + { + // Here we destroy the item: so we do not want that a user create a scenario where we destroy + // other players precious items + + static std::set r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess + // lazy intialisation + if (r2PlotItemSheetId.empty()) + { + for (uint32 i = 0 ; i <= 184 ; ++i) + { + r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i))); + } + } + + // A npc give a mission to take an item given by another npc + // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor + if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end()) + { + nlwarning("!!!!!!!!!!!!"); + nlwarning("!!!!!!!!!!!! Someone is trying to hack us"); + nlwarning("!!!!!!!!!!!!"); + nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt()); + nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId ); + nlwarning("!!!!!!!!!!!!"); + nlwarning("!!!!!!!!!!!!"); + return ; + } + } uint32 quantity; NLMISC::fromString(itemAndQty[1], quantity); @@ -1746,7 +1775,6 @@ Then user events are triggered on the group to inform it about what happens: - user_event_1: triggered after the player has received the mission items from the npc. Warning: this function can only be called after the event "player_target_npc". -Warning: only works on an R2 shard for R2 plot items. Arguments: s(missionItems), s(missionText), c(groupToNotify) -> @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...". @@ -1850,6 +1878,36 @@ void giveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack) return; } + + // if LD use this the function outside a ring shard + if (IsRingShard) + { + static std::set r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess + // lazy intialisation + if (r2PlotItemSheetId.empty()) + { + for (uint32 i = 0 ; i <= 184 ; ++i) + { + r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i))); + } + } + + // A npc give a mission to give a item to another npc + // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor + if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end()) + { + nlwarning("!!!!!!!!!!!!"); + nlwarning("!!!!!!!!!!!! Someone is trying to hack us"); + nlwarning("!!!!!!!!!!!!"); + nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt()); + nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId ); + nlwarning("!!!!!!!!!!!!"); + nlwarning("!!!!!!!!!!!!"); + return ; + } + + } + uint32 quantity; NLMISC::fromString(itemAndQty[1], quantity); if (quantity == 0) @@ -2605,7 +2663,7 @@ void rename_s_(CStateInstance* entity, CScriptStack& stack) msgout.serial(row); msgout.serial(name); sendMessageViaMirror("IOS", msgout); - bot->setCustomName(name); + spawnBot->getPersistent().setCustomName(name); } } } @@ -2668,6 +2726,182 @@ void maxHitRange_f_(CStateInstance* entity, CScriptStack& stack) } } + +//---------------------------------------------------------------------------- +/** @page code + +@subsection addUserModel_sss_ + +Arguments: -> + +arg0: is the user model id (a name) +arg1: is the base sheet +arg3: is the script (in hex) + +@code +()addUserModel("toto", "ccbha1", "xxxxx"); +@endcode + +*/ +void addUserModel_sss_(CStateInstance* entity, CScriptStack& stack) +{ + string script = stack.top(); + stack.pop(); + + string baseSheet = stack.top(); + stack.pop(); + + string userModelId = stack.top(); // prefix with ARK_ to prevent stupid overwrite + stack.pop(); + + IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); + CAIInstance* const aiInstance = dynamic_cast(managerParent); + if (!aiInstance) + return; + + std::vector args; + args.push_back(CAIActions::CArg(900+aiInstance->getInstanceNumber())); + args.push_back(CAIActions::CArg("ARK_"+userModelId)); + args.push_back(CAIActions::CArg(baseSheet)); + args.push_back(CAIActions::CArg(scriptHex_decode(script))); + + CAIActions::execute("USR_MDL", args); +} + +//---------------------------------------------------------------------------- +/** @page code + +@subsection addCustomLoot_ss_ + +Arguments: -> + +arg0: is the custom table id (a name) +arg1: is the script with syntax : :,:,... + +@code +()addUserModel("toto", ":,:,..."); +@endcode + +*/ +void addCustomLoot_ss_(CStateInstance* entity, CScriptStack& stack) +{ + string script = stack.top(); + stack.pop(); + + string customTableId = stack.top(); + stack.pop(); + + std::vector loots; + NLMISC::splitString(script, ",", loots); + if (loots.empty()) + return; + + IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); + CAIInstance* const aiInstance = dynamic_cast(managerParent); + if (!aiInstance) + return; + + std::vector args; + uint32 nbTables = 1; + args.push_back(CAIActions::CArg(nbTables)); + args.push_back(CAIActions::CArg(900+aiInstance->getInstanceNumber())); + uint32 lootSets = loots.size(); + args.push_back(CAIActions::CArg(lootSets)); + args.push_back(CAIActions::CArg(customTableId)); + float moneyProba = 0.0; + args.push_back(CAIActions::CArg(moneyProba)); + float moneyFactor = 0.0; + args.push_back(CAIActions::CArg(moneyFactor)); + uint32 moneyBase = 0; + args.push_back(CAIActions::CArg(moneyBase)); + + FOREACHC(it, std::vector, loots) + { + std::vector lootSet; + NLMISC::splitString(*it, ":", lootSet); + if (lootSet.size() == 2) + { + args.push_back(CAIActions::CArg(lootSet[0])); + args.push_back(CAIActions::CArg(scriptHex_decode(lootSet[1]))); + } + } + + CAIActions::execute("CUSTOMLT", args); +} + + +//---------------------------------------------------------------------------- +/** @page code + +@subsection setUserModel_s_ +Set the user model of a creature + +Arguments: -> s(userModel) + +@code +()setUserModel('my_model'); + +@endcode + +*/ +void setUserModel_s_(CStateInstance* entity, CScriptStack& stack) +{ + string userModel = stack.top(); + stack.pop(); + + CGroup* group = entity->getGroup(); + + FOREACH(botIt, CCont, group->bots()) + { + CBot* bot = *botIt; + + //if (!bot->isSpawned()) return; + + if (bot->getRyzomType() == RYZOMID::npc) + { + CBotNpc* botNpc = NLMISC::safe_cast(bot); + botNpc->setUserModelId("ARK_"+userModel); + } + } +} + + +//---------------------------------------------------------------------------- +/** @page code + +@subsection setCustomLoot_s_ +Set the custom loot of a creature + +Arguments: -> s(customTable) + +@code +()setUserModel('my_loot_table'); + +@endcode + +*/ +void setCustomLoot_s_(CStateInstance* entity, CScriptStack& stack) +{ + string customTable = stack.top(); + stack.pop(); + + CGroup* group = entity->getGroup(); + + FOREACH(botIt, CCont, group->bots()) + { + CBot* bot = *botIt; + + //if (!bot->isSpawned()) return; + + if (bot->getRyzomType() == RYZOMID::npc) + { + CBotNpc* botNpc = NLMISC::safe_cast(bot); + botNpc->setCustomLootTableId(customTable); + } + } +} + + ////---------------------------------------------------------------------------- ///** @page code // @@ -2728,6 +2962,135 @@ void maxHitRange_f_(CStateInstance* entity, CScriptStack& stack) // } //} +//---------------------------------------------------------------------------- +/** @page code + +@subsection setEventCode_sss +Sets and event to execute when event triggers + +Arguments: +s(event) -> @param[in] The name of the state +s(event) -> @param[in] The name of the event +s(code) -> @param[in] The string to execute code in hex + +@code +()maxHitRange(50); // Set the max hit range in 50 meters all npc in group +@endcode + +*/ +// CBotNpc +void setEventCode_sss_(CStateInstance* entity, CScriptStack& stack) +{ + string code = stack.top(); + stack.pop(); + + string event_name = stack.top(); + stack.pop(); + + string state_name = stack.top(); + stack.pop(); + + + //////////// Special Cases for Ark + if (event_name == "notify_on_death") + { + strFindReplace(code, "http://", ""); + strFindReplace(code, "https://", ""); + strFindReplace(code, "/index.php?", " "); + + CGroup* group = entity->getGroup(); + FOREACH(botIt, CCont, group->bots()) + { + CBot* bot = *botIt; + + if (!bot->isSpawned()) return; + + CBotNpc* botNpc = NLMISC::safe_cast(bot); + if (botNpc) + { + CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn(); + if (spawnBotNpc) + { + CAINotifyDeathMsg *msg = new CAINotifyDeathMsg; + msg->Url = code; + msg->TargetRowId = spawnBotNpc->dataSetRow(); + msg->send("EGS"); + } + } + } + return; + } + /////////////////////////////////////////////////////// + + + CAIEventDescription eventDescription; + CAIEventActionNode::TSmartPtr eventAction; + CAIEventReaction* event; + + // Create event handler + eventDescription.EventType = event_name; + + // Create event action + eventAction = new CAIEventActionNode; + eventAction->Action = "code"; + eventAction->Weight = 1; + + code = scriptHex_decode(code); + vector lines_of_code; + NLMISC::splitString(code, "\n", lines_of_code); + if (!lines_of_code.empty()) + { + FOREACHC(it, vector, lines_of_code) + { + nlinfo("Code: %s", (*it).c_str()); + eventAction->Args.push_back(*it); + } + } + + // Register event action + eventDescription.Action = eventAction; + eventAction = NULL; + + CGroup* group = entity->getGroup(); + CGroupNpc* npcGroup = NLMISC::safe_cast(group); + CStateMachine* sm = &npcGroup->getEventContainer(); + CAIStatePositional* statePositional; + + uint32 stateAlias = npcGroup->getStateAlias(state_name); + if (stateAlias == 0) + { + nlinfo("STATE %s not found !", state_name.c_str()); + statePositional = new CAIStatePositional(sm, 0, state_name); + npcGroup->setStateAlias(state_name, statePositional->getAlias()); + sm->states().addChild(statePositional); + } + else + { + statePositional = safe_cast(sm->states().getChildByAlias(stateAlias)); + } + + uint32 stateEventAlias = npcGroup->getStateEventAlias(event_name); + /*if (stateEventAlias != 0) + { + CAIEventReaction er = sm->eventReactions().getChildByAlias(stateEventAlias); + er->setGroup(0); + }*/ + // sm->eventReactions().removeChildByIndex(sm->eventReactions().getChildIndexByAlias(stateEventAlias)); + + // Register event handler + stateEventAlias = sm->getLastStateEventAlias(); + event = new CAIEventReaction(sm, stateEventAlias, eventDescription.EventType); + event->processEventDescription(&eventDescription, sm); + event->setGroup(npcGroup->getAlias()); + nlinfo("Add Event: %s(%d) in State : %d", event_name.c_str(), stateEventAlias, statePositional->getAlias()); + event->setState(statePositional->getAlias()); + npcGroup->setStateEventAlias(event_name, stateEventAlias); + + + sm->eventReactions().addChild(event); + event = NULL; +} + std::map nfGetNpcGroupNativeFunctions() { @@ -2793,6 +3156,13 @@ std::map nfGetNpcGroupNativeFunctions() REGISTER_NATIVE_FUNC(functions, maxHitRange_f_); + REGISTER_NATIVE_FUNC(functions, setEventCode_sss_); + + REGISTER_NATIVE_FUNC(functions, addUserModel_sss_); + REGISTER_NATIVE_FUNC(functions, addCustomLoot_ss_); + REGISTER_NATIVE_FUNC(functions, setUserModel_s_); + REGISTER_NATIVE_FUNC(functions, setCustomLoot_s_); + // REGISTER_NATIVE_FUNC(functions, hideMissionStepIcon_b_); // REGISTER_NATIVE_FUNC(functions, hideMissionGiverIcon_b_); diff --git a/ryzom/server/src/ai_service/nf_state_instance.cpp b/ryzom/server/src/ai_service/nf_state_instance.cpp index fef5e7568..a541903a7 100644 --- a/ryzom/server/src/ai_service/nf_state_instance.cpp +++ b/ryzom/server/src/ai_service/nf_state_instance.cpp @@ -537,6 +537,45 @@ void import_s_(CStateInstance* entity, CScriptStack& stack) nlwarning("unknown library %s", LibName.c_str()); } +void setVar_sf_(CStateInstance* entity, CScriptStack& stack) +{ + float value = (float)stack.top(); + stack.pop(); + std::string varName = (std::string)stack.top(); + TStringId varId=CStringMapper::map(varName); + stack.pop(); + + entity->setLogicVar(varId, value); +} + +void getVar_s_f(CStateInstance* entity, CScriptStack& stack) +{ + std::string varName = (std::string)stack.top(); + TStringId varId=CStringMapper::map(varName); + + stack.top() = entity->getLogicVar(varId); +} + +void setVar_ss_(CStateInstance* entity, CScriptStack& stack) +{ + std::string value = (std::string)stack.top(); + stack.pop(); + std::string varName = (std::string)stack.top(); + TStringId varId=CStringMapper::map(varName); + stack.pop(); + + entity->setStrLogicVar(varId, value); +} + +void getVar_s_s(CStateInstance* entity, CScriptStack& stack) +{ + std::string varName = (std::string)stack.top(); + TStringId varId=CStringMapper::map(varName); + + stack.top() = entity->getStrLogicVar(varId); +} + + //---------------------------------------------------------------------------- /** @page code @@ -804,6 +843,10 @@ std::map nfGetStateInstanceNativeFunctions() REGISTER_NATIVE_FUNC(functions, setNelVar_ss_); REGISTER_NATIVE_FUNC(functions, getNelVar_s_s); REGISTER_NATIVE_FUNC(functions, delNelVar_ss_); + REGISTER_NATIVE_FUNC(functions, setVar_sf_); + REGISTER_NATIVE_FUNC(functions, getVar_s_f); + REGISTER_NATIVE_FUNC(functions, setVar_ss_); + REGISTER_NATIVE_FUNC(functions, getVar_s_s); REGISTER_NATIVE_FUNC(functions, setGlobalNelVar_sf_); REGISTER_NATIVE_FUNC(functions, setGlobalNelVar_ss_); REGISTER_NATIVE_FUNC(functions, getStateName__s); diff --git a/ryzom/server/src/ai_service/npc_description_msg.cpp b/ryzom/server/src/ai_service/npc_description_msg.cpp index 62ba4d9f8..e13345577 100644 --- a/ryzom/server/src/ai_service/npc_description_msg.cpp +++ b/ryzom/server/src/ai_service/npc_description_msg.cpp @@ -719,7 +719,17 @@ bool CNpcChatProfileImp::parseChatArgs(CAIInstance *aiInstance, const std::strin // split to webpage name and the true webpage _WebPageName.clear(); AI_SHARE::stringToWordAndTail(tail, _WebPageName, tail); + _WebPage = tail; + + //replace  & by space (hack for Ark) + size_t pos = 0; + while((pos = _WebPage.find(" &", pos)) != string::npos) + { + _WebPage.replace(pos, 6, " "); + pos++; + } + } // guild creator diff --git a/ryzom/server/src/ai_service/script_compiler.h b/ryzom/server/src/ai_service/script_compiler.h index a03de1f8d..00a0ed2af 100644 --- a/ryzom/server/src/ai_service/script_compiler.h +++ b/ryzom/server/src/ai_service/script_compiler.h @@ -19,6 +19,20 @@ #include "script_vm.h" + +static std::string scriptHex_decode(std::string str) +{ + std::string output; + for (size_t i=0; i<(str.length()-1); i+=2) + { + char c1 = str[i], c2 = str[i+1]; + char buffer[3] = { c1, c2, '\0' }; + char c = (char)strtol(buffer, NULL, 16); + output.push_back(c); + } + return output; +} + // Forward declarations class CStateInstance; diff --git a/ryzom/server/src/ai_service/state_instance.h b/ryzom/server/src/ai_service/state_instance.h index a8bd93db6..79329bcbc 100644 --- a/ryzom/server/src/ai_service/state_instance.h +++ b/ryzom/server/src/ai_service/state_instance.h @@ -653,4 +653,5 @@ void CPersistentStateInstance::setStartState(CAIState* state) CStateInstance::init(_StartState); } +#include "event_reaction_include.h" #endif diff --git a/ryzom/server/src/entities_game_service/admin.cpp b/ryzom/server/src/entities_game_service/admin.cpp index 9d7243f89..8ae1a0e18 100644 --- a/ryzom/server/src/entities_game_service/admin.cpp +++ b/ryzom/server/src/entities_game_service/admin.cpp @@ -42,7 +42,6 @@ #include "nel/misc/algo.h" #include "nel/misc/sstring.h" #include "nel/misc/i18n.h" -#include "nel/misc/string_view.h" #include "nel/net/admin.h" #include "nel/net/service.h" @@ -58,6 +57,7 @@ #include "game_share/http_client.h" #include "server_share/log_command_gen.h" #include "server_share/r2_vision.h" +#include "server_share/mongo_wrapper.h" #include "egs_sheets/egs_sheets.h" #include "egs_sheets/egs_static_rolemaster_phrase.h" @@ -90,13 +90,13 @@ #include "guild_manager/guild_char_proxy.h" #include "guild_manager/fame_manager.h" #include "mission_manager/mission_solo.h" -#include "mission_manager/mission_solo.h" #include "position_flag_manager.h" //#include "name_manager.h" #include "zone_manager.h" #include "player_manager/character_game_event.h" #include "game_event_manager.h" #include "dyn_chat_egs.h" +#include "admin_log.h" #include "pvp_manager/pvp.h" #include "pvp_manager/pvp_manager_2.h" @@ -189,12 +189,17 @@ AdminCommandsInit[] = "summonPet", true, "connectUserChannel", true, "connectLangChannel", true, + "setDontTranslateLangs", true, "updateTarget", true, "resetName", true, "showOnline", true, - // Web commands managment + "openTargetApp", true, + + // DEPECRATED !!! "webExecCommand", true, + "webDelCommandsIds", true, + "webAddCommandsIds", true, "addPetAnimal", true, "addSkillPoints", true, @@ -232,14 +237,13 @@ AdminCommandsInit[] = "allowSummonPet", true, "setPetAnimalSatiety", true, "getPetAnimalSatiety", true, -#ifdef RYZOM_FORGE_PET_NAME "setPetAnimalName", true, -#endif "taskPass", true, "setFamePlayer", true, "guildMOTD", true, // CSR commands + "setSalt", true, "motd", false, "broadcast", false, "summon", true, @@ -377,9 +381,7 @@ AdminCommandsInit[] = "addFactionAttackableToTarget", true, "eventCreateNpcGroup", true, -#ifdef RYZOM_FORGE_EXECSCRIPT "eScript", true, -#endif "eventNpcGroupScript", true, "eventSetBotName", true, "eventSetBotScale", true, @@ -402,11 +404,10 @@ AdminCommandsInit[] = "eventSetBotFacing", true, "eventGiveControl", true, "eventLeaveControl", true, + "eventSpawnDamageLine", true, -#ifdef RYZOM_FORGE "setOrganization", true, "setOrganizationStatus", true, -#endif "addGuildBuilding", true, }; @@ -416,11 +417,14 @@ static string CommandsPrivilegesFileName; static string PositionFlagsFileName; static const char * DefaultPriv = ":DEV:"; +static string Salt; + // forward declarations static void loadCommandsPrivileges(const string & fileName, bool init); void cbRemoteClientCallback (uint32 rid, const std::string &cmd, const std::string &entityNames); // + // get AI instance and remove it form the group name bool getAIInstanceFromGroupName(string& groupName, uint32& instanceNumber) { @@ -553,8 +557,16 @@ void initAdmin () void initCommandsPrivileges(const std::string & fileName) { + //init the admin log system + CConfigFile::CVar *varPtr = IService::getInstance()->ConfigFile.getVarPtr("AdminLogFile"); + if ( !varPtr ) + AdminLog.init("admin_cmds.log"); + else + AdminLog.init( varPtr->asString() ); + H_AUTO(initCommandsPrivileges); + initSalt(); loadCommandsPrivileges(fileName, true); } @@ -684,6 +696,34 @@ void initPositionFlags(const std::string & fileName) PositionFlagsFileName = fileName; } +struct SaltFileLoadCallback: public IBackupFileReceiveCallback +{ + std::string FileName; + + SaltFileLoadCallback(const std::string& fileName): FileName(fileName) {} + + virtual void callback(const CFileDescription& fileDescription, NLMISC::IStream& dataStream) + { + // if the file isn't found then just give up + DROP_IF(fileDescription.FileName.empty()," file not found: "<< FileName, return); + + dataStream.serial(Salt); + nlinfo("Salt loaded : %s", Salt.c_str()); + } +}; + +void initSalt() +{ + H_AUTO(initSalt); + + string fileNameAndPath = Bsi.getLocalPath() + "salt_egs.txt"; + if (CFile::fileExists(fileNameAndPath)) + { + nlinfo("Salt loading : salt_egs.txt"); + Bsi.syncLoadFile("salt_egs.txt", new SaltFileLoadCallback("salt_egs.txt")); + } +} + string getStringFromHash(const string &hash) { ucstring finaltext; @@ -716,6 +756,21 @@ void getUCstringFromHash(const string &hash, ucstring &finaltext) } } +const string &getSalt() +{ + if (Salt.empty()) Salt = "qdRUODw9Vk78Y5MW4Ec1J0FKxjyNgrCfI"; + + return Salt; +} + +void saveSalt(const string salt) +{ + Salt = salt; + CBackupMsgSaveFile msg("salt_egs.txt", CBackupMsgSaveFile::SaveFile, Bsi ); + msg.DataMsg.serial(Salt); + Bsi.sendFile(msg); +} + static void selectEntities (const string &entityName, vector &entities) { H_AUTO(selectEntities); @@ -964,7 +1019,7 @@ ENTITY_VARIABLE(Money, "Money") if (get) { - value = toString(c->getMoney()); + value = toString("%" NL_I64 "u", c->getMoney()); } else { @@ -1016,7 +1071,7 @@ ENTITY_VARIABLE(MoneyGuild, "MoneyGuild") if (get) { - value = toString(guild->getMoney()); + value = toString("%" NL_I64 "u", guild->getMoney()); } else { @@ -1176,16 +1231,22 @@ ENTITY_VARIABLE(Position, "Position of a player (in meter) ,[, vector res; + float fx = 0, fy = 0, fz = 0; sint32 x = 0, y = 0, z = 0; - sint32 cell = 0; + + TDataSetRow dsr = e->getEntityRowId(); + CMirrorPropValueRO playerCell(TheDataset, dsr, DSPropertyCELL); + sint32 cell = playerCell; if (get) { - x = e->getState().X() / 1000; - y = e->getState().Y() / 1000; - z = e->getState().Z() / 1000; - - value = toString ("%d,%d,%d", x, y, z); + fx = e->getState().X() / 1000.f; + fy = e->getState().Y() / 1000.f; + fz = e->getState().Z() / 1000.f; + if (cell < 0) + value = toString ("%.2f,%.2f,%.2f@%d", fx, fy, fz, -cell); + else + value = toString ("%.2f,%.2f,%.2f", fx, fy, fz); } else { @@ -1194,15 +1255,15 @@ ENTITY_VARIABLE(Position, "Position of a player (in meter) ,[, explode (value, string(","), res); if (res.size() >= 2) { - fromString(res[0], x); - x *= 1000; - fromString(res[1], y); - y *= 1000; + fromString(res[0], fx); + x = sint32(fx*1000); + fromString(res[1], fy); + y = sint32(fy*1000); } if (res.size() >= 3) { - fromString(res[2], z); - z *= 1000; + fromString(res[2], fz); + z = sint32(fz*1000); } } else if ( value.find('@') != string::npos ) @@ -1516,7 +1577,7 @@ NLMISC_COMMAND (createItemInTmpInv, "Create an item and put it in the player tem else { if (sheetName.find(".") == string::npos) - sheetName += ".sitem"; + sheetName += ".item"; sheet = CSheetId(sheetName.c_str()); } @@ -2438,7 +2499,6 @@ NLMISC_COMMAND(getPetAnimalSatiety,"Get the satiety of pet animal (petIndex in 0 return true; } -#ifdef RYZOM_FORGE_PET_NAME NLMISC_COMMAND(setPetAnimalName, "Set the name of a pet animal"," []") { if (args.size () < 2) return false; @@ -2456,7 +2516,6 @@ NLMISC_COMMAND(setPetAnimalName, "Set the name of a pet animal"," ") { @@ -3113,7 +3172,7 @@ void cbClientAdmin (NLNET::CMessage& msgin, const std::string &serviceName, NLNE } } - std::string targetName = string("implicite"); + std::string targetName = string("Implicite"); // add the eid of the player or target if necessary if (cmd->AddEId) @@ -3123,7 +3182,7 @@ void cbClientAdmin (NLNET::CMessage& msgin, const std::string &serviceName, NLNE log_Command_ExecOnTarget(c->getTarget(), cmdName, arg); res += c->getTarget().toString(); targetEid = c->getTarget(); - targetName = NLMISC::toString("(%s,%s)", c->getTarget().toString().c_str(), CEntityIdTranslator::getInstance()->getByEntity(c->getTarget()).toString().c_str()); + targetName = NLMISC::toString("%s,%s", c->getTarget().toString().c_str(), CEntityIdTranslator::getInstance()->getByEntity(c->getTarget()).toString().c_str()); } else { @@ -3147,9 +3206,10 @@ void cbClientAdmin (NLNET::CMessage& msgin, const std::string &serviceName, NLNE strFindReplace(res, "#target", c->getTarget().toString().c_str()); strFindReplace(res, "#gtarget", string("#"+c->getTarget().toString()).c_str()); } - nlinfo ("ADMIN: Player (%s,%s) will execute client admin command '%s' on target %s", eid.toString().c_str(), csName.c_str(), res.c_str(), targetName.c_str()); + ADMINLOG("/a %s %s %s", csName.c_str(), targetName.c_str(), res.c_str()); +// nlinfo ("ADMIN: Player (%s,%s) will execute client admin command '%s' on target %s", eid.toString().c_str(), csName.c_str(), res.c_str(), targetName.c_str()); - audit(cmd, res, eid, csName, targetName); + //audit(cmd, res, eid, csName, targetName); CLightMemDisplayer *CmdDisplayer = new CLightMemDisplayer("CmdDisplayer"); CLog *CmdLogger = new CLog( CLog::LOG_NO ); @@ -3185,7 +3245,8 @@ void cbClientAdminOffline (NLNET::CMessage& msgin, const std::string &serviceNam string cmdName, arg; msgin.serial (cmdName, arg); - nlinfo("ADMIN: Executing admin /c command: eid=%s onTarget=%s cmdName=%s arg=%s",eid.toString().c_str(),characterName.c_str(),cmdName.c_str(),arg.c_str()); + ADMINLOG("/c %s %s %s", eid.toString().c_str(), characterName.c_str(), cmdName.c_str(), arg.c_str()); + //nlinfo("ADMIN: Executing admin /c command: eid=%s onTarget=%s cmdName=%s arg=%s",eid.toString().c_str(),characterName.c_str(),cmdName.c_str(),arg.c_str()); // find the character CCharacter *c = PlayerManager.getChar( eid ); @@ -3265,7 +3326,8 @@ void cbClientAdminOffline (NLNET::CMessage& msgin, const std::string &serviceNam std::string csName = CEntityIdTranslator::getInstance()->getByEntity(eid).toString(); std::string targetName = NLMISC::toString("(%s,%s)", CEntityIdTranslator::getInstance()->getByEntity( ucstring(characterName) ).toString().c_str(), characterName.c_str() ); - nlinfo("ADMINOFFLINE: Player (%s,%s) will execute client admin command '%s' on target %s", eid.toString().c_str(), csName.c_str(), res.c_str(), targetName.c_str()); + ADMINLOG("/o %s %s %s", csName.c_str(), targetName.c_str(), res.c_str()); + //nlinfo("ADMINOFFLINE: Player (%s,%s) will execute client admin command '%s' on target %s", eid.toString().c_str(), csName.c_str(), res.c_str(), targetName.c_str()); NLMISC::ICommand::execute(res, *InfoLog); } @@ -4498,25 +4560,24 @@ NLMISC_COMMAND (connectUserChannel, "Connect to user channels", " getUserDynChannel(nameLwr); + string name = toLower(args[1]); + TChanID channel = inst->getUserDynChannel(name); if (args.size() < 3) - pass = nameLwr; + pass = toLower(name); else pass = args[2]; - if ( (channel == DYN_CHAT_INVALID_CHAN) && (pass != nlstr("*")) && (pass != nlstr("***")) ) - channel = inst->createUserChannel(nameLwr, pass); + if ( (channel == DYN_CHAT_INVALID_CHAN) && (pass != string("*")) && (pass != string("***")) ) + channel = inst->createUserChannel(name, pass); if (channel != DYN_CHAT_INVALID_CHAN) { string channelPass = inst->getPassUserChannel(channel); - if ( (channel != DYN_CHAT_INVALID_CHAN) && (pass == nlstr("***")) && (c->havePriv(":DEV:") || c->havePriv(":SGM:") || c->havePriv(":GM:") || c->havePriv(":EM:"))) + if ( pass == string("***") && (c->havePriv(":DEV:") || c->havePriv(":SGM:") || c->havePriv(":GM:") || c->havePriv(":EM:"))) { - inst->deleteUserChannel(nameLwr); + inst->deleteUserChannel(name); } else if (channelPass == pass) { @@ -4527,7 +4588,7 @@ NLMISC_COMMAND (connectUserChannel, "Connect to user channels", " addFactionChannelToCharacter(channel, c, true, true); } - else if (pass == nlstr("*")) + else if (pass == string("*")) { inst->removeFactionChannelForCharacter(channel, c, true); } @@ -4535,7 +4596,7 @@ NLMISC_COMMAND (connectUserChannel, "Connect to user channels", " ") { if ((args.size() < 2) || (args.size() > 3)) @@ -4559,7 +4619,7 @@ NLMISC_COMMAND (connectLangChannel, "Connect to lang channel", " string action; string lang = args[1]; - if (lang != "en" && lang != "fr" && lang != "de" && lang != "ru" && lang != "es") + if (lang != "en" && lang != "fr" && lang != "de" && lang != "ru" && lang != "es" && lang != "rf") return false; bool leave = false; if (args.size() > 2) @@ -4589,7 +4649,28 @@ NLMISC_COMMAND (connectLangChannel, "Connect to lang channel", " CCharacter::sendDynamicSystemMessage( eid, "EGS_CHANNEL_INVALID_NAME", params ); return false; } -#endif + + +NLMISC_COMMAND (setDontTranslateLangs, "Set langs that a player dont want to see translated", " ") +{ + if (args.size() != 2) + return false; + + GET_CHARACTER + + TDataSetRow player = c->getEntityRowId(); + + + CMessage msgout("SET_USER_DONT_TRANSLATE_LANGS"); + msgout.serial(player); + string langs = args[1]; + msgout.serial(langs); + CUnifiedNetwork::getInstance()->send("IOS", msgout); + c->setDontTranslate(langs); + +} + + NLMISC_COMMAND (updateTarget, "Update current target", "") { @@ -4598,6 +4679,64 @@ NLMISC_COMMAND (updateTarget, "Update current target", "") return true; } +NLMISC_COMMAND (setSalt, "Set Salt", " ") +{ + if (args.size() != 2) + return false; + + GET_CHARACTER + + string salt = args[1]; + if (salt.empty()) + return false; + + saveSalt(salt); + return true; +} + +// !!! Deprecated !!! +NLMISC_COMMAND (webAddCommandsIds, "Add ids of commands will be run from webig", " ") +{ + if (args.size() != 4) + return false; + + GET_CHARACTER + + string web_app_url = args[2]; + string indexes = args[3]; + string salt = getSalt(); + + if (salt.empty()) + { + nlwarning("no salt"); + return false; + } + + c->addWebCommandCheck(web_app_url, indexes, salt); + return true; +} + +// !!! Deprecated !!! +NLMISC_COMMAND (webDelCommandsIds, "Del ids of commands", " ") +{ + if (args.size() != 2) + return false; + + GET_CHARACTER + + string web_app_url = args[1]; + uint item_idx = c->getWebCommandCheck(web_app_url); + if (item_idx == INVENTORIES::NbBagSlots) + return false; + + CInventoryPtr inv = c->getInventory(INVENTORIES::bag); + CGameItemPtr item = inv->getItem(item_idx); + inv->removeItem(item_idx); + item.deleteItem(); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=deleted"); + return true; +} + CInventoryPtr getInv(CCharacter *c, const string &inv) { CInventoryPtr inventoryPtr = NULL; @@ -4626,7 +4765,6 @@ CInventoryPtr getInv(CCharacter *c, const string &inv) return inventoryPtr; } -#ifdef RYZOM_FORGE NLMISC_COMMAND (webExecCommand, "Execute a web command", " [] [] []") { @@ -4667,8 +4805,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " infos; CGameItemPtr item; @@ -4680,14 +4817,14 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_index", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_index"); return false; } if (next_step && (iindex != saved_index+1)) { // Next step commands wants an index who follow the last used index. if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_next_index", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_next_index"); return false; } @@ -4696,12 +4833,12 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getLastConnectedDate()) + index + command + toString(c->getEntityRowId().getIndex()); - NLMISC::CHashKey realhmacEid = NLMISC::getHMacSHA1((uint8*)&checksumEid[0], checksumEid.size(), (uint8*)&salt[0], salt.size()); - NLMISC::CHashKey realhmacRowId = NLMISC::getHMacSHA1((uint8*)&checksumRowId[0], checksumRowId.size(), (uint8*)&salt[0], salt.size()); - if (realhmacEid != hmacBin && realhmacRowId != hmacBin && command != "is_valid_index") + string realhmacEid = getHMacSHA1((uint8*)&checksumEid[0], checksumEid.size(), (uint8*)&salt[0], salt.size()).toString(); + string realhmacRowId = getHMacSHA1((uint8*)&checksumRowId[0], checksumRowId.size(), (uint8*)&salt[0], salt.size()).toString(); + if (realhmacEid != hmac && realhmacRowId != hmac && command != "is_valid_index") { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_auth", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_auth"); return false; } } @@ -4711,7 +4848,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getInventory(INVENTORIES::bag); if (!c->havePriv(":DEV:") || (web_app_url != "debug")) { - uint item_idx = c->checkWebCommand(web_app_url, index+command, hmac, getSalt()); + uint item_idx = c->checkWebCommand(web_app_url, index+command, hmac, ""); if (item_idx == INVENTORIES::NbBagSlots) { nlwarning("Bad web command check"); @@ -4736,11 +4873,11 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " isValidWebCommandIndex(iindex)) { - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=unvalid_index", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=unvalid_index"); } else { - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished&desc=valid_index", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished&desc=valid_index"); } return true; } @@ -4782,7 +4919,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_inventory", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_inventory"); return false; } @@ -4810,7 +4947,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_items", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_items"); return false; } @@ -4905,7 +5042,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getSheetId() == sheetId && itemPtr->quality() == quality ) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=allready_have_item", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=allready_have_item"); return false; } } @@ -4922,20 +5059,24 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=cant_add_item", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=cant_add_item"); return false; } if (new_item != NULL) // When the item is stacked, it's deleted by addItemToInventory. Need be checked again to prevent crash of egs { + ucstring customValue; + if (command_args.size() >= 6 && command_args[5] != "*") { - new_item->setPhraseId(command_args[5], true); + customValue.fromUtf8(command_args[5]); + new_item->setCustomName(customValue); } if (command_args.size() >= 7 && command_args[6] != "*") { - new_item->setPhraseId(command_args[6], true); + customValue.fromUtf8(command_args[6]); + new_item->setCustomText(customValue); } if (command_args.size() >= 8) @@ -4976,7 +5117,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " max_x || y > max_y)) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_position", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_position"); return false; } } @@ -5005,7 +5146,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " value) || (command_args[2] == "above" && fame < value)) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_fame", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_fame"); return false; } @@ -5066,14 +5207,14 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type"); return false; } CCharacter * targetTarget = dynamic_cast(CEntityBaseManager::getEntityBasePtr(target)); if (targetTarget->getLeagueId() == DYN_CHAT_INVALID_CHAN || c->getLeagueId() != targetTarget->getLeagueId()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_league", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_league"); return false; } } @@ -5082,14 +5223,14 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type"); return false; } CCharacter * targetTarget = dynamic_cast(CEntityBaseManager::getEntityBasePtr(target)); if (targetTarget->getGuildId() == 0 || c->getGuildId() != targetTarget->getGuildId()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_guild", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_guild"); return false; } } @@ -5098,14 +5239,14 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type"); return false; } CCharacter * targetTarget = dynamic_cast(CEntityBaseManager::getEntityBasePtr(target)); if (targetTarget->getTeamId() == CTEAM::InvalidTeamId || c->getTeamId() != targetTarget->getTeamId()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_team", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_team"); return false; } } @@ -5120,7 +5261,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getType()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_sheet", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_sheet"); return false; } } @@ -5129,7 +5270,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type"); return false; } vector aliases; @@ -5147,7 +5288,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_bot", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_bot"); return false; } } @@ -5156,14 +5297,14 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_type"); return false; } CEntityBase *entityBase = PlayerManager.getCharacterByName(CShardNames::getInstance().makeFullNameFromRelative(c->getHomeMainlandSessionId(), command_args[2])); if (entityBase == NULL) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_player", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_player"); return false; } } else @@ -5180,7 +5321,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " haveBrick(CSheetId(command_args[1]))) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_brick", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_brick"); return false; } } @@ -5203,7 +5344,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_brick_action", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_brick_action"); return false; } } @@ -5234,7 +5375,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_inventory", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=bad_inventory"); return false; } @@ -5270,7 +5411,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_items", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_items"); return false; } } @@ -5279,7 +5420,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=have_items", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=have_items"); return false; } } @@ -5317,7 +5458,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getState() != OUTPOSTENUMS::DefenseRound)) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc="+command_args[2], getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc="+command_args[2]); return false; } } @@ -5703,7 +5844,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_vpx_def", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_vpx_def"); } } //************************************************* @@ -6012,7 +6153,12 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " removePlayerFromRoom( c ); uint16 ownerId = buildingPlayer->getOwnerIdx( entityBase->getId() ); + nlinfo("Gettting ownerId : %d", ownerId); + buildingPlayer->addUser(c, 0, ownerId, cell); + + //CBuildingManager::getInstance()->setRoomLifeTime(cell, TGameCycle(NLMISC::TGameTime(4*60*60) / CTickEventHandler::getGameTimeStep())); + nlinfo("Gettting cell : %d", cell); c->teleportCharacter(x,y,z,allowPetTp,true,h,0xFF,cell); } //} @@ -6145,7 +6291,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getSkillValue(skillEnum) < wantedValue) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_skill", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_skill"); return true; } } @@ -6157,7 +6303,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getBestChildSkillValue(skillEnum) < wantedValue) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_best_skill", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_best_skill"); return true; } } @@ -6183,7 +6329,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " isDead()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_dead", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_dead"); return true; } } @@ -6191,7 +6337,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " isDead()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_alive", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_alive"); return true; } } @@ -6199,7 +6345,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getPVPFlag()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_tag", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_tag"); return true; } } @@ -6207,7 +6353,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getPvPRecentActionFlag()) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_flag", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=not_flag"); return true; } } @@ -6229,7 +6375,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getMoney() < wantedMoney) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_money", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_money"); return true; } } @@ -6259,7 +6405,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_guild", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_guild"); return true; } @@ -6271,7 +6417,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getMoney() < wantedMoney) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_money", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_money"); return true; } } @@ -6303,7 +6449,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " wanted_grade) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_rank", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_rank"); return true; } } @@ -6355,7 +6501,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getFactionPoint(clan) < value) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_faction_points", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_faction_points"); return true; } } @@ -6394,7 +6540,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " getPvpPoint() < value) { if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_pvp_points", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=failed&desc=no_enough_pvp_points"); return true; } } @@ -6480,7 +6626,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " (CEntityBaseManager::getEntityBasePtr(target)); } if (destPlayer) - destPlayer->sendUrl(app+" "+params, ""); + destPlayer->sendUrl(app+" "+params); } @@ -6574,6 +6720,12 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " removeMission(missionAlias, 0); c->removeMissionFromHistories(missionAlias); } + else if (action == "finish") + { + TAIAlias missionAlias = CAIAliasTranslator::getInstance()->getMissionUniqueIdFromName(command_args[2]); + c->removeMission(missionAlias, 0, true); + c->removeMissionFromHistories(missionAlias); + } else if (action == "add_compass" && command_args.size() == 4) { TVectorParamCheck params(1); @@ -6648,7 +6800,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished"); } } else @@ -6658,7 +6810,7 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " validateWebCommandIndex(iindex); } if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished"); } } else @@ -6667,12 +6819,11 @@ NLMISC_COMMAND (webExecCommand, "Execute a web command", " validateWebCommandIndex(iindex); if (send_url) - c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished", getSalt()); + c->sendUrl(web_app_url+"&player_eid="+c->getId().toString()+"&event=finished"); } return true; } -#endif //---------------------------------------------------------------------------- ENTITY_VARIABLE (PriviledgePVP, "Priviledge Pvp Mode") @@ -6706,9 +6857,9 @@ ENTITY_VARIABLE (FullPVP, "Full Pvp Mode") } else { - if (value=="1" || value=="on" || toLowerAscii(value)=="pvp" || toLowerAscii(value)=="true" ) + if (value=="1" || value=="on" || toLower(value)=="pvp" || toLower(value)=="true" ) c->setFullPVP(true); - else if (value=="0" || value=="off" || toLowerAscii(value)=="false" ) + else if (value=="0" || value=="off" || toLower(value)=="false" ) c->setFullPVP(false); // c->setPVPRecentActionFlag(); CPVPManager2::getInstance()->setPVPModeInMirror(c); @@ -6895,7 +7046,6 @@ NLMISC_COMMAND(listGuildMembers, "display guild members list", " ") { @@ -6979,7 +7129,6 @@ NLMISC_COMMAND(roomKick, "kick player from room", " ") return true; } -#endif //---------------------------------------------------------------------------- NLMISC_COMMAND(guildInvite, "send a guild invite to a player character", " ") @@ -7863,7 +8012,6 @@ NLMISC_COMMAND(addGuildBuilding, "sadd a building to guild", " ") { @@ -7910,7 +8058,6 @@ NLMISC_COMMAND(setOrganizationStatus, "set the organization status of a player", return true; } -#endif //---------------------------------------------------------------------------- NLMISC_COMMAND(eventCreateNpcGroup, "create an event npc group", " [] [] [] [] [] [] [client_sheet] [inVIllage?inOutpost?inStable?inAtys?]") @@ -7918,7 +8065,6 @@ NLMISC_COMMAND(eventCreateNpcGroup, "create an event npc group", " < if (args.size () < 3) return false; GET_CHARACTER - uint32 instanceNumber = c->getInstanceNumber(); sint32 x = c->getX(); sint32 y = c->getY(); sint32 z = c->getZ(); @@ -7932,9 +8078,10 @@ NLMISC_COMMAND(eventCreateNpcGroup, "create an event npc group", " < return true; } - NLMISC::CSheetId sheetId(args[2]); - if (sheetId==CSheetId::Unknown) - sheetId = args[2] + ".creature"; + string sheetName = args[2]; + if (sheetName.find(".creature") == string::npos) + sheetName += ".creature"; + NLMISC::CSheetId sheetId(sheetName); if (sheetId==CSheetId::Unknown) { log.displayNL("invalid sheet id"); @@ -8006,10 +8153,20 @@ NLMISC_COMMAND(eventCreateNpcGroup, "create an event npc group", " < look += ".creature"; } - // See if another AI instance has been specified - if ( ! getAIInstanceFromGroupName(botsName, instanceNumber)) + + //Get instance number from position + CContinent * continent = CZoneManager::getInstance().getContinent(x, y); + if (!continent) { - return true; + log.displayNL("ERR: invalid continent"); + return false; + } + uint32 instanceNumber = CUsedContinent::instance().getInstanceForContinent((CONTINENT::TContinent)continent->getId()); + + if (instanceNumber == ~0) + { + log.displayNL("ERR: invalid continent"); + return false; } TDataSetRow dsr = c->getEntityRowId(); @@ -8063,7 +8220,6 @@ NLMISC_COMMAND(eventNpcGroupScript, "executes a script on an event npc group", " return true; } -#ifdef RYZOM_FORGE_EXECSCRIPT //---------------------------------------------------------------------------- NLMISC_COMMAND(eScript, "executes a script on an event npc group", "