// 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" #include "script_compiler.h" #include "ai_grp_npc.h" #include "group_profile.h" #include "ai_generic_fight.h" #include "server_share/msg_brick_service.h" #include "continent_inline.h" #include "dyn_grp_inline.h" #include "ai_script_data_manager.h" #include "nf_helpers.h" #include "ai_aggro.h" #include "game_share/send_chat.h" #include "game_share/string_manager_sender.h" #include using std::string; using std::vector; using namespace NLMISC; using namespace AIVM; using namespace AICOMP; using namespace AITYPES; using namespace RYAI_MAP_CRUNCH; //---------------------------------------------------------------------------- /** @page code @subsection spawn__ Spawns the current group. Arguments: -> */ // CGroup void spawn__(CStateInstance* entity, CScriptStack& stack) { if (!entity) { nlwarning("spawnInstance failed"); return; } CGroup const* const grp = entity->getGroup(); if (grp) { if (grp->isSpawned()) grp->getSpawnObj()->spawnBots(); } } //---------------------------------------------------------------------------- /** @page code @subsection despawn_f_ Depawns the current group. Arguments: f(Immediatly) -> @param Immediatly is whether the groups spawns immediatly (1) or not (0) @code ()despawn(0); // despawn ()despawn(1); // despawn immediatement @endcode */ // CGroup void despawn_f_(CStateInstance* entity, CScriptStack& stack) { float const immediatly = stack.top(); stack.pop(); if (!entity) { nlwarning("despawnInstance failed"); return; } CGroup* const grp = entity->getGroup(); if (!grp) { nlwarning("despawn : entity '%s'%s is not a group ? ", entity->aliasTreeOwner()->getAliasFullName().c_str(), entity->aliasTreeOwner()->getAliasString().c_str()); return; } grp->despawnBots(immediatly!=0); } //---------------------------------------------------------------------------- /** @page code @subsection isAlived__f Test if the group is alived Arguments: -> f(Immediatly) @return 1 if group is spawned @code (alive)isAlived(); @endcode */ // CGroup void isAlived__f(CStateInstance* entity, CScriptStack& stack) { if (!entity) { stack.push(0.0f); return; } CGroup* const grp = entity->getGroup(); if (!grp) { nlwarning("isAlived__f : entity '%s'%s is not a group ? ", entity->aliasTreeOwner()->getAliasFullName().c_str(), entity->aliasTreeOwner()->getAliasString().c_str()); stack.push(0.0f); return; } if (!grp->isSpawned()) { stack.push(0.0f); return; } for (uint i=0; ibots().size(); ++i) { const CBot *const bot = grp->getBot(i); if ( !bot || !bot->isSpawned()) continue; CAIEntityPhysical *const ep=bot->getSpawnObj(); if (ep->isAlive()) { stack.push(1.0f); return; } } stack.push(0.0f); return; } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPos_ssfff_c Used to create a dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(Dispersion) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] Dispersion is the dispersion radius in meters when spawning a group @param[out] Group is the newly created group @code (@grp)newNpcChildGroupPos("class_forager", "state_machine_1", x, y, 30); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPos_ssfff_c(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; if (dispersionRadius<0.) dispersionRadius = 0.; stack.push(spawnNewGroup(entity, stack, aiInstance, CAIVector(x, y), -1, dispersionRadius)); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPos_ssfff_ Used to create a dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(Dispersion) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] Dispersion is the dispersion radius in meters when spawning a group @code ()newNpcChildGroupPos("class_forager", "state_machine_1", x, y, 30); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPos_ssfff_(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; if (dispersionRadius<0.) dispersionRadius = 0.; spawnNewGroup(entity, stack, aiInstance, CAIVector(x, y), -1, dispersionRadius); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPos_ssff_c Used to create a dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[out] Group is the newly created group @code (@grp)newNpcChildGroupPos("class_forager", "state_machine_1", x, y); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPos_ssff_c(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupPos_ssfff_c(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPos_ssff_ Used to create a dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @code ()newNpcChildGroupPos("class_forager", "state_machine_1", x, y); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPos_ssff_(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupPos_ssfff_(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPosMl_ssffff_c Used to create a multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(BaseLevel), f(Dispersion) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] BaseLevel is the base level of the spawned group @param[in] Dispersion is the dispersion radius in meters when spawning a group @param[out] Group is the newly created group @code (@grp)newNpcChildGroupPosMl("class_forager", "state_machine_1", x, y, 13, 7.5); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPosMl_ssffff_c(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; if (dispersionRadius<0.) dispersionRadius = 0.; stack.push(spawnNewGroup(entity, stack, aiInstance, CAIVector(x,y), -1, dispersionRadius)); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPosMl_ssffff_ Used to create a multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(BaseLevel), f(Dispersion) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] BaseLevel is the base level of the spawned group @param[in] Dispersion is the dispersion radius in meters when spawning a group @code ()newNpcChildGroupPosMl("class_forager", "state_machine_1", x, y, 13, 7.5); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPosMl_ssffff_(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; if (dispersionRadius<0.) dispersionRadius = 0.; spawnNewGroup(entity, stack, aiInstance, CAIVector(x,y), -1, dispersionRadius); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPosMl_ssfff_c Used to create a multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(BaseLevel) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] BaseLevel is the base level of the spawned group @param[out] Group is the newly created group @code (@grp)newNpcChildGroupPosMl("class_forager", "state_machine_1", x, y, 13); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPosMl_ssfff_c(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupPosMl_ssffff_c(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupPosMl_ssfff_ Used to create a multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), f(x), f(y), f(BaseLevel) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] x position of the spawned group on x axis @param[in] y position of the spawned group on y axis @param[in] BaseLevel is the base level of the spawned group @code ()newNpcChildGroupPosMl("class_forager", "state_machine_1", x, y, 13); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupPosMl_ssfff_(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupPosMl_ssffff_(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection getMidPos__ff Returns the position (x, y) of the current group. Arguments: -> f(x), f(y) @param[out] x position of the group on x axis @param[out] y position of the group on y axis @code (x, y)getMidPos(); @endcode */ // CGroup void getMidPos__ff(CStateInstance* entity, CScriptStack& stack) { CGroup* const group = entity->getGroup(); CAIVector vect; if (group->isSpawned()) { if (!group->getSpawnObj()->calcCenterPos(vect)) group->getSpawnObj()->calcCenterPos(vect, true); } float x((float)vect.x().asDouble()); float y((float)vect.y().asDouble()); stack.push(y); stack.push(x); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroup_sssf_c Used to create dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(Dispersion) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] Dispersion is the dispersion radius in meters when spawning a group @param[out] Group is the newly created group @code (@grp)newNpcChildGroup("class_forager", "state_machine_1", $zone, 10); @endcode */ // CGroup not in a family behaviour void newNpcChildGroup_sssf_c(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); TStringId const zoneName = CStringMapper::map(stack.top()); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; CNpcZone const* spawnZone = aiInstance->getZone(zoneName); if (!spawnZone) { string StateMachine = stack.top(); stack.pop(); stack.pop(); nlwarning("newNpcChildGroup failed : spawnZone Not Found ! for StateMachine : %s", StateMachine.c_str()); return; } if (dispersionRadius<0.) { dispersionRadius = 0.; CNpcZonePlace const* spawnZonePlace = dynamic_cast(spawnZone); if (spawnZonePlace!=NULL) dispersionRadius = spawnZonePlace->getRadius(); } stack.push(spawnNewGroup(entity, stack, aiInstance, spawnZone->midPos(), -1, dispersionRadius)); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroup_sssf_ Used to create dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(Dispersion) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] Dispersion is the dispersion radius in meters when spawning a group @code ()newNpcChildGroup("class_forager", "state_machine_1", $zone, 10); @endcode */ // CGroup not in a family behaviour void newNpcChildGroup_sssf_(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); TStringId const zoneName = CStringMapper::map(stack.top()); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; CNpcZone const* spawnZone = aiInstance->getZone(zoneName); if (!spawnZone) { string StateMachine = stack.top(); stack.pop(); stack.pop(); nlwarning("newNpcChildGroup failed : spawnZone Not Found ! for StateMachine : %s", StateMachine.c_str()); return; } if (dispersionRadius<0.) { dispersionRadius = 0.; CNpcZonePlace const* spawnZonePlace = dynamic_cast(spawnZone); if (spawnZonePlace!=NULL) dispersionRadius = spawnZonePlace->getRadius(); } spawnNewGroup(entity, stack, aiInstance, spawnZone->midPos(), -1, dispersionRadius); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroup_sss_c Used to create dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(Dispersion) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[out] Group is the newly created group @code (@grp)newNpcChildGroup("class_forager", "state_machine_1", $zone); @endcode */ // CGroup not in a family behaviour void newNpcChildGroup_sss_c(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroup_sssf_c(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroup_sss_ Used to create dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(Dispersion) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @code ()newNpcChildGroup("class_forager", "state_machine_1", $zone); @endcode */ // CGroup not in a family behaviour void newNpcChildGroup_sss_(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroup_sssf_(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupMl_sssff_c Used to create multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(BaseLevel), f(Dispersion) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] BaseLevel is the base level of the spawned group @param[in] Dispersion is the dispersion radius in meters when spawning a group @param[out] Group is the newly created group @code (@grp)newNpcChildGroupMl("class_forager", "state_machine_1", $zone, 2, 50); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupMl_sssff_c(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); int baseLevel = (int)(float)stack.top(); stack.pop(); TStringId const zoneName = CStringMapper::map(stack.top()); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; CNpcZone const* spawnZone = aiInstance->getZone(zoneName); if (!spawnZone) { string StateMachine = stack.top(); stack.pop(); stack.pop(); nlwarning("newNpcChildGroup failed : spawnZone Not Found ! for StateMachine : %s", StateMachine.c_str()); return; } if (dispersionRadius<0.) { dispersionRadius = 0.; CNpcZonePlace const* spawnZonePlace = dynamic_cast(spawnZone); if (spawnZonePlace!=NULL) dispersionRadius = spawnZonePlace->getRadius(); } stack.push(spawnNewGroup(entity, stack, aiInstance, spawnZone->midPos(), baseLevel, dispersionRadius)); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupMl_sssff_ Used to create multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(BaseLevel), f(Dispersion) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] BaseLevel is the base level of the spawned group @param[in] Dispersion is the dispersion radius in meters when spawning a group @code ()newNpcChildGroupMl("class_forager", "state_machine_1", $zone, 2, 50); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupMl_sssff_(CStateInstance* entity, CScriptStack& stack) { double dispersionRadius = (double)(float)stack.top(); stack.pop(); int baseLevel = (int)(float)stack.top(); stack.pop(); TStringId const zoneName = CStringMapper::map(stack.top()); stack.pop(); IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) return; CNpcZone const* spawnZone = aiInstance->getZone(zoneName); if (!spawnZone) { string StateMachine = stack.top(); stack.pop(); stack.pop(); nlwarning("newNpcChildGroup failed : spawnZone Not Found ! for StateMachine : %s", StateMachine.c_str()); return; } if (dispersionRadius<0.) { dispersionRadius = 0.; CNpcZonePlace const* spawnZonePlace = dynamic_cast(spawnZone); if (spawnZonePlace!=NULL) dispersionRadius = spawnZonePlace->getRadius(); } spawnNewGroup (entity, stack, aiInstance, spawnZone->midPos(), baseLevel, dispersionRadius); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupMl_sssf_c Used to create multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(BaseLevel) -> c(Group) @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] BaseLevel is the base level of the spawned group @param[out] Group is the newly created group @code (@grp)newNpcChildGroupMl("class_forager", "state_machine_1", $zone, 12); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupMl_sssf_c(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupMl_sssff_c(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection newNpcChildGroupMl_sssf_ Used to create multilevel dynamic npc group (a parent/children relation is defined). Arguments: s(GroupTemplate), s(StateMachine), s(Zone), f(BaseLevel) -> @param[in] GroupTemplate is the name of a template npc group defined in the same AIInstance @param[in] StateMachine is the name of a state machine defined in the same AIInstance @param[in] Zone is returned by the getZoneWithFlags methods @param[in] BaseLevel is the base level of the spawned group @code ()newNpcChildGroupMl("class_forager", "state_machine_1", $zone, 12); @endcode */ // CGroup not in a family behaviour void newNpcChildGroupMl_sssf_(CStateInstance* entity, CScriptStack& stack) { stack.push((float)-1.0f); newNpcChildGroupMl_sssff_(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection spawnManager_s_ Spawns a manager. Arguments: s(ManagerName) -> @param[in] ManagerName is the name of the manager to spawn @code ()spawnManager("NPC Manager"); // Spawns all the NPCs in the NPC manager called "NPC Manager" @endcode */ // CGroup not in a family behaviour void spawnManager_s_(CStateInstance* entity, CScriptStack& stack) { string ManagerName = stack.top(); stack.pop(); breakable { if (!entity) { nlwarning("SpawnManager error entity not spawned"); break; } IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { nlwarning("SpawnManager error AIInstance not Found"); break; } /** :TODO: If CStringFilter is available use it here and test the whole */ string/*CStringFilter*/ managerFilter(ManagerName); FOREACH(itManager, CAliasCont, aiInstance->managers()) { CManager* manager = *itManager; if (manager && managerFilter==manager->getName()) { manager->spawn(); } } return; } nlwarning("SpawnManager error"); } //---------------------------------------------------------------------------- /** @page code @subsection despawnManager_s_ Despawns a manager. Arguments: s(ManagerName) -> @param[in] ManagerName is the name of the manager to despawn @code ()despawnManager("NPC Manager"); // Despawns all the NPCs in the NPC manager called "NPC Manager" @endcode */ // CGroup not in a family behaviour void despawnManager_s_(CStateInstance* entity, CScriptStack& stack) { string ManagerName = stack.top(); stack.pop(); breakable { if (!entity) { nlwarning("DespawnManager error entity not spawned"); break; } IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { nlwarning("DespawnManager error AIInstance not Found"); break; } // :TODO: If CStringFilter is available use it here and test the whole string/*CStringFilter*/ managerFilter(ManagerName); FOREACH(itManager, CAliasCont, aiInstance->managers()) { CManager *manager=*itManager; if (manager && managerFilter==manager->getName()) { manager->despawnMgr(); } } return; } } //---------------------------------------------------------------------------- /** @page code @subsection getGroupTemplateWithFlags_sss_s Returns the name of a randomly chosen group template corresponding to specified flags. Arguments: s(OneOf), s(Mandatory), s(ExceptFlags) -> s(GroupTemplate) @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptFlags is a '|' separated list of flags @param[out] GroupTemplate is a group template matching at least one of OneOf flags, all Manadatory flags and none of ExceptFlags @code ($group)getGroupTemplateWithFlags("food|rest", "invasion|outpost"); // Get a group which matches 'invasion', 'outpost' and either 'food' or 'rest' flags. @endcode */ // CGroup not in a family behaviour void getGroupTemplateWithFlags_sss_s(CStateInstance* entity, CScriptStack& stack) { string exceptProperties = stack.top(); stack.pop(); string mandatoryProperties = stack.top(); stack.pop(); string oneOfProperties = stack.top(); stack.pop(); // If no AI instance return IManagerParent* managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* aiInstance = dynamic_cast(managerParent); if (!aiInstance) { stack.push(string()); return; } // Fill the property sets. AITYPES::CPropertySet oneOfSet = readSet(oneOfProperties); AITYPES::CPropertySet mandatorySet = readSet(mandatoryProperties); AITYPES::CPropertySet exceptSet = readSet(exceptProperties); vector const*> groupDescs; FOREACH (itCont, CCont, aiInstance->continents()) { FOREACH (itRegion, CCont, itCont->regions()) { FOREACH (itFamily, CCont, itRegion->groupFamilies()) { FOREACH (itGroupDesc, CCont >, itFamily->groupDescs()) { // Skip groups not meeting the criteria if (!itGroupDesc->properties().containsPartOfNotStrict(oneOfSet)) continue; if (!itGroupDesc->properties().containsAllOf(mandatorySet)) continue; if (itGroupDesc->properties().containsPartOfStrict(exceptSet)) continue; groupDescs.push_back(*itGroupDesc); } } } } if (groupDescs.size()==0) { nlwarning("getGroupTemplateWithFlags failed: no group template found that contains all of '%s' and a part of'%s' ", mandatoryProperties.c_str(), oneOfProperties.c_str()); stack.push(string()); return; } CGroupDesc const* groupDesc = groupDescs[CAIS::rand16((uint32)groupDescs.size())]; stack.push(groupDesc->getFullName()); return; } //---------------------------------------------------------------------------- /** @page code @subsection getGroupTemplateWithFlags_ss_s Returns the name of a randomly chosen group template corresponding to specified flags. Arguments: s(OneOf), s(Mandatory) -> s(GroupTemplate) @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[out] GroupTemplate is a group template matching at least one of OneOf flags and all Manadatory flags @code ($group)getGroupTemplateWithFlags("food|rest", "invasion|outpost"); // Get a group which matches 'invasion', 'outpost' and either 'food' or 'rest' flags. @endcode */ // CGroup not in a family behaviour void getGroupTemplateWithFlags_ss_s(CStateInstance* entity, CScriptStack& stack) { stack.push(std::string("")); getGroupTemplateWithFlags_sss_s(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection getZoneWithFlags_ssss_s The first parameter is a list of 'one match enough' zone flags. The zones must have at least one of these flags to be selected. The second parameter is a list of 'all required' zone flag. The zones must have all these flags to be selected. The fourth parameter is a list of 'all forbidden' zone flag. The zones must not have any of these flags to be selected. Additionnaly the returned zone cannot be ExceptZone. Last argument specifies a list of flags that the selected zone cannot have. With the list of selectable zone, the system then select a zone based on entity density in each selected zone. The system will select the least populated zone. It then returns the zone name. Priority is given to current Cell, and after that to all cells. Arguments: s(OneOf), s(Mandatory), s(ExceptZone), s(ExceptProperties) -> s(Zone) @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptZone is a zone name @param[in] ExceptProperties is a '|' separated list of flags @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getZoneWithFlags("food|rest", "invasion|outpost", $oldzone); // Get a zone which matches invasion, outpost and either food or rest flags with the best score except $oldzone. ($zone)getZoneWithFlags("boss", "", $oldzone); // Get a zone which matches boss flag with the best score except the $oldzone @endcode */ // CGroup not in a family behaviour void getZoneWithFlags_ssss_s(CStateInstance* entity, CScriptStack& stack) { string exceptProperties = stack.top(); stack.pop(); TStringId curZoneId= CStringMapper::map(stack.top()); stack.pop(); string mandatoryProperties = stack.top(); stack.pop(); string oneOfProperties = stack.top(); stack.pop(); // If no AI instance return IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { stack.push(string()); return; } // Fill the property sets. AITYPES::CPropertySet oneOfSet = readSet(oneOfProperties); AITYPES::CPropertySet mandatorySet = readSet(mandatoryProperties); AITYPES::CPropertySet exceptSet = readSet(exceptProperties); // Get current zone CNpcZone const* const curZone = aiInstance->getZone(curZoneId); // Create the scorer CZoneScorerMandatoryAndOneOfPlusExcept const scorer(oneOfSet, mandatorySet, exceptSet, curZone); getZoneWithFlags_helper(entity, stack, aiInstance, scorer); } //---------------------------------------------------------------------------- /** @page code @subsection getZoneWithFlags_sss_s @sa @ref getZoneWithFlags_ssss_s Arguments: s(OneOf), s(Mandatory), s(ExceptZone) -> s(Zone) @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptZone is a zone name @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getZoneWithFlags("food|rest", "invasion|outpost", $oldzone); // Get a zone which matches invasion, outpost and either food or rest flags with the best score except $oldzone. ($zone)getZoneWithFlags("boss", "", $oldzone); // Get a zone which matches boss flag with the best score except the $oldzone @endcode */ // CGroup not in a family behaviour void getZoneWithFlags_sss_s(CStateInstance* entity, CScriptStack& stack) { stack.push(std::string("")); getZoneWithFlags_ssss_s(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection getNearestZoneWithFlags_ffsss_s @sa @ref getZoneWithFlags_ssss_s The zone returned by this function is the nearest from the specified point and taking into account the zone free space. Arguments: f(X), f(Y), s(OneOf), s(Mandatory), s(ExceptProperties) -> s(Zone) @param[in] X is the position of the zone we are looking for on the X axis @param[in] Y is the position of the zone we are looking for on the X axis @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptProperties is a '|' separated list of flags @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getNearestZoneWithFlags(x, y, "food|rest", "invasion|outpost", "stayaway"); @endcode */ // CGroup not in a family behaviour void getNearestZoneWithFlags_ffsss_s(CStateInstance* entity, CScriptStack& stack) { string exceptProperties = stack.top(); stack.pop(); string mandatoryProperties = stack.top(); stack.pop(); string oneOfProperties = stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); // If no AI instance return IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { stack.push(string()); return; } // Fill the property sets. AITYPES::CPropertySet oneOfSet = readSet(oneOfProperties); AITYPES::CPropertySet mandatorySet = readSet(mandatoryProperties); AITYPES::CPropertySet exceptSet = readSet(exceptProperties); // Create the scorer CZoneScorerMandatoryAndOneOfAndDistAndSpace const scorer(oneOfSet, mandatorySet, exceptSet, CAIVector(x, y)); getZoneWithFlags_helper(entity, stack, aiInstance, scorer); } //---------------------------------------------------------------------------- /** @page code @subsection getNearestZoneWithFlags_ffss_s @sa @ref getZoneWithFlags_sss_s The zone returned by this function is the nearest from the specified point and taking into account the zone free space. Arguments: f(X), f(Y), s(OneOf), s(Mandatory), s(ExceptProperties) -> s(Zone) @param[in] X is the position of the zone we are looking for on the X axis @param[in] Y is the position of the zone we are looking for on the X axis @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getNearestZoneWithFlags(x, y, "food|rest", "invasion|outpost"); @endcode */ // CGroup not in a family behaviour void getNearestZoneWithFlags_ffss_s(CStateInstance* entity, CScriptStack& stack) { stack.push(std::string("")); getNearestZoneWithFlags_ffsss_s(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection getNearestZoneWithFlagsStrict_ffsss_s @sa @ref getZoneWithFlags_ssss_s The zone returned by this function is the nearest from the specified point without taking into account the zone free space. Arguments: f(X), f(Y), s(OneOf), s(Mandatory), s(ExceptProperties) -> s(Zone) @param[in] X is the position of the zone we are looking for on the X axis @param[in] Y is the position of the zone we are looking for on the X axis @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptProperties is a '|' separated list of flags @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getNearestZoneWithFlagsStrict(x, y, "food|rest", "invasion|outpost", "stayaway"); @endcode */ // CGroup not in a family behaviour void getNearestZoneWithFlagsStrict_ffsss_s(CStateInstance* entity, CScriptStack& stack) { string exceptProperties = stack.top(); stack.pop(); string mandatoryProperties = stack.top(); stack.pop(); string oneOfProperties = stack.top(); stack.pop(); float const y = stack.top(); stack.pop(); float const x = stack.top(); stack.pop(); // If no AI instance return IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { stack.push(string()); return; } // Fill the property sets. AITYPES::CPropertySet oneOfSet = readSet(oneOfProperties); AITYPES::CPropertySet mandatorySet = readSet(mandatoryProperties); AITYPES::CPropertySet exceptSet = readSet(exceptProperties); // Create the scorer const CZoneScorerMandatoryAndOneOfAndDist scorer(oneOfSet, mandatorySet, exceptSet, CAIVector(x, y)); getZoneWithFlags_helper(entity, stack, aiInstance, scorer); } //---------------------------------------------------------------------------- /** @page code @subsection getNearestZoneWithFlagsStrict_ffss_s @sa @ref getZoneWithFlags_sss_s The zone returned by this function is the nearest from the specified point without taking into account the zone free space. Arguments: f(X), f(Y), s(OneOf), s(Mandatory), s(ExceptProperties) -> s(Zone) @param[in] X is the position of the zone we are looking for on the X axis @param[in] Y is the position of the zone we are looking for on the X axis @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[out] Zone is a zone matching at least one of OneOf flags, all Manadatory flags and can't be ExceptZone @code ($zone)getNearestZoneWithFlagsStrict(x, y, "food|rest", "invasion|outpost"); @endcode */ // CGroup not in a family behaviour void getNearestZoneWithFlagsStrict_ffss_s(CStateInstance* entity, CScriptStack& stack) { stack.push(std::string("")); getNearestZoneWithFlagsStrict_ffsss_s(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection getNeighbourZoneWithFlags_ssss_s @sa @ref getZoneWithFlags_ssss_s Here priority is given to current Cell, after that to neighbour Cells, and finally to all cells. CurrentZone cannot be returned. Arguments: s(CurrentZone), s(OneOf), s(Mandatory), s(ExceptProperties) -> s(Zone) @param[in] CurrentZone is a zone name @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[in] ExceptProperties is a '|' separated list of flags @param[out] Zone is a zone matching the specified flags @code ($zone1)getNeighbourZoneWithFlags($zone, "boss", "", $exceptflag); // Get the zone in the neighbour cells of the one containing $zone which matches specified flags @endcode */ // CGroup not in a family behaviour void getNeighbourZoneWithFlags_ssss_s(CStateInstance* entity, CScriptStack& stack) { string exceptProperties = stack.top(); stack.pop(); string mandatoryProperties = stack.top(); stack.pop(); string oneOfProperties = stack.top(); stack.pop(); TStringId curZoneId = CStringMapper::map(stack.top()); stack.pop(); // If no AI instance return IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner(); CAIInstance* const aiInstance = dynamic_cast(managerParent); if (!aiInstance) { stack.push(string()); return; } // If curzone is invalid return CNpcZone const* const curZone = aiInstance->getZone(curZoneId); if (!curZone) { nlwarning("getNeighbourZoneWithFlags failed: current zone is invalid!"); nlwarning(" - current zone: %s", CStringMapper::unmap(curZoneId).c_str()); nlwarning(" - oneOfProperties: %s", oneOfProperties.c_str()); nlwarning(" - mandatoryProperties: %s", mandatoryProperties.c_str()); nlwarning(" - exceptProperties: %s", exceptProperties.c_str()); stack.push(string()); return; } // Fill the property sets. AITYPES::CPropertySet oneOfSet = readSet(oneOfProperties); AITYPES::CPropertySet mandatorySet = readSet(mandatoryProperties); AITYPES::CPropertySet exceptSet = readSet(exceptProperties); // Create the scorer CZoneScorerMandatoryAndOneOfPlusExcept const scorer(oneOfSet, mandatorySet, exceptSet, curZone); vector cells; curZone->getOwner()->getNeighBourgCellList(cells); cells.push_back(curZone->getOwner()); std::random_shuffle(cells.begin(), cells.end()); CNpcZone const* const newZone = CCellZone::lookupNpcZoneScorer(cells, scorer); if (newZone) { stack.push(newZone->getAliasTreeOwner().getAliasFullName()); return; } nlwarning("getNeighbourgZoneWithFlags failed: no zone found"); nlwarning(" - current zone: %s", CStringMapper::unmap(curZoneId).c_str()); nlwarning(" - oneOfProperties: %s", oneOfProperties.c_str()); nlwarning(" - mandatoryProperties: %s", mandatoryProperties.c_str()); nlwarning(" - exceptProperties: %s", exceptProperties.c_str()); stack.push(string()); return; } //---------------------------------------------------------------------------- /** @page code @subsection getNeighbourZoneWithFlags_sss_s @sa @ref getZoneWithFlags_sss_s Here priority is given to current Cell, after that to neighbour Cells, and finally to all cells. CurrentZone cannot be returned. Arguments: s(CurrentZone), s(OneOf), s(Mandatory) -> s(Zone) @param[in] CurrentZone is a zone name @param[in] OneOf is a '|' separated list of flags @param[in] Mandatory is a '|' separated list of flags @param[out] Zone is a zone matching the specified flags @code ($zone1)getNeighbourZoneWithFlags($zone, "boss", ""); // Get the zone in the neighbour cells of the one containing $zone which matches specified flags @endcode */ // CGroup not in a family behaviour void getNeighbourZoneWithFlags_sss_s(CStateInstance* entity, CScriptStack& stack) { stack.push(std::string("")); getNeighbourZoneWithFlags_ssss_s(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection setAggro_ff_ Sets aggro parameters of current group. Arguments: f(Range), f(NbTicks) -> @param[in] Range is a aggro range in meters @param[in] NbTicks is a aggro update period in ticks @code ()setAggro(Range, NbTicks); // Sets the aggro range of the group to Range, updated every NbTicks ticks @endcode */ // CGroup void setAggro_ff_(CStateInstance* entity, CScriptStack& stack) { sint32 updateNbTicks = (sint32)(float)stack.top(); stack.pop(); float aggroRange = stack.top(); stack.pop(); CGroup* const grp = entity->getGroup(); if (grp) { grp->_AggroRange = aggroRange; if (updateNbTicks>0) grp->_UpdateNbTicks = updateNbTicks; } } //---------------------------------------------------------------------------- /** @page code @subsection setCanAggro_f_ Let a bot aggro or prevent it from aggroing. Arguments: f(CanAggro) -> @param[in] CanAggro tells whether the bot can aggro (!=0) or not (==0) @code ()setCanAggro(0); @endcode */ // CGroup void setCanAggro_f_(CStateInstance* entity, CScriptStack& stack) { bool canAggro = ((float)stack.top())!=0.f; stack.pop(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* spBot = bot->getSpawnObj(); if (spBot) spBot->setCanAggro(canAggro); } } //---------------------------------------------------------------------------- /** @page code @subsection clearAggroList_f_ Reset the aggrolist of a bot. Arguments: f(bool don't send lost aggro message to EGS) -> @code ()clearAggroList(0/1); @endcode */ // CGroup void clearAggroList_f_(CStateInstance* entity, CScriptStack& stack) { bool sendAggroLostMessage = ((float)stack.top())==0.f; stack.pop(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* spBot = bot->getSpawnObj(); if (spBot) spBot->clearAggroList(sendAggroLostMessage); } } //---------------------------------------------------------------------------- /** @page code @subsection clearAggroList__ Reset the aggrolist of a bot. Arguments: -> @code ()clearAggroList(); @endcode */ // CGroup void clearAggroList__(CStateInstance* entity, CScriptStack& stack) { stack.push((float)0.f); clearAggroList_f_(entity, stack); } //---------------------------------------------------------------------------- /** @page code @subsection setMode_s_ Sets the mode of every bot of the group. Valid modes are: - Normal - Sit - Eat - Rest - Alert - Hungry - Death Arguments: s(Mode) -> @param[in] Mode is the mode name to set @code ()setMode("Alert"); @endcode */ // CGroup void setMode_s_(CStateInstance* entity, CScriptStack& stack) { string NewMode = stack.top(); stack.pop(); MBEHAV::EMode mode = MBEHAV::stringToMode(NewMode); if (mode==MBEHAV::UNKNOWN_MODE) return; FOREACH(botIt, CCont, entity->getGroup()->bots()) { if (botIt->getSpawnObj()) botIt->getSpawnObj()->setMode(mode); } } //---------------------------------------------------------------------------- /** @page code @subsection setAutoSpawn_f_ Determine if the current group should respawn automatically after despawn. Arguments: f(AutoSpawn) -> @param[in] AutoSpawn is whether the group automatically rewpawns (1) or not (0) @code ()setAutoSpawn(1); @endcode */ // CGroup void setAutoSpawn_f_(CStateInstance* entity, CScriptStack& stack) { float const autoSpawn = stack.top(); stack.pop(); CGroup* group = entity->getGroup(); group->setAutoSpawn(autoSpawn!=0.f); } //---------------------------------------------------------------------------- // HP related methods /** @page code @subsection setHPLevel_f_ Sets the current HP level of each bot of the group. Arguments: f(Coef) -> @param[in] Coef is the percentage of its max HP each creature will have @code ()setHPLevel(0.8); @endcode */ // CGroup void setHPLevel_f_(CStateInstance* entity, CScriptStack& stack) { float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)(sbot->maxHitPoints()*coef)); } } //---------------------------------------------------------------------------- // HP related methods /** @page code @subsection setHPScale_f__f_ Sets the current HP level of level of each bot of the group. its maxHitPoints eg: for a bot HP = 850 and MaxHP = 1000 ()setHPScale_f_(0); HP will be 0 so DeltaHp = 850 ()setHPScale_f_(1); HP will be 100 so DeltaHp = 150 ()setHPScale_f_(0.5); HP will be 500 so DeltaHp = 350 if bot HP = 840 and Max abd setHpRatio(0) HP will be 0 Arguments: f(Coef) -> @param[in] Coef is the percentage of its max HP each creature will *BE* @code ()setHPLevel(0.8); @endcode */ // CGroup void setHPScale_f_(CStateInstance* entity, CScriptStack& stack) { float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)( sbot->maxHitPoints() *coef - sbot->currentHitPoints()) ); } } /** @page code @subsection scaleHP_f_ Scales the bots HP. Arguments: f(Coef) -> @param[in] Coef is the percentage of its current HP each creature will have @code ()scaleHP(2); @endcode */ // CGroup void scaleHP_f_(CStateInstance* entity, CScriptStack& stack) { float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)(sbot->currentHitPoints()*coef)); } } /** @page code @subsection setBotHPScaleByAlias_fs_ Same as setHpSacale but only on a specific bot of a groupe from the current group by its bot alias Arguments: f(alias),f(Coef), -> @param[in] alias is the alias of the bot @param[in] Coef is the percentage of its current HP each creature will have @code ()scaleHpByAlias(2, '(A:1000:10560)'); @endcode */ // CGroup void setBotHPScaleByAlias_fs_(CStateInstance* entity, CScriptStack& stack) { uint32 alias = LigoConfig.aliasFromString((string)stack.top()) ; stack.pop(); float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (bot->getAlias() != alias) { continue; } if (!bot->isSpawned()) return; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)( sbot->maxHitPoints() *coef - sbot->currentHitPoints()) ); } } /** @page code @subsection downScaleHP_f_ Scales the bots HP down. Arguments: f(Coef) -> @param[in] Coef is a value @code ()downScaleHP(2); @endcode */ // CGroup void downScaleHP_f_(CStateInstance* entity, CScriptStack& stack) { float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); clamp(coef, 0.f, 1.f); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)(sbot->currentHitPoints()*(coef-1))); } } /** @page code @subsection upScaleHP_f_ Scales the bots HP up. Arguments: f(Coef) -> @param[in] Coef is a value @code ()upScaleHP(2); @endcode */ // CGroup void upScaleHP_f_(CStateInstance* entity, CScriptStack& stack) { float coef = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); clamp(coef, 0.f, 1.f); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* spBot = bot->getSpawnObj(); if (spBot) { msgList.Entities.push_back(spBot->dataSetRow()); msgList.DeltaHp.push_back((sint32)((spBot->maxHitPoints()-spBot->currentHitPoints())*coef)); } } } /** @page code @subsection addHP_f_ Add HP to the bots. Arguments: f(HP) -> @param[in] HP is the amount of hit points to add to each bot @code ()addHP(500); @endcode */ // CGroup void addHP_f_(CStateInstance* entity, CScriptStack& stack) { float addHP = stack.top(); stack.pop(); CChangeCreatureHPMsg& msgList = CAIS::instance().getCreatureChangeHP(); FOREACH(bot, CCont, entity->getGroup()->bots()) { if (!bot->isSpawned()) continue; CSpawnBot* const sbot = bot->getSpawnObj(); msgList.Entities.push_back(sbot->dataSetRow()); msgList.DeltaHp.push_back((sint32)(sbot->currentHitPoints()+addHP)); } } //---------------------------------------------------------------------------- /** @page code @subsection aiAction_s_ Triggers an AI action, defined by its sheet name, on the current target. Arguments: s(actionSheetId) -> @param[in] actionSheetId is a the sheet name of the action @code ()aiAction("kick_his_ass.aiaction"); @endcode */ // CGroup void aiAction_s_(CStateInstance* entity, CScriptStack& stack) { std::string actionName = stack.top(); stack.pop(); NLMISC::CSheetId sheetId(actionName); if (sheetId==NLMISC::CSheetId::Unknown) { nlwarning("Action SheetId Unknown %s", actionName.c_str()); return; } AISHEETS::IAIActionCPtr action = AISHEETS::CSheets::getInstance()->lookupAction(sheetId); if (action.isNull()) { nlwarning("Action SheetId Unknown %s", actionName.c_str()); return; } FOREACH(botIt, CCont, entity->getGroup()->bots()) { CSpawnBot* pbot = botIt->getSpawnObj(); if (pbot!=NULL) { CSpawnBot& bot = *pbot; if (!bot.getAIProfile()) continue; // OK CBotProfileFight* profile = dynamic_cast(bot.getAIProfile()); if (!profile) continue; // OK if (!profile->atAttackDist()) continue; // NOT OK TDataSetRow dataSetRow; if ((CAIEntityPhysical*)bot.getTarget()) dataSetRow = bot.getTarget()->dataSetRow(); CEGSExecuteAiActionMsg msg(bot.dataSetRow(), dataSetRow, action->SheetId(), bot._DamageCoef, bot._DamageSpeedCoef); msg.send(egsString); bot.setActionFlags(RYZOMACTIONFLAGS::Attacks); // OK } } } //---------------------------------------------------------------------------- /** @page code @subsection aiActionSelf_s_ Triggers an AI action, defined by its sheet name, on the bot itself. Arguments: s(actionSheetId) -> @param[in] actionSheetId is a the sheet name of the action @code ()aiActionSelf("defensive_aura.aiaction"); @endcode */ // CGroup void aiActionSelf_s_(CStateInstance* entity, CScriptStack& stack) { std::string actionName = stack.top(); stack.pop(); NLMISC::CSheetId sheetId(actionName); if (sheetId==NLMISC::CSheetId::Unknown) { nlwarning("Action SheetId Unknown %s", actionName.c_str()); return; } AISHEETS::IAIActionCPtr action = AISHEETS::CSheets::getInstance()->lookupAction(sheetId); if (action.isNull()) { nlwarning("Action SheetId Unknown %s", actionName.c_str()); return; } FOREACH(botIt, CCont, entity->getGroup()->bots()) { CSpawnBot* pbot = botIt->getSpawnObj(); if (pbot!=NULL) { CSpawnBot& bot = *pbot; CEGSExecuteAiActionMsg msg(bot.dataSetRow(), bot.dataSetRow(), action->SheetId(), bot._DamageCoef, bot._DamageSpeedCoef); msg.send(egsString); bot.setActionFlags(RYZOMACTIONFLAGS::Attacks); // OK } } } //---------------------------------------------------------------------------- /** @page code @subsection addProfileParameter_s_ Adds a profile parameter to the current group. Arguments: s(parameterName) -> @param[in] parameterName is a the id of the parameter to add @code ()addProfileParameter("running"); // équivalent à un parameter "running" dans la primitive du groupe @endcode */ // CGroup void addProfileParameter_s_(CStateInstance* entity, CScriptStack& stack) { std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, "", 0.f); } //---------------------------------------------------------------------------- /** @page code @subsection addProfileParameter_ss_ Adds a profile parameter to the current group. Arguments: s(parameterName),s(parameterContent) -> @param[in] parameterName is a the id of the parameter to add @param[in] parameterContent is the value of the parameter @code ()addProfileParameter("foo", "bar"); // équivalent à un parameter "foo:bar" dans la primitive du groupe @endcode */ // CGroup void addProfileParameter_ss_(CStateInstance* entity, CScriptStack& stack) { std::string value = (std::string)stack.top(); stack.pop(); std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, value, 0.f); } //---------------------------------------------------------------------------- /** @page code @subsection addProfileParameter_sf_ Adds a profile parameter to the current group. Arguments: s(parameterName),f(parameterContent) -> @param[in] parameterName is a the id of the parameter to add @param[in] parameterContent is the value of the parameter @code ()addProfileParameter("foo", 0.5); // équivalent à un parameter "foo:0.5" dans la primitive du groupe @endcode */ // CGroup void addProfileParameter_sf_(CStateInstance* entity, CScriptStack& stack) { float value = (float)stack.top(); stack.pop(); std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, "", value); } //---------------------------------------------------------------------------- /** @page code @subsection removeProfileParameter_s_ removes a profile parameter from the current group. Arguments: s(parameterName) -> @param[in] parameterName is a the id of the parameter to remove @code ()removeProfileParameter("running"); // retire le paramètre "running" ou "running:<*>" du groupe @endcode */ // CGroup void removeProfileParameter_s_(CStateInstance* entity, CScriptStack& stack) { std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->removeProfileParameter(name); } //---------------------------------------------------------------------------- /** @page code @subsection addPersistentProfileParameter_s_ Adds a profile parameter to the current group. Arguments: s(parameterName) -> @param[in] parameterName is a the id of the parameter to add @code ()addProfileParameter("running"); // équivalent à un parameter "running" dans la primitive du groupe @endcode */ // CGroup void addPersistentProfileParameter_s_(CStateInstance* entity, CScriptStack& stack) { std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, "", 0.f); group->addProfileParameter(name, "", 0.f); } //---------------------------------------------------------------------------- /** @page code @subsection addPersistentProfileParameter_ss_ Adds a profile parameter to the current group. Arguments: s(parameterName),s(parameterContent) -> @param[in] parameterName is a the id of the parameter to add @param[in] parameterContent is the value of the parameter @code ()addPersistentProfileParameter("foo", "bar"); // équivalent à un parameter "foo:bar" dans la primitive du groupe @endcode */ // CGroup void addPersistentProfileParameter_ss_(CStateInstance* entity, CScriptStack& stack) { std::string value = (std::string)stack.top(); stack.pop(); std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, value, 0.f); group->addProfileParameter(name, value, 0.f); } //---------------------------------------------------------------------------- /** @page code @subsection addPersistentProfileParameter_sf_ Adds a profile parameter to the current group. Arguments: s(parameterName),f(parameterContent) -> @param[in] parameterName is a the id of the parameter to add @param[in] parameterContent is the value of the parameter @code ()addPersistentProfileParameter("foo", 0.5); // équivalent à un parameter "foo:0.5" dans la primitive du groupe @endcode */ // CGroup void addPersistentProfileParameter_sf_(CStateInstance* entity, CScriptStack& stack) { float value = (float)stack.top(); stack.pop(); std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->addProfileParameter(name, "", value); group->addProfileParameter(name, "", value); } //---------------------------------------------------------------------------- /** @page code @subsection removePersistentProfileParameter_s_ removes a profile parameter from the current group. Arguments: s(parameterName) -> @param[in] parameterName is a the id of the parameter to remove @code ()removeProfileParameter("running"); // retire le paramètre "running" ou "running:<*>" du groupe @endcode */ // CGroup void removePersistentProfileParameter_s_(CStateInstance* entity, CScriptStack& stack) { std::string name = (std::string)stack.top(); stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) group->getSpawnObj()->removeProfileParameter(name); group->removeProfileParameter(name); } //---------------------------------------------------------------------------- /** @page code @subsection getOutpostState__s Returns the name of the current outpost state (group must be in an outpost). Arguments: -> s(StateName) @param[out] StateName is the name of the current outpost state @code ($name)getOutpostState(); @endcode */ // CGroup void getOutpostStateName__s(CStateInstance* si, CScriptStack& stack) { string str; breakable { if (!si) break; CGroup* group = si->getGroup(); if (!group) break; COutpostManager* manager = dynamic_cast(group->getOwner()); if (!manager) break; str = static_cast(manager->getOwner())->getStateName(); } stack.push(str); } //---------------------------------------------------------------------------- /** @page code @subsection isOutpostTribeOwner__f Returns whether the current outpost owner is a tribe (group must be in an outpost). Arguments: -> f(TribeOwner) @param[out] TribeOwner is whether the current outpost owner is a tribe (1) or not (0) @code (tribeOwner)isOutpostTribeOwner(); @endcode */ // CGroup void isOutpostTribeOwner__f(CStateInstance* si, CScriptStack& stack) { float tribeOwner = 0.f; breakable { if (!si) break; CGroup* group = si->getGroup(); if (!group) break; COutpostManager* manager = dynamic_cast(group->getOwner()); if (!manager) break; if (!static_cast(manager->getOwner())->isBelongingToAGuild()) tribeOwner = 1.f; } stack.push(tribeOwner); } //---------------------------------------------------------------------------- /** @page code @subsection isOutpostGuildOwner__f Returns whether the current outpost owner is a guild (group must be in an outpost). Arguments: -> f(TribeOwner) @param[out] TribeOwner is whether the current outpost owner is a guild (1) or not (0) @code (guildOwner)isOutpostGuildOwner(); @endcode */ // CGroup void isOutpostGuildOwner__f(CStateInstance* si, CScriptStack& stack) { float guildOwner = 0.f; breakable { if (!si) break; CGroup* group = si->getGroup(); if (!group) break; COutpostManager* manager = dynamic_cast(group->getOwner()); if (!manager) break; if (static_cast(manager->getOwner())->isBelongingToAGuild()) guildOwner = 1.f; } stack.push(guildOwner); } //---------------------------------------------------------------------------- /** @page code @subsection getEventParam_f_f Returns the content of a param Arguments: f(paramIndex) -> f(value) @param[in] paramIndex is the parameter index passed by EGS ai_event message @param[out] value is a the value of the parameter @code (val)getEventParam(2); @endcode */ // CGroup void getEventParam_f_f(CStateInstance* entity, CScriptStack& stack) { uint32 varId = (uint32)((float)stack.top()); CGroup* group = entity->getGroup(); stack.top() = group->getEventParamFloat(varId); } //---------------------------------------------------------------------------- /** @page code @subsection getEventParam_f_s Returns the content of a param Arguments: f(paramIndex) -> s(value) @param[in] paramIndex is the parameter index passed by EGS ai_event message @param[out] value is a the value of the parameter @code ($val)getEventParam(2); @endcode */ // CGroup void getEventParam_f_s(CStateInstance* entity, CScriptStack& stack) { uint32 varId = (uint32)((float)stack.top()); CGroup* group = entity->getGroup(); stack.top() = group->getEventParamString(varId); } // -- Boss functions //---------------------------------------------------------------------------- /** @page code @subsection getPlayerStat_ss_f Get some player stat. A player EntityId is used to identify the player. This EntityId is passed as string as argument. The EntityId can be obtains via getCurrentPlayerAggroListTarget or getRandomPlayerAggroListTarget. The player must be in the same AI Instance (same continent). If the player is not in the same Ai Instance or the input string is empty the function return zero and *display* a warning message on the log. You can think of using isPlayerAlived to be sure that the id is still a valid value. If param is not one of "HP", "MaxHp", "RatioHp" zero is return and a warning message is printed on the log. - The "Hp" stat is the property CURRENT_HIT_POINTS as seen in the mirror. - The "MaxHp" stat is the property MAX_HIT_POINTS as seen in the mirror. - The "RatioHp" stat is (Hp * 100) / MaxHp Be careful the argument is case sensitive. Arguments: s(playerEidAsString), s(statName) -> s(result) @param[in] playerEidAsString is EntityId as string from the player we want infos @param[in] statName is the name of the property (can be "HP", "MaxHp", "RatioHp") @param[out] value is a the value of the parameter @code ($playerEid)getCurrentPlayerEid(); print($playerEid); //log (0x00001fbd50:00:00:81) (maxHp)getPlayerStat($playerEid, "MaxHp"); @endcode */ void getPlayerStat_ss_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getPlayerStat_ss_f"; // reaed input std::string statName = ((std::string)stack.top()); stack.pop(); std::string playerEidStr = ((std::string)stack.top()); stack.pop(); // get Dataset of the player to have access to mirror values NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); TDataSetRow playerRow = TheDataset.getDataSetRow( playerEid ); if (! TheDataset.isAccessible( playerRow ) ) { nlwarning("Try to call %s with on a player '%s' that is not accessible. The isPlayerAlived function must be called to be sure that the palyer is still alived.", funName.c_str(), playerEidStr.c_str() ); stack.push((float)0); return; } if (statName == "Hp" ) { // return DSPropertyCURRENT_HIT_POINTS Mirror value CMirrorPropValue mirrorSymbol( TheDataset, playerRow, DSPropertyCURRENT_HIT_POINTS ); stack.push((float)mirrorSymbol.getValue()); return; } else if (statName == "MaxHp") { // return DSPropertyMAX_HIT_POINTS Mirror value CMirrorPropValue mirrorSymbol( TheDataset, playerRow, DSPropertyMAX_HIT_POINTS ); stack.push((float)mirrorSymbol.getValue()); return; } else if (statName == "RatioHp") { // return percentage of live (read from mirror values) CMirrorPropValue mirrorSymbol( TheDataset, playerRow, DSPropertyCURRENT_HIT_POINTS ); CMirrorPropValue mirrorSymbol2( TheDataset, playerRow, DSPropertyMAX_HIT_POINTS ); stack.push((float)(100.0*mirrorSymbol.getValue() / mirrorSymbol2.getValue())); return; } else { nlwarning("Try to call %s with wrong state %s", funName.c_str(), statName.c_str() ); } stack.push((float)0); return; } /** @page code @subsection getPlayerDistance_fs_f Get the distance between a player and a bot in meters. A player EntityId is used to identify the player. This EntityId is passed as string as argument. The EntityId can be obtains via getCurrentPlayerAggroListTarget or getRandomPlayerAggroListTarget. The player must be in the same AI Instance (same continent). If the player is not in the same Ai Instance or the input string is empty the function return -1 and display a warning message on the log. You can think of using isPlayerAlived to be sure that the player id is still a valid value. A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index the function returns -1 and display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. Arguments: s(playerEidAsString), f(botIndex) -> s(result) @param[in] playerEidAsString is EntityId as string from the player we want infos @param[in] botIndex is the index of an bot member of the current group @param[out] value is a the distance between the player on the bot (or -1 if error) @code ($playerEid)getCurrentPlayerEid(); (index)getBotIndexByName("toto"); (distance)getPlayerDistance(index, $playerEid); @endcode */ void getPlayerDistance_fs_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getPlayerDistance_is_f"; // read input params std::string playerEidStr = ((std::string)stack.top()); stack.pop(); sint32 botIndex = (sint32)((float)stack.top()); stack.pop(); // get the player Eid NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); // get the Spawn bot by its index CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); stack.push((float) -1); return; } if (!group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push((float)-1); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push((float)-1); return; } // get DataSetRow of player to read mirro values TDataSetRow playerRow = TheDataset.getDataSetRow( playerEid ); if (! TheDataset.isAccessible( playerRow ) ) { nlwarning("Try to call %s with on a player '%s' that is not accessible. The isPlayerAlived function must be called to be sure that the player is still alived.", funName.c_str(), playerEidStr.c_str() ); stack.push((float)-1); return; } // calculate the size between position of the player (from mirro) to position of the spawn bot. CMirrorPropValue mirrorSymbol( TheDataset, playerRow, DSPropertyPOSX ); CMirrorPropValue mirrorSymbol2( TheDataset, playerRow, DSPropertyPOSY ); const double dx = double (mirrorSymbol.getValue()) - double(spBot->x().asInt()); const double dy = double (mirrorSymbol2.getValue()) - double(spBot->y().asInt()); const double dist2 = (dx*dx + dy*dy) / 1000000.0; const double dist = sqrt(dist2); stack.push(float(dist)); return; } /** @page code @subsection getCurrentPlayerAggroListTarget_f_s Get the player entity id (as string) that has the most aggro in the aggro list of a bot. A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function returns an empty string and display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. A player EntityId is used to identify the player. This EntityId is returned as result. If the aggro list is empty an empty string is returned (but *NO* warning messages) Arguments: f(botIndex) -> s(playerEidAsString) @param[in] botIndex is the index of an bot member of the current group @param[out] playerEidAsString is EntityId as string from the player we want infos @code ($playerId)getCurrentPlayerAggroListTarget(4); (distance)getPlayerDistance(4, $playerId); @endcode */ void getCurrentPlayerAggroListTarget_f_s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getCurrentPlayerAggroListTarget_f_s"; // get input params sint32 botIndex = (sint32)((float)stack.top()); stack.pop(); // get spawn Bot CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); stack.push(std::string("")); return; } if (!group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } // iteratre throug aggro list to have the eid with max aggro CBotAggroOwner::TBotAggroList const& aggroList = spBot->getBotAggroList(); CBotAggroOwner::TBotAggroList::const_iterator aggroIt(aggroList.begin()), aggroEnd(aggroList.end()); TDataSetRow foundRow = TDataSetRow(); float foundAggro = 0; for (; aggroIt != aggroEnd; ++aggroIt) { float aggro = aggroIt->second->finalAggro(); if (aggro > foundAggro) { if (CMirrors::getEntityId(aggroIt->first).getType() != RYZOMID::player) { continue; } CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(aggroIt->first); if (!ep) { continue; } CBotPlayer const* const player = NLMISC::safe_cast(ep); if (!player) { continue; } if (!player->isAggroable()) { continue; } foundAggro = aggro; foundRow = aggroIt->first; } } if ( foundRow == TDataSetRow()) { // Empty aggro list so no warning displayed stack.push(std::string("")); return; } const NLMISC::CEntityId& found = CMirrors::getEntityId(foundRow); stack.push(std::string(found.toString())); } /** @page code @subsection getRandomPlayerAggroListTarget_f_s Get a player entity id (as string) from the aggro list of a bot. A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function returns an empty string and display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. A player EntityId is used to identify the player. This EntityId is returned as result. If the aggro list is empty an empty string is returned (but *NO* warning messages) Arguments: f(botIndex) -> s(playerEidAsString) @param[in] botIndex is the index of an bot member of the current group @param[out] playerEidAsString is EntityId as string from the player we want infos @code ($playerId)getRandomPlayerAggroListTarget(4); ()setAggroListTarget(4, $playerId); @endcode */ void getRandomPlayerAggroListTarget_f_s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getRandomPlayerAggroListTarget_f_s"; // test botIndex uint32 botIndex = (uint32)((float)stack.top()); stack.pop(); // get Spawn bot CGroup* group = entity->getGroup(); if (!group || !group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } // make a vector of aggroable player CBotAggroOwner::TBotAggroList const& aggroList = spBot->getBotAggroList(); //typedef std::map TBotAggroList; CBotAggroOwner::TBotAggroList::const_iterator aggroIt(aggroList.begin()), aggroEnd(aggroList.end()); TDataSetRow foundRow = TDataSetRow(); std::vector playerAggroable; for (; aggroIt != aggroEnd; ++aggroIt) { if (CMirrors::getEntityId(aggroIt->first).getType() != RYZOMID::player) { continue; } CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(aggroIt->first); if (!ep) { continue; } CBotPlayer const* const player = NLMISC::safe_cast(ep); if (!player) { continue; } if (!player->isAggroable()) { continue; } playerAggroable.push_back(aggroIt->first); } if ( playerAggroable.empty()) { stack.push(std::string("")); return; } // chose a randomly a player among the container uint32 index = static_cast( static_cast(playerAggroable.size())*rand()/(RAND_MAX+1.0) ); const NLMISC::CEntityId& found = CMirrors::getEntityId(playerAggroable[ index ]); stack.push(std::string(found.toString())); } /** @page code @subsection getAggroListElement_ff_s Get a player entity id (as string) from the aggro list of a bot by it aggro list index. A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function returns an empty string and display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. A aggro list index is used to identified the player in the aggro list. The size of the aggro list is given by getAggroListSize_f_s ) Arguments: f(botIndex), f(aggroListIndex) -> s(playerEidAsString) @param[in] botIndex is the index of an bot member of the current group @param[in] aggroListIndex is the index of a aggroable player in the aggro list of the bot identified by botIndex @param[out] playerEidAsString is EntityId as string from the player we want infos @code (aggroSize)getAggorListSize(0); // to much player are attacking the boss if (aggoroSize > 10) { ($player1)getAggroListElement(0); ($player2)getAggroListElement(1); ($player3)getAggroListElement(2); // so the boss teleport 3 person from its aggro list at the end of the world teleportPlayer($player1, 14233, 123123, 0, 0); teleportPlayer($player2, 14233, 123123, 0, 0); teleportPlayer($player2, 14233, 123123, 0, 0); clearAggroList(); } @endcode */ void getAggroListElement_ff_s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getAggroListElement_ff_s"; // get params uint32 elementIndex = (uint32)((float)stack.top()); stack.pop(); sint32 botIndex = (uint32)((float)stack.top()); stack.pop(); // get spawn bot by its index CGroup* group = entity->getGroup(); if (!group || !group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(std::string("")); return; } // make a vector of aggroable player CBotAggroOwner::TBotAggroList const& aggroList = spBot->getBotAggroList(); //typedef std::map TBotAggroList; CBotAggroOwner::TBotAggroList::const_iterator aggroIt(aggroList.begin()), aggroEnd(aggroList.end()); TDataSetRow foundRow = TDataSetRow(); std::vector playerAggroable; for (; aggroIt != aggroEnd; ++aggroIt) { if (CMirrors::getEntityId(aggroIt->first).getType() != RYZOMID::player) { continue; } CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(aggroIt->first); if (!ep) { continue; } CBotPlayer const* const player = NLMISC::safe_cast(ep); if (!player) { continue; } if (!player->isAggroable()) { continue; } playerAggroable.push_back(aggroIt->first); } if ( playerAggroable.empty()) { stack.push(std::string("")); return; } // if index outside return ""; if (0 > elementIndex && elementIndex >= playerAggroable.size()) { stack.push(std::string("")); return; } const NLMISC::CEntityId& found = CMirrors::getEntityId(playerAggroable[ uint32(elementIndex) ]); stack.push(std::string(found.toString())); } /** @page code @subsection getAggroListSize_f_f Get a size of the aggro lsit of a bot (list of aggroable PLAYER) A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function returns an empty string and display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. A number is used to indicate the size of the aggro lsit Arguments: f(aggroListIndex) -> f(sizeOfAggroList) @param[in] botIndex is the index of an bot member of the current group. @param[out] sizeOfAggroList The size of the aggro list index. @code (aggroSize)getAggorListSize(0); // to much player are attacking the boss if (aggoroSize > 10) { ($player1)getAggroListElement(0); ($player2)getAggroListElement(1); ($player3)getAggroListElement(2); // so the boss teleport 3 person from its aggro list at the end of the world teleportPlayer($player1, 14233, 123123, 0, 0); teleportPlayer($player2, 14233, 123123, 0, 0); teleportPlayer($player2, 14233, 123123, 0, 0); clearAggroList(); } @endcode */ void getAggroListSize_f_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getRandomPlayerAggroListTarget_f_s"; // input params uint32 botIndex = (uint32)((float)stack.top()); stack.pop(); // get spawn bot by index of the bot CGroup* group = entity->getGroup(); if (!group || !group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(float(0) ); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); stack.push(float(0) ); return; } // make a vector of aggroable player CBotAggroOwner::TBotAggroList const& aggroList = spBot->getBotAggroList(); //typedef std::map TBotAggroList; CBotAggroOwner::TBotAggroList::const_iterator aggroIt(aggroList.begin()), aggroEnd(aggroList.end()); TDataSetRow foundRow = TDataSetRow(); std::vector playerAggroable; for (; aggroIt != aggroEnd; ++aggroIt) { if (CMirrors::getEntityId(aggroIt->first).getType() != RYZOMID::player) { continue; } CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(aggroIt->first); if (!ep) { continue; } CBotPlayer const* const player = NLMISC::safe_cast(ep); if (!player) { continue; } if (!player->isAggroable()) { continue; } playerAggroable.push_back(aggroIt->first); } // return the size of the aggro list stack.push(float(playerAggroable.size())); } /** @page code @subsection setAggroListTarget_fs_ Maximize the Aggro of a target from the Aggro list of one bot (this id can be given by getRandomPlayerAggroListTarget).. A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. A player EntityId is used to identify the player. If the entityId is not from the aggro list then a warning message is shown in the log. If the entityId is a empty string nothing no message are displayed. Arguments: f(botIndex) >s(playerEidAsString) > @param[in] botIndex is the index of an bot member of the current group @param[in] playerEidAsString is EntityId of the player that will be attacked @code ($playerId)getRandomPlayerAggroListTarget(4); ()setAggroListTarget(4, $playerId); @endcode */ void setAggroListTarget_fs_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "setAggroListTarget_fs_"; std::string playerEidStr = ((std::string)stack.top()); stack.pop(); sint32 botIndex = (sint32)((float)stack.top());stack.pop(); // get the entity id NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); // get the spawn bot by its indesx CGroup* group = entity->getGroup(); if (!group || !group->isSpawned() || botIndex <0 || group->bots().size() <= static_cast(botIndex)) { nlwarning("%s used bad index %d/%d (or group not spawn)", funName.c_str(), botIndex, group->bots().size()); return; } CBot* bot = group->getBot(botIndex); CSpawnBot* spBot = bot?bot->getSpawnObj():0; if (!spBot) { nlwarning("%s used bad index %d/%d (or bot not spawn)", funName.c_str(), botIndex, group->bots().size()); return; } // test if player eid is ok if (playerEidStr.empty()) { return; } TDataSetRow playerRow = TheDataset.getDataSetRow( playerEid ); bool accessible = TheDataset.isAccessible( playerRow ); bool pj = playerEid.getType() == RYZOMID::player; CAIEntityPhysical* ep = accessible && pj ? CAIS::instance().getEntityPhysical(playerRow):0; CBotPlayer const* const player = ep ? NLMISC::safe_cast(ep):0; if (!ep || !player ||!player->isAggroable() ) { nlwarning("Try to call %s with on a player '%s' that is not accessible (or not aggroable). The isPlayerAlived function must be called to be sure that the player is still alived.", funName.c_str(), playerEidStr.c_str() ); return; } // maximize aggro from the bot for the player spBot->maximizeAggroFor(playerRow); } /** @page code @subsection setGroupAggroListTarget_s_ Maximize the Aggro of a target from the Aggro list of one bot to his group (this id can be given by getRandomPlayerAggroListTarget).. A player EntityId is used to identify the player. If the entityId is not from the aggro list then a warning message is shown in the log. If the entityId is a empty string nothing no message are displayed. Arguments: s(playerEidAsString) > @param[in] playerEidAsString is EntityId of the player that will be attacked by the group @code ($playerId)getRandomPlayerAggroListTarget(4); ()setGroupAggroListTarget($playerId); @endcode */ void setGroupAggroListTarget_s_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "setGroupAggroListTarget_s_"; // read params std::string playerEidStr = ((std::string)stack.top()); stack.pop(); // read eid of the player NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); // test if player eid is ok if (playerEidStr.empty()) { return; } TDataSetRow playerRow = TheDataset.getDataSetRow( playerEid ); bool accessible = TheDataset.isAccessible( playerRow ); bool pj = playerEid.getType() == RYZOMID::player; CAIEntityPhysical* ep = accessible && pj ? CAIS::instance().getEntityPhysical(playerRow):0; CBotPlayer const* const player = ep ? NLMISC::safe_cast(ep):0; if (!ep || !player ||!player->isAggroable() ) { nlwarning("Try to call %s with on a player '%s' that is not accessible (or not aggroable). The isPlayerAlived function must be called to be sure that the player is still alived.", funName.c_str(), playerEidStr.c_str() ); return; } // test if group is spawn CGroup* group = entity->getGroup(); if (!group || !group->isSpawned() || group->bots().isEmpty() ) { nlwarning("Call %s on a empty/not spawned group", funName.c_str() ); return; } // apply maximizeAggroFor on all member of the group CCont::iterator itBot = group->bots().begin(); CCont::iterator itEnd = group->bots().end(); for (; itBot != itEnd; ++itBot) { CSpawnBot* spBot = itBot->getSpawnObj(); if (!spBot) { continue; } spBot->maximizeAggroFor(playerRow); } } /** @page code @subsection setManagerAggroListTarget_ss_ Maximize the Aggro of a target of one bot to some groups of his manage. A player EntityId is used to identify the player. If the entityId is not from the aggro list then a warning message is shown in the log. If the entityId is a empty string nothing no message are displayed. A string is used to select groups of the manager (groups name must contains this string) Arguments: f(botIndex), s(playerEidAsString) > @param[in] playerId is EntityId of the player @param[in] nameElement The element of the name of all group we are interest in. @code ($playerId)getRandomPlayerAggroListTarget(0); ()setManagerAggroListTarget($playerId, "group_bandit_"); // all group that have name like "group_bandit_*" will attack the player @endcode */ void setManagerAggroListTarget_ss_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "setManagerAggroListTarget_ss_"; // read params std::string playerEidStr = ((std::string)stack.top()); stack.pop(); std::string groupNameStr = ((std::string)stack.top()); stack.pop(); // test if player eid is ok NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); if (playerEidStr.empty()) { return; } TDataSetRow playerRow = TheDataset.getDataSetRow( playerEid ); bool accessible = TheDataset.isAccessible( playerRow ); bool pj = playerEid.getType() == RYZOMID::player; CAIEntityPhysical* ep = accessible && pj ? CAIS::instance().getEntityPhysical(playerRow):0; CBotPlayer const* const player = ep ? NLMISC::safe_cast(ep):0; if (!ep || !player ||!player->isAggroable() ) { nlwarning("Try to call %s with on a player '%s' that is not accessible (or not aggroable). The isPlayerAlived function must be called to be sure that the player is still alived.", funName.c_str(), playerEidStr.c_str() ); return; } CGroup* group = entity->getGroup(); if (!group ) { nlwarning("Call %s on a non exisiting group", funName.c_str() ); return; } // get the manager of the group CManager* manager = group->getOwner(); if (!manager) { nlwarning("Call %s on a non exisiting manager", funName.c_str() ); return; } // for all group of the manager maximize aaggro *IF* the group name contains groupNameStr group=manager->getNextValidGroupChild(); while (group!=NULL) { if ( ! (!group || !group->isSpawned() || group->bots().isEmpty() ) && group->getName().find(groupNameStr) != std::string::npos ) { // apply maximizeAggroFor on all member of the group CCont::iterator itBot = group->bots().begin(); CCont::iterator itEnd = group->bots().end(); for (; itBot != itEnd; ++itBot) { CSpawnBot* spBot = itBot->getSpawnObj(); if (!spBot) { continue; } spBot->maximizeAggroFor(playerRow); } } group = manager->getNextValidGroupChild(group); } } /** @page code @subsection getBotIndexByName_s_f Get the index of a bot of a group by its name (or return -1). Mainly usefull for scripted boss. botIndex begins at zero for the first member of the group, -1 if the bot is not found. Arguments:, s(botName) > f(botIndex) @param[in] name is the name of the bot @param[out] botIndex is the index of an bot member of the current group @code (botIndex)getBotIndexByName("boss_random_aggro"); ($playerId)getRandomPlayerAggroListTarget(botIndex); ()setAggroListTarget(botIndex, $playerId); } @endcode */ void getBotIndexByName_s_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getBotIndexByName_s_f"; // read params std::string botName = (std::string)stack.top(); stack.pop(); // test if group is spawned CGroup* group = entity->getGroup(); if (!group || group->bots().isEmpty() ) { nlwarning("Call %s on a empty group", funName.c_str() ); stack.push((float)-1); return; } // Search on all persistent bot of the groupe the bot that have the searched name CCont::iterator itBot = group->bots().begin(); CCont::iterator itEnd = group->bots().end(); sint32 index = -1; for (; itBot != itEnd; ++itBot) { ++index; const std::string& name = itBot->getName(); if (name == botName) { stack.push((float)index); return; } } // Bot not found stack.push((float)-1); return; } /** @page code @subsection isGroupAlived__f Test if a group is alived (at least one member is alived) A bot index is used to identify the bot. If there is only one spawned bot in a group then the index value is 0. If we want the bot index of a specific bot the getBotIndexByName function can be used. The function getBotCount can be used to know the number of bot in a group. The function isGroupAlived or isBotAlived can be used to verify if the index is valid. If no bot are identified by the bot index (or bot not spawaned) the function display a warning message. You can this of using isBotAlived to be sure that the bot index is still a valid value. Arguments: s(playerid) > f(success) @param[out] sucess is 1.0f if there is at least one member of the group alived (0.0f if not alived bot are found) @code ($alived)isGroupAlived(); if ($alived == 1.0f) { } @endcode */ void isGroupAlived__f(CStateInstance* entity, CScriptStack& stack) { isAlived__f(entity, stack); } /** @page code @subsection isBotAlived_f_f Test if a bot of the current group is alived. The bot is identified by its index. Arguments: f(botIndex) > f(success) @param[int] botIndex the index of the bot. @param[out] sucess is 1.0f if there is at least one member of the group alived (0.0f if not alived bot are found) @code ($botIndex)getBotIndexByName("boss_3"); $alived)isBotAlived($botIndex); if (alived == 1.0f) { } @endcode */ void isBotAlived_f_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "isBotAlived_f_f"; // get input index sint32 botIndex = (sint32)((float)stack.top()); stack.pop(); // get Spawn Bot CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); stack.push((float)0); return; } if (!group->isSpawned() || group->bots().isEmpty() || botIndex < 0 || group->bots().size() < static_cast(botIndex)) { stack.push(0.0f);return; } const CBot *const bot = group->getBot(botIndex); if ( !bot || !bot->isSpawned()) { stack.push(0.0f); return;}; CAIEntityPhysical *const ep=bot->getSpawnObj(); if ( !ep) { stack.push(0.0f); return;}; // test if spawn bot is alive if (ep->isAlive()) { stack.push(1.0f); return;} stack.push(0.0f); return; } /** @page code @subsection isPlayerAlived_s_f Test if a a player is is alived. A player EntityId is used to identify the player. If the player is not connected, not in the same continent, or dead 0 is returned. Arguments: s(playerId) > f(success) @param[in] palyerId @param[out] success is 1.0f if the entity id of a player is alived. @code ($botIndex)getBotIndexByName("boss_3"); ($playerId)getCurrentPlayerAggroListTarget($botIndex): (alived)isPlayerlived($playerId); if (alived == 1.0f) { } @endcode */ void isPlayerAlived_s_f(CStateInstance* entity, CScriptStack& stack) { // get input data std::string playerEidStr = (std::string)stack.top(); stack.pop(); //get CAIEntityPhysical of the player NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); TDataSetRow playeDataSetRow = TheDataset.getDataSetRow( playerEid) ; if (playerEidStr.empty() || !TheDataset.isAccessible( playeDataSetRow ) || playerEid.getType() != RYZOMID::player ) { stack.push(0.0f); return; } CAIEntityPhysical* ep = CAIS::instance().getEntityPhysical(playeDataSetRow); if (!ep) { stack.push(0.0f); return; } // test if the player is alived if (ep->isAlive()) { stack.push(1.0f); return; } stack.push(0.0f); return; } /** @page code @subsection getServerTimeStr__s Gets the server time as string "eg 21:17:14 the x" This function is usefull for stat purpose or debug (to know when a boss is down) Arguments: > s(serverTime) @param[out] The server time as debug string @code ($serverTime)getServerTimeAsString(); @endcode */ void getServerTimeStr__s(CStateInstance* entity, CScriptStack& stack) { //get the server time (as debug string) time_t currentTime; time( ¤tTime ); std::string str = NLMISC::toString("%s", asctime(localtime(¤tTime))); stack.push(str); } /** @page code @subsection getServerTime__s Gets the server time as number (it is the number of seconde since 1970). This value is useful for saving the server date on a file Arguments: > s(serverTime) @param[out] The server time as string (a float is not sharp enough @code ($serverTime)getServerTime(); @endcode */ void getServerTime__s(CStateInstance* entity, CScriptStack& stack) { //get the server time (as time stamp) time_t currentTime; time( ¤tTime ); std::string ret = NLMISC::toString("%d", currentTime); // convert to string stack.push((std::string)ret); } /** @page code @subsection getRyzomDateStr__s Gets the ryzom date as string Arguments: > s(ryzomDateStr) @param[out] ryzomTimeAndDateAsString The time and date of the ryzom univers as debug string. @code ($ryzomDateStr)getRyzomDateStr); @endcode */ void getRyzomDateStr__s(CStateInstance* entity, CScriptStack& stack) { // Display time using ryzom style std::string result; const CRyzomTime &rt = CTimeInterface::getRyzomTime(); result = NLMISC::toString("%d:%d:00", (int) floorf(rt.getRyzomTime()) , (int) floorf(60.f * fmodf(rt.getRyzomTime(), 1.f))); uint32 month = rt.getRyzomMonth(); MONTH::EMonth monthInCycle = rt.getRyzomMonthInCurrentCycle(); std::string monthName = MONTH::toString((MONTH::EMonth) monthInCycle); uint32 dayOfMonth = rt.getRyzomDayOfMonth(); std::string dayName = WEEKDAY::toString((WEEKDAY::EWeekDay) rt.getRyzomDayOfWeek()); result += NLMISC::toString(" / %s %d %s(%d) %d", dayName.c_str(), (int) (dayOfMonth + 1), monthName.c_str(), (int) (month + 1), (int) rt.getRyzomYear()); stack.push( result ); } /** @page code @subsection getRyzomDate__s Gets the ryzom tick game cycle. Useful for computing difference of time. The return value is a string and not a float because float is not sharp enought? Arguments: > s(tickGameCycle) @param[out] TickGameCycle The time of the ryzom univers (stops when server stops). 10 tick is 1 second. @code ($tickGameCycle)getRyzomDate(); @endcode */ void getRyzomDate__s(CStateInstance* entity, CScriptStack& stack) { //get GameTickCycle const NLMISC::TGameCycle ryzomTime= CTickEventHandler::getGameCycle(); std::string ret = NLMISC::toString("%d", ryzomTime); // convert to string stack.push((std::string)ret); } class CPhraseParameters { public: enum TMode {NpcMsg, SystemMsg, EmoteMsg}; TVectorParamCheck Values; }; CPhraseParameters PhraseParameters; //---------------------------------------------------------------------------- /** @page code @subsection phraseBegin__ Clear the parameters stack. Used when creating a customized msg via phraseEndSystemMsg_fss_, phraseEndNpcMsg_fss_, phraseEndSystemMsg_fss_. It Must be called at start of phrase if we are not sure that the parameter stack is clean. Parameter stack is unclean when someone has called a phrasePush* function but *not* a phraseEnd function before (which is *very* bad). Arguments: -> @code ()phraseBegin(); ()phrasePushValue("money", 15); ()phrasePushValue("integer", 15); ()phrasePushValue("time", 15); ()phraseEndSystemMsg(0, "say", "PHRASE_FROM_PHRASE_WITH_3_PARAMETERS"); @endcode */ // CGroup void phraseBegin__(CStateInstance* entity, CScriptStack& stack) { PhraseParameters.Values.clear(); } //---------------------------------------------------------------------------- /** @page code @subsection phrasePushValue_sf_ This function push a value as number on the parameter stack. This stack is used by phraseEndSystemMsg_fss_, phraseEndNpcMsg_fss_, phraseEndSystemMsg_fss_ functions. @see phraseBegin__ @see phrasePushString_ss_ @see phraseEndSystemMsg_fss_ @see phraseEndNpcMsg_fss_ @see phraseEndSystemMsg_fss_ The value *Must* be an number and only few type are handled. - money - integer - time Some value other can be handled *BUT* the value *MUST* be the C++ code enum value. So LD must ask to coder to do a scripting fonction that have as input a string that contains enum value as string eg "matis" and that return the C++ enum value as int (eg 4). - skill - faction - power_type - race - damage_type - characteristic - score - body_part Arguments: s(paramType), f(value) -> @param[in] paramType the type of the parameter @param[in] value the value of the parameter @code ()phraseBegin(); ()phrasePushValue("money", 15); ()phrasePushValue("integer", 15); ()phrasePushValue("time", 15); ()phraseEndSystemMsg(0, "say", "PHRASE_WITH_3_PARAMETERS"); @endcode */ // CGroup void phrasePushValue_sf_(CStateInstance* entity, CScriptStack& stack) { float f = (float)stack.top();stack.pop(); // get the value as float std::string typeStr = (std::string)stack.top(); stack.pop(); // get the param type // create the good STRING_MANAGER::TParam in fonction of its type and is value STRING_MANAGER::TParamType type = STRING_MANAGER::stringToParamType(typeStr); STRING_MANAGER::TParam param; param.Type = type; switch( type ) { case STRING_MANAGER::money: { param.Money = static_cast(f); break; } case STRING_MANAGER::integer: { param.Int = static_cast(f); break; } case STRING_MANAGER::time: { param.Time = static_cast(f); break; } case STRING_MANAGER::skill: case STRING_MANAGER::faction: case STRING_MANAGER::power_type: case STRING_MANAGER::race: case STRING_MANAGER::damage_type: case STRING_MANAGER::characteristic: case STRING_MANAGER::score: case STRING_MANAGER::body_part: { param.Enum = static_cast( f ); break; } default: param.Type = STRING_MANAGER::invalid_value; break; } PhraseParameters.Values.push_back(param); } //---------------------------------------------------------------------------- /** @page code @subsection phrasePushString_ss_ This function push a value as string on the parameter stack. This stack is used by phraseEndSystemMsg_fss_, phraseEndNpcMsg_fss_, phraseEndSystemMsg_fss_ functions. @see phraseBegin__ @see phrasePushValue_sf_ @see phraseEndSystemMsg_fss_ @see phraseEndNpcMsg_fss_ @see phraseEndSystemMsg_fss_ The value *Must* be a string. The string is internal converted to number, sheetId, entityId when needed. Input as a number: - money - integer - time Input as string literal - literal Input as an entityId (obtains via getCurrentPlayerAggroListTarget_f_s, getRandomPlayerAggroListTarget_f_s, getBotEid_f_s, getCurrentSpeakerEid__s, getAggroListElement_ff_s) - player - bot - entity Input as sheetId name (example: "toto.sbrick", "toto.sitem", "toto.creature") - item - outpost - creature_model - creature - sphrase - sbrick Input as string identifier - place - event_faction - title - bot_name Input as stringId (number): *WARNING* LD must as coder to do function that return string_id if they want to use that type - dyn_string_id - string_id Input must be a enum value: Some value other can be handled *BUT* the value *MUST* be the C++ code enum value. So LD must ask to coder to do a scripting fonction that have as input a string that contains enum value as string eg "matis" and that return the C++ enum value as int (eg 4). - skill - faction - power_type - race - damage_type - characteristic - score - body_part Arguments: s(paramType), f(value) -> @param[in] paramType the type of the parameter @param[in] value the value of the parameter @code ($playerEid)getCurrentPlayerEid(); ($botEid)group3.getBotEid(4); ()phraseBegin(); ()phrasePushValue("integer", 15); ()phrasePushString("integer", "15"); ()phrasePushString("literal", "Test 123"); ()phrasePushString("player", $playerEid); ()phrasePushString("bot", $botEid); ()phrasePushString("item", "abc.sitem"); ()phrasePushString("sbrick", "toto.sbrick"); ()phrasePushString("creature_model", "toto.creature"); ()phraseEndSystemMsg(0, "say", "PHRASE_WITH_SOME_PARAMETERS"); @endcode */ void phrasePushString_ss_(CStateInstance* entity, CScriptStack& stack ) { std::string s = (std::string)stack.top(); stack.pop(); // get the param value std::string typeStr = (std::string)stack.top(); stack.pop(); // get the param type // construct a STRING_MANAGER::TParam in fonction of its type and its value STRING_MANAGER::TParamType type = STRING_MANAGER::stringToParamType(typeStr); STRING_MANAGER::TParam param; param.Type = type; switch( type ) { case STRING_MANAGER::money: { param.Money = static_cast(atoi( s.c_str() )); break; } case STRING_MANAGER::player: case STRING_MANAGER::bot: case STRING_MANAGER::entity: { NLMISC::CEntityId id; uint32 alias = 0; id.fromString(s.c_str()); CAIEntityPhysical* entityPhysical=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(id)); if (entityPhysical) { switch( entityPhysical->getRyzomType()) { case RYZOMID::creature: case RYZOMID::npc: { CSpawnBot* sb = NLMISC::safe_cast(entityPhysical); alias = sb->getPersistent().getAlias(); break; } default: ; // player or other have no alias } } param.setEIdAIAlias( id, alias ); } break; case STRING_MANAGER::integer: { param.Int = atoi( s.c_str()); break; } case STRING_MANAGER::time: { param.Time = atoi( s.c_str()); break; } case STRING_MANAGER::item: case STRING_MANAGER::outpost: case STRING_MANAGER::creature_model: case STRING_MANAGER::creature: case STRING_MANAGER::sphrase: case STRING_MANAGER::sbrick: { if (s.empty()) { nlwarning("Sheet name expected but not found in script"); param.SheetId= CSheetId(); } else { param.SheetId = CSheetId(s); } break; } case STRING_MANAGER::place: case STRING_MANAGER::event_faction: case STRING_MANAGER::title: case STRING_MANAGER::bot_name: { param.Identifier = s; break; } case STRING_MANAGER::skill: case STRING_MANAGER::faction: case STRING_MANAGER::power_type: case STRING_MANAGER::race: case STRING_MANAGER::damage_type: case STRING_MANAGER::characteristic: case STRING_MANAGER::score: case STRING_MANAGER::body_part: { param.Enum = static_cast( atoi( s.c_str()) ); break; } case STRING_MANAGER::literal: param.Literal.fromUtf8(s); break; case STRING_MANAGER::dyn_string_id: case STRING_MANAGER::string_id: param.StringId = static_cast( atoi( s.c_str()) ); break; case STRING_MANAGER::self: case STRING_MANAGER::role: case STRING_MANAGER::compass: case STRING_MANAGER::guild: case STRING_MANAGER::ecosystem: case STRING_MANAGER::classification_type: default: param.Type = STRING_MANAGER::invalid_value; break; } PhraseParameters.Values.push_back(param); } static void phraseEnd(CStateInstance* entity, CScriptStack& stack, CPhraseParameters::TMode mode) { std::string funName = "phraseEnd"; // get Function parameters std::string phraseId = (std::string)stack.top();stack.pop(); std::string sayMode; if (mode != CPhraseParameters::EmoteMsg) { sayMode = (std::string)stack.top();stack.pop(); } sint32 botIndex = (sint32)((float)stack.top());stack.pop(); // Verify is bot is alived CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); PhraseParameters.Values.clear(); return; } if (!group->isSpawned() || group->bots().isEmpty() || botIndex < 0 || group->bots().size() < static_cast(botIndex)) { PhraseParameters.Values.clear(); return; } const CBot *const bot = group->getBot(botIndex); if ( !bot || !bot->isSpawned()) { PhraseParameters.Values.clear(); return; } CSpawnBot *const sp= bot->getSpawnObj(); if ( !sp || !sp->isAlive() ) { PhraseParameters.Values.clear(); return; } // parse type of chat CChatGroup::TGroupType groupType; if (mode != CPhraseParameters::EmoteMsg) { groupType = CChatGroup::stringToGroupType(sayMode); } // verify phraseId validity if (phraseId.empty()) { nlwarning("%s %d: Phrase identifier is empty", funName.c_str(),static_cast(mode)); return; } // send chat to client switch (mode) { case CPhraseParameters::NpcMsg: npcChatParamToChannel(sp->dataSetRow(), groupType, phraseId.c_str(), PhraseParameters.Values); break; case CPhraseParameters::SystemMsg: STRING_MANAGER::sendSystemStringToClientAudience(sp->dataSetRow(),std::vector(), groupType, phraseId.c_str(), PhraseParameters.Values); break; case CPhraseParameters::EmoteMsg: STRING_MANAGER::sendCustomEmoteTextToClientAudience(sp->dataSetRow(),std::vector(), phraseId.c_str(), PhraseParameters.Values); break; } PhraseParameters.Values.clear(); } //---------------------------------------------------------------------------- /** @page code @subsection phraseEndNpcMsg_fss_ Send a message with parameter through a bot says. Parameters are taken from the parameters stack. @see phrasePushValue_sf_ @see phrasePushString_ss_ Arguments: s(botIndex), s(sayType), s(phraseIdentifier) -> @param[in] botIndex the Position of the bot in the group ( see getBotIndexByName_s_f) @param[in] sayType Is the type of the say dialog ("say", "shout", "civilization", "territory", "universe", "arround", "system", "region") @param[in] phraseIdentifier Is the identifier phrase as seen in phrase_wk.uxt @code ()phrasePushString("literal", "text non traduit"); ()groupOf5Bot.phraseEndNpcMsg(4, "say", "PHRASE_TOTO"); @endcode WARNING In this case in the phrase_wk.txt PHRASE_TOTO must be defined as @code PHRASE_TOTO(bot b, literal l) { [I am $b$, on this text is not translated $l$ ] } @endcode The first parameter is ALWAYS the bot that says the text (value is automaticaly set) */ void phraseEndNpcMsg_fss_(CStateInstance* entity, CScriptStack& stack) { phraseEnd(entity, stack, CPhraseParameters::NpcMsg); } //---------------------------------------------------------------------------- /** @page code @subsection phraseEndSystemMsg_fss_ Send a message with parameter through system braodcast msg. Parameters are taken from the parameters stack. @see phrasePushValue_sf_ @see phrasePushString_ss_ Arguments: s(botIndex), s(sayType), s(phraseIdentifier) -> @param[in] botIndex the Position of the bot in the group ( see getBotIndexByName_s_f) @param[in] sayType Is the type of the say dialog ("say", "shout", "civilization", "territory", "universe", "arround", "system", "region") @param[in] phraseIdentifier Is the identifier phrase as seen in phrase_wk.uxt @code ()phrasePushString("literal", "Test des levels designer"); ()groupOf5Bot.phraseEndSystemMsg(4, "say", "PHRASE_TOTO"); @endcode *WARNING* In this case in the phrase_wk.txt PHRASE_TOTO must be defined as @code PHRASE_TOTO(literal l) { [$l$] } @endcode The first parameter is *NOT* automaticaly set to the bot that send the system msg as phraseEndNpcMsg_fss_. Because the msg can be send as "around". The broadcas msg must be send by a bot (that have a valid position) */ void phraseEndSystemMsg_fss_(CStateInstance* entity, CScriptStack& stack) { phraseEnd(entity, stack, CPhraseParameters::SystemMsg); } //---------------------------------------------------------------------------- /** @page code @subsection phraseEndEmoteMsg_fs_ Send a custom emote message with parameter through system braodcast msg. Parameters are taken from the parameters stack. @see phrasePushValue_sf_ @see phrasePushString_ss_ Arguments: s(botIndex), s(phraseIdentifier) -> @param[in] botIndex the Position of the bot in the group ( see getBotIndexByName_s_f) @param[in] phraseIdentifier Is the identifier phrase as seen in phrase_wk.uxt @code ($playerEid)getCurrentPlayerEid(); ($botEid)getCurrentSpeakerEid(); ()phrasePushString("player", $playerEid); ()groupOf5Bot.phraseEndEmoteMsg(,"PHRASE_TOTO1"); ()phrasePushString("bot", $botEid); ()groupOf5Bot.phraseEndEmoteMsg(,"PHRASE_TOTO2"); @endcode *WARNING* In this case in the phrase_wk.txt PHRASE_TOTO must be defined as @code PHRASE_TOTO1(player p) { [$p$ is laughing ] } PHRASE_TOTO2(bot b) { [$b$ is sad ] } @endcode The first parameter is NOT automaticaly the bot that says the text as phraseEndNpcMsg_fss_ because a bot can make a player doing a emote text. The emote msg must be send by a bot (that have a valid position). */ void phraseEndEmoteMsg_fs_(CStateInstance* entity, CScriptStack& stack) { phraseEnd(entity, stack, CPhraseParameters::EmoteMsg); } //---------------------------------------------------------------------------- /** @page code @subsection queryEgs_sscfs_ Send a query msg to egs to know infos on a player. Answer is asynchronous so we have to indicates a group and a user event that will be triggered when answer will come back to AIS Possible info to know are - Name - Hp - MaxHp - RatioHp - Sap - MaxSap - RatioSap - Focus - MaxFocus - RatioFocus - Stamina - MaxStamina - RatioStamina Arguments: s(botIndex), s(query), c(groupThatWillBeTriggered), f(idOfTheUserEvent), s(msgId) @param[in] botIndex the Position of the bot in the group ( see getBotIndexByName_s_f) @param[in] query The query we want to send @param[in] groupThatWillBeTriggered The group that will receive a user_event when the answer will come @param[in] idOfTheUserEvent The number of the user event that will be triggered @param[in] msgId The id of the msg Answer will be given by the getParam @code //Sening msg to EGS (@groupToNotify)boss_group.context(); ()queryEgs("Name", $playerEid, @groupToNotify, 4, "MSG_NAME"); ()queryEgs("Hp", $playerEid, @groupToNotify, 4, "msg1"); ()queryEgs("MaxHp", $playerEid, @groupToNotify, 4, "msg2"); ()queryEgs("RatioHp", $playerEid, @groupToNotify, 4, "msg3"); ()queryEgs("Sap", $playerEid, @groupToNotify, 4, "msg4"); ()queryEgs("MaxSap", $playerEid, @groupToNotify, 4, "msg5"); ()queryEgs("RatioSap", $playerEid, @groupToNotify, 4, "msg6"); ()queryEgs("Focus", $playerEid, @groupToNotify, 4, "msg7"); ()queryEgs("MaxFocus", $playerEid, @groupToNotify, 4, "msg8"); ()queryEgs("RatioFocus", $playerEid, @groupToNotify, 4, "msg9"); ()queryEgs("Stamina", $playerEid, @groupToNotify, 4, "msg10"); ()queryEgs("MaxStamina", $playerEid, @groupToNotify, 4, "msg11"); ()queryEgs("RatioStamina", $playerEid, @groupToNotify, 4, "msg12"); ()queryEgs("BestSkillLevel", $playerEid, @groupToNotify, 4, "msg13"); @endcode Answer of the EGS @code // the user_event 4 of groupToNotify will be trigered ($msgName)getEventParam(0); // the msg name ($ret)getEventParam(1); // the return ($funName)getEventParam(2); // the name of the function ($playerEid)getEventParam(3); // the id of the player ($param1)getEventParam(4); // empty ot item, or sbrick or botEid if ($msgName == "MSG_NAME") { ()phrasePushString("literal", $ret); ()phrasePushString("player", $playerEid); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_MSG"); } else { ()phrasePushString("player", $playerEid); ()phrasePushString("literal", $funName); ()phrasePushString("literal", $msgName); ()phrasePushString("integer", $ret); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_EGS_QUERY"); } @endcode */ void queryEgs_sscfs_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "queryEgs_sscfs_"; // read input params string literal = (string)stack.top(); stack.pop(); float useEventId = (float)stack.top(); stack.pop(); CGroupNpc* const groupToNotify = dynamic_cast( (IScriptContext*)stack.top() ); stack.pop(); string param1 = (string)stack.top(); stack.pop(); string func = (string)stack.top(); stack.pop(); // create a CUserEventMsg to send to EGS CGroup* const group = entity ? entity->getGroup() : 0; IManagerParent* const managerParent = (group&& group->getOwner()) ? group->getOwner()->getOwner():0; CAIInstance* const aiInstance = dynamic_cast(managerParent); if (aiInstance == NULL) { nlwarning("%s failed: the AI instance of the entity is NULL", funName.c_str()); DEBUG_STOP; return; } CQueryEgs msg; msg.InstanceId = aiInstance->getInstanceNumber(); msg.GroupAlias = groupToNotify->getAlias(); msg.EventId = (int)useEventId; msg.Params.push_back(literal); msg.Params.push_back(func); msg.Params.push_back(param1); msg.send("EGS"); } /* @subsection queryEgs_ssscfs_ Send a query msg to egs to know infos on a player. Answer is asynchronous so we have to indicates a group and a user event that will be triggered when answer will come back to AIS Possible info to know are: - KnowBrick (to knwo if the player know a specific brick); return value is 0 or 1 (if the player know the brick) - IsInInventory (to knwo has an item in inventory of in equipement); return value is 0, 1(item in equipment), 2(item in bag) - Target (to know if a player target a bot; return value is 0, 1(player target the bot) Arguments: s(botIndex), s(query), s(queryParam), c(groupThatWillBeTriggered), f(idOfTheUserEvent), s(msgId) @param[in] botIndex the Position of the bot in the group ( see getBotIndexByName_s_f) @param[in] query The query we want to send @param[in] queryParam Is the paramter of the query (a .creature of IsInInventory, a .sbrick for KnowBrick, a Bot EntityId for Target @param[in] groupThatWillBeTriggered The group that will receive a user_event when the answer will come @param[in] idOfTheUserEvent The number of the user event that will be triggered @param[in] msgId The id of the msg Answer will be given by the getParam @code //Sening msg to EGS //($playerEid)getCurrentPlayerEid(); (@groupToNotify)boss_group.context(); ()queryEgs("KnowBrick", $playerEid, "bfma01.sbrick", @groupToNotify, 4, "MSG_BRICK"); ()queryEgs("IsInInventory", $playerEid, "iccm1bm.sitem", @groupToNotify, 4, "MSG_ITEM"); ()queryEgs("Target", $playerEid, $botEid, @groupToNotify, 4, "msg14"); @endcode Answer of the EGS @code ($msgName)getEventParam(0); // the msg name ($ret)getEventParam(1); // the return ($funName)getEventParam(2); // the name of the function ($playerEid)getEventParam(3); // the id of the player ($param1)getEventParam(4); // empty ot item, or sbrick or botEid if ($msgName == "MSG_ITEM") { ()phrasePushString("item", $param1); ()phrasePushString("integer", $ret); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_EGS_QUERY_ITEM"); } else if ($msgName == "MSG_BRICK") { ()phrasePushString("sbrick", $param1); ()phrasePushString("integer", $ret); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_EGS_QUERY_BRICK"); } else if ($msgName == "MSG_NAME") { ()phrasePushString("literal", $ret); ()phrasePushString("player", $playerEid); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_MSG"); } else { ()phrasePushString("player", $playerEid); ()phrasePushString("literal", $funName); ()phrasePushString("literal", $msgName); ()phrasePushString("integer", $ret); ()boss_group.phraseEndNpcMsg(0, "say", "TEST_BOSS_TEST_EGS_QUERY"); } @endcode */ void queryEgs_ssscfs_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "queryEgs_ssscfs_"; // get input params string literal = (string)stack.top(); stack.pop(); float useEventId = (float)stack.top(); stack.pop(); CGroupNpc* const groupToNotify = dynamic_cast( (IScriptContext*)stack.top() ); stack.pop(); string param2 = (string)stack.top(); stack.pop(); string param1 = (string)stack.top(); stack.pop(); string func = (string)stack.top(); stack.pop(); // create CQueryEgs to send to egs CGroup* const group = entity ? entity->getGroup() : 0; IManagerParent* const managerParent = (group&& group->getOwner()) ? group->getOwner()->getOwner():0; CAIInstance* const aiInstance = dynamic_cast(managerParent); if (aiInstance == NULL) { nlwarning("%s failed: the AI instance of the entity is NULL", funName.c_str()); DEBUG_STOP; return; } CQueryEgs msg; msg.InstanceId = aiInstance->getInstanceNumber(); msg.GroupAlias = groupToNotify->getAlias(); msg.EventId = (int)useEventId; msg.Params.push_back(literal); msg.Params.push_back(func); msg.Params.push_back(param1); msg.Params.push_back(param2); msg.send("EGS"); } /** @page code @subsection summonPlayer_fs_ Summon a player to a bot. Can be used by a boss to teleport a player. Or the create a teleporter. A player EntityId is used to identify the player. A index is used to identified the bot (index for the current group) Arguments: f(botIndex), s(playerEid) > @param[in] botIndex is the index of the bot in the current group(static group) @param[in] playerEid The entityId of the player @code ($playerId)getRandomPlayerAggroListTarget(0); ()teleportPlayer(0, $playerEid); // teleport player to the boss. @endcode */ void summonPlayer_fs_(CStateInstance* entity, CScriptStack& stack) { std::string funName = "summonPlayer_fffs_"; std::string playerEidStr = ((std::string)stack.top()); stack.pop(); sint32 botIndex = (sint32)((float)stack.top()); stack.pop(); // Verify is bot is alived CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); return; } if (!group->isSpawned() || group->bots().isEmpty() || botIndex < 0 || group->bots().size() < static_cast(botIndex)) { return; } const CBot *const bot = group->getBot(botIndex); if ( !bot || !bot->isSpawned()) { return; } CSpawnBot *const sp= bot->getSpawnObj(); if ( !sp || !sp->isAlive() ) { return; } // Read position for mirror TDataSetRow row = sp->dataSetRow(); if (! TheDataset.isAccessible( row ) ) { return; } CMirrorPropValue mirrorSymbolX( TheDataset, row, DSPropertyPOSX ); CMirrorPropValue mirrorSymbolY( TheDataset, row, DSPropertyPOSY ); // retrieve the CBotPlayer NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); CAIEntityPhysical *charEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(playerEid)); if (!charEntity) return; // do nothing if one of them is dead if (!charEntity->isAlive() ) { return; } // send teleport MSG TDataSetRow CharacterRowId = charEntity->dataSetRow(); NLMISC::CEntityId player = CMirrors::getEntityId(CharacterRowId); if (player != NLMISC::CEntityId::Unknown) { sint32 x2 = mirrorSymbolX.getValue(); sint32 y2 = mirrorSymbolY.getValue(); sint32 z2 = 0; float t = 0; // TODO direction to mob? NLNET::CMessage msgout( "TELEPORT_PLAYER" ); nlWrite(msgout, serial, player ); msgout.serial( const_cast(x2) ); msgout.serial( const_cast(y2) ); msgout.serial( const_cast(z2) ); msgout.serial( const_cast(t) ); sendMessageViaMirror( "EGS", msgout ); } } /** @page code @subsection teleportPlayer_sffff_ Teleport a player to a position A player EntityId is used to identify the player. The position is identified by the value x,y,z and the heading Arguments: s(playerId), f(x), f(y), f(z), f(heading) > @param[in] playerId is EntityId of the player @param[in] x,y,z,heading is the new position of the player @code ($playerId)getRandomPlayerAggroListTarget(0); ()teleportPlayer($playerEid, 1000, 1000, 100, 0); @endcode */ void teleportPlayer_sffff_(CStateInstance* entity, CScriptStack& stack) { // get player and position parameters float t = (float)stack.top(); stack.pop(); // heading float z = (float)stack.top(); stack.pop(); float y = (float)stack.top(); stack.pop(); float x = (float)stack.top(); stack.pop(); std::string playerEidStr = (std::string)stack.top(); stack.pop(); NLMISC::CEntityId playerEid; playerEid.fromString(playerEidStr.c_str()); // retrieve the CBotPlayer CAIEntityPhysical *charEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(playerEid)); if (!charEntity) return; // do nothing if one of them is dead if (!charEntity->isAlive() ) { return; } // teleport player to position if (playerEid != NLMISC::CEntityId::Unknown) { sint32 x2 = static_cast(x*1000); sint32 y2 = static_cast(y*1000); sint32 z2 = static_cast(z*1000); NLNET::CMessage msgout( "TELEPORT_PLAYER" ); msgout.serial( const_cast(playerEid) ); msgout.serial( const_cast(x2) ); msgout.serial( const_cast(y2) ); msgout.serial( const_cast(z2) ); msgout.serial( const_cast(t) ); sendMessageViaMirror( "EGS", msgout ); } } //---------------------------------------------------------------------------- /** @page code @subsection getBotEid_f_s Get the bot EntityId by its Index. Arguments: f(botIndex) -> s(botEid) @param[in] botIndex the Position of the bot in the group @param[out] botEid The entity Id given by the bot (or empty) if the indexed bot is not from the group @code (index)getBotIndexByName("bot_toto"); ($botEid)getBotEid(index); if (index != -1) { ()phrasePushValue("entity", $botEid); ()phraseEndEmoteMsg(index, "PHRASE_YOUR_ARE_CLICKING_ON_ME"); } @endcode */ void getBotEid_f_s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getBotEid_f_s"; // get spawn Bot by its index CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); stack.push(std::string(CEntityId::Unknown.toString())); return; } sint32 botIndex = (sint32)((float)stack.top());stack.pop(); if (!group->isSpawned() || group->bots().isEmpty() || botIndex < 0 || group->bots().size() < static_cast(botIndex)) { stack.push(std::string(CEntityId::Unknown.toString())); return; } const CBot *const bot = group->getBot(botIndex); if ( !bot || !bot->isSpawned()) { stack.push(std::string(CEntityId::Unknown.toString())); return;} CSpawnBot *const sb= bot->getSpawnObj(); if ( !sb ||!sb->isAlive()) {stack.push(std::string(CEntityId::Unknown.toString())); return;}; // return the entity id of the spawn bot CEntityId id= sb->getEntityId(); stack.push(std::string(id.toString())); } //---------------------------------------------------------------------------- /** @page code @subsection getBotIndex_s_f Get the bot Index by its entityId. Entity Id of a bot can be given via getCurrentSpeackerEid(). It can be usefull to Known the index of the bot in the group with the EntityId. Arguments: s(botEid) -> f(botIndex), @param[in] botEid The entity Id given by the bot @param[out] botIndex the Position of the bot in the group (or -1 if the entity_id is not from a bot of the group) @code ($botEid)getCurrentSpeakerEid(); (index)getBotIndex($botEid); if (index != -1) { ()phrasePushValue("entity", $botEid); ()phraseEndNpcg(index, "PHRASE_YOUR_ARE_CLICKING_ON_ME"); } @endcode */ void getBotIndex_s_f(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getBotEid_f_s"; CGroup* group = entity->getGroup(); if (!group) { nlwarning("%s on a non Npc Group, doesn't work", funName.c_str()); stack.push(std::string(CEntityId::Unknown.toString())); return; } std::string botEid = (std::string)stack.top(); stack.pop(); // look in the group if the bot is alived uint32 botIndex = 0, last = group->bots().size(); for ( ;botIndex != last; ++botIndex) { if (!group->isSpawned() || group->bots().isEmpty() || group->bots().size() < static_cast(botIndex)) { continue; } const CBot *const bot = group->getBot(botIndex); if ( !bot || !bot->isSpawned()) { continue; } CSpawnBot *const sb= bot->getSpawnObj(); if ( !sb ||!sb->isAlive()) {continue;}; CEntityId id= sb->getEntityId(); // if bot is alived return its index if (botEid == id.toString()) { stack.push((float)botIndex); return; } } stack.push((float)-1); return; } /** @page code @subsection getCurrentPlayerEid__s Get the entity id of the player that is clicking on a bot. WARNING the function is only valid when called from an player_target_npc event. Arguments: -> s(playerEidAsString), @param[out] playerEidAsString is EntityId as string from the player. @code ($playerEid)getCurrentPlayerEid(); (index)getBotIndexByName("toto"); (distance)getPlayerDistance(index, $playerEid); phrasePushString("player", $playerEid); phrasePushValue("interger", distance); phraseEndNpcMsg(index, "say", "MSG_BOT_B_SAYS_THE_PLAYER_P_IS_AT_DISTANCE_D"); @endcode */ void getCurrentPlayerEid__s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getCurrentPlayerEid__s"; CEntityId id = CEntityId::Unknown; // if we are in player_target_npc event TempPlayer is valid if (!TempPlayer) { //TempPlayer is invalid so return Unkwn eid std::string s = id.toString(); stack.push(s); return; } // TempPlayer is valid so return eid id = TempPlayer->getEntityId(); std::string s = id.toString(); stack.push(s); return; } /** @page code @subsection getCurrentSpeakerEid__s Get the entity id of the bot at which the player as that is clicking at. WARNING the function is only valid when called from an player_target_npc event. Arguments: -> s(botEidAsString), @param[out] botEidAsString is EntityId as string from the bot. @code ($botEid)getCurrentSpeakerEid(); ($playerEid)getCurrentSpeakerEid(); phrasePushString("player", $playerEid); phrasePushValue("bot", $botEid); phraseEndEmotMsg(index, "EMOT_PLAYER_INSULT_BOT"); @endcode */ void getCurrentSpeakerEid__s(CStateInstance* entity, CScriptStack& stack) { std::string funName = "getCurrentSpeakerEid__s"; CEntityId id = CEntityId::Unknown; // if we are in player_target_npc event TempSpeaker is valid if (!TempSpeaker) { //TempSpeaker is invalid so return Unkwn eid std::string s = id.toString(); stack.push(s); return; } //TempSpeaker is valid so return correct eid id = TempSpeaker->getEntityId(); std::string s = id.toString(); stack.push(s); return; } ////////////////////////////////////////////////////////////////////////////// // Undocumented methods // ////////////////////////////////////////////////////////////////////////////// //---------------------------------------------------------------------------- // CGroup void setSheet_s_(CStateInstance* entity, CScriptStack& stack) { string sheetname = stack.top(); stack.pop(); CSheetId sheetId(sheetname+".creature"); if (sheetId==CSheetId::Unknown) return; FOREACH(itBot, CCont, entity->getGroup()->bots()) { CBot* bot = *itBot; if (bot) { AISHEETS::ICreatureCPtr const sheet = AISHEETS::CSheets::getInstance()->lookup(sheetId); if (!sheet.isNull()) bot->triggerSetSheet(sheet); } } } //---------------------------------------------------------------------------- // CGroup void setHealer_f_(CStateInstance* entity, CScriptStack& stack) { bool value = ((float)stack.top())!=0.f; stack.pop(); CGroup* group = entity->getGroup(); if (group->isSpawned()) { FOREACH(itBot, CCont, group->bots()) { CBot* bot = *itBot; if (bot) { bot->setHealer(value); // break; // :NOTE: Only set first bot as a healer } } } } /****************************************************************************/ //---------------------------------------------------------------------------- /** @page code @subsection sitDown__ Make the group sit Down Arguments: -> @code ()sitDown(); @endcode */ // CStateInstance void sitDown__(CStateInstance* entity, CScriptStack& stack) { CGroup* group = entity->getGroup(); if (!group) { nlwarning("sitDown__ failed"); return; } CAILogicActionSitDownHelper::sitDown(group); } //---------------------------------------------------------------------------- /** @page code @subsection standUp Make the group stand up (if was previously stand down) Arguments: -> @code ()standUp(); @endcode */ // CStateInstance void standUp__(CStateInstance* entity, CScriptStack& stack) { CGroup* group = entity->getGroup(); if (!group) { nlwarning("standUp__ failed"); return; } CAILogicActionSitDownHelper::standUp(group); } //---------------------------------------------------------------------------- /** @page code @subsection standUp Use to implement setConditionRet Arguments: -> @code ()setConditionRet(1); @endcode */ // CStateInstance void setConditionSuccess_f_(CStateInstance* entity, CScriptStack& stack) { bool conditionState = (uint32)((float)stack.top()) != 0; CGroup* group = entity->getGroup(); if (!group) { nlwarning("setConditionSuccess_f_ failed"); return; } CAILogicDynamicIfHelper::setConditionSuccess(conditionState); } std::map nfGetGroupNativeFunctions() { std::map functions; #define REGISTER_NATIVE_FUNC(cont, func) cont.insert(std::make_pair(std::string(#func), &func)) REGISTER_NATIVE_FUNC(functions, spawn__); REGISTER_NATIVE_FUNC(functions, despawn_f_); REGISTER_NATIVE_FUNC(functions, isAlived__f); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPos_ssfff_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPos_ssfff_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPos_ssff_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPos_ssff_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPosMl_ssffff_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPosMl_ssffff_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPosMl_ssfff_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupPosMl_ssfff_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroup_sssf_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroup_sssf_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroup_sss_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroup_sss_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupMl_sssff_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupMl_sssff_); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupMl_sssf_c); REGISTER_NATIVE_FUNC(functions, newNpcChildGroupMl_sssf_); REGISTER_NATIVE_FUNC(functions, spawnManager_s_); REGISTER_NATIVE_FUNC(functions, sitDown__); REGISTER_NATIVE_FUNC(functions, standUp__); REGISTER_NATIVE_FUNC(functions, despawnManager_s_); REGISTER_NATIVE_FUNC(functions, getMidPos__ff); REGISTER_NATIVE_FUNC(functions, getGroupTemplateWithFlags_sss_s); REGISTER_NATIVE_FUNC(functions, getGroupTemplateWithFlags_ss_s); REGISTER_NATIVE_FUNC(functions, getZoneWithFlags_ssss_s); REGISTER_NATIVE_FUNC(functions, getZoneWithFlags_sss_s); REGISTER_NATIVE_FUNC(functions, getNearestZoneWithFlags_ffsss_s); REGISTER_NATIVE_FUNC(functions, getNearestZoneWithFlags_ffss_s); REGISTER_NATIVE_FUNC(functions, getNearestZoneWithFlagsStrict_ffsss_s); REGISTER_NATIVE_FUNC(functions, getNearestZoneWithFlagsStrict_ffss_s); REGISTER_NATIVE_FUNC(functions, getNeighbourZoneWithFlags_ssss_s); REGISTER_NATIVE_FUNC(functions, getNeighbourZoneWithFlags_sss_s); REGISTER_NATIVE_FUNC(functions, setAggro_ff_); REGISTER_NATIVE_FUNC(functions, setCanAggro_f_); REGISTER_NATIVE_FUNC(functions, clearAggroList_f_); REGISTER_NATIVE_FUNC(functions, clearAggroList__); REGISTER_NATIVE_FUNC(functions, setMode_s_); REGISTER_NATIVE_FUNC(functions, setAutoSpawn_f_); REGISTER_NATIVE_FUNC(functions, setHPLevel_f_); REGISTER_NATIVE_FUNC(functions, setHPScale_f_); REGISTER_NATIVE_FUNC(functions, scaleHP_f_); REGISTER_NATIVE_FUNC(functions, setBotHPScaleByAlias_fs_); REGISTER_NATIVE_FUNC(functions, downScaleHP_f_); REGISTER_NATIVE_FUNC(functions, upScaleHP_f_); REGISTER_NATIVE_FUNC(functions, addHP_f_); REGISTER_NATIVE_FUNC(functions, aiAction_s_); REGISTER_NATIVE_FUNC(functions, aiActionSelf_s_); REGISTER_NATIVE_FUNC(functions, addProfileParameter_s_); REGISTER_NATIVE_FUNC(functions, addProfileParameter_ss_); REGISTER_NATIVE_FUNC(functions, addProfileParameter_sf_); REGISTER_NATIVE_FUNC(functions, removeProfileParameter_s_); REGISTER_NATIVE_FUNC(functions, addPersistentProfileParameter_s_); REGISTER_NATIVE_FUNC(functions, addPersistentProfileParameter_ss_); REGISTER_NATIVE_FUNC(functions, addPersistentProfileParameter_sf_); REGISTER_NATIVE_FUNC(functions, removePersistentProfileParameter_s_); REGISTER_NATIVE_FUNC(functions, getOutpostStateName__s); REGISTER_NATIVE_FUNC(functions, isOutpostTribeOwner__f); REGISTER_NATIVE_FUNC(functions, isOutpostGuildOwner__f); REGISTER_NATIVE_FUNC(functions, getEventParam_f_f); REGISTER_NATIVE_FUNC(functions, getEventParam_f_s); REGISTER_NATIVE_FUNC(functions, setSheet_s_); REGISTER_NATIVE_FUNC(functions, setHealer_f_); REGISTER_NATIVE_FUNC(functions, setConditionSuccess_f_); // Boss functions (custom text) REGISTER_NATIVE_FUNC(functions, phraseBegin__); REGISTER_NATIVE_FUNC(functions, phrasePushValue_sf_); REGISTER_NATIVE_FUNC(functions, phrasePushString_ss_); REGISTER_NATIVE_FUNC(functions, phraseEndNpcMsg_fss_); REGISTER_NATIVE_FUNC(functions, phraseEndSystemMsg_fss_); REGISTER_NATIVE_FUNC(functions, phraseEndEmoteMsg_fs_); // Boss functions (Bot infos) REGISTER_NATIVE_FUNC(functions, getBotIndex_s_f); REGISTER_NATIVE_FUNC(functions, getBotEid_f_s); REGISTER_NATIVE_FUNC(functions, getCurrentSpeakerEid__s); REGISTER_NATIVE_FUNC(functions, getBotIndexByName_s_f); REGISTER_NATIVE_FUNC(functions, isGroupAlived__f); REGISTER_NATIVE_FUNC(functions, isBotAlived_f_f); // Boss functions (Player infos) REGISTER_NATIVE_FUNC(functions, isPlayerAlived_s_f); REGISTER_NATIVE_FUNC(functions, getPlayerStat_ss_f); REGISTER_NATIVE_FUNC(functions, getPlayerDistance_fs_f); REGISTER_NATIVE_FUNC(functions, getCurrentPlayerEid__s); REGISTER_NATIVE_FUNC(functions, queryEgs_sscfs_); REGISTER_NATIVE_FUNC(functions, queryEgs_ssscfs_); // Boss functions (Aggro list) REGISTER_NATIVE_FUNC(functions, getCurrentPlayerAggroListTarget_f_s); REGISTER_NATIVE_FUNC(functions, getRandomPlayerAggroListTarget_f_s); REGISTER_NATIVE_FUNC(functions, getAggroListElement_ff_s); REGISTER_NATIVE_FUNC(functions, getAggroListSize_f_f); REGISTER_NATIVE_FUNC(functions, setAggroListTarget_fs_); REGISTER_NATIVE_FUNC(functions, setGroupAggroListTarget_s_); REGISTER_NATIVE_FUNC(functions, setManagerAggroListTarget_ss_); // Boss functions (Time infos) REGISTER_NATIVE_FUNC(functions, getServerTimeStr__s); REGISTER_NATIVE_FUNC(functions, getServerTime__s); REGISTER_NATIVE_FUNC(functions, getRyzomDateStr__s); REGISTER_NATIVE_FUNC(functions, getRyzomDate__s); // Boss function (teleport actions) REGISTER_NATIVE_FUNC(functions, teleportPlayer_sffff_); REGISTER_NATIVE_FUNC(functions, summonPlayer_fs_); #undef REGISTER_NATIVE_FUNC return functions; }