Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / mirrors.cpp
bloba7306f979e7ae7ab7eea0aa72e269d09f517e82c
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) 2020 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/>.
22 #include "stdpch.h"
24 #include "mirrors.h"
25 #include "game_share/tick_event_handler.h"
26 #include "nel/net/service.h"
27 #include "game_share/misc_const.h"
28 #include "game_share/fame.h"
29 #include "ai_bot_npc.h"
30 #include "ai_grp_npc.h"
31 #include "server_share/r2_variables.h"
32 #include "server_share/r2_vision.h"
35 #include "ai.h"
36 #include "ai_player.h"
38 using namespace std;
39 using namespace NLMISC;
40 using namespace NLNET;
41 using namespace MBEHAV;
43 CMirror CMirrors::Mirror;
44 CMirroredDataSet *CMirrors::DataSet = NULL;
45 CMirroredDataSet *CMirrors::FameDataSet = NULL;
47 const uint MAX_NB_ENTITIES_ISOLATED = 25000;
48 CEntityId *IsolatedEntityId = NULL;
49 sint32 *IsolatedX = NULL;
50 sint32 *IsolatedY = NULL;
51 sint32 *IsolatedZ = NULL;
52 float *IsolatedTheta = NULL;
53 uint32 *IsolatedSheet = NULL;
54 uint32 *IsolatedSheetServer = NULL;
55 TDataSetRow *IsolatedTarget = NULL; // sint32
56 uint32 *IsolatedMode = NULL; // only mode enum
57 uint32 *IsolatedBehaviour = NULL;
58 uint32 *IsolatedCurrentHitPoints = NULL;
59 uint32 *IsolatedMaxHitPoints = NULL;
60 uint32 *IsolatedBestRoleLevel = NULL;
61 uint8 *IsolatedCombatState = NULL;
63 extern CVariable<double> RingMaxSelectDist;
66 * Initialisation 2
68 void cbMirrorIsReady( CMirror *mirror )
70 CMirrors::initMirror();
73 void cbMirrorReadyForAddEntity( CMirror *mirror )
76 // Init fx manager
77 const TDeclaredEntityRangeOfType& declERT = TheDataset.getDeclaredEntityRanges();
79 TDeclaredEntityRangeOfType::const_iterator it = declERT.find( RYZOMID::fx_entity );
80 const TDeclaredEntityRange& range = GET_ENTITY_TYPE_RANGE(it);
81 CFxEntityManager::getInstance()->init( range.baseIndex(), range.size() );
86 #define initIsolatedPropTable( name, thetype, defaultvalue ) \
87 name = new thetype [MAX_NB_ENTITIES_ISOLATED]; \
88 for ( i=0; i!=MAX_NB_ENTITIES_ISOLATED; ++i ) \
89 name[i] = defaultvalue
92 * Initialisation 1
94 void CMirrors::init( void (*cbUpdate)(), void (*cbSync)(), void (*cbRelease)() )
96 // Init the mirror system
97 vector<string> datasetNames;
98 datasetNames.push_back( "fe_temp" );
99 datasetNames.push_back( "fame" );
100 Mirror.init( datasetNames, cbMirrorIsReady, cbUpdate, cbSync, cbRelease, AISTag );
101 Mirror.addCallbackWhenMirrorReadyForUse( cbMirrorReadyForAddEntity );
106 * Init after the mirror init
108 void CMirrors::initMirror()
110 //Mirror.declareEntityTypeUser( RYZOMID::player );
112 Mirror.declareEntityTypeOwner( RYZOMID::npc, TotalMaxNpc.get() );
113 Mirror.declareEntityTypeOwner( RYZOMID::creature, TotalMaxFauna.get()+TotalMaxPet.get() );
114 Mirror.declareEntityTypeOwner( RYZOMID::fx_entity, TotalMaxFx );
116 DataSet = &(Mirror.getDataSet("fe_temp"));
117 DataSet->declareProperty( "AIInstance", PSOReadWrite | PSONotifyChanges );
118 DataSet->declareProperty( "X", PSOReadWrite | PSONotifyChanges );
119 DataSet->declareProperty( "Y", PSOReadWrite | PSONotifyChanges );
120 DataSet->declareProperty( "Z", PSOReadWrite );
121 DataSet->declareProperty( "Theta", PSOReadWrite );
122 DataSet->declareProperty( "Sheet", PSOReadWrite );
123 DataSet->declareProperty( "SheetServer", PSOReadWrite );
124 DataSet->declareProperty( "NPCAlias", PSOWriteOnly );
125 DataSet->declareProperty( "Mode", PSOReadWrite | PSONotifyChanges );
126 DataSet->declareProperty( "Behaviour", PSOReadWrite | PSONotifyChanges );
127 DataSet->declareProperty( "Target", PSOReadWrite | PSONotifyChanges );
128 DataSet->declareProperty( "CurrentHitPoints", PSOReadOnly );
129 DataSet->declareProperty( "CurrentRunSpeed", PSOReadOnly );
130 DataSet->declareProperty( "CurrentWalkSpeed", PSOReadOnly );
131 DataSet->declareProperty( "MaxHitPoints", PSOReadOnly );
132 DataSet->declareProperty( "BestRoleLevel", PSOReadOnly );
133 DataSet->declareProperty( "CombatState", PSOReadOnly );
134 DataSet->declareProperty( "TeamId", PSOReadOnly | PSONotifyChanges );
135 DataSet->declareProperty( "InOutpostZoneAlias", PSOReadWrite );
136 DataSet->declareProperty( "InOutpostZoneSide", PSOReadWrite );
137 DataSet->declareProperty( "VisualPropertyA", PSOReadWrite );
138 DataSet->declareProperty( "VisualPropertyB", PSOReadWrite );
139 DataSet->declareProperty( "VisualPropertyC", PSOReadWrite );
140 DataSet->declareProperty( "ActionFlags", PSOReadWrite );
141 DataSet->declareProperty( "VisionCounter", PSOReadOnly );
142 DataSet->declareProperty( "Fuel", PSOReadOnly );
143 DataSet->declareProperty( "WhoSeesMe", PSOReadWrite );
144 DataSet->declareProperty( "ContextualProperty", PSOReadWrite );
146 initRyzomVisualPropertyIndices( *DataSet );
148 // init fame dataset
149 FameDataSet = &(Mirror.getDataSet("fame"));
150 CFameInterface::getInstance().setFameDataSet(FameDataSet);
152 Mirror.setNotificationCallback( CMirrors::processMirrorUpdates );
155 // a big bad global var !
156 extern CAIEntityPhysical *TempSpeaker;
157 extern CBotPlayer *TempPlayer;
160 void CMirrors::processMirrorUpdates()
162 TDataSetRow entityIndex;
163 CEntityId *pEntityId;
165 CFameInterface &fi = CFameInterface::getInstance();
167 // Process added entities
168 DataSet->beginAddedEntities();
169 while ((entityIndex = DataSet->getNextAddedEntity()) != LAST_CHANGED)
171 const CEntityId& entityId = DataSet->getEntityId(entityIndex);
173 // Create CAIPlayer object and add to maps
174 if (entityId.getType() != RYZOMID::player)
175 continue;
178 CMirrorPropValue<uint32> _instanceNumber(*DataSet, entityIndex, DSPropertyAI_INSTANCE);
179 const uint32 askedInstance=_instanceNumber();
180 CAIInstance *const aii = CAIS::instance().getAIInstance(askedInstance);
182 if (!aii)
184 // The player is not in an AIInstance of that ais.
185 // Floods on player connections, so no warning
186 // nlwarning("AIInstance %d not found to spawn player %s", askedInstance, entityId.toString().c_str());
187 // FOREACH(it,CCont<CAIInstance>, CAIS::instance().AIList())
188 // {
189 // nlwarning("exist AIInstance %d at index %d", (*it)->getInstanceNumber(), (*it)->getIndex());
190 // }
191 continue;
193 // store the player in the correct instance.
194 aii->getPlayerMgr()->addSpawnedPlayer(entityIndex, entityId);
198 DataSet->endAddedEntities();
200 FameDataSet->beginAddedEntities();
201 while( (entityIndex = FameDataSet->getNextAddedEntity()) != LAST_CHANGED )
203 fi.createFameOwner(*FameDataSet, entityIndex);
205 FameDataSet->endAddedEntities();
207 // Process removed entities
208 DataSet->beginRemovedEntities();
209 while( (entityIndex = DataSet->getNextRemovedEntity(&pEntityId)) != LAST_CHANGED )
211 CAIEntityPhysical *entityPhysPtr = CAIS::instance().getEntityPhysical(entityIndex);
212 // just to check if its a player .. and remove it from Manager and Maps ..
213 if ( entityPhysPtr
214 && entityPhysPtr->getRyzomType()==RYZOMID::player)
216 CBotPlayer *botPlayer=NLMISC::safe_cast<CBotPlayer*>(entityPhysPtr);
217 botPlayer->getOwner()->removeDespawnedPlayer(entityIndex);
221 DataSet->endRemovedEntities();
223 FameDataSet->beginRemovedEntities();
224 while( (entityIndex = FameDataSet->getNextRemovedEntity(&pEntityId)) != LAST_CHANGED )
226 fi.removeFameOwner(*FameDataSet, entityIndex);
228 FameDataSet->endRemovedEntities();
230 // Process properties changed and notified in the mirror
231 TPropertyIndex propIndex;
232 DataSet->beginChangedValues();
233 DataSet->getNextChangedValue( entityIndex, propIndex );
234 while (entityIndex != LAST_CHANGED)
236 if (propIndex == DSPropertyPOSX || propIndex == DSPropertyPOSY )
238 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
239 if (entityId.getType() == RYZOMID::player)
241 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
242 if (entityPhys)
244 CBotPlayer *player=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
245 if (player)
247 CAIInstance* aiInstance = player->getAIInstance();
248 aiInstance->updateZoneTrigger(player);
253 else if (propIndex == DSPropertyTEAM_ID)
255 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
256 if (entityId.getType() == RYZOMID::player)
258 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
259 if (entityPhys)
261 CBotPlayer *player=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
262 if (player)
263 player->getOwner()->updatePlayerTeam(entityIndex);
269 else if (propIndex == DSPropertyAI_INSTANCE)
271 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
272 if (entityId.getType() == RYZOMID::player)
274 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
275 if (entityPhys)
277 // this player is already in a manager, despawn it
278 CBotPlayer *const botPlayer=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
279 botPlayer->getOwner()->removeDespawnedPlayer(entityIndex);
282 // could be done with mirrored values. (!?).
283 CMirrorPropValue<uint32> _instanceNumber(*DataSet, entityIndex, DSPropertyAI_INSTANCE);
284 const uint32 askedInstance=_instanceNumber();
286 CAIInstance *aii = CAIS::instance().getAIInstance(askedInstance);
288 if (aii!=NULL)
290 // store the player in the correct instance.
291 aii->getPlayerMgr()->addSpawnedPlayer(entityIndex, entityId);
293 else
295 if (askedInstance!=std::numeric_limits<uint32>::max())
297 // no need to warn for ai number instance not in this AIS !
298 // nlwarning("AIInstance %u not found on AIInstance changement for player %s", askedInstance, entityId.toString().c_str());
299 // FOREACH(it,CCont<CAIInstance>, CAIS::instance().AIList())
300 // {
301 // nlwarning("exist AIInstance %d at index %d", (*it)->getInstanceNumber(), (*it)->getIndex());
302 // }
307 else if (propIndex == DSPropertyTARGET_ID)
309 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
310 if (entityPhys)
312 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
313 if (entityId.getType() == RYZOMID::player)
315 CBotPlayer *player=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
317 // check if target is a local npc bot then handle bot targeted event
318 CMirrorPropValueRO<TYPE_TARGET_ID> tgt(TheDataset, entityIndex, DSPropertyTARGET_ID);
319 CAIEntityPhysical *target = CAIS::instance().getEntityPhysical(tgt());
321 // update the targering list
322 if (entityIndex==TDataSetRow()) // no target.
324 player->setTarget(NULL);
326 else
328 if (player->isAggressive())
330 CAIEntityPhysical *oldTarget=player->getTarget();
331 if ( !oldTarget
332 || oldTarget!=target )
333 player->setTarget(target);
335 else
337 CAIEntityPhysical *oldTarget=player->getVisualTarget();
338 if ( !oldTarget
339 || oldTarget!=target )
340 player->setVisualTarget(target);
344 CSpawnBotNpc *bnpc = dynamic_cast<CSpawnBotNpc *>(target);
345 if (bnpc)
347 TempSpeaker = bnpc;
348 TempPlayer = player;
349 CGroupNpc &grpNpc = bnpc->getPersistent().grp();
353 if (IsRingShard)
355 CAIPos playerPos(player->pos());
356 CAIPos npcPos(bnpc->pos());
357 double dist = npcPos.quickDistTo(playerPos);
358 bool dm = false;
360 uint64 whoSeesMe = CMirrors::whoSeesMe(player->dataSetRow());
361 if (!R2_VISION::isEntityVisibleToPlayers(whoSeesMe))
363 // The player is invisible, its a Dm / Gm: no zone event must be triggered
364 dm = true;
367 if (!dm && dist <= RingMaxSelectDist)
369 grpNpc.processStateEvent(grpNpc.getEventContainer().EventPlayerTargetNpc);
372 else
374 // generate en event on this bot group
375 grpNpc.processStateEvent(grpNpc.getEventContainer().EventPlayerTargetNpc);
380 // grpNpc.processStateEvent(grpNpc.getEventContainer().EventPlayerTargetNpc);
381 // grpNpc.getEventContainer().EventPlayerTargetNpc.processStateEvent(&grpNpc);
383 // if player is in follow mode, then generate an suplementary event
384 if (player->getFollowMode())
385 grpNpc.processStateEvent(grpNpc.getEventContainer().EventPlayerFollowNpc);
386 // grpNpc.getEventContainer().EventPlayerFollowNpc.processStateEvent(&grpNpc);
388 TempPlayer = NULL;
389 TempSpeaker = NULL;
394 else if (propIndex == DSPropertyMODE)
396 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
397 if (entityPhys)
399 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
400 if (entityId.getType() == RYZOMID::player)
402 CBotPlayer *player=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
404 // if the player have a target, move it to the correct target list.
406 if (player->isAggressive())
408 CAIEntityPhysical *target=player->getVisualTarget();
409 if (target)
410 player->setTarget(target);
412 else
414 CAIEntityPhysical *target=player->getTarget();
415 if (target)
416 player->setVisualTarget(target);
421 /* else if (propIndex == DSPropertyBEHAVIOUR)
423 CAIEntityPhysical *entityPhys=CAIS::instance().getEntityPhysical(entityIndex);
424 if (entityPhys)
426 const CEntityId &entityId=DataSet->getEntityId(entityIndex);
427 if (entityId.getType() == RYZOMID::player)
429 CBotPlayer *player=NLMISC::safe_cast<CBotPlayer*>(entityPhys);
431 MBEHAV::EBehaviour const behaviour = player->getBehaviour();
432 if( behaviour == MBEHAV::CAST_OFF_SUCCESS );
434 CAIEntityPhysical *target=player->getVisualTarget();
435 if (target)
436 player->setTarget(target);
441 */ DataSet->getNextChangedValue( entityIndex, propIndex );
443 DataSet->endChangedValues();
446 void CMirrors::release()
448 Mirror.release();
452 bool CMirrors::mirrorIsReady()
454 return Mirror.mirrorIsReady();
458 //---------------------------------------------------------------------------------
459 // Methods for retrieving data from mirrors
461 TDataSetRow CMirrors::createEntity( CEntityId& entityId )
463 // in ais, we always use entityId auto asigment by mirror
464 if (Mirror.createEntity( entityId , true))
465 return DataSet->getDataSetRow( entityId );
466 else
467 return TDataSetRow();
471 void CMirrors::declareEntity( const TDataSetRow& entityIndex )
473 DataSet->declareEntity( entityIndex ); // only in the main dataset
477 void CMirrors::removeEntity( const CEntityId& entityId )
479 Mirror.removeEntity( entityId );
483 bool CMirrors::exists( const TDataSetRow& entityIndex )
485 /// is that a bug??
486 return DataSet->getEntityId( entityIndex ).asUint64() == 0;
487 //return ! ((uint64)(DataSet->getEntityId( entityIndex )) != 0);
490 const NLMISC::CEntityId& CMirrors::getEntityId( const TDataSetRow& entityIndex )
492 return DataSet->getEntityId( entityIndex );
495 TDataSetRow CMirrors::getDataSetRow( const NLMISC::CEntityId& entityId )
497 return DataSet->getDataSetRow( entityId );
500 uint16 CMirrors::getTeamId(const TDataSetRow& entityIndex)
502 CMirrorPropValueRO<TYPE_TEAM_ID> value( *DataSet, entityIndex, DSPropertyTEAM_ID );
503 // if ( value()==0 )
504 // return CTEAM::InvalidTeamId;
505 return value;
510 CAICoord CMirrors::x( const TDataSetRow& entityIndex )
512 CMirrorPropValueRO<TYPE_POSX> value( *DataSet, entityIndex, DSPropertyPOSX );
513 if ( value()==0 )
514 return CAICoord();
515 return CAICoord((double)value()/(double)1000);
518 CAICoord CMirrors::y( const TDataSetRow& entityIndex )
520 CMirrorPropValueRO<TYPE_POSY> value( *DataSet, entityIndex, DSPropertyPOSY );
521 if ( value()==0 )
522 return CAICoord();
523 return CAICoord((double)value()/(double)1000);
526 sint32 CMirrors::z( const TDataSetRow& entityIndex )
528 CMirrorPropValueRO<TYPE_POSZ> value( *DataSet, entityIndex, DSPropertyPOSZ );
529 return value();
532 float CMirrors::theta( const TDataSetRow& entityIndex )
534 CMirrorPropValueRO<TYPE_ORIENTATION> value( *DataSet, entityIndex, DSPropertyORIENTATION );
535 return value();
539 void CMirrors::setPosAndTheta( const TDataSetRow& entityIndex, sint32 posX, sint32 posY, sint32 posZ, float angleRad )
541 #ifdef NL_DEBUG
542 //nldebug( "%d: E%d: Setting pos to %d %d", CTickEventHandler::getGameCycle(), entityIndex, posX, posY );
543 /*if ( posX == 0 )
544 nldebug( "E%d: X is zero", entityIndex );
545 if ( posY == 0 )
546 nldebug( "E%d: Y is zero", entityIndex );*/
547 #endif
549 CMirrorPropValue<TYPE_POSX> valueX( *DataSet, entityIndex, DSPropertyPOSX );
550 CMirrorPropValue<TYPE_POSY> valueY( *DataSet, entityIndex, DSPropertyPOSY );
551 CMirrorPropValue<TYPE_POSZ> valueZ( *DataSet, entityIndex, DSPropertyPOSZ );
552 CMirrorPropValue<TYPE_ORIENTATION> valueT( *DataSet, entityIndex, DSPropertyORIENTATION );
553 valueX = posX;
554 valueY = posY;
555 valueZ = posZ;
556 valueT = angleRad;
557 //nldebug( "%u: Pos&theta of E%d set", CTickEventHandler::getGameCycle(), entityIndex );
561 void CMirrors::setTheta( const TDataSetRow& entityIndex, float angleRad )
563 CMirrorPropValue<TYPE_ORIENTATION> valueT( *DataSet, entityIndex, DSPropertyORIENTATION );
564 valueT = angleRad;
565 //nldebug( "%u: Theta of E%d set", CTickEventHandler::getGameCycle(), entityIndex );
569 NLMISC::CSheetId CMirrors::sheet( const TDataSetRow& entityIndex )
571 CMirrorPropValueRO<TYPE_SHEET> value( *DataSet, entityIndex, DSPropertySHEET );
572 return NLMISC::CSheetId(value());
575 void CMirrors::initSheet( const TDataSetRow& entityIndex, const NLMISC::CSheetId& sheetId )
577 CMirrorPropValue<TYPE_SHEET> value( *DataSet, entityIndex, DSPropertySHEET );
578 value = sheetId.asInt();
581 NLMISC::CSheetId CMirrors::sheetServer( const TDataSetRow& entityIndex )
583 CMirrorPropValueRO<TYPE_SHEET> value( *DataSet, entityIndex, DSPropertySHEET_SERVER );
584 return NLMISC::CSheetId(value());
587 void CMirrors::initSheetServer( const TDataSetRow& entityIndex, const NLMISC::CSheetId& sheetId )
589 nlassert(sheetId != NLMISC::CSheetId::Unknown);
590 CMirrorPropValue<TYPE_SHEET> value( *DataSet, entityIndex, DSPropertySHEET_SERVER );
591 value = sheetId.asInt();
594 void CMirrors::initNPCAlias( const TDataSetRow& entityIndex, TAIAlias alias )
596 CMirrorPropValue<TYPE_ALIAS> value( *DataSet, entityIndex, DSPropertyNPC_ALIAS );
597 value = alias;
600 TDataSetRow CMirrors::target( const TDataSetRow& entityIndex )
602 CMirrorPropValueRO<TYPE_TARGET_ID> value( *DataSet, entityIndex, DSPropertyTARGET_ID );
603 return value();
606 void CMirrors::setVPA( const TDataSetRow& entityIndex, const SAltLookProp &prop )
608 CMirrorPropValue<SAltLookProp> value( *DataSet, entityIndex, DSPropertyVPA );
609 value = prop;
612 void CMirrors::setVisualPropertyA( const TDataSetRow& entityIndex, const SPropVisualA &prop )
614 CMirrorPropValue<SPropVisualA> value( *DataSet, entityIndex, DSPropertyVPA );
615 value = prop;
618 void CMirrors::setVisualPropertyB( const TDataSetRow& entityIndex, const SPropVisualB &prop )
620 CMirrorPropValue<SPropVisualB> value( *DataSet, entityIndex, DSPropertyVPB );
621 value = prop;
624 void CMirrors::setVisualPropertyC( const TDataSetRow& entityIndex, const SPropVisualC &prop )
626 CMirrorPropValue<SPropVisualC> value( *DataSet, entityIndex, DSPropertyVPC );
627 value = prop;
630 void CMirrors::setBehaviour( const TDataSetRow& entityIndex, MBEHAV::EBehaviour b )
632 CMirrorPropValue<MBEHAV::CBehaviour> value( *DataSet, entityIndex, DSPropertyBEHAVIOUR );
633 value = b;
636 uint32 CMirrors::bestRoleLevel( const TDataSetRow& entityIndex )
638 CMirrorPropValueRO<TYPE_BEST_ROLE_LEVEL> value( *DataSet, entityIndex, DSPropertyBEST_ROLE_LEVEL );
639 return value();
642 uint64 CMirrors::whoSeesMe( const TDataSetRow& entityIndex )
644 BOMB_IF( !DataSet->isAccessible(entityIndex), "Try to access to the WhoSeesMe property in the mirror but with an invalid ", return 0L);
645 CMirrorPropValueRO<TYPE_WHO_SEES_ME> value( *DataSet, entityIndex, DSPropertyWHO_SEES_ME );
646 return value();
650 * Set target (row)
652 void CMirrors::setTarget(const TDataSetRow& entityIndex, const TDataSetRow& target)
654 CMirrorPropValue<TYPE_TARGET_ID> value( *DataSet, entityIndex, DSPropertyTARGET_ID );
655 value = target;
658 /// :KLUDGE: This method implementation should be in game_share instead of AIS and EGS. Both me and the other one are lazy bastards.
659 void MBEHAV::TMode::setModeAndPos( EMode mode, const TDataSetRow& entityIndex )
661 Mode = mode;
662 CMirrorPropValueRO<TYPE_POSX> propX( TheDataset, entityIndex, DSPropertyPOSX );
663 Pos.X16 = (uint16)(propX() >> 4);
664 CMirrorPropValueRO<TYPE_POSY> propY( TheDataset, entityIndex, DSPropertyPOSY );
665 Pos.Y16 = (uint16)(propY() >> 4);
666 //nldebug( "Setting MODE %s for E%u with current pos %d, %d", modeToString( mode ).c_str(), entityIndex.getIndex(), propX(), propY() );
669 #include "event_reaction_include.h"