Added ai command setEquipment
[ryzomcore.git] / ryzom / server / src / ai_service / nf_grp_npc.cpp
blob0a67aea43cf402c901113d4b9df741cce4beee9f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdpch.h"
22 #include "script_compiler.h"
24 #include "ai_grp_npc.h"
25 #include "group_profile.h"
26 #include "ai_generic_fight.h"
27 #include "server_share/msg_brick_service.h"
29 #include "continent_inline.h"
30 #include "dyn_grp_inline.h"
32 #include "ai_player.h"
34 #include "ai_script_data_manager.h"
35 #include "ais_control.h"
37 using std::string;
38 using std::vector;
39 using namespace NLMISC;
40 using namespace AIVM;
41 using namespace AICOMP;
42 using namespace AITYPES;
43 using namespace RYAI_MAP_CRUNCH;
45 // a big bad global var !
46 extern CAIEntityPhysical *TempSpeaker;
47 extern CBotPlayer *TempPlayer;
49 /****************************************************************************/
50 /* CGroupNpc methods */
51 /****************************************************************************/
53 //----------------------------------------------------------------------------
54 // setfactionProp
55 /** @page code
57 @subsection setFactionProp_ss_
58 Set a property of the faction profile. Valid values for Property are:
59 - faction
60 - ennemyFaction
61 - friendFaction
63 There are special faction that are automatically attributed to entities.
64 Special factions are:
65 - Player
66 - Predator
67 - Famous<faction> where faction is the name of a Ryzom faction with uppercase
68 initials and without underscores (ie: tribe_beachcombers ->
69 FamousTribeBeachcombers), it represents players with a positive fame with the
70 specified faction
71 - outpost:<id>:<side> where id is the outpost alias as an int, and side is
72 either attacker ou defender (these faction may see their format change soon
73 and shouldn't be used without prior discussion with the responsible coder), it
74 represents players and squads fighting in an outpost conflict
76 Arguments: s(Property),s(Content) ->
77 @param Property is the property to modify
78 @param Content is a '|' seperated list of faction names
80 @code
81 ()setFactionProp("ennemyFaction", "Player");
82 @endcode
85 // CGroupNpc
86 void setFactionProp_ss_(CStateInstance* entity, CScriptStack& stack)
88 std::string params = stack.top(); // ToChange
89 stack.pop();
90 TStringId const factionType = CStringMapper::map(stack.top());
91 stack.pop();
93 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
94 if (!grpNpc)
96 nlwarning("setFactionProp on a non Npc Group, doesnt work");
97 return;
100 static TStringId factionStrId = CStringMapper::map("faction");
101 static TStringId ennemyFactionStrId = CStringMapper::map("ennemyFaction");
102 static TStringId friendFactionStrId = CStringMapper::map("friendFaction");
103 breakable
105 CPropertySetWithExtraList<TAllianceId> tmpSet;
106 CStringSeparator const sep(params,"|");
107 while (sep.hasNext())
108 tmpSet.addProperty(AITYPES::CPropertyId::create(sep.get()));
110 if (factionType==factionStrId)
112 grpNpc->faction() = tmpSet;
113 break;
115 if (factionType==ennemyFactionStrId)
117 grpNpc->ennemyFaction() = tmpSet;
118 break;
120 if (factionType==friendFactionStrId)
122 grpNpc->friendFaction() = tmpSet;
123 break;
125 nlwarning("'%s' is not a correct faction name", CStringMapper::unmap(factionType).c_str());
126 return;
130 void setOupostMode_ss_(CStateInstance* entity, CScriptStack& stack)
132 std::string sideStr = stack.top(); stack.pop();
133 std::string aliasStr = stack.top();
134 CGroupNpc* const npcGroup = dynamic_cast<CGroupNpc*>(entity->getGroup());
135 if ( ! npcGroup)
137 nlwarning("setOutpostMode on a non Npc Group, doesnt work");
138 return;
141 OUTPOSTENUMS::TPVPSide side;
142 if (sideStr == "attacker")
144 side = OUTPOSTENUMS::OutpostAttacker;
146 else if (sideStr == "owner")
148 side = OUTPOSTENUMS::OutpostOwner;
150 else
152 nlwarning("setOutpostMode: invalid side");
155 npcGroup->setOutpostSide(side);
156 npcGroup->setOutpostFactions(aliasStr, side);
157 FOREACH(botIt, CCont<CBot>, npcGroup->bots())
159 CBot* bot = *botIt;
160 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
161 if (botNpc)
163 CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn();
164 if (spawnBotNpc)
166 spawnBotNpc->setOutpostSide(side);
167 spawnBotNpc->setOutpostAlias(LigoConfig.aliasFromString(aliasStr));
171 if (npcGroup->isSpawned())
172 npcGroup->getSpawnObj()->sendInfoToEGS();
175 //----------------------------------------------------------------------------
176 /** @page code
178 @subsection moveToZone_ss_
179 Moves the current group from one zone to another.
181 Arguments: s(From), s(To) ->
182 @param[in] From is a zone name
183 @param[in] To is a zone name
185 @code
186 ()moveToZone($zone1, $zone2); // Move the current group from $zone1 to $zone2
187 @endcode
190 // Spawned CGroupNpc not in a family behaviour
191 void moveToZone_ss_(CStateInstance* entity, CScriptStack& stack)
193 TStringId const zoneDest = CStringMapper::map(stack.top());
194 stack.pop();
195 TStringId const zoneSrc = CStringMapper::map(stack.top());
196 stack.pop();
198 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
199 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
200 if (!aiInstance)
201 return;
203 if (!entity)
205 nlwarning("moveToZone failed: no state instance!");
206 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
207 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
208 return;
211 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
212 if (!group)
214 nlwarning("moveToZone failed: no NPC group!");
215 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
216 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
217 return;
219 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
220 if (!spawnGroup)
222 nlwarning("moveToZone failed: no spawned group!");
223 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
224 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
225 return;
228 CNpcZone const* const destZone = aiInstance->getZone(zoneDest);
229 CNpcZone const* const srcZone = aiInstance->getZone(zoneSrc);
231 if (!destZone)
233 nlwarning("moveToZone: getZone destination failed!");
234 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
235 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
237 if (!srcZone)
239 nlwarning("moveToZone: getZone source failed!");
240 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
241 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
243 if (!destZone || !srcZone)
244 return;
246 if (destZone==srcZone)
248 nlwarning("Trying to find a path between two same zones in %s, aborting moveToZone", entity->getActiveState()->getAliasFullName().c_str());
249 nlwarning(" - from %s", CStringMapper::unmap(zoneSrc).c_str());
250 nlwarning(" - to %s", CStringMapper::unmap(zoneDest).c_str());
251 return;
254 spawnGroup->movingProfile().setAIProfile(new CGrpProfileDynFollowPath(spawnGroup, srcZone, destZone, AITYPES::CPropertySet()));
257 //----------------------------------------------------------------------------
258 /** @page code
260 @subsection setActivity_s_
261 Changes the activity of the group. Valid activities are:
262 - "no_change"
263 - "escorted"
264 - "guard"
265 - "guard_escorted"
266 - "normal"
267 - "faction"
268 - "faction_no_assist"
269 - "bandit"
271 Arguments: s(Activity) ->
272 @param[in] Activity is an activity name the group will take
274 @code
275 ()setActivity("bandit"); // Gives a bandit activity to the group
276 @endcode
279 // Spawned CGroupNpc not in a family behaviour
280 void setActivity_s_(CStateInstance* entity, CScriptStack& stack)
282 string activity = stack.top();
283 stack.pop();
285 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
286 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
287 if (!aiInstance)
288 return;
290 if (!entity) { nlwarning("setActivity failed!"); return; }
292 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
293 if (!group)
294 { nlwarning("setActivity failed: no NPC group");
295 return;
297 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
298 if (!spawnGroup)
299 { nlwarning("setActivity failed: no spawned group");
300 return;
303 breakable
305 if (activity=="no_change")
306 break;
308 if (activity=="escorted")
310 spawnGroup->activityProfile().setAIProfile(new CGrpProfileEscorted(spawnGroup));
311 break;
313 if (activity=="guard")
315 spawnGroup->activityProfile().setAIProfile(new CGrpProfileGuard(spawnGroup));
316 break;
318 if (activity=="guard_escorted")
320 spawnGroup->activityProfile().setAIProfile(new CGrpProfileGuardEscorted(spawnGroup));
321 break;
324 if (activity=="normal")
326 spawnGroup->activityProfile().setAIProfile(new CGrpProfileNormal(spawnGroup));
327 break;
329 if (activity=="faction")
331 spawnGroup->activityProfile().setAIProfile(new CGrpProfileFaction(spawnGroup));
332 break;
334 if (activity=="faction_no_assist")
336 CGrpProfileFaction * grpPro = new CGrpProfileFaction(spawnGroup);
337 grpPro->noAssist();
338 spawnGroup->activityProfile().setAIProfile(grpPro);
339 break;
341 if (activity=="bandit")
343 spawnGroup->activityProfile().setAIProfile(new CGrpProfileBandit(spawnGroup));
344 break;
347 nlwarning("trying to set activity profile to an unknown profile name");
351 //----------------------------------------------------------------------------
352 /** @page code
354 @subsection waitInZone_s_
355 Makes the group wander in the specified zone. It's useful to prevent the group
356 from looping previous movement profile).
358 Arguments: s(Zone) ->
359 @param[in] Zone is a zone name
361 @code
362 ()waitInZone($zone); // Makes the group wander in $zone
363 @endcode
366 // Spawned CGroupNpc not in a family behaviour
367 void waitInZone_s_(CStateInstance* entity, CScriptStack& stack)
369 std::string zoneName = stack.top();
370 TStringId const zoneDest = CStringMapper::map(zoneName);
371 stack.pop();
373 if (!entity)
375 nlwarning("waitInZone failed!");
376 return;
379 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
380 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
381 if (!aiInstance)
383 // :TODO: Check is that warning was a flood
384 //nlwarning("waitInZone failed!");
385 return;
388 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
389 if (!group)
391 nlwarning("waitInZone failed: no NPC group");
392 return;
394 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
395 if (!spawnGroup)
397 nlwarning("waitInZone failed: no spawned group");
398 return;
401 CNpcZone const* const destZone = aiInstance->getZone(zoneDest);
403 if (!destZone)
405 nlwarning("waitInZone: getZone destination failed! (%s)", zoneName.c_str());
406 return;
408 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWander(spawnGroup, destZone));
411 //----------------------------------------------------------------------------
412 /** @page code
414 @subsection stopMoving__
415 Makes the group stop moving and stand at its current position.
417 Arguments: ->
419 @code
420 ()stopMoving(); // Makes the group stop moving
421 @endcode
424 // Spawned CGroupNpc not in a family behaviour
425 void stopMoving__(CStateInstance* entity, CScriptStack& stack)
427 if (!entity)
429 nlwarning("stopMoving failed!");
430 return;
433 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
434 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
435 if (!aiInstance)
436 return;
438 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
439 if (!group)
441 nlwarning("stopMoving failed: no NPC group");
442 return;
444 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
445 if (!spawnGroup)
447 nlwarning("stopMoving failed: no spawned group");
448 return;
451 spawnGroup->movingProfile().setAIProfile(new CGrpProfileIdle(spawnGroup));
454 //----------------------------------------------------------------------------
455 /** @page code
457 @subsection startWander_f_
458 Set activity to wander in current pos:
460 Arguments: f(Radius) ->
461 @param[in] Radius dispersion of wander activity
463 @code
464 ()startWander(100); // Gives a wander activity to the group with dispersion of 100
465 @endcode
468 // Spawned CGroupNpc not in a family behaviour
469 void startWander_f_(CStateInstance* entity, CScriptStack& stack)
471 uint32 dispersionRadius = (uint32)(float&)stack.top();
472 stack.pop();
474 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
475 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
476 if (!aiInstance)
477 return;
479 if (!entity) { nlwarning("setActivity failed!"); return; }
481 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
482 if (!group)
483 { nlwarning("startWander failed: no NPC group");
484 return;
486 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
487 if (!spawnGroup)
488 { nlwarning("startWander failed: no spawned group");
489 return;
492 CAIVector centerPos;
493 if (!spawnGroup->calcCenterPos(centerPos)) // true if there's some bots in the group.
494 { nlwarning("startWander failed: no center pos");
495 return;
498 NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> destZone = NLMISC::CSmartPtr<CNpcZonePlaceNoPrim>(new CNpcZonePlaceNoPrim());
499 destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(centerPos, 0, 0), (uint32)(dispersionRadius*1000.));
500 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone));
503 //----------------------------------------------------------------------------
504 /** @page code
506 @subsection startMoving_fff_
507 Set activity to wander in current pos:
509 Arguments: f(Radius) ->
510 @param[in] Radius dispersion of wander activity
512 @code
513 ()startMoving(100,-100,10); // Moves the group to 100,-100 with radius of 10
514 @endcode
517 // Spawned CGroupNpc not in a family behaviour
518 void startMoving_fff_(CStateInstance* entity, CScriptStack& stack)
520 uint32 dispersionRadius = (uint32)(float&)stack.top();
521 stack.pop();
522 float const y = (float&)stack.top();
523 stack.pop();
524 float const x = (float&)stack.top();
525 stack.pop();
527 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
528 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
529 if (!aiInstance)
530 return;
532 if (!entity) { nlwarning("setActivity failed!"); return; }
534 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
535 if (!group)
536 { nlwarning("setActivity failed: no NPC group");
537 return;
539 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
540 if (!spawnGroup)
541 { nlwarning("setActivity failed: no spawned group");
542 return;
545 NLMISC::CSmartPtr<CNpcZonePlaceNoPrim> destZone = NLMISC::CSmartPtr<CNpcZonePlaceNoPrim>(new CNpcZonePlaceNoPrim());
546 destZone->setPosAndRadius(AITYPES::vp_auto, CAIPos(CAIVector(x, y), 0, 0), (uint32)(dispersionRadius*1000.));
547 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWanderNoPrim(spawnGroup, destZone));
549 return;
552 //----------------------------------------------------------------------------
553 /** @page code
555 @subsection followPlayer_sf_
556 Set activity to follow the given player
558 Arguments: s(PlayerEid) f(Radius) ->
559 @param[in] PlayerEid id of player to follow
560 @param[in] Radius dispersion of wander activity
562 @code
563 ()followPlayer("(0x0002015bb4:01:88:88)",10);
564 @endcode
567 // Spawned CGroupNpc not in a family behaviour
568 void followPlayer_sf_(CStateInstance* entity, CScriptStack& stack)
570 uint32 dispersionRadius = (uint32)(float&)stack.top(); stack.pop();
571 NLMISC::CEntityId playerId = NLMISC::CEntityId((std::string)stack.top());
573 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
574 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
575 if (!aiInstance)
576 return;
578 if (!entity) { nlwarning("followPlayer failed!"); return; }
580 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
581 if (!group)
582 { nlwarning("followPlayer failed: no NPC group");
583 return;
585 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
586 if (!spawnGroup)
587 { nlwarning("followPlayer failed: no spawned group");
588 return;
591 if (playerId == CEntityId::Unknown)
593 nlwarning("followPlayer failed: unknown player");
594 DEBUG_STOP;
595 return;
598 spawnGroup->movingProfile().setAIProfile(new CGrpProfileFollowPlayer(spawnGroup, TheDataset.getDataSetRow(playerId), dispersionRadius));
600 return;
604 //----------------------------------------------------------------------------
605 /** @page code
607 @subsection wander__
608 Makes the group wander in the current npc state zone.
610 Arguments: ->
612 @code
613 ()wander(); // Makes the group wander
614 @endcode
617 // Spawned CGroupNpc not in a family behaviour
618 void wander__(CStateInstance* entity, CScriptStack& stack)
620 if (!entity)
622 nlwarning("wander failed!");
623 return;
626 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
627 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
628 if (!aiInstance)
629 return;
631 CGroupNpc* group = dynamic_cast<CGroupNpc*>(entity->getGroup());
632 if (!group)
634 nlwarning("wander failed: no NPC group");
635 return;
637 CSpawnGroupNpc* spawnGroup = group->getSpawnObj();
638 if (!spawnGroup)
640 nlwarning("wander failed: no spawned group");
641 return;
644 spawnGroup->movingProfile().setAIProfile(new CGrpProfileWander(spawnGroup));
647 //----------------------------------------------------------------------------
648 /** @page code
650 @subsection setAttackable_f_
651 Sets the group as being attackable (or not) by bots and players. 0 means not
652 attackable, 1 means attackable.
654 Arguments: f(Attackable) ->
655 @param[in] Attackable tells whether the group is attackable
657 @code
658 ()setAttackable(1); // Make the group attackable by players and bots
659 @endcode
662 // CGroupNpc
663 void setAttackable_f_(CStateInstance* entity, CScriptStack& stack)
665 bool const attackable = (float&)stack.top()!=0.0f;
666 stack.pop();
668 CGroup* group = entity->getGroup();
670 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
672 npcGroup->setPlayerAttackable(attackable);
673 npcGroup->setBotAttackable(attackable);
675 if (npcGroup->isSpawned())
676 npcGroup->getSpawnObj()->sendInfoToEGS();
679 //----------------------------------------------------------------------------
680 /** @page code
682 @subsection setPlayerAttackable_f_
683 Sets the group as being attackable (or not) by players. 0 means not
684 attackable, 1 means attackable.
686 Arguments: f(Attackable) ->
687 @param[in] Attackable tells whether the group is attackable
689 @code
690 ()setPlayerAttackable(1); // Make the group attackable by players
691 @endcode
694 // CGroupNpc
695 void setPlayerAttackable_f_(CStateInstance* entity, CScriptStack& stack)
697 bool attackable = (float&)stack.top()!=0.0f;
698 stack.pop();
700 CGroup* group = entity->getGroup();
701 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
703 npcGroup->setPlayerAttackable(attackable);
705 if (npcGroup->isSpawned())
706 npcGroup->getSpawnObj()->sendInfoToEGS();
709 //----------------------------------------------------------------------------
710 // setBotAttackable_f_
711 // Arguments: f(Attackable) ->
712 /** @page code
714 @subsection setBotAttackable_f_
715 Sets the group as being attackable (or not) by bots. 0 means not attackable, 1
716 means attackable.
718 Arguments: f(Attackable) ->
719 @param[in] Attackable tells whether the group is attackable
721 @code
722 ()setBotAttackable(0); // Make the group not attackable by bots
723 @endcode
726 // CGroupNpc
727 void setBotAttackable_f_(CStateInstance* entity, CScriptStack& stack)
729 bool attackable = (float&)stack.top()!=0.0f;
730 stack.pop();
732 CGroup* group = entity->getGroup();
733 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
735 npcGroup->setBotAttackable(attackable);
737 if (npcGroup->isSpawned())
738 npcGroup->getSpawnObj()->sendInfoToEGS();
741 //----------------------------------------------------------------------------
742 // setFactionAttackableAbove_sff_
743 // Arguments: s(Faction),f(Threshold),f(Attackable) ->
744 /** @page code
746 @subsection setFactionAttackableAbove_sff_
747 Sets the group as being attackable (or not) by players having their fame
748 matching the specified condition. If player fame for Faction is above
749 specified Threshold the bot will be attackable by that player or not depending
750 on Attackable parameter, 0 meaning not attackable, 1 meaning attackable.
752 Note: Bots must not be player attackable for the faction to be taken into
753 account.
755 Arguments: s(Faction),f(Threshold),f(Attackable) ->
756 @param[in] Faction tells the faction against which player fame will be tested
757 @param[in] Threshold is the test threshold above which player will be able to attack
758 @param[in] Attackable tells whether the group is attackable or not
760 @code
761 ()setFactionAttackableAbove("TribeNightTurners", 0, 1); // Make the group attackable by players with positive tribe_night_turners fame
762 @endcode
765 // CGroupNpc
766 void setFactionAttackableAbove_sff_(CStateInstance* entity, CScriptStack& stack)
768 bool attackable = (float&)stack.top()!=0.0f;
769 stack.pop();
770 sint32 threshold = (sint32)(float&)stack.top();
771 stack.pop();
772 std::string faction = (std::string&)stack.top();
773 stack.pop();
775 CGroup* group = entity->getGroup();
776 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
778 npcGroup->setFactionAttackableAbove(faction, threshold, attackable);
780 if (npcGroup->isSpawned())
781 npcGroup->getSpawnObj()->sendInfoToEGS();
784 //----------------------------------------------------------------------------
785 // setFactionAttackableBelow_sff_
786 // Arguments: s(Faction),f(Threshold),f(Attackable) ->
787 /** @page code
789 @subsection setFactionAttackableBelow_sff_
790 Sets the group as being attackable (or not) by players having their fame
791 matching the specified condition. If player fame for Faction is below
792 specified Threshold the bot will be attackable by that player or not depending
793 on Attackable parameter, 0 meaning not attackable, 1 meaning attackable.
795 Note: Bots must not be player attackable for the faction to be taken into
796 account.
798 Arguments: s(Faction),f(Threshold),f(Attackable) ->
799 @param[in] Faction tells the faction against which player fame will be tested
800 @param[in] Threshold is the test threshold below which player will be able to attack
801 @param[in] Attackable tells whether the group is attackable or not
803 @code
804 ()setFactionAttackableBelow("TribeMatisianBorderGuards", 0, 1); // Make the group attackable by players with negative tribe_matisian_border_guards fame
805 @endcode
808 // CGroupNpc
809 void setFactionAttackableBelow_sff_(CStateInstance* entity, CScriptStack& stack)
811 bool attackable = (float&)stack.top()!=0.0f;
812 stack.pop();
813 sint32 threshold = (sint32)(float&)stack.top();
814 stack.pop();
815 std::string faction = (std::string&)stack.top();
816 stack.pop();
818 CGroup* group = entity->getGroup();
819 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
821 npcGroup->setFactionAttackableBelow(faction, threshold, attackable);
823 if (npcGroup->isSpawned())
824 npcGroup->getSpawnObj()->sendInfoToEGS();
827 //----------------------------------------------------------------------------
828 /** @page code
830 @subsection addBotChat_s_
831 Add an entry in the botchat menu of every bot of the group. Specified text ID
832 can be created dynamically with setSimplePhrase (@ref setSimplePhrase_ss_).
834 Arguments: s(BotChat) ->
835 @param[in] BotChat is a text ID
837 @code
838 ()addBotChat("menu:QUESTION:REPONSE");
839 @endcode
842 // CGroupNpc
843 void addBotChat_s_(CStateInstance* entity, CScriptStack& stack)
845 string const botChat = (string)stack.top();
846 stack.pop();
848 CGroup* group = entity->getGroup();
849 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
851 FOREACH(botIt, CCont<CBot>, group->bots())
853 CBot* bot = *botIt;
854 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
855 if (botNpc)
857 if (!botNpc->getChat())
858 botNpc->newChat();
859 botNpc->getChat()->add(botNpc->getAIInstance(), botChat);
860 CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn();
861 if (spawnBotNpc)
863 spawnBotNpc->setCurrentChatProfile(botNpc->getChat());
867 if (npcGroup->isSpawned())
868 npcGroup->getSpawnObj()->sendInfoToEGS();
871 //----------------------------------------------------------------------------
872 /** @page code
874 @subsection clearBotChat__
875 Removes all entries from the botchat menu of every bot of the group.
877 Arguments: ->
879 @code
880 ()clearBotChat();
881 @endcode
884 // CGroupNpc
885 void clearBotChat__(CStateInstance* entity, CScriptStack& stack)
887 CGroup* group = entity->getGroup();
888 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
890 FOREACH(botIt, CCont<CBot>, group->bots())
892 CBot* bot = *botIt;
893 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
894 if (botNpc)
896 if (!botNpc->getChat())
897 botNpc->newChat();
898 botNpc->getChat()->clear();
899 CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn();
900 if (spawnBotNpc)
902 spawnBotNpc->setCurrentChatProfile(botNpc->getChat());
906 if (npcGroup->isSpawned())
907 npcGroup->getSpawnObj()->sendInfoToEGS();
910 //----------------------------------------------------------------------------
911 /** @page code
913 @subsection ignoreOffensiveActions_f_
914 Make the bots of the group ignore offensive actions issued on them.
916 Arguments: f(IgnoreOffensiveActions) ->
917 @param[in] IgnoreOffensiveActions is tells whethere to ignore offensive actions (1) or not (0)
919 @code
920 ()ignoreOffensiveActions(1);
921 @endcode
924 // CGroup
925 void ignoreOffensiveActions_f_(CStateInstance* entity, CScriptStack& stack)
927 bool const ignoreOffensiveActions = ((float)stack.top())!=0.f;
928 stack.pop();
930 CGroup* group = entity->getGroup();
931 FOREACH(botIt, CCont<CBot>, group->bots())
933 CBot* bot = *botIt;
934 if (bot)
935 bot->setIgnoreOffensiveActions(ignoreOffensiveActions);
939 //----------------------------------------------------------------------------
940 /** @page code
942 @subsection setDespawnTime_f_
943 Sets the time before current group being despawned.
945 Arguments: f(DespawnTime) ->
946 @param[in] DespawnTime is the despawn time in ticks (-1 will set "pseudo-infinite")
948 @code
949 ()setDespawnTime(80);
950 ()setDespawnTime(-1);
951 @endcode
954 // CGroupNpc
955 void setDespawnTime_f_(CStateInstance* entity, CScriptStack& stack)
957 float time = stack.top();
958 stack.pop();
960 CGroup* group = entity->getGroup();
961 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
963 uint32 despawntime = (uint32)(sint32)time; // -1 => ~0
964 // TODO:kxu: fix CAITimer!
965 if (despawntime > 0x7fffffff)
966 despawntime = 0x7fffffff; // AI timers do not treat properly delta times greater than the max signed int
967 npcGroup->despawnTime() = despawntime;
971 /** @page code
973 @subsection despawnBotByAlias_s_
974 Despawn a specific Bot by its alias
976 Arguments: f(alias), ->
977 @param[in] alias is the alias of the bot
980 @code
981 ()despawnBotByAlias('(A:1000:10560)');
982 @endcode
985 // CGroup
986 void despawnBotByAlias_s_(CStateInstance* entity, CScriptStack& stack)
988 uint32 alias = LigoConfig.aliasFromString((string)stack.top()); stack.pop();
989 CGroup* group = entity->getGroup();
990 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
992 FOREACH(botIt, CCont<CBot>, group->bots())
995 CBot* bot = *botIt;
997 if (bot->getAlias() != alias) { continue; }
998 if (!bot->isSpawned()) return;
999 if (bot->getRyzomType() == RYZOMID::npc)
1001 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
1002 CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn();
1003 spawnBotNpc->spawnGrp().addBotToDespawnAndRespawnTime(&spawnBotNpc->getPersistent(), 1, spawnBotNpc->spawnGrp().getPersistent().respawnTime());
1009 //----------------------------------------------------------------------------
1010 /** @page code
1012 @subsection setRespawnTime_f_
1013 Sets the time in game cycles before current group being respawned.
1015 Arguments: f(RespawnTime) ->
1016 @param[in] RespawnTime is the respawn time in ticks (-1 will set "pseudo-infinite")
1018 @code
1019 ()setRespawnTime(80);
1020 ()setRespawnTime(-1);
1021 @endcode
1024 // CGroupNpc
1025 void setRespawnTime_f_(CStateInstance* entity, CScriptStack& stack)
1027 float const time = stack.top();
1028 stack.pop();
1030 CGroup* group = entity->getGroup();
1031 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
1033 uint32 respawntime = (uint32)(sint32)time; // -1 => ~0
1034 // TODO:kxu: fix CAITimer!
1035 if (respawntime > 0x7fffffff)
1036 respawntime = 0x7fffffff; // AI timers do not treat properly delta times greater than the max signed int
1037 npcGroup->respawnTime() = respawntime;
1040 //----------------------------------------------------------------------------
1041 /** @page code
1043 @subsection addHpUpTrigger_ff_
1044 Registers a trigger on HP increases. Whenever the HP level of a bot upcross
1045 the threshold it triggers the specified user event. Several triggers can be
1046 registered on the same group, even with the same threshold and event.
1048 Arguments: f(threshold),f(user_event_n) ->
1049 @param[in] threshold is a HP threshold
1050 @param[in] user_event_n is the user event to trigger
1052 @code
1053 ()addHpUpTrigger(0.5, 4);
1054 @endcode
1057 // CGroupNpc
1058 void addHpUpTrigger_ff_(CStateInstance* entity, CScriptStack& stack)
1060 int eventId = (int)(float)stack.top();
1061 stack.pop();
1062 float threshold = (float)stack.top();
1063 stack.pop();
1064 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1065 if (!grpNpc)
1067 nlwarning("Trying to add a hp up trigger (%f) listener (user event %d) in a group which is not an NPC group.", threshold, eventId);
1068 return;
1070 grpNpc->addHpUpTrigger(threshold, eventId);
1073 //----------------------------------------------------------------------------
1074 /** @page code
1076 @subsection delHpUpTrigger_ff_
1077 Unregisters a trigger on HP increases. The same values used when registering
1078 the trigger must be passed. If several triggers were defined with the same
1079 parameters only one is removed.
1081 Arguments: f(threshold),f(user_event_n) ->
1082 @param[in] threshold is a HP threshold
1083 @param[in] user_event_n is the user event to trigger
1085 @code
1086 ()delHpUpTrigger(0.5, 4);
1087 @endcode
1090 // CGroupNpc
1091 void delHpUpTrigger_ff_(CStateInstance* entity, CScriptStack& stack)
1093 int eventId = (int)(float)stack.top();
1094 stack.pop();
1095 float threshold = (float)stack.top();
1096 stack.pop();
1097 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1098 if (!grpNpc)
1100 nlwarning("Trying to delete a hp up trigger (%f) listener (user event %d) in a group which is not an NPC group.", threshold, eventId);
1101 return;
1103 grpNpc->delHpUpTrigger(threshold, eventId);
1106 //----------------------------------------------------------------------------
1107 /** @page code
1109 @subsection addHpDownTrigger_ff_
1110 Registers a trigger on HP decreases. Whenever the HP level of a bot downcross
1111 the threshold it triggers the specified user event. Several triggers can be
1112 registered on the same group, even with the same threshold and event.
1114 Arguments: f(threshold),f(user_event_n) ->
1115 @param[in] threshold is a HP threshold
1116 @param[in] user_event_n is the user event to trigger
1118 @code
1119 ()addHpDownTrigger(0.5, 5);
1120 @endcode
1123 // CGroupNpc
1124 void addHpDownTrigger_ff_(CStateInstance* entity, CScriptStack& stack)
1126 int eventId = (int)(float)stack.top();
1127 stack.pop();
1128 float threshold = (float)stack.top();
1129 stack.pop();
1130 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1131 if (!grpNpc)
1133 nlwarning("Trying to add a hp down trigger (%f) listener (user event %d) in a group which is not an NPC group.", threshold, eventId);
1134 return;
1136 grpNpc->addHpDownTrigger(threshold, eventId);
1139 //----------------------------------------------------------------------------
1140 /** @page code
1142 @subsection delHpDownTrigger_ff_
1143 Unregisters a trigger on HP decreases. The same values used when registering
1144 the trigger must be passed. If several triggers were defined with the same
1145 parameters only one is removed.
1147 Arguments: f(threshold),f(user_event_n) ->
1148 @param[in] threshold is a HP threshold
1149 @param[in] user_event_n is the user event to trigger
1151 @code
1152 ()delHpDownTrigger(0.5, 5);
1153 @endcode
1156 // CGroupNpc
1157 void delHpDownTrigger_ff_(CStateInstance* entity, CScriptStack& stack)
1159 int eventId = (int)(float)stack.top();
1160 stack.pop();
1161 float threshold = (float)stack.top();
1162 stack.pop();
1163 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1164 if (!grpNpc)
1166 nlwarning("Trying to delete a hp down trigger (%f) listener (user event %d) in a group which is not an NPC group.", threshold, eventId);
1167 return;
1169 grpNpc->delHpDownTrigger(threshold, eventId);
1172 //----------------------------------------------------------------------------
1173 /** @page code
1175 @subsection addHpUpTrigger_fs_
1176 @sa @ref addHpUpTrigger_ff_
1178 These triggers call a script function instead of trigger a user event.
1180 Arguments: f(threshold),s(callback) ->
1181 @param[in] threshold is a HP threshold
1182 @param[in] callback is the script callback to trigger
1184 @code
1185 ()addHpUpTrigger(0.5, "onHPIncrease");
1186 @endcode
1189 // CGroupNpc
1190 void addHpUpTrigger_fs_(CStateInstance* entity, CScriptStack& stack)
1192 std::string cbFunc = (std::string)stack.top();
1193 stack.pop();
1194 float threshold = (float)stack.top();
1195 stack.pop();
1196 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1197 if (!grpNpc)
1199 nlwarning("Trying to add a hp up trigger (%f) listener (%s) in a group which is not an NPC group.", threshold, cbFunc.c_str());
1200 return;
1202 grpNpc->addHpUpTrigger(threshold, cbFunc);
1205 //----------------------------------------------------------------------------
1206 /** @page code
1208 @subsection delHpUpTrigger_fs_
1209 @sa @ref delHpUpTrigger_ff_
1211 This function is used to remove script function triggers.
1213 Arguments: f(threshold),s(callback) ->
1214 @param[in] threshold is a HP threshold
1215 @param[in] callback is the script callback to trigger
1217 @code
1218 ()delHpUpTrigger(0.5, "onHPIncrease");
1219 @endcode
1222 // CGroupNpc
1223 void delHpUpTrigger_fs_(CStateInstance* entity, CScriptStack& stack)
1225 std::string cbFunc = (std::string)stack.top();
1226 stack.pop();
1227 float threshold = (float)stack.top();
1228 stack.pop();
1229 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1230 if (!grpNpc)
1232 nlwarning("Trying to delete a hp up trigger (%f) listener (%s) in a group which is not an NPC group.", threshold, cbFunc.c_str());
1233 return;
1235 grpNpc->delHpUpTrigger(threshold, cbFunc);
1238 //----------------------------------------------------------------------------
1239 /** @page code
1241 @subsection addHpDownTrigger_fs_
1242 @sa @ref addHpDownTrigger_ff_
1244 These triggers call a script function instead of trigger a user event.
1246 Arguments: f(threshold),s(callback) ->
1247 @param[in] threshold is a HP threshold
1248 @param[in] callback is the script callback to trigger
1250 @code
1251 ()addHpDownTrigger(0.5, "onHPDecrease");
1252 @endcode
1255 // CGroupNpc
1256 void addHpDownTrigger_fs_(CStateInstance* entity, CScriptStack& stack)
1258 std::string cbFunc = (std::string)stack.top();
1259 stack.pop();
1260 float threshold = (float)stack.top();
1261 stack.pop();
1262 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1263 if (!grpNpc)
1265 nlwarning("Trying to add a hp down trigger (%f) listener (%s) in a group which is not an NPC group.", threshold, cbFunc.c_str());
1266 return;
1268 grpNpc->addHpDownTrigger(threshold, cbFunc);
1271 //----------------------------------------------------------------------------
1272 /** @page code
1274 @subsection delHpDownTrigger_fs_
1275 @sa @ref delHpDownTrigger_ff_
1277 This function is used to remove script function triggers.
1279 Arguments: f(threshold),s(callback) ->
1280 @param[in] threshold is a HP threshold
1281 @param[in] callback is the script callback to trigger
1283 @code
1284 ()delHpDownTrigger(0.5, "onHPDecrease");
1285 @endcode
1288 // CGroupNpc
1289 void delHpDownTrigger_fs_(CStateInstance* entity, CScriptStack& stack)
1291 std::string cbFunc = (std::string)stack.top();
1292 stack.pop();
1293 float threshold = (float)stack.top();
1294 stack.pop();
1295 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1296 if (!grpNpc)
1298 nlwarning("Trying to delete a hp down trigger (%f) listener (%s) in a group which is not an NPC group.", threshold, cbFunc.c_str());
1299 return;
1301 grpNpc->delHpDownTrigger(threshold, cbFunc);
1304 //----------------------------------------------------------------------------
1305 /** @page code
1307 @subsection addNamedEntityListener_ssf_
1308 Associates a listeners with a named entity property. Whenever that field
1309 changes the specified user event is triggered. Valid property names are:
1310 - state
1311 - param1
1312 - param2
1313 Name of the entity cannot be listened, because it cannot change. Several
1314 listeners (even with the same parameters) can be associated to each property.
1316 Arguments: s(name),s(prop),f(event) ->
1317 @param[in] name is a the name of the named entity to listen
1318 @param[in] prop is a the property of the named entity to listen
1319 @param[in] event is a the user event to trigger
1321 @code
1322 ()addNamedEntityListener("Invasion", "state", 6);
1323 @endcode
1326 // CGroupNpc
1327 void addNamedEntityListener_ssf_(CStateInstance* entity, CScriptStack& stack)
1329 int event = (int)(float)stack.top();
1330 stack.pop();
1331 std::string prop = (std::string)stack.top();
1332 stack.pop();
1333 std::string name = (std::string)stack.top();
1334 stack.pop();
1336 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1337 if (!grpNpc)
1339 nlwarning("Trying to create a named entity (%s:%s) listener (user event %d) in a group which is not an NPC group.", name.c_str(), prop.c_str(), event);
1340 return;
1342 grpNpc->addNamedEntityListener(name, prop, event);
1345 //----------------------------------------------------------------------------
1346 /** @page code
1348 @subsection delNamedEntityListener_ssf_
1349 Removes a listener from a named entity property. If several listeners with the
1350 same parameters are registered, only one is removed.
1352 Arguments: s(name),s(prop),f(event) ->
1353 @param[in] name is a the name of the named entity to listen
1354 @param[in] prop is a the property of the named entity to listen
1355 @param[in] event is a the user event to trigger
1357 @code
1358 ()delNamedEntityListener("Invasion", "state", 6);
1359 @endcode
1362 // CGroupNpc
1363 void delNamedEntityListener_ssf_(CStateInstance* entity, CScriptStack& stack)
1365 int event = (int)(float)stack.top();
1366 stack.pop();
1367 std::string prop = (std::string)stack.top();
1368 stack.pop();
1369 std::string name = (std::string)stack.top();
1370 stack.pop();
1372 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1373 if (!grpNpc)
1375 nlwarning("Trying to delete a named entity (%s:%s) listener (user event %d) in a group which is not an NPC group.", name.c_str(), prop.c_str(), event);
1376 return;
1378 grpNpc->delNamedEntityListener(name, prop, event);
1381 //----------------------------------------------------------------------------
1382 /** @page code
1384 @subsection addNamedEntityListener_sss_
1385 @sa @ref addNamedEntityListener_ssf_
1387 These listeners call a script function instead of triggering a user event.
1389 Arguments: s(name),s(prop),s(cbFunc) ->
1390 @param[in] name is a the name of the named entity to listen
1391 @param[in] prop is a the property of the named entity to listen
1392 @param[in] cbFunc is a the callback to trigger
1394 @code
1395 ()addNamedEntityListener("Invasion", "state", "onStateChange");
1396 @endcode
1399 // CGroupNpc
1400 void addNamedEntityListener_sss_(CStateInstance* entity, CScriptStack& stack)
1402 std::string cbFunc = (std::string)stack.top();
1403 stack.pop();
1404 std::string prop = (std::string)stack.top();
1405 stack.pop();
1406 std::string name = (std::string)stack.top();
1407 stack.pop();
1409 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1410 if (!grpNpc)
1412 nlwarning("Trying to create a named entity (%s:%s) listener (%s) in a group which is not an NPC group.", name.c_str(), prop.c_str(), cbFunc.c_str());
1413 return;
1415 grpNpc->addNamedEntityListener(name, prop, cbFunc);
1418 //----------------------------------------------------------------------------
1419 /** @page code
1421 @subsection delNamedEntityListener_sss_
1422 @sa @ref delNamedEntityListener_ssf_
1424 This function removes script function listeners.
1426 Arguments: s(name),s(prop),s(cbFunc) ->
1427 @param[in] name is a the name of the named entity to listen
1428 @param[in] prop is a the property of the named entity to listen
1429 @param[in] cbFunc is a the callback to trigger
1431 @code
1432 ()delNamedEntityListener("Invasion", "state", "onStateChange");
1433 @endcode
1436 // CGroupNpc
1437 void delNamedEntityListener_sss_(CStateInstance* entity, CScriptStack& stack)
1439 std::string cbFunc = (std::string)stack.top();
1440 stack.pop();
1441 std::string prop = (std::string)stack.top();
1442 stack.pop();
1443 std::string name = (std::string)stack.top();
1444 stack.pop();
1446 CGroupNpc* const grpNpc = dynamic_cast<CGroupNpc*>(entity->getGroup());
1447 if (!grpNpc)
1449 nlwarning("Trying to delete a named entity (%s:%s) listener (%s) in a group which is not an NPC group.", name.c_str(), prop.c_str(), cbFunc.c_str());
1450 return;
1452 grpNpc->delNamedEntityListener(name, prop, cbFunc);
1455 //----------------------------------------------------------------------------
1456 /** @page code
1458 @subsection setPlayerController_ss_
1460 Make a player control a npc.
1462 Arguments: s(botId),s(playerId) ->
1463 @param[in] botId is the entity id of the bot the player will control
1464 @param[in] playerId is the entity id of the player that will control the bot
1466 @code
1467 ()setPlayerController("(0x0002015bb4:01:88:88)", "(0x0000004880:00:00:00)");
1468 @endcode
1471 // CSpawnBotNpc
1472 void setPlayerController_ss_(CStateInstance* entity, CScriptStack& stack)
1474 NLMISC::CEntityId playerId = NLMISC::CEntityId((std::string)stack.top());
1475 stack.pop();
1476 NLMISC::CEntityId botId = NLMISC::CEntityId((std::string)stack.top());
1477 stack.pop();
1478 if (botId != NLMISC::CEntityId::Unknown)
1480 CGroup* grp = NULL;
1481 CSpawnBotNpc* bot = NULL;
1482 CAIEntityPhysicalLocator *inst = CAIEntityPhysicalLocator::getInstance();
1483 if (inst)
1485 CAIEntityPhysical *botEntity = inst->getEntity(botId);
1486 if (botEntity)
1488 if ((bot = dynamic_cast<CSpawnBotNpc*>(botEntity)) && (&bot->getPersistent().getGroup())==entity->getGroup())
1490 if (playerId != NLMISC::CEntityId::Unknown)
1492 CAIEntityPhysical *playerEntity = inst->getEntity(playerId);
1493 CBotPlayer* player = NULL;
1494 if (playerEntity && (player = dynamic_cast<CBotPlayer*>(playerEntity)))
1495 bot->setPlayerController(player);
1497 else
1498 bot->setPlayerController(NULL);
1501 else
1502 nlwarning("Bot entity not found");
1504 else
1505 nlwarning("Instance not found");
1509 //----------------------------------------------------------------------------
1510 /** @page code
1512 @subsection clearPlayerController_s_
1514 Stop the control of a npc by a player.
1516 Arguments: s(botId) ->
1517 @param[in] botId is the entity id of the bot
1519 @code
1520 ()clearPlayerController("(0x0002015bb4:01:88:88)");
1521 @endcode
1524 // CSpawnBotNpc
1525 void clearPlayerController_s_(CStateInstance* entity, CScriptStack& stack)
1527 NLMISC::CEntityId botId = NLMISC::CEntityId((std::string)stack.top());
1528 stack.pop();
1529 if (botId!=NLMISC::CEntityId::Unknown)
1531 CGroup* grp = NULL;
1532 CSpawnBotNpc* bot = NULL;
1533 if ((bot = dynamic_cast<CSpawnBotNpc*>(CAIEntityPhysicalLocator::getInstance()->getEntity(botId)))
1534 && (&bot->getPersistent().getGroup())==entity->getGroup())
1536 bot->setPlayerController(NULL);
1541 //----------------------------------------------------------------------------
1542 /** @page code
1544 @subsection activateEasterEgg_fffsffffsss_
1546 Call the EGS function CCharacterControl::activateEasterEgg
1548 Arguments: f(easterEggId), f(scenarioId), f(actId), s(items), f(x), f(y), f(z),f(heading), f(groupname), f(name), f(clientSheet)->
1549 @param[in] items is the sheet/quantity vector (a string of format "item1:qty1;item2:qty2;...")
1551 @code
1552 ()activateEasterEgg(2, 1601, 4, "toto.sitem:2;tata.sitem:1;titi.sitem:3", 1247, 4627, 0, 0);
1553 @endcode
1556 void activateEasterEgg_fffsffffsss_(CStateInstance* si, CScriptStack& stack)
1558 std::string clientSheet = (std::string)stack.top(); stack.pop();
1559 std::string name = (std::string)stack.top(); stack.pop();
1560 std::string grpCtrl = (std::string)stack.top(); stack.pop();
1561 float heading = (float)stack.top(); stack.pop();
1562 float z = (float)stack.top(); stack.pop();
1563 float y = (float)stack.top(); stack.pop();
1564 float x = (float)stack.top(); stack.pop();
1566 string items = (string)stack.top(); stack.pop();
1567 uint32 actId = static_cast<uint32>( (float)stack.top()); stack.pop();
1568 TSessionId scenarioId( static_cast<uint32>( (float)stack.top())); stack.pop();
1569 uint32 easterEgg = static_cast<uint32>( (float)stack.top()); stack.pop();
1571 IAisControl::getInstance()->activateEasterEgg(easterEgg, scenarioId, actId, items, x, y, z, heading, grpCtrl, name, clientSheet);
1574 //----------------------------------------------------------------------------
1575 /** @page code
1577 @subsection deactivateEasterEgg_fff_
1579 Call the EGS function CCharacterControl::deactivateEasterEgg
1581 Arguments: f(easterEggId), f(scenarioId), f(actId) ->
1583 @code
1584 ()deactivateEasterEgg(0, 4);
1585 @endcode
1588 void deactivateEasterEgg_fff_(CStateInstance* si, CScriptStack& stack)
1590 uint32 actId = static_cast<uint32>( (float)stack.top()); stack.pop();
1591 TSessionId scenarioId( static_cast<uint32>( (float)stack.top())); stack.pop();
1592 uint32 easterEgg = static_cast<uint32>( (float)stack.top()); stack.pop();
1594 IAisControl::getInstance()->deactivateEasterEgg(easterEgg, scenarioId, actId);
1597 //----------------------------------------------------------------------------
1598 /** @page code
1600 @subsection receiveMissionItems_ssc_
1602 The npc will ask mission items to the targeter player.
1604 A new entry of the npc contextual menu will propose to the targeter player
1605 to give mission items to the npc if he has the requested items.
1607 Then user events are triggered on the group to inform it about what happens:
1608 - user_event_1: triggered if the player has the requested mission items.
1609 - user_event_2: triggered if the player hasn't the requested mission items.
1610 - user_event_3: triggered after the player has given the mission items to the npc.
1612 Warning: this function can only be called after the event "player_target_npc".
1614 Arguments: s(missionItems), s(missionText), c(groupToNotify) ->
1615 @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...".
1616 @param[in] missionText is the text which will appear in the npc contextual menu.
1617 @param[in] groupToNotify is the npc group which will receive the user events.
1619 @code
1620 (@groupToNotify)group_name.context();
1621 ()receiveMissionItems("toto.sitem:2;tata.sitem:1;titi.sitem:3", "Mission text", @groupToNotify);
1622 @endcode
1625 // CSpawnBotNpc
1626 void receiveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack)
1628 CGroupNpc* const groupToNotify = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() );
1629 stack.pop();
1630 string missionText = (string)stack.top();
1631 stack.pop();
1632 string missionItems = (string)stack.top();
1633 stack.pop();
1635 if (groupToNotify == NULL)
1637 nlwarning("receiveMissionItems failed: groupToNotify is NULL");
1638 DEBUG_STOP;
1639 return;
1642 if (missionItems.empty())
1644 nlwarning("receiveMissionItems failed: missionItems is empty");
1645 DEBUG_STOP;
1646 return;
1649 nlassert(entity != NULL);
1650 CGroup* const group = entity->getGroup();
1651 IManagerParent* const managerParent = group->getOwner()->getOwner();
1652 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
1653 if (aiInstance == NULL)
1655 nlwarning("receiveMissionItems failed: the AI instance of the entity is NULL");
1656 DEBUG_STOP;
1657 return;
1660 CBotPlayer* const talkingPlayer = TempPlayer;
1661 CSpawnBotNpc* const talkingNpc = dynamic_cast<CSpawnBotNpc*>(TempSpeaker);
1662 if ( talkingPlayer == NULL
1663 || talkingNpc == NULL
1664 || talkingNpc->getPersistent().getOwner() != group) // check if the talking npc is in this group
1666 nlwarning("receiveMissionItems failed: invalid interlocutors");
1667 DEBUG_STOP;
1668 return;
1671 // do nothing if one of them is dead
1672 if (!talkingPlayer->isAlive() || !talkingNpc->isAlive())
1674 return;
1677 // turn the npc to face the player
1678 talkingNpc->setTheta(talkingNpc->pos().angleTo(talkingPlayer->pos()));
1680 // send the request to the EGS
1681 CGiveItemRequestMsg msg;
1682 msg.InstanceId = aiInstance->getInstanceNumber();
1683 msg.GroupAlias = groupToNotify->getAlias();
1684 msg.CharacterRowId = talkingPlayer->dataSetRow();
1685 msg.CreatureRowId = talkingNpc->dataSetRow();
1686 msg.MissionText = missionText;
1688 // extract items and quantities from the missionItems string
1689 std::vector<std::string> itemAndQtyList;
1690 NLMISC::splitString(missionItems, ";", itemAndQtyList);
1691 if (itemAndQtyList.empty())
1693 nlwarning("receiveMissionItems failed: the provided mission items string is invalid");
1694 DEBUG_STOP;
1695 return;
1697 FOREACHC(it, std::vector<std::string>, itemAndQtyList)
1699 std::vector<std::string> itemAndQty;
1700 NLMISC::splitString(*it, ":", itemAndQty);
1701 if (itemAndQty.size() != 2)
1703 nlwarning("receiveMissionItems failed: the provided mission items string is invalid");
1704 DEBUG_STOP;
1705 return;
1708 const CSheetId sheetId(itemAndQty[0]);
1709 if (sheetId == CSheetId::Unknown)
1711 nlwarning("receiveMissionItems failed: invalid mission item sheet '%s'", itemAndQty[0].c_str());
1712 DEBUG_STOP;
1713 return;
1715 // if LD use this the function outside a ring shard
1716 if (IsRingShard)
1718 // Here we destroy the item: so we do not want that a user create a scenario where we destroy
1719 // other players precious items
1721 static std::set<CSheetId> r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess
1722 // lazy intialisation
1723 if (r2PlotItemSheetId.empty())
1725 for (uint32 i = 0 ; i <= 184 ; ++i)
1727 r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i)));
1731 // A npc give a mission to take an item given by another npc
1732 // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor
1733 if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end())
1735 nlwarning("!!!!!!!!!!!!");
1736 nlwarning("!!!!!!!!!!!! Someone is trying to hack us");
1737 nlwarning("!!!!!!!!!!!!");
1738 nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt());
1739 nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId );
1740 nlwarning("!!!!!!!!!!!!");
1741 nlwarning("!!!!!!!!!!!!");
1742 return ;
1746 uint32 quantity;
1747 NLMISC::fromString(itemAndQty[1], quantity);
1748 if (quantity == 0)
1750 nlwarning("receiveMissionItems failed: invalid quantity '%s'", itemAndQty[1].c_str());
1751 DEBUG_STOP;
1752 return;
1755 msg.Items.push_back(sheetId);
1756 msg.Quantities.push_back(quantity);
1758 nlassert(!msg.Items.empty());
1759 nlassert(msg.Items.size() == msg.Quantities.size());
1761 msg.send("EGS");
1764 //----------------------------------------------------------------------------
1765 /** @page code
1767 @subsection giveMissionItems_ssc_
1769 The npc will give mission items to the targeter player.
1771 A new entry of the npc contextual menu will propose to the targeter player
1772 to receive mission items from the npc.
1774 Then user events are triggered on the group to inform it about what happens:
1775 - user_event_1: triggered after the player has received the mission items from the npc.
1777 Warning: this function can only be called after the event "player_target_npc".
1779 Arguments: s(missionItems), s(missionText), c(groupToNotify) ->
1780 @param[in] missionItems is the list of mission items, the string format is "item1:qty1;item2:qty2;...".
1781 @param[in] missionText is the text which will appear in the npc contextual menu.
1782 @param[in] groupToNotify is the npc group which will receive the user events.
1784 @code
1785 (@groupToNotify)group_name.context();
1786 ()giveMissionItems("toto.sitem:2;tata.sitem:1;titi.sitem:3", "Mission text", @groupToNotify);
1787 @endcode
1790 // CSpawnBotNpc
1791 void giveMissionItems_ssc_(CStateInstance* entity, CScriptStack& stack)
1793 CGroupNpc* const groupToNotify = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() );
1794 stack.pop();
1795 string missionText = (string)stack.top();
1796 stack.pop();
1797 string missionItems = (string)stack.top();
1798 stack.pop();
1800 if (groupToNotify == NULL)
1802 nlwarning("giveMissionItems failed: groupToNotify is NULL");
1803 DEBUG_STOP;
1804 return;
1807 if (missionItems.empty())
1809 nlwarning("giveMissionItems failed: missionItems is empty");
1810 DEBUG_STOP;
1811 return;
1814 nlassert(entity != NULL);
1815 CGroup* const group = entity->getGroup();
1816 IManagerParent* const managerParent = group->getOwner()->getOwner();
1817 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
1818 if (aiInstance == NULL)
1820 nlwarning("giveMissionItems failed: the AI instance of the entity is NULL");
1821 DEBUG_STOP;
1822 return;
1825 CBotPlayer* const talkingPlayer = TempPlayer;
1826 CSpawnBotNpc* const talkingNpc = dynamic_cast<CSpawnBotNpc*>(TempSpeaker);
1827 if ( talkingPlayer == NULL
1828 || talkingNpc == NULL
1829 || talkingNpc->getPersistent().getOwner() != group) // check if the talking npc is in this group
1831 nlwarning("giveMissionItems failed: invalid interlocutors");
1832 DEBUG_STOP;
1833 return;
1836 // do nothing if one of them is dead
1837 if (!talkingPlayer->isAlive() || !talkingNpc->isAlive())
1839 return;
1842 // turn the npc to face the player
1843 talkingNpc->setTheta(talkingNpc->pos().angleTo(talkingPlayer->pos()));
1845 // send the request to the EGS
1846 CReceiveItemRequestMsg msg;
1847 msg.InstanceId = aiInstance->getInstanceNumber();
1848 msg.GroupAlias = groupToNotify->getAlias();
1849 msg.CharacterRowId = talkingPlayer->dataSetRow();
1850 msg.CreatureRowId = talkingNpc->dataSetRow();
1851 msg.MissionText = missionText;
1853 // extract items and quantities from the missionItems string
1854 std::vector<std::string> itemAndQtyList;
1855 NLMISC::splitString(missionItems, ";", itemAndQtyList);
1856 if (itemAndQtyList.empty())
1858 nlwarning("giveMissionItems failed: the provided mission items string is invalid");
1859 DEBUG_STOP;
1860 return;
1862 FOREACHC(it, std::vector<std::string>, itemAndQtyList)
1864 std::vector<std::string> itemAndQty;
1865 NLMISC::splitString(*it, ":", itemAndQty);
1866 if (itemAndQty.size() != 2)
1868 nlwarning("giveMissionItems failed: the provided mission items string is invalid");
1869 DEBUG_STOP;
1870 return;
1873 const CSheetId sheetId(itemAndQty[0]);
1874 if (sheetId == CSheetId::Unknown)
1876 nlwarning("giveMissionItems failed: invalid mission item sheet '%s'", itemAndQty[0].c_str());
1877 DEBUG_STOP;
1878 return;
1882 // if LD use this the function outside a ring shard
1883 if (IsRingShard)
1885 static std::set<CSheetId> r2PlotItemSheetId; // :TODO: use R2Share::CRingAccess
1886 // lazy intialisation
1887 if (r2PlotItemSheetId.empty())
1889 for (uint32 i = 0 ; i <= 184 ; ++i)
1891 r2PlotItemSheetId.insert( CSheetId( NLMISC::toString("r2_plot_item_%d.sitem", i)));
1895 // A npc give a mission to give a item to another npc
1896 // but the item instead of being a r2_plot_item is a normal item like system_mp or big armor
1897 if ( r2PlotItemSheetId.find(sheetId) == r2PlotItemSheetId.end())
1899 nlwarning("!!!!!!!!!!!!");
1900 nlwarning("!!!!!!!!!!!! Someone is trying to hack us");
1901 nlwarning("!!!!!!!!!!!!");
1902 nlwarning("ERROR/HACK : an npc is trying to give to a player a item that is not a plot item SheetId='%s' sheetIdAsInt=%u",sheetId.toString().c_str(), sheetId.asInt());
1903 nlwarning("His ai instanceId is %u, use log to know the sessionId and the user ", msg.InstanceId );
1904 nlwarning("!!!!!!!!!!!!");
1905 nlwarning("!!!!!!!!!!!!");
1906 return ;
1911 uint32 quantity;
1912 NLMISC::fromString(itemAndQty[1], quantity);
1913 if (quantity == 0)
1915 nlwarning("giveMissionItems failed: invalid quantity '%s'", itemAndQty[1].c_str());
1916 DEBUG_STOP;
1917 return;
1920 msg.Items.push_back(sheetId);
1921 msg.Quantities.push_back(quantity);
1923 nlassert(!msg.Items.empty());
1924 nlassert(msg.Items.size() == msg.Quantities.size());
1926 msg.send("EGS");
1929 //----------------------------------------------------------------------------
1930 /** @page code
1932 @subsection talkTo_sc_
1934 A new entry of the npc contextual menu will propose to the targeter player to talk to the npc.
1936 Then user events are triggered on the group to inform it about what happens:
1937 - user_event_1: triggered each time (because of the TRICK).
1938 - user_event_3: triggered after the player has talked to the npc.
1940 Warning: this function can only be called after the event "player_target_npc".
1942 Arguments: s(missionText), c(groupToNotify) ->
1943 @param[in] missionText is the text which will appear in the npc contextual menu.
1944 @param[in] groupToNotify is the npc group which will receive the user events.
1946 @code
1947 (@groupToNotify)group_name.context();
1948 ()talkTo("Mission text", @groupToNotify);
1949 @endcode
1952 // CSpawnBotNpc
1953 void talkTo_sc_(CStateInstance* entity, CScriptStack& stack)
1955 CGroupNpc* const groupToNotify = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() );
1956 stack.pop();
1957 string missionText = (string)stack.top();
1958 stack.pop();
1960 if (groupToNotify == NULL)
1962 nlwarning("talkTo failed: groupToNotify is NULL");
1963 DEBUG_STOP;
1964 return;
1967 nlassert(entity != NULL);
1968 CGroup* const group = entity->getGroup();
1969 IManagerParent* const managerParent = group->getOwner()->getOwner();
1970 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
1971 if (aiInstance == NULL)
1973 nlwarning("talkTo failed: the AI instance of the entity is NULL");
1974 DEBUG_STOP;
1975 return;
1978 CBotPlayer* const talkingPlayer = TempPlayer;
1979 CSpawnBotNpc* const talkingNpc = dynamic_cast<CSpawnBotNpc*>(TempSpeaker);
1980 if ( talkingPlayer == NULL
1981 || talkingNpc == NULL
1982 || talkingNpc->getPersistent().getOwner() != group) // check if the talking npc is in this group
1984 nlwarning("talkTo failed: invalid interlocutors");
1985 DEBUG_STOP;
1986 return;
1989 // do nothing if one of them is dead
1990 if (!talkingPlayer->isAlive() || !talkingNpc->isAlive())
1992 return;
1995 // turn the npc to face the player
1996 talkingNpc->setTheta(talkingNpc->pos().angleTo(talkingPlayer->pos()));
1998 // send the request to the EGS
1999 // TRICK: here we use an empty give item request
2000 CGiveItemRequestMsg msg;
2001 msg.InstanceId = aiInstance->getInstanceNumber();
2002 msg.GroupAlias = groupToNotify->getAlias();
2003 msg.CharacterRowId = talkingPlayer->dataSetRow();
2004 msg.CreatureRowId = talkingNpc->dataSetRow();
2005 msg.MissionText = missionText;
2006 nlassert(msg.Items.empty());
2007 nlassert(msg.Quantities.empty());
2009 msg.send("EGS");
2013 //give_reward
2014 void giveReward_ssssc_(CStateInstance* entity, CScriptStack& stack)
2016 CGroupNpc* const groupToNotify = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() );
2017 stack.pop();
2019 string notEnoughPointsText = (string)stack.top();
2020 stack.pop();
2022 string inventoryFullText = (string)stack.top();
2023 stack.pop();
2025 string rareRewardText = (string)stack.top();
2026 stack.pop();
2028 string rewardText = (string)stack.top();
2029 stack.pop();
2032 if (groupToNotify == NULL)
2034 nlwarning("giveReward failed: groupToNotify is NULL");
2035 DEBUG_STOP;
2036 return;
2039 nlassert(entity != NULL);
2040 CGroup* const group = entity->getGroup();
2041 IManagerParent* const managerParent = group->getOwner()->getOwner();
2042 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2043 if (aiInstance == NULL)
2045 nlwarning("giveReward failed: the AI instance of the entity is NULL");
2046 DEBUG_STOP;
2047 return;
2050 CEntityId charEid(group->getEventParamString(0)); // must be player char EID
2051 CEntityId npcEid(group->getEventParamString(1)); // must be NPC EID
2053 if (charEid == CEntityId::Unknown || npcEid == CEntityId::Unknown)
2055 nlwarning("giveReward failed: the chareter or npc can't be retrreived from the event parameter");
2056 DEBUG_STOP;
2057 return;
2060 // retrieve the CBotPlayer
2061 CAIEntityPhysical *charEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(charEid));
2062 if (!charEntity)
2063 return;
2064 CBotPlayer *talkingPlayer=NLMISC::safe_cast<CBotPlayer*>(charEntity);
2065 if (!talkingPlayer)
2066 return;
2068 // retrieve the CSpawnBotNpcBotPlayer
2069 CAIEntityPhysical *npcEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(npcEid));
2070 if (!npcEntity)
2071 return;
2072 CSpawnBotNpc *talkingNpc=NLMISC::safe_cast<CSpawnBotNpc*>(npcEntity);
2073 if (!talkingNpc)
2074 return;
2076 // CBotPlayer* const talkingPlayer = TempPlayer;
2077 // CSpawnBotNpc* const talkingNpc = dynamic_cast<CSpawnBotNpc*>(TempSpeaker);
2078 if ( talkingPlayer == NULL
2079 || talkingNpc == NULL)
2081 nlwarning("giveReward failed: invalid interlocutors");
2082 DEBUG_STOP;
2083 return;
2086 // do nothing if one of them is dead
2087 if (!talkingPlayer->isAlive() || !talkingNpc->isAlive())
2089 return;
2092 // turn the npc to face the player
2093 talkingNpc->setTheta(talkingNpc->pos().angleTo(talkingPlayer->pos()));
2095 TDataSetRow CharacterRowId = talkingPlayer->dataSetRow();
2096 TDataSetRow CreatureRowId = talkingNpc->dataSetRow();
2098 IAisControl::getInstance()->giveRewardMessage(CharacterRowId, CreatureRowId,
2099 rewardText, rareRewardText, inventoryFullText, notEnoughPointsText);
2107 //give_reward
2108 void teleportNear_fffc_(CStateInstance* entity, CScriptStack& stack)
2110 CGroupNpc* const groupToNotify = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() );
2111 stack.pop();
2113 float z = (float)stack.top(); stack.pop();
2114 float y = (float)stack.top(); stack.pop();
2115 float x = (float)stack.top(); stack.pop();
2118 if (groupToNotify == NULL)
2120 nlwarning("teleportNear failed: groupToNotify is NULL");
2121 DEBUG_STOP;
2122 return;
2125 nlassert(entity != NULL);
2126 CGroup* const group = entity->getGroup();
2127 IManagerParent* const managerParent = group->getOwner()->getOwner();
2128 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2129 if (aiInstance == NULL)
2131 nlwarning("giveReward failed: the AI instance of the entity is NULL");
2132 DEBUG_STOP;
2133 return;
2136 CEntityId charEid(group->getEventParamString(0)); // must be player char EID
2137 CEntityId npcEid(group->getEventParamString(1)); // must be NPC EID
2139 if (charEid == CEntityId::Unknown || npcEid == CEntityId::Unknown)
2141 nlwarning("giveReward failed: the character or npc can't be retreived from the event parameter");
2142 DEBUG_STOP;
2143 return;
2146 // retrieve the CBotPlayer
2147 CAIEntityPhysical *charEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(charEid));
2148 if (!charEntity)
2149 return;
2150 CBotPlayer *talkingPlayer=NLMISC::safe_cast<CBotPlayer*>(charEntity);
2151 if (!talkingPlayer)
2152 return;
2154 // retrieve the CSpawnBotNpcBotPlayer
2155 CAIEntityPhysical *npcEntity=CAIS::instance().getEntityPhysical(TheDataset.getDataSetRow(npcEid));
2156 if (!npcEntity)
2157 return;
2158 CSpawnBotNpc *talkingNpc=NLMISC::safe_cast<CSpawnBotNpc*>(npcEntity);
2159 if (!talkingNpc)
2160 return;
2162 // CBotPlayer* const talkingPlayer = TempPlayer;
2163 // CSpawnBotNpc* const talkingNpc = dynamic_cast<CSpawnBotNpc*>(TempSpeaker);
2164 if ( talkingPlayer == NULL
2165 || talkingNpc == NULL)
2167 nlwarning("giveReward failed: invalid interlocutors");
2168 DEBUG_STOP;
2169 return;
2172 // do nothing if one of them is dead
2173 if (!talkingPlayer->isAlive() || !talkingNpc->isAlive())
2175 return;
2178 // turn the npc to face the player
2179 /// talkingNpc->setTheta(talkingNpc->pos().angleTo(talkingPlayer->pos()));
2181 TDataSetRow CharacterRowId = talkingPlayer->dataSetRow();
2182 TDataSetRow CreatureRowId = talkingNpc->dataSetRow();
2184 NLMISC::CEntityId player = CMirrors::getEntityId(CharacterRowId);
2185 if (player != NLMISC::CEntityId::Unknown)
2187 IAisControl::getInstance()->teleportNearMessage(player, x, y, z);
2196 // Return an "alive" bot from a group by the bot name
2197 static CSpawnBot* getSpawnBotFromGroupByName(CGroupNpc* const group, const std::string& botname)
2199 // Group exist
2200 if (!group) { return 0; }
2202 // Group is in a valid AIInstance
2204 if (!group->getOwner()) { return 0; }
2205 IManagerParent* const managerParent = group->getOwner()->getOwner();
2206 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2207 if (!aiInstance) { return 0; }
2210 // Bot is spawn and alive
2213 CSpawnBot* spawnBot=0;
2214 CAliasCont<CBot> const& bots = group->bots();
2215 CCont<CBot> & cc_bots = group->bots();
2216 CBot* child;
2217 if (botname == "")
2218 child = bots.getFirstChild();
2219 else
2220 child = bots.getChildByName(botname);
2221 if (!child) { return 0; }
2223 spawnBot = child->getSpawnObj();
2224 if (!spawnBot || !spawnBot->isAlive()) { return 0; }
2225 return spawnBot;
2228 nlassert(0); //this path is never used
2229 return 0;
2232 //----------------------------------------------------------------------------
2233 /** @page code
2235 @subsection facing_cscs_
2237 The npc1 will turn to npc2
2240 Arguments: c(group1), s(botname1), c(group2), s(botname2), ->
2241 @param[in] group1 is the npc group of the boot that turn to the other bot
2242 @param[in] botname1 is the name of the bot
2243 @param[in] group2 is the npc group of the boot that is the target
2244 @param[in] botname2 is the name of the bot that is targeted
2247 @code
2248 (@group1)group_name1.context();
2249 (@group2)group_name2.context();
2250 ()facing(@group1, "bob", @group2, "bobette");
2251 @endcode
2254 // CSpawnBotNpc
2258 void facing_cscs_(CStateInstance* entity, CScriptStack& stack)
2260 string botname2 = (string)stack.top(); stack.pop();
2261 CGroupNpc* const group2 = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() ); stack.pop();
2262 string botname1 = (string)stack.top(); stack.pop();
2263 CGroupNpc* const group1 = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() ); stack.pop();
2265 CSpawnBot* bot1 = getSpawnBotFromGroupByName(group1, botname1);
2266 CSpawnBot* bot2 = getSpawnBotFromGroupByName(group2, botname2);
2268 if (!bot1 || !bot2) { return; }
2270 CSpawnBotNpc* bot = dynamic_cast<CSpawnBotNpc*>(bot1);
2271 if (!bot) { return; }
2272 // Same than setTheta but initial theta is restored few secondes later
2273 bot->setFacing(bot1->pos().angleTo(bot2->pos()));
2274 // bot1->setTheta(bot1->pos().angleTo(bot2->pos()));
2277 //----------------------------------------------------------------------------
2278 /** @page code
2280 @subsection npcSay_css_
2282 Make a npc say a text
2284 There are 3 type of text
2285 - Classic StringId (IOS does traduction)
2286 - RING "complete text (no traduction)
2287 - RING StringId (DSS does traduction)
2289 Arguments: c(group), s(botname), s(text), ->
2290 @param[in] group is the npc group of the boot that does the emote
2291 @param[in] botname is the name of the bot
2292 @param[in] text is the name of the emote
2294 @code
2295 (@group)group_name.context();
2296 ()npcSay(@group, "bob", "DSS_1601 RtEntryText_6") ;// Send To dss
2297 ()npcSay(@group, "bob", "RAW Ca farte?"); // phrase direcly send to IOS as raw (for debug)
2298 ()npcSay(@group, "bob", "answer_group_no_m"); //phrase id
2300 @endcode
2304 // CSpawnBotNpc
2306 #include "game_share/chat_group.h"
2307 #include "game_share/send_chat.h"
2309 void execSayHelper(CSpawnBot *spawnBot, NLMISC::CSString text, CChatGroup::TGroupType mode = CChatGroup::say)
2311 if (spawnBot)
2313 NLMISC::CSString prefix = text.left(4);
2314 if (prefix=="DSS_")
2317 NLMISC::CSString phrase = text.right(text.length() - 4);
2318 NLMISC::CSString idStr = phrase.strtok(" ",false,false,false,false);
2319 uint32 scenarioId = atoi(idStr.c_str());
2320 forwardToDss(spawnBot->dataSetRow(), mode, phrase, scenarioId);
2321 return;
2324 if (prefix=="RAW ")
2326 std::string phrase = text.right(text.length()-4);
2327 ucstring ucstr = phrase;
2328 npcChatToChannelSentence(spawnBot->dataSetRow(), mode, ucstr);
2329 return;
2332 //Classic phrase ID
2333 npcChatToChannel(spawnBot->dataSetRow(), mode, text);
2337 void npcSay_css_(CStateInstance* entity, CScriptStack& stack)
2339 string text = (string)stack.top(); stack.pop();
2340 string botname = (string)stack.top(); stack.pop();
2341 CGroupNpc* const group = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() ); stack.pop();
2343 CSpawnBot* spawnBot = getSpawnBotFromGroupByName(group, botname);
2345 if (!spawnBot) { return; }
2347 execSayHelper(spawnBot, text);
2350 //----------------------------------------------------------------------------
2351 /** @page code
2353 @subsection npcSay_ss_
2355 Make a npc say a text
2357 Arguments: s(text), s(mode) ->
2358 @param[in] text is the text to say. prefix with ID: to use an id
2359 @param[in] mode is the mode to use (say, shout)
2361 @code
2362 ()npcSay("Hello!","say"); // phrase direcly send to IOS as raw
2363 ()npcSay("ID:answer_group_no_m","shout"); // phrase id
2364 @endcode
2368 void npcSay_ss_(CStateInstance* entity, CScriptStack& stack)
2370 std::string sMode = (std::string)stack.top(); stack.pop();
2371 std::string text = (std::string)stack.top(); stack.pop();
2373 CChatGroup::TGroupType mode = CChatGroup::say;
2374 mode = CChatGroup::stringToGroupType(sMode);
2375 CGroup* group = entity->getGroup();
2377 if (group->isSpawned())
2379 FOREACH(itBot, CCont<CBot>, group->bots())
2381 CBot* bot = *itBot;
2382 if (bot)
2384 if (bot->isSpawned())
2386 CSpawnBot *spawnBot = bot->getSpawnObj();
2387 std::string prefix = NLMISC::CSString(text).left(3);
2388 if (NLMISC::nlstricmp(prefix.c_str(), "id:") == 0) {
2389 text = NLMISC::CSString(text).right(text.length()-3);
2390 execSayHelper(spawnBot, text, mode);
2392 else {
2393 execSayHelper(spawnBot, "RAW " + text, mode);
2403 //----------------------------------------------------------------------------
2404 /** @page code
2406 @subsection deactivateEasterEgg_fff_
2408 Call the DSS function CAnimationModule::dssMessage
2410 Arguments: f(easterEggId), f(scenarioId), f(actId) ->
2412 @code
2413 ()dssMessage(114, "BC", "Bob", "Life is harde");
2414 @endcode
2418 void dssMessage_fsss_(CStateInstance* entity, CScriptStack& stack)
2420 string msg = (string)stack.top(); stack.pop();
2421 string who = (string)stack.top(); stack.pop();
2422 string mode = (string)stack.top(); stack.pop();
2423 float instance = (float)stack.top(); stack.pop();
2425 IAisControl::getInstance()->dssMessage(TSessionId(uint32(instance)), mode, who, msg);
2426 return;
2430 //----------------------------------------------------------------------------
2431 /** @page code
2433 @subsection setScenarioPoints
2435 Call the DSS function CAnimationModule::setScenarioPoints
2437 Arguments: f(scenarioInstance), f(scenarioPoints) ->
2439 @code
2440 ()setScenarioPoints(114, 42);
2441 @endcode
2445 void setScenarioPoints_ff_(CStateInstance* entity, CScriptStack& stack)
2447 float scenarioPoints = (float)stack.top(); stack.pop();
2448 float instance = (float)stack.top(); stack.pop();
2450 IAisControl::getInstance()->setScenarioPoints(TSessionId(uint32(instance)), scenarioPoints);
2451 return;
2454 //----------------------------------------------------------------------------
2455 /** @page code
2457 @subsection startScenarioTiming
2459 Call the DSS function CAnimationModule::startScenarioTiming
2461 Arguments: f(scenarioInstance),
2463 @code
2464 ()startScenarioTiming(114, 42);
2465 @endcode
2469 void startScenarioTiming_f_(CStateInstance* entity, CScriptStack& stack)
2471 float instance = (float)stack.top(); stack.pop();
2473 IAisControl::getInstance()->startScenarioTiming(TSessionId(uint32(instance)));
2474 return;
2477 //----------------------------------------------------------------------------
2478 /** @page code
2480 @subsection endScenarioTiming
2482 Call the DSS function CAnimationModule::endScenarioTiming
2484 Arguments: f(scenarioInstance),
2486 @code
2487 ()endScenarioTiming(114, 42);
2488 @endcode
2492 void endScenarioTiming_f_(CStateInstance* entity, CScriptStack& stack)
2494 float instance = (float)stack.top(); stack.pop();
2496 IAisControl::getInstance()->endScenarioTiming(TSessionId(uint32(instance)));
2497 return;
2500 //----------------------------------------------------------------------------
2501 /** @page code
2503 @subsection emote_css_
2505 Make a npc launch a emote
2508 Arguments: c(group1), s(botname1), c(group2), s(botname2), ->
2509 @param[in] group1 is the npc group of the boot that does the emote
2510 @param[in] botname1 is the name of the bot
2511 @param[in] emote is the name of the emote
2514 @code
2515 (@group1)group_name.context();
2516 ()emote(@group1, "bob", "sad")
2517 @endcode
2520 // CSpawnBotNpc
2523 /****************************************************************************/
2525 void emote_css_(CStateInstance* entity, CScriptStack& stack)
2527 string emoteName = (string)stack.top(); stack.pop();
2528 string botname = (string)stack.top(); stack.pop();
2529 CGroupNpc* const group = dynamic_cast<CGroupNpc*>( (IScriptContext*)stack.top() ); stack.pop();
2531 CSpawnBot* spawnBot = getSpawnBotFromGroupByName(group, botname);
2533 if (!spawnBot) { return; }
2535 //CBot& bot = spawnBot->getPersistent();
2537 // The entity Id must be valid (we know that the bot is alive so its entity Id must be ok)
2538 NLMISC::CEntityId entityId=spawnBot->getEntityId();
2539 if (entityId == NLMISC::CEntityId::Unknown)
2541 return;
2544 // Is the emote valid
2545 uint32 emoteId = CAIS::instance().getEmotNumber(emoteName);
2546 if (emoteId == std::numeric_limits<uint32>::max())
2548 return;
2551 // Get the behaviour Id
2552 MBEHAV::EBehaviour behaviourId = (MBEHAV::EBehaviour)(emoteId + MBEHAV::EMOTE_BEGIN);
2554 // Change the behaviour
2555 NLNET::CMessage msgout("SET_BEHAVIOUR");
2556 msgout.serial(entityId);
2557 MBEHAV::CBehaviour bh(behaviourId);
2558 bh.Data = (uint16)(CTimeInterface::gameCycle());
2559 msgout.serial(bh);
2561 NLNET::CUnifiedNetwork::getInstance()->send( "EGS", msgout );
2565 void emote_ss_(CStateInstance* entity, CScriptStack& stack)
2567 string emoteName = (string)stack.top(); stack.pop();
2568 NLMISC::CEntityId botId = NLMISC::CEntityId((std::string)stack.top());
2570 if (botId == NLMISC::CEntityId::Unknown)
2572 return;
2575 // Is the emote valid
2576 uint32 emoteId = CAIS::instance().getEmotNumber(emoteName);
2577 if (emoteId == std::numeric_limits<uint32>::max())
2579 return;
2582 // Get the behaviour Id
2583 MBEHAV::EBehaviour behaviourId = (MBEHAV::EBehaviour)(emoteId + MBEHAV::EMOTE_BEGIN);
2585 // Change the behaviour
2586 NLNET::CMessage msgout("SET_BEHAVIOUR");
2587 msgout.serial(botId);
2588 MBEHAV::CBehaviour bh(behaviourId);
2589 bh.Data = (uint16)(CTimeInterface::gameCycle());
2590 msgout.serial(bh);
2592 NLNET::CUnifiedNetwork::getInstance()->send( "EGS", msgout );
2597 void emote_s_(CStateInstance* entity, CScriptStack& stack)
2599 string emoteName = (string)stack.top(); stack.pop();
2601 // Is the emote valid
2602 uint32 emoteId = CAIS::instance().getEmotNumber(emoteName);
2603 if (emoteId == std::numeric_limits<uint32>::max())
2604 return;
2606 // Get the behaviour Id
2607 MBEHAV::EBehaviour behaviourId = (MBEHAV::EBehaviour)(emoteId + MBEHAV::EMOTE_BEGIN);
2610 CGroup* group = entity->getGroup();
2612 if (group->isSpawned())
2614 FOREACH(itBot, CCont<CBot>, group->bots())
2616 CBot* bot = *itBot;
2617 if (bot)
2619 // Change the behaviour
2620 if (bot->isSpawned())
2622 CSpawnBot *spawnBot = bot->getSpawnObj();
2623 if (spawnBot)
2625 CEntityId botId = spawnBot->getEntityId();
2626 NLNET::CMessage msgout("SET_BEHAVIOUR");
2627 msgout.serial(botId);
2628 MBEHAV::CBehaviour bh(behaviourId);
2629 bh.Data = (uint16)(CTimeInterface::gameCycle());
2630 msgout.serial(bh);
2632 NLNET::CUnifiedNetwork::getInstance()->send( "EGS", msgout );
2641 void rename_s_(CStateInstance* entity, CScriptStack& stack)
2643 string newName = (string)stack.top(); stack.pop();
2644 ucstring name;
2645 name.fromUtf8(newName);
2646 CGroup* group = entity->getGroup();
2648 if (group->isSpawned())
2650 FOREACH(itBot, CCont<CBot>, group->bots())
2652 CBot* bot = *itBot;
2653 if (bot)
2655 if (bot->isSpawned())
2657 CSpawnBot *spawnBot = bot->getSpawnObj();
2658 if (spawnBot)
2660 TDataSetRow row = spawnBot->dataSetRow();
2661 NLNET::CMessage msgout("CHARACTER_NAME");
2662 msgout.serial(row);
2663 msgout.serial(name);
2664 sendMessageViaMirror("IOS", msgout);
2665 spawnBot->getPersistent().setCustomName(name);
2674 void vpx_s_(CStateInstance* entity, CScriptStack& stack)
2676 string vpx = (string)stack.top(); stack.pop();
2678 CGroup* group = entity->getGroup();
2680 if (group->isSpawned())
2682 FOREACH(itBot, CCont<CBot>, group->bots())
2684 CBotNpc* bot = NLMISC::safe_cast<CBotNpc*>(*itBot);
2685 if (bot)
2687 bot->setVisualProperties(vpx);
2688 bot->sendVisualProperties();
2694 //----------------------------------------------------------------------------
2695 /** @page code
2697 @subsection maxHitRange_f_
2698 Sets the max hit range possible for player, in meters
2700 Arguments: f(MaxHitRange) ->
2701 @param[in] MaxHitRange set the max range for player can hit this npc group
2703 @code
2704 ()maxHitRange(50); // Set the max hit range in 50 meters all npc in group
2705 @endcode
2708 // CBotNpc
2709 void maxHitRange_f_(CStateInstance* entity, CScriptStack& stack)
2711 float maxHitRange = (float&)stack.top(); stack.pop();
2712 CGroup* group = entity->getGroup();
2713 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
2715 FOREACH(botIt, CCont<CBot>, group->bots())
2717 CBot* bot = *botIt;
2719 if (!bot->isSpawned()) return;
2720 if (bot->getRyzomType() == RYZOMID::npc)
2722 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
2723 botNpc->setMaxHitRangeForPlayer(maxHitRange);
2728 //----------------------------------------------------------------------------
2729 /** @page code
2731 @subsection setEquipement_s_
2733 Arguments: ->
2735 arg1: is the equipment list (in hex)
2737 @code
2738 ()setEquipement("xxxxx");
2739 @endcode
2742 void setEquipment_s_(CStateInstance* entity, CScriptStack& stack)
2744 string script = stack.top();
2745 stack.pop();
2747 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
2748 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2749 if (!aiInstance)
2750 return;
2752 std::vector<CAIActions::CArg> args;
2753 std::vector<std::string> equipements;
2754 NLMISC::splitString(script, ";", equipements);
2756 CGroup* group = entity->getGroup();
2758 FOREACH(botIt, CCont<CBot>, group->bots())
2760 CBot* bot = *botIt;
2762 if (bot->getRyzomType() == RYZOMID::npc)
2764 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
2765 if (botNpc==NULL)
2766 return;
2767 botNpc->equipmentInit();
2769 FOREACHC(it, std::vector<std::string>, equipements)
2771 botNpc->equipmentAdd(*it);
2778 //----------------------------------------------------------------------------
2779 /** @page code
2781 @subsection addUserModel_sss_
2783 Arguments: ->
2785 arg0: is the user model id (a name)
2786 arg1: is the base sheet
2787 arg3: is the script (in hex)
2789 @code
2790 ()addUserModel("toto", "ccbha1", "xxxxx");
2791 @endcode
2794 void addUserModel_sss_(CStateInstance* entity, CScriptStack& stack)
2796 string script = stack.top();
2797 stack.pop();
2799 string baseSheet = stack.top();
2800 stack.pop();
2802 string userModelId = stack.top(); // prefix with ARK_ to prevent stupid overwrite
2803 stack.pop();
2805 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
2806 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2807 if (!aiInstance)
2808 return;
2810 std::vector<CAIActions::CArg> args;
2811 args.push_back(CAIActions::CArg(900+aiInstance->getInstanceNumber()));
2812 args.push_back(CAIActions::CArg("ARK_"+userModelId));
2813 args.push_back(CAIActions::CArg(baseSheet));
2814 args.push_back(CAIActions::CArg(scriptHex_decode(script)));
2816 CAIActions::execute("USR_MDL", args);
2819 //----------------------------------------------------------------------------
2820 /** @page code
2822 @subsection addCustomLoot_ss_
2824 Arguments: ->
2826 arg0: is the custom table id (a name)
2827 arg1: is the script with syntax : <PROBA_1>:<hex:LOOT_SET_1>,<PROBA_2>:<hex:LOOT_SET_2>,...
2829 @code
2830 ()addCustomLoot("toto", "<PROBA_1>:<hex:LOOT_SET_1>,<PROBA_2>:<hex:LOOT_SET_2>,...");
2831 @endcode
2834 void addCustomLoot_ss_(CStateInstance* entity, CScriptStack& stack)
2836 string script = stack.top();
2837 stack.pop();
2839 string customTableId = stack.top();
2840 stack.pop();
2842 std::vector<std::string> loots;
2843 NLMISC::splitString(script, ",", loots);
2844 if (loots.empty())
2845 return;
2847 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
2848 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
2849 if (!aiInstance)
2850 return;
2852 std::vector<CAIActions::CArg> args;
2853 uint32 nbTables = 1;
2854 args.push_back(CAIActions::CArg(nbTables));
2855 args.push_back(CAIActions::CArg(900+aiInstance->getInstanceNumber()));
2856 uint32 lootSets = loots.size();
2857 args.push_back(CAIActions::CArg(lootSets));
2858 args.push_back(CAIActions::CArg(customTableId));
2859 float moneyProba = 0.0;
2860 args.push_back(CAIActions::CArg(moneyProba));
2861 float moneyFactor = 0.0;
2862 args.push_back(CAIActions::CArg(moneyFactor));
2863 uint32 moneyBase = 0;
2864 args.push_back(CAIActions::CArg(moneyBase));
2866 FOREACHC(it, std::vector<std::string>, loots)
2868 std::vector<string> lootSet;
2869 NLMISC::splitString(*it, ":", lootSet);
2870 if (lootSet.size() == 2)
2872 args.push_back(CAIActions::CArg(lootSet[0]));
2873 args.push_back(CAIActions::CArg(scriptHex_decode(lootSet[1])));
2877 CAIActions::execute("CUSTOMLT", args);
2881 //----------------------------------------------------------------------------
2882 /** @page code
2884 @subsection setUserModel_s_
2885 Set the user model of a creature
2887 Arguments: -> s(userModel)
2889 @code
2890 ()setUserModel('my_model');
2892 @endcode
2895 void setUserModel_s_(CStateInstance* entity, CScriptStack& stack)
2897 string userModel = stack.top();
2898 stack.pop();
2900 CGroup* group = entity->getGroup();
2902 FOREACH(botIt, CCont<CBot>, group->bots())
2904 CBot* bot = *botIt;
2906 if (bot->getRyzomType() == RYZOMID::npc)
2908 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
2909 botNpc->setUserModelId("ARK_"+userModel);
2910 if (bot->isSpawned())
2911 bot->getSpawnObj()->sendInfoToEGS();
2917 //----------------------------------------------------------------------------
2918 /** @page code
2920 @subsection setCustomLoot_s_
2921 Set the custom loot of a creature
2923 Arguments: -> s(customTable)
2925 @code
2926 ()setUserModel('my_loot_table');
2928 @endcode
2931 void setCustomLoot_s_(CStateInstance* entity, CScriptStack& stack)
2933 string customTable = stack.top();
2934 stack.pop();
2936 CGroup* group = entity->getGroup();
2938 FOREACH(botIt, CCont<CBot>, group->bots())
2940 CBot* bot = *botIt;
2942 //if (!bot->isSpawned()) return;
2944 if (bot->getRyzomType() == RYZOMID::npc)
2946 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
2947 botNpc->setCustomLootTableId(customTable);
2948 if (bot->isSpawned())
2949 bot->getSpawnObj()->sendInfoToEGS();
2955 ////----------------------------------------------------------------------------
2956 ///** @page code
2958 //@subsection hideMissionStepIcon_b_
2959 //Allows to hide icons for current missions steps having interactions with this NPC (default: shown)
2961 //Arguments: b(hide) ->
2962 //@param[in] hide true to hide the icon for all mission steps relating to this NPC (default: false)
2964 //*/
2965 //// CBotNpc
2966 //void hideMissionStepIcon_b_(CStateInstance* entity, CScriptStack& stack)
2968 // bool b = (bool&)stack.top(); stack.pop();
2969 // CGroup* group = entity->getGroup();
2970 // CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
2972 // FOREACH(botIt, CCont<CBot>, group->bots())
2973 // {
2974 // CBot* bot = *botIt;
2976 // if (!bot->isSpawned()) return;
2977 // if (bot->getRyzomType() == RYZOMID::npc)
2978 // {
2979 // CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
2980 // botNpc->setMissionStepIconHidden(b);
2981 // }
2982 // }
2985 ////----------------------------------------------------------------------------
2986 ///** @page code
2988 //@subsection hideMissionGiverIcon_b_
2989 //Allows to hide icons for missions proposed by this NPC (default: shown)
2991 //Arguments: b(hide) ->
2992 //@param[in] hide true to hide the icon for all missions propsed by this NPC (default: false)
2994 //*/
2995 //// CBotNpc
2996 //void hideMissionGiverIcon_b_(CStateInstance* entity, CScriptStack& stack)
2998 // bool b = (bool&)stack.top(); stack.pop();
2999 // CGroup* group = entity->getGroup();
3000 // CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
3002 // FOREACH(botIt, CCont<CBot>, group->bots())
3003 // {
3004 // CBot* bot = *botIt;
3006 // if (!bot->isSpawned()) return;
3007 // if (bot->getRyzomType() == RYZOMID::npc)
3008 // {
3009 // CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
3010 // botNpc->setMissionGiverIconHidden(b);
3011 // }
3012 // }
3015 //----------------------------------------------------------------------------
3016 /** @page code
3018 @subsection setEventCode_sss
3019 Sets and event to execute when event triggers
3021 Arguments:
3022 s(event) -> @param[in] The name of the state
3023 s(event) -> @param[in] The name of the event
3024 s(code) -> @param[in] The string to execute code in hex
3026 @code
3027 ()maxHitRange(50); // Set the max hit range in 50 meters all npc in group
3028 @endcode
3031 // CBotNpc
3032 void setEventCode_sss_(CStateInstance* entity, CScriptStack& stack)
3034 string code = stack.top();
3035 stack.pop();
3037 string event_name = stack.top();
3038 stack.pop();
3040 string state_name = stack.top();
3041 stack.pop();
3044 //////////// Special Cases for Ark
3045 if (event_name == "notify_on_death")
3047 strFindReplace(code, "http://", "");
3048 strFindReplace(code, "https://", "");
3049 strFindReplace(code, "/index.php?", " ");
3050 strFindReplace(code, "&nbsp&", " ");
3052 CGroup* group = entity->getGroup();
3053 FOREACH(botIt, CCont<CBot>, group->bots())
3055 CBot* bot = *botIt;
3057 if (!bot->isSpawned()) return;
3059 CBotNpc* botNpc = NLMISC::safe_cast<CBotNpc*>(bot);
3060 if (botNpc)
3062 CSpawnBotNpc* spawnBotNpc = botNpc->getSpawn();
3063 if (spawnBotNpc)
3065 CAINotifyDeathMsg *msg = new CAINotifyDeathMsg;
3066 msg->Url = code;
3067 msg->TargetRowId = spawnBotNpc->dataSetRow();
3068 msg->send("EGS");
3072 return;
3074 ///////////////////////////////////////////////////////
3077 CAIEventDescription eventDescription;
3078 CAIEventActionNode::TSmartPtr eventAction;
3079 CAIEventReaction* event;
3081 // Create event handler
3082 eventDescription.EventType = event_name;
3084 // Create event action
3085 eventAction = new CAIEventActionNode;
3086 eventAction->Action = "code";
3087 eventAction->Weight = 1;
3089 code = scriptHex_decode(code);
3090 vector<string> lines_of_code;
3091 NLMISC::splitString(code, "\n", lines_of_code);
3092 uint32 line = 0;
3093 if (!lines_of_code.empty())
3095 nlinfo("=== Code ===");
3096 FOREACHC(it, vector<string>, lines_of_code)
3098 nlinfo("#%d %s", line, (*it).c_str());
3099 eventAction->Args.push_back(*it);
3100 line++;
3102 nlinfo("=== * ===");
3105 // Register event action
3106 eventDescription.Action = eventAction;
3107 eventAction = NULL;
3109 CGroup* group = entity->getGroup();
3110 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
3111 CStateMachine* sm = &npcGroup->getEventContainer();
3112 CAIStatePositional* statePositional;
3114 uint32 stateAlias = npcGroup->getStateAlias(state_name);
3115 if (stateAlias == 0)
3117 nlinfo("STATE %s not found !", state_name.c_str());
3118 statePositional = new CAIStatePositional(sm, 0, state_name);
3119 npcGroup->setStateAlias(state_name, statePositional->getAlias());
3120 sm->states().addChild(statePositional);
3122 else
3124 statePositional = safe_cast<CAIStatePositional*>(sm->states().getChildByAlias(stateAlias));
3127 uint32 stateEventAlias = npcGroup->getStateEventAlias(event_name);
3128 /*if (stateEventAlias != 0)
3130 CAIEventReaction er = sm->eventReactions().getChildByAlias(stateEventAlias);
3131 er->setGroup(0);
3133 // sm->eventReactions().removeChildByIndex(sm->eventReactions().getChildIndexByAlias(stateEventAlias));
3135 // Register event handler
3136 stateEventAlias = sm->getLastStateEventAlias();
3137 event = new CAIEventReaction(sm, stateEventAlias, eventDescription.EventType);
3138 event->processEventDescription(&eventDescription, sm);
3139 event->setGroup(npcGroup->getAlias());
3140 nlinfo("Add Event: %s(%d) in State : %d for group %d", event_name.c_str(), stateEventAlias, statePositional->getAlias(), npcGroup->getAlias());
3141 event->setState(statePositional->getAlias());
3142 npcGroup->setStateEventAlias(event_name, stateEventAlias);
3145 sm->eventReactions().addChild(event);
3146 event = NULL;
3150 //----------------------------------------------------------------------------
3151 /** @page code
3153 @subsection setParent_s_
3155 A a link child -> parent from child
3158 Arguments: parent(direction)
3159 @param[in] the name of group who will be the parent of this group
3161 @code
3162 ()setParent("group_name");
3163 @endcode
3167 void setParent_s_(CStateInstance* entity, CScriptStack& stack)
3170 string parent = stack.top();
3172 std::vector<CGroup*> grps;
3173 entity->getGroup()->getAIInstance()->findGroup(grps, parent);
3174 if (grps.size() > 0)
3176 CGroup* parentGroup = grps.back();
3177 CGroupNpc* parentNpcGroup = NLMISC::safe_cast<CGroupNpc*>(parentGroup);
3179 CGroup* group = entity->getGroup();
3180 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
3181 if (npcGroup && parentNpcGroup)
3182 npcGroup->getPersistentStateInstance()->setParentStateInstance(parentNpcGroup->getPersistentStateInstance());
3188 //----------------------------------------------------------------------------
3189 /** @page code
3191 @subsection addHealGroup_s_
3193 Add a link to be able to heal a group
3196 Arguments: group(direction)
3197 @param[in] the name of group who will able to heal
3199 @code
3200 ()addHealGroup("group_name");
3201 @endcode
3205 void addHealGroup_s_(CStateInstance* entity, CScriptStack& stack)
3208 string healGroup = stack.top();
3210 std::vector<CGroup*> healGrps;
3211 entity->getGroup()->getAIInstance()->findGroup(healGrps, healGroup);
3213 CGroup* group = entity->getGroup();
3214 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
3216 for (uint i=0; i<healGrps.size(); ++i)
3218 CGroupNpc* healNpcGroup = NLMISC::safe_cast<CGroupNpc*>(healGrps[i]);
3219 if (healNpcGroup)
3220 npcGroup->addHealGroup(healNpcGroup);
3224 //----------------------------------------------------------------------------
3225 /** @page code
3227 @subsection resetHealGroups_
3229 Add a link to be able to heal a group
3231 @code
3232 ()resetHealGroups();
3233 @endcode
3237 void resetHealGroups_(CStateInstance* entity, CScriptStack& stack)
3239 CGroup* group = entity->getGroup();
3240 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
3241 npcGroup->resetHealGroups();
3245 //----------------------------------------------------------------------------
3246 /** @page code
3248 @subsection spawnGroup_fsssffff_
3249 Spawn new group.
3251 Arguments: f(NbrBots), f(spawnBot) s(Sheet), s(Name), s(Look), f(x), f(y), f(orientation), f(dispersion) ->
3253 @code
3255 @endcode
3258 // CGroup
3259 void spawnGroup_ffsssffff_c(CStateInstance* entity, CScriptStack& stack)
3261 double dispersionRadius = (double)(float)stack.top();
3262 stack.pop();
3264 double orientation = (double)(float)stack.top();
3265 stack.pop();
3267 double y = (double)(float)stack.top();
3268 stack.pop();
3270 double x = (double)(float)stack.top();
3271 stack.pop();
3273 string look = (string)stack.top();
3274 stack.pop();
3276 string name = (string)stack.top();
3277 stack.pop();
3279 CSheetId sheetId((string)stack.top());
3280 stack.pop();
3282 bool spawn = (float&)stack.top()!=0.0f;
3283 stack.pop();
3285 uint nbBots = (uint)(float)stack.top();
3286 stack.pop();
3288 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
3289 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
3290 if (!aiInstance)
3291 return;
3293 CGroupNpc* grp = dynamic_cast<CGroupNpc*>(entity->getGroup());
3294 if (grp)
3296 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
3297 CGroupNpc* npcGroup;
3298 if (spawnGroup)
3299 npcGroup = aiInstance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(x, y), dispersionRadius, spawn, orientation, name, look, spawnGroup->getCell());
3300 else
3301 npcGroup = aiInstance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(x, y), dispersionRadius, spawn, orientation, name, look);
3302 if (npcGroup)
3303 stack.push(npcGroup->getPersistentStateInstance());
3307 void spawnGroup_ffsssffff_(CStateInstance* entity, CScriptStack& stack)
3309 double dispersionRadius = (double)(float)stack.top();
3310 stack.pop();
3312 double orientation = (double)(float)stack.top();
3313 stack.pop();
3315 double y = (double)(float)stack.top();
3316 stack.pop();
3318 double x = (double)(float)stack.top();
3319 stack.pop();
3321 string look = (string)stack.top();
3322 stack.pop();
3324 string name = (string)stack.top();
3325 stack.pop();
3327 CSheetId sheetId((string)stack.top());
3328 stack.pop();
3330 bool spawn = (float&)stack.top()!=0.0f;
3331 stack.pop();
3333 uint nbBots = (uint)(float)stack.top();
3334 stack.pop();
3336 IManagerParent* const managerParent = entity->getGroup()->getOwner()->getOwner();
3337 CAIInstance* const aiInstance = dynamic_cast<CAIInstance*>(managerParent);
3338 if (!aiInstance)
3339 return;
3341 CGroupNpc* grp = dynamic_cast<CGroupNpc*>(entity->getGroup());
3342 if (grp)
3344 CSpawnGroupNpc* spawnGroup = grp->getSpawnObj();
3345 CGroupNpc* npcGroup;
3346 if (spawnGroup)
3347 npcGroup = aiInstance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(x, y), dispersionRadius, spawn, orientation, name, look, spawnGroup->getCell());
3348 else
3349 npcGroup = aiInstance->eventCreateNpcGroup(nbBots, sheetId, CAIVector(x, y), dispersionRadius, spawn, orientation, name, look);
3354 std::map<std::string, FScrptNativeFunc> nfGetNpcGroupNativeFunctions()
3356 std::map<std::string, FScrptNativeFunc> functions;
3358 #define REGISTER_NATIVE_FUNC(cont, func) cont.insert(std::make_pair(std::string(#func), &func))
3360 REGISTER_NATIVE_FUNC(functions, setFactionProp_ss_);
3361 REGISTER_NATIVE_FUNC(functions, setOupostMode_ss_);
3362 REGISTER_NATIVE_FUNC(functions, moveToZone_ss_);
3363 REGISTER_NATIVE_FUNC(functions, setActivity_s_);
3364 REGISTER_NATIVE_FUNC(functions, startWander_f_);
3365 REGISTER_NATIVE_FUNC(functions, startMoving_fff_);
3366 REGISTER_NATIVE_FUNC(functions, waitInZone_s_);
3367 REGISTER_NATIVE_FUNC(functions, stopMoving__);
3368 REGISTER_NATIVE_FUNC(functions, followPlayer_sf_);
3369 REGISTER_NATIVE_FUNC(functions, wander__);
3370 REGISTER_NATIVE_FUNC(functions, setAttackable_f_);
3371 REGISTER_NATIVE_FUNC(functions, setPlayerAttackable_f_);
3372 REGISTER_NATIVE_FUNC(functions, setBotAttackable_f_);
3373 REGISTER_NATIVE_FUNC(functions, setFactionAttackableAbove_sff_);
3374 REGISTER_NATIVE_FUNC(functions, setFactionAttackableBelow_sff_);
3375 REGISTER_NATIVE_FUNC(functions, addBotChat_s_);
3376 REGISTER_NATIVE_FUNC(functions, clearBotChat__);
3377 REGISTER_NATIVE_FUNC(functions, ignoreOffensiveActions_f_);
3378 REGISTER_NATIVE_FUNC(functions, setDespawnTime_f_);
3379 REGISTER_NATIVE_FUNC(functions, setRespawnTime_f_);
3380 REGISTER_NATIVE_FUNC(functions, addHpUpTrigger_ff_);
3381 REGISTER_NATIVE_FUNC(functions, delHpUpTrigger_ff_);
3382 REGISTER_NATIVE_FUNC(functions, addHpDownTrigger_ff_);
3383 REGISTER_NATIVE_FUNC(functions, delHpDownTrigger_ff_);
3384 REGISTER_NATIVE_FUNC(functions, addHpUpTrigger_fs_);
3385 REGISTER_NATIVE_FUNC(functions, delHpUpTrigger_fs_);
3386 REGISTER_NATIVE_FUNC(functions, addHpDownTrigger_fs_);
3387 REGISTER_NATIVE_FUNC(functions, delHpDownTrigger_fs_);
3388 REGISTER_NATIVE_FUNC(functions, addNamedEntityListener_ssf_);
3389 REGISTER_NATIVE_FUNC(functions, delNamedEntityListener_ssf_);
3390 REGISTER_NATIVE_FUNC(functions, addNamedEntityListener_sss_);
3391 REGISTER_NATIVE_FUNC(functions, delNamedEntityListener_sss_);
3392 REGISTER_NATIVE_FUNC(functions, setPlayerController_ss_);
3393 REGISTER_NATIVE_FUNC(functions, clearPlayerController_s_);
3394 REGISTER_NATIVE_FUNC(functions, activateEasterEgg_fffsffffsss_);
3395 REGISTER_NATIVE_FUNC(functions, deactivateEasterEgg_fff_);
3396 REGISTER_NATIVE_FUNC(functions, receiveMissionItems_ssc_);
3397 REGISTER_NATIVE_FUNC(functions, giveMissionItems_ssc_);
3398 REGISTER_NATIVE_FUNC(functions, talkTo_sc_);
3399 REGISTER_NATIVE_FUNC(functions, facing_cscs_);
3400 REGISTER_NATIVE_FUNC(functions, emote_css_);
3401 REGISTER_NATIVE_FUNC(functions, emote_ss_);
3402 REGISTER_NATIVE_FUNC(functions, emote_s_);
3403 REGISTER_NATIVE_FUNC(functions, rename_s_);
3404 REGISTER_NATIVE_FUNC(functions, vpx_s_);
3405 REGISTER_NATIVE_FUNC(functions, npcSay_css_);
3406 REGISTER_NATIVE_FUNC(functions, npcSay_ss_);
3407 REGISTER_NATIVE_FUNC(functions, dssMessage_fsss_);
3408 REGISTER_NATIVE_FUNC(functions, despawnBotByAlias_s_);
3409 REGISTER_NATIVE_FUNC(functions, giveReward_ssssc_);
3410 REGISTER_NATIVE_FUNC(functions, teleportNear_fffc_);
3412 REGISTER_NATIVE_FUNC(functions, setScenarioPoints_ff_);
3413 REGISTER_NATIVE_FUNC(functions, startScenarioTiming_f_);
3414 REGISTER_NATIVE_FUNC(functions, endScenarioTiming_f_);
3416 REGISTER_NATIVE_FUNC(functions, maxHitRange_f_);
3418 REGISTER_NATIVE_FUNC(functions, setEventCode_sss_);
3419 REGISTER_NATIVE_FUNC(functions, setParent_s_);
3420 REGISTER_NATIVE_FUNC(functions, addHealGroup_s_);
3422 REGISTER_NATIVE_FUNC(functions, setEquipment_s_);
3423 REGISTER_NATIVE_FUNC(functions, addUserModel_sss_);
3424 REGISTER_NATIVE_FUNC(functions, addCustomLoot_ss_);
3425 REGISTER_NATIVE_FUNC(functions, setUserModel_s_);
3426 REGISTER_NATIVE_FUNC(functions, setCustomLoot_s_);
3428 REGISTER_NATIVE_FUNC(functions, spawnGroup_ffsssffff_);
3429 REGISTER_NATIVE_FUNC(functions, spawnGroup_ffsssffff_c);
3431 // REGISTER_NATIVE_FUNC(functions, hideMissionStepIcon_b_);
3432 // REGISTER_NATIVE_FUNC(functions, hideMissionGiverIcon_b_);
3435 #undef REGISTER_NATIVE_FUNC
3437 return functions;