1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
24 #include <nel/misc/types_nl.h>
29 #include <nel/misc/types_nl.h>
30 #include <nel/misc/event_listener.h>
31 #include <nel/misc/command.h>
32 #include <nel/misc/log.h>
33 #include <nel/misc/time_nl.h>
34 #include <nel/misc/displayer.h>
35 #include <nel/misc/vector.h>
36 #include <nel/misc/vectord.h>
37 #include <nel/misc/time_nl.h>
39 #include <nel/3d/u_camera.h>
40 #include <nel/3d/u_driver.h>
41 #include <nel/3d/u_text_context.h>
42 #include <nel/3d/u_instance.h>
43 #include <nel/3d/u_scene.h>
44 #include <nel/3d/u_material.h>
45 #include <nel/3d/u_landscape.h>
46 #include <nel/3d/u_skeleton.h>
48 #include <nel/3d/u_visual_collision_manager.h>
49 #include <nel/3d/u_visual_collision_entity.h>
51 #include <nel/pacs/u_move_container.h>
52 #include <nel/pacs/u_move_primitive.h>
53 #include <nel/pacs/u_global_retriever.h>
54 #include <nel/pacs/u_global_position.h>
56 #include "snowballs_client.h"
59 #include "animation.h"
64 #include "mouse_listener.h"
65 #include "landscape.h"
72 using namespace NLMISC
;
74 using namespace NLPACS
;
82 // A map of entities. All entities are later referred by their unique id
83 map
<uint32
, CEntity
> Entities
;
87 // The size of the world, in meter
88 float WorldWidth
= 20*160;
89 float WorldHeight
= 6*160;
91 // Entity Id, only used offline
92 uint32 NextEID
= 1000000;
95 float PlayerSpeed
= 10.0f
; // 6.5 km/h
96 float SnowballSpeed
= 15.0f
; // 36 km/h
98 // these variables are set with the config file
100 // Setup for the name up the character
101 float EntityNameSize
;
102 CRGBA EntityNameColor
;
104 bool _TestCLS
= false;
107 // Set the state of the entity (Appear, Normal, Disappear)
108 void CEntity::setState (TState state
)
111 StateStartTime
= LocalTime
;
115 // Get an map iterator on a entity, specified by its id
116 EIT
findEntity (uint32 eid
, bool needAssert
)
118 EIT entity
= Entities
.find (eid
);
119 if (entity
== Entities
.end () && needAssert
)
121 nlerror ("Entity %u not found", eid
);
126 // -- -- things like Creature, Effect, Scenery seem more flexible than Self, Other, Snowball
127 // -- -- random keywords: entitybehavior (animations), entityinteraction (targetable, menu, )
128 // Creates an entity, given its id, its type (Self, Other, Snowball), its start and server positions.
129 void addEntity (uint32 eid
, std::string name
, CEntity::TType type
, const CVector
&startPosition
, const CVector
&serverPosition
)
131 // nlinfo ("adding entity %u", eid);
133 // Check that the entity doesn't exist yet
134 EIT eit
= findEntity (eid
, false);
135 if (eit
!= Entities
.end ())
137 nlerror ("Entity %d already exist", eid
);
140 // Create a new entity
141 eit
= (Entities
.insert (make_pair (eid
, CEntity()))).first
;
142 CEntity
&entity
= (*eit
).second
;
144 // Check that in the case the entity newly created is a Self, there's not a Self yet.
145 if (type
== CEntity::Self
)
148 nlerror("Self entity already created");
157 entity
.Position
= startPosition
;
159 entity
.ServerPosition
= serverPosition
;
160 entity
.VisualCollisionEntity
= VisualCollisionManager
->createEntity();
162 // setup the move primitive and the mesh instance depending on the type of entity
166 // create a move primitive associated to the entity
167 entity
.MovePrimitive
= MoveContainer
->addCollisionablePrimitive(0, 1);
169 entity
.MovePrimitive
->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder
);
170 // the entity should slide against obstacles
171 entity
.MovePrimitive
->setReactionType(UMovePrimitive::Slide
);
172 // do not generate event if there is a collision
173 entity
.MovePrimitive
->setTriggerType(UMovePrimitive::NotATrigger
);
174 // which entity should collide against me
175 entity
.MovePrimitive
->setCollisionMask(OtherCollisionBit
+SnowballCollisionBit
+StaticCollisionBit
);
176 // the self collision bit
177 entity
.MovePrimitive
->setOcclusionMask(SelfCollisionBit
);
178 // the self is an obstacle
179 entity
.MovePrimitive
->setObstacle(true);
180 // the size of the cylinder
181 entity
.MovePrimitive
->setRadius(1.0f
);
182 entity
.MovePrimitive
->setHeight(1.8f
);
183 // only use one world image, so use insert in world image 0
184 entity
.MovePrimitive
->insertInWorldImage(0);
185 // retrieve the start position of the entity
186 entity
.MovePrimitive
->setGlobalPosition(CVectorD(startPosition
.x
, startPosition
.y
, startPosition
.z
), 0);
188 // create instance of the mesh character
189 entity
.Instance
= Scene
->createInstance ("gnu.shape");
190 entity
.Skeleton
= Scene
->createSkeleton ("gnu.skel");
191 // use the instance on the skeleton
192 entity
.Skeleton
.bindSkin (entity
.Instance
);
194 // Allow the skeleton to cast shadows.
195 entity
.Skeleton
.enableCastShadowMap(true);
197 entity
.Instance
.hide ();
199 entity
.Angle
= MouseListener
->getOrientation();
201 // setup final parameters
202 entity
.Speed
= PlayerSpeed
;
203 entity
.Particule
= Scene
->createInstance ("appear.ps");
204 entity
.setState (CEntity::Appear
);
205 playAnimation (entity
, LogInAnim
);
206 playAnimation (entity
, IdleAnim
);
210 entity
.MovePrimitive
= MoveContainer
->addCollisionablePrimitive(0, 1);
211 entity
.MovePrimitive
->setPrimitiveType(UMovePrimitive::_2DOrientedCylinder
);
212 entity
.MovePrimitive
->setReactionType(UMovePrimitive::Slide
);
213 entity
.MovePrimitive
->setTriggerType(UMovePrimitive::NotATrigger
);
214 entity
.MovePrimitive
->setCollisionMask(OtherCollisionBit
+SelfCollisionBit
+SnowballCollisionBit
);
215 entity
.MovePrimitive
->setOcclusionMask(OtherCollisionBit
);
216 entity
.MovePrimitive
->setObstacle(true);
217 entity
.MovePrimitive
->setRadius(1.0f
);
218 entity
.MovePrimitive
->setHeight(1.8f
);
219 entity
.MovePrimitive
->insertInWorldImage(0);
220 entity
.MovePrimitive
->setGlobalPosition(CVectorD(startPosition
.x
, startPosition
.y
, startPosition
.z
), 0);
222 entity
.Instance
= Scene
->createInstance ("gnu.shape");
223 entity
.Skeleton
= Scene
->createSkeleton ("gnu.skel");
224 entity
.Skeleton
.bindSkin (entity
.Instance
);
225 entity
.Instance
.hide ();
227 entity
.Speed
= PlayerSpeed
;
228 entity
.Particule
= Scene
->createInstance ("appear.ps");
229 entity
.setState (CEntity::Appear
);
230 playAnimation (entity
, LogInAnim
);
231 playAnimation (entity
, IdleAnim
);
234 case CEntity::Snowball
:
235 entity
.MovePrimitive
= NULL
;
237 // allows collision snapping to the ceiling
238 entity
.VisualCollisionEntity
->setCeilMode(true);
240 entity
.Instance
= Scene
->createInstance ("snowball.shape");
241 entity
.Skeleton
= NULL
;
242 entity
.Speed
= SnowballSpeed
;
245 //#ifdef NL_OS_WINDOWS
246 // playSound (entity, SoundId);
248 entity
.setState (CEntity::Normal
);
252 if (!entity
.Skeleton
.empty())
253 entity
.Skeleton
.setPos (startPosition
);
255 entity
.Instance
.setPos (startPosition
);
258 // if (entity.Source != NULL)
259 // entity.Source->setPosition (startPosition);
261 if (!entity
.Particule
.empty())
262 entity
.Particule
.setPos (startPosition
);
266 // effectively remove the entity (don't play animation)
267 void deleteEntity (CEntity
&entity
)
269 if (!entity
.Particule
.empty())
271 Scene
->deleteInstance (entity
.Particule
);
272 entity
.Particule
= NULL
;
275 deleteAnimation (entity
);
277 if (!entity
.Skeleton
.empty())
279 entity
.Skeleton
.detachSkeletonSon (entity
.Instance
);
280 Scene
->deleteSkeleton (entity
.Skeleton
);
281 entity
.Skeleton
= NULL
;
284 if (!entity
.Instance
.empty())
286 Scene
->deleteInstance (entity
.Instance
);
287 entity
.Instance
= NULL
;
290 if (entity
.VisualCollisionEntity
!= NULL
)
292 VisualCollisionManager
->deleteEntity (entity
.VisualCollisionEntity
);
293 entity
.VisualCollisionEntity
= NULL
;
296 if (entity
.MovePrimitive
!= NULL
)
298 MoveContainer
->removePrimitive(entity
.MovePrimitive
);
299 entity
.MovePrimitive
= NULL
;
302 //#ifdef NL_OS_WINDOWS
303 // deleteSound (entity);
306 // nlinfo ("Remove the entity %u from the Entities list", entity.Id);
307 EIT eit
= findEntity (entity
.Id
);
308 Entities
.erase (eit
);
312 // Remove an entity specified by its id
313 // The entity passes into the Disappear state
314 void removeEntity (uint32 eid
)
316 // nlinfo ("removing entity %u", eid);
318 // look for the entity
319 EIT eit
= findEntity (eid
);
320 CEntity
&entity
= (*eit
).second
;
322 // if there is a particle system linked, delete it
323 if (!entity
.Particule
.empty())
325 Scene
->deleteInstance (entity
.Particule
);
326 entity
.Particule
= NULL
;
329 // if (entity.Type == CEntity::Other)
332 entity
.Particule
= Scene
->createInstance("disappear.ps");
333 entity
.Particule
.setPos (entity
.Position
);
336 playAnimation (entity
, LogOffAnim
, true);
337 entity
.setState (CEntity::Disappear
);
340 void removeAllEntitiesExceptUs ()
345 for (eit
= Entities
.begin (); eit
!= Entities
.end (); )
347 nexteit
= eit
; nexteit
++;
349 CEntity
&entity
= (*eit
).second
;
351 if (entity
.Type
!= CEntity::Self
)
353 // effectively remove the entity (don't play animation)
354 deleteEntity (entity
);
361 void deleteAllEntities()
364 for (eit
= Entities
.begin(); eit
!= Entities
.end(); )
366 nexteit
= eit
; nexteit
++;
367 CEntity
&entity
= (*eit
).second
;
368 deleteEntity (entity
);
378 // What to do when the entity appears
379 void stateAppear (CEntity
&entity
)
381 // after 1 second, show the instance
382 if (LocalTime
> entity
.StateStartTime
+ 1.0)
384 if (entity
.Instance
.getVisibility () != UTransform::Show
)
385 entity
.Instance
.show ();
388 // after 5 seconds, delete the particle system (if any)
389 // and pass the entity into the Normal state
390 if (LocalTime
> entity
.StateStartTime
+ 3.0)
392 if (!entity
.Particule
.empty())
394 Scene
->deleteInstance (entity
.Particule
);
395 entity
.Particule
= NULL
;
398 entity
.setState (CEntity::Normal
);
401 if (entity
.MovePrimitive
!= NULL
)
402 entity
.MovePrimitive
->move(CVector(0,0,0), 0);
405 // What to do when the entity disappears
406 void stateDisappear (CEntity
&entity
)
408 // after 1 second, remove the mesh and all collision stuff
409 if (LocalTime
> entity
.StateStartTime
+ 1.0)
411 if (entity
.Instance
.getVisibility () != UTransform::Hide
)
413 if (entity
.Type
== CEntity::Self
)
416 nlerror("Self entity doesn't exist");
420 entity
.Instance
.hide ();
424 // after 5 seconds, remove the particle system and the entity entry
425 if (LocalTime
> entity
.StateStartTime
+ 3.0)
427 deleteEntity (entity
);
431 if (entity
.MovePrimitive
!= NULL
)
432 entity
.MovePrimitive
->move(CVector(0,0,0), 0);
436 void stateNormal (CEntity
&entity
)
438 double dt
= LocalTimeDelta
;
442 oldPos
= entity
.Position
;
443 CVector pDelta
= entity
.Position
- entity
.ServerPosition
;
444 CVector pDeltaOri
= pDelta
;
447 // -- -- simple random bots =) share with server
449 // find a new random server position
450 if (entity
.Type
== CEntity::Other
&& entity
.AutoMove
)
452 switch (entity
.BotState
)
455 // choose something to do
456 if (frand(1.0f
) > 0.5f
)
463 if (pDelta
.norm() < 0.1f
|| LocalTime
- entity
.BotStateStart
> 3.000)
470 // entity.IsWalking = true;
471 // entity.IsAiming = false;
476 entity
.IsWalking
= false;
477 entity
.IsAiming
= true;
478 entity
.BotStateStart
= LocalTime
;
483 entity
.IsWalking
= false;
484 entity
.IsAiming
= true;
485 if (LocalTime
- entity
.BotStateStart
> 1.000)
488 entity
.BotStateStart
= LocalTime
;
489 CVector AimingPosition
= entity
.Position
+CVector(0.0f
, 0.0f
, 2.0f
);
490 CVector direction
= CVector((float)(cos(entity
.Angle
)), (float)(sin(entity
.Angle
)), 0.3f
).normed();
491 CVector AimedTarget
= getTarget(AimingPosition
,
494 shotSnowball(NextEID
++, entity
.Id
, AimingPosition
, AimedTarget
, SnowballSpeed
, 3.0f
);
499 entity
.IsWalking
= false;
500 entity
.IsAiming
= false;
501 if (LocalTime
- entity
.BotStateStart
> 1.000)
504 entity
.BotStateStart
= LocalTime
;
508 // choose a direction
509 float EntityMaxSpeed
= 10.0f
;
510 entity
.AuxiliaryAngle
+= frand(1.5f
)-0.75f
;
511 entity
.ServerPosition
+= CVector((float)cos(entity
.AuxiliaryAngle
),
512 (float)sin(entity
.AuxiliaryAngle
),
513 0.0f
)*EntityMaxSpeed
;
515 entity
.BotStateStart
= LocalTime
;
521 if (entity
.Type
== CEntity::Snowball
&& LocalTime
>= entity
.Trajectory
.getStopTime())
524 CVector tp(1140,-833,30);
525 nlinfo("dist=%f", (entity.Position-tp).norm());
526 if ((entity.Position-tp).norm()<30.0f)
528 static UInstance t = NULL;
531 Scene->deleteInstance (t);
533 t = Scene->createInstance("pills.ps");
534 t->setScale (10,10,10);
541 removeEntity(entity
.Id
);
546 // control the character animation
547 if (entity
.Type
!= CEntity::Snowball
)
549 if (entity
.IsAiming
&& !entity
.WasAiming
)
552 playAnimation (entity
, PrepareSnowBall
, true);
553 playAnimation (entity
, PrepareSnowBallCycle
, false);
555 else if (entity
.WasAiming
&& !entity
.IsAiming
)
558 playAnimation (entity, ThrowSnowball, true);
560 if (entity.IsWalking)
561 playAnimation (entity, WalkAnim);
563 playAnimation (entity, IdleAnim);
565 else if (entity
.WasAiming
&& entity
.IsAiming
)
567 // currently aiming => do northing
569 else if (!entity
.WasWalking
&& entity
.IsWalking
)
571 playAnimation (entity
, PrepareWalkAnim
, true);
572 playAnimation (entity
, WalkAnim
);
574 else if (entity
.WasWalking
&& !entity
.IsWalking
)
576 playAnimation (entity
, IdleAnim
, true);
579 entity
.WasAiming
= entity
.IsAiming
;
580 entity
.WasWalking
= entity
.IsWalking
;
584 entity
.ImmediateSpeed
= CVector::Null
;
586 if (entity
.Type
== CEntity::Self
)
589 newPos
= MouseListener
->getPosition();
590 // get new orientation
591 entity
.Angle
= MouseListener
->getOrientation();
593 // Interpolate the character orientation towards the server angle
594 // for smoother movements
595 float sweepDistance
= entity
.AuxiliaryAngle
-entity
.InterpolatedAuxiliaryAngle
;
596 if (sweepDistance
> (float)Pi
)
598 sweepDistance
-= (float)Pi
*2.0f
;
599 entity
.InterpolatedAuxiliaryAngle
+= (float)Pi
*2.0f
;
601 if (sweepDistance
< -(float)Pi
)
603 sweepDistance
+= (float)Pi
*2.0f
;
604 entity
.InterpolatedAuxiliaryAngle
-= (float)Pi
*2.0f
;
606 float sweepAngle
= 4.0f
*sweepDistance
;
607 entity
.InterpolatedAuxiliaryAngle
+= (float)(sweepAngle
*dt
);
608 if ((entity
.AuxiliaryAngle
-entity
.InterpolatedAuxiliaryAngle
)*sweepAngle
<= 0.0)
609 entity
.InterpolatedAuxiliaryAngle
= entity
.AuxiliaryAngle
;
610 entity
.Angle
+= entity
.InterpolatedAuxiliaryAngle
;
612 // tell the move container how much the entity should move
613 if (entity
.IsWalking
)
615 entity
.ImmediateSpeed
= (newPos
-oldPos
)/(float)dt
;
616 if (_TestCLS
) entity
.MovePrimitive
->setGlobalPosition(newPos
, 0);
617 else entity
.MovePrimitive
->move(entity
.ImmediateSpeed
, 0);
620 else if (entity
.Type
== CEntity::Other
)
622 // go to the server position with linear interpolation
623 // -- -- useful for speed limiting on frontend service
624 // -- -- random note: also, get rid of the position service,
625 // and move the snowball physics to a more useful service
627 // Interpolate orientation for smoother motions
628 // AuxiliaryAngle -> the server imposed angle
629 // InterpolatedAuxiliaryAngle -> the angle showed by the entity
630 float sweepDistance
= entity
.AuxiliaryAngle
-entity
.InterpolatedAuxiliaryAngle
;
631 if (sweepDistance
> (float)Pi
)
633 sweepDistance
-= (float)Pi
*2.0f
;
634 entity
.InterpolatedAuxiliaryAngle
+= (float)Pi
*2.0f
;
636 if (sweepDistance
< -(float)Pi
)
638 sweepDistance
+= (float)Pi
*2.0f
;
639 entity
.InterpolatedAuxiliaryAngle
-= (float)Pi
*2.0f
;
641 float sweepAngle
= 4.0f
*sweepDistance
;
642 entity
.InterpolatedAuxiliaryAngle
+= (float)(sweepAngle
*dt
);
643 if ((entity
.AuxiliaryAngle
-entity
.InterpolatedAuxiliaryAngle
)*sweepAngle
<= 0.0)
644 entity
.InterpolatedAuxiliaryAngle
= entity
.AuxiliaryAngle
;
646 entity
.Angle
= entity
.InterpolatedAuxiliaryAngle
;
648 // if (entity.IsWalking)
649 if (pDelta
.norm() > 0.1f
)
652 entity
.ImmediateSpeed
= pDelta
*(-entity
.Speed
);
653 entity
.MovePrimitive
->move(entity
.ImmediateSpeed
, 0);
654 entity
.IsWalking
= true;
658 entity
.IsWalking
= false;
661 else if (entity
.Type
== CEntity::Snowball
)
663 // go to the server position using trajectory interpolation
664 CVector newPos
= entity
.Trajectory
.eval(LocalTime
);
665 if (newPos
!= entity
.Position
)
667 entity
.Position
= entity
.Trajectory
.eval(LocalTime
);
668 entity
.Instance
.show ();
682 void updateEntities ()
684 // compute the delta t that has elapsed since the last update (in seconds)
685 double dt
= LocalTimeDelta
;
689 for (eit
= Entities
.begin (); eit
!= Entities
.end (); )
691 nexteit
= eit
; nexteit
++;
693 CEntity
&entity
= (*eit
).second
;
695 switch (entity
.State
)
697 case CEntity::Appear
:
698 stateAppear (entity
);
700 case CEntity::Normal
:
701 stateNormal (entity
);
703 case CEntity::Disappear
:
704 stateDisappear (entity
);
714 // evaluate collisions
715 MoveContainer
->evalCollision(dt
, 0);
717 // snap entities to the ground
718 for (eit
= Entities
.begin (); eit
!= Entities
.end (); eit
++)
720 CEntity
&entity
= (*eit
).second
;
721 UGlobalPosition gPos
;
723 if (entity
.MovePrimitive
!= NULL
)
725 // get the global position in pacs coordinates system
726 entity
.MovePrimitive
->getGlobalPosition(gPos
, 0);
727 // convert it in a vector 3d
728 entity
.Position
= GlobalRetriever
->getGlobalPosition(gPos
);
729 // get the good z position
730 gPos
.LocalPosition
.Estimation
.z
= 0.0f
;
731 entity
.Position
.z
= GlobalRetriever
->getMeanHeight(gPos
);
733 // check position retrieving
735 UGlobalPosition gPosCheck;
736 gPosCheck = GlobalRetriever->retrievePosition(entity.Position);
737 if (gPos.InstanceId != gPosCheck.InstanceId ||
738 gPos.LocalPosition.Surface != gPosCheck.LocalPosition.Surface)
740 nlwarning("Checked UGlobalPosition differs from store");
741 // gPos.InstanceId = gPosCheck.InstanceId;
742 // gPos.LocalPosition.Surface = gPosCheck.LocalPosition.Surface;
745 // snap to the ground
746 if (!GlobalRetriever
->isInterior(gPos
))
747 entity
.VisualCollisionEntity
->snapToGround(entity
.Position
);
749 if (entity
.Type
== CEntity::Other
&&
750 (entity
.ServerPosition
-entity
.Position
)*entity
.ImmediateSpeed
< 0.0f
)
752 // nlinfo("detected over entity %d", entity.Id);
753 entity
.ServerPosition
.z
= entity
.Position
.z
;
754 entity
.Position
= entity
.ServerPosition
;
755 if (!GlobalRetriever
->isInterior(gPos
))
756 entity
.VisualCollisionEntity
->snapToGround(entity
.Position
);
757 entity
.MovePrimitive
->setGlobalPosition(CVectorD(entity
.Position
.x
, entity
.Position
.y
, entity
.Position
.z
), 0);
762 if (!entity
.Instance
.empty())
768 jdir
= CVector(-(float)cos(entity
.Angle
- (Pi
* 0.5)), -(float)sin(entity
.Angle
- (Pi
* 0.5)), 0.0f
);
771 jdir
= CVector(-(float)cos(entity
.Angle
- (Pi
* 0.5)), -(float)sin(entity
.Angle
- (Pi
* 0.5)), 0.0f
);
773 case CEntity::Snowball
:
774 jdir
= entity
.Trajectory
.evalSpeed(LocalTime
).normed();
778 if (!entity
.Skeleton
.empty())
780 entity
.Skeleton
.setPos(entity
.Position
);
781 entity
.Skeleton
.setRotQuat(jdir
);
784 entity
.Instance
.setPos(entity
.Position
);
785 entity
.Instance
.setRotQuat(jdir
);
789 // if (entity.Source != NULL)
790 // entity.Source->setPosition (entity.Position);
792 if (!entity
.Particule
.empty())
794 entity
.Particule
.setPos(entity
.Position
);
797 if (entity
.Type
== CEntity::Self
)
799 MouseListener
->setPosition(entity
.Position
);
804 // Draw entities names up the characters
805 void renderEntitiesNames ()
807 // Setup the driver in matrix mode
808 Driver
->setMatrixMode3D (Camera
);
809 // Setup the drawing context
810 TextContext
->setHotSpot (UTextContext::MiddleTop
);
811 TextContext
->setColor (EntityNameColor
);
812 TextContext
->setFontSize ((uint32
)EntityNameSize
);
814 for (EIT eit
= Entities
.begin (); eit
!= Entities
.end (); eit
++)
816 CEntity
&entity
= (*eit
).second
;
817 if (!entity
.Instance
.empty() && entity
.Type
== CEntity::Other
)
819 CMatrix mat
= Camera
.getMatrix();
820 mat
.setPos(entity
.Position
+ CVector(0.0f
, 0.0f
, 4.0f
));
822 TextContext
->render3D(mat
, entity
.Name
);
828 // The entities preferences callback
829 void cbUpdateEntities (CConfigFile::CVar
&var
)
831 if (var
.Name
== "EntityNameColor") EntityNameColor
.set (var
.asInt(0), var
.asInt(1), var
.asInt(2), var
.asInt(3));
832 else if (var
.Name
== "EntityNameSize") EntityNameSize
= var
.asFloat ();
833 else nlwarning ("Unknown variable update %s", var
.Name
.c_str());
838 ConfigFile
->setCallback ("EntityNameColor", cbUpdateEntities
);
839 ConfigFile
->setCallback ("EntityNameSize", cbUpdateEntities
);
841 cbUpdateEntities (ConfigFile
->getVar ("EntityNameColor"));
842 cbUpdateEntities (ConfigFile
->getVar ("EntityNameSize"));
845 void releaseEntities()
847 // Remove config file callbacks
848 ConfigFile
->setCallback("EntityNameColor", NULL
);
849 ConfigFile
->setCallback("EntityNameSize", NULL
);
851 // Delete all entities (should already have been called normally)
856 // Reset the pacs position of an entity, in case pacs went wrong
857 void resetEntityPosition(uint32 eid
)
859 uint32 sbid
= NextEID
++;
860 EIT eit
= findEntity (eid
);
862 CEntity
&entity
= (*eit
).second
;
864 UGlobalPosition gPos
;
865 // get the global position
866 gPos
= GlobalRetriever
->retrievePosition(entity
.Position
);
867 // convert it in a vector 3d
868 entity
.Position
= GlobalRetriever
->getGlobalPosition(gPos
);
869 // get the good z position
870 gPos
.LocalPosition
.Estimation
.z
= 0.0f
;
871 entity
.Position
.z
= GlobalRetriever
->getMeanHeight(gPos
);
873 // snap to the ground
874 if (entity
.VisualCollisionEntity
!= NULL
&& !GlobalRetriever
->isInterior(gPos
))
875 entity
.VisualCollisionEntity
->snapToGround(entity
.Position
);
877 if (entity
.MovePrimitive
!= NULL
)
878 entity
.MovePrimitive
->setGlobalPosition(CVector(entity
.Position
.x
, entity
.Position
.y
, entity
.Position
.z
), 0);
882 void shotSnowball(uint32 sid
, uint32 eid
, const CVector
&start
, const CVector
&target
, float speed
, float deflagRadius
)
885 CVector direction
= (target
-start
).normed();
887 // create a new snowball entity
888 addEntity(sid
, "", CEntity::Snowball
, start
, target
);
889 EIT sit
= findEntity (sid
);
890 CEntity
&snowball
= (*sit
).second
;
892 snowball
.AutoMove
= 1;
893 snowball
.Trajectory
.init(start
, target
, speed
, LocalTime
+ 1.000);
894 snowball
.Instance
.hide();
896 EIT eit
= findEntity (eid
);
897 CEntity
&entity
= (*eit
).second
;
900 playAnimation (entity
, ThrowSnowball
, true);
902 if (entity
.IsWalking
)
904 playAnimation (entity
, PrepareWalkAnim
, true);
905 playAnimation (entity
, WalkAnim
);
908 playAnimation (entity
, IdleAnim
);
917 NLMISC_COMMAND(remove_entity
,"remove a local entity","<eid>")
919 // check args, if there s not the right number of parameter, return bad
920 if(args
.size() != 1) return false;
922 uint32 eid
= (uint32
)atoi(args
[0].c_str());
929 NLMISC_COMMAND(add_entity
,"add a local entity","<nb_entities> <auto_update>")
931 // check args, if there s not the right number of parameter, return bad
932 if(args
.size() != 2) return false;
934 uint nb
= (uint
)atoi(args
[0].c_str());
936 for (uint i
= 0; i
< nb
; i
++)
938 uint32 eid
= NextEID
++;
939 CVector
start(ConfigFile
->getVar("StartPoint").asFloat(0), ConfigFile
->getVar("StartPoint").asFloat(1), ConfigFile
->getVar("StartPoint").asFloat(2));
940 addEntity (eid
, "Entity"+toString(eid
), CEntity::Other
, start
, start
);
941 EIT eit
= findEntity (eid
);
942 (*eit
).second
.AutoMove
= atoi(args
[1].c_str()) == 1;
948 NLMISC_COMMAND(speed
,"set the player speed","[<entity_id>] <speed in km/h>")
950 // check args, if there s not the right number of parameter, return bad
953 float speed
= min( max( (float)atof(args
[0].c_str()), 0.1f
), 200.0f
); // speed range in km/h
956 MouseListener
->setSpeed( speed
/ 3.6f
);
957 Self
->Speed
= speed
/ 3.6f
;
960 else if(args
.size() == 2)
962 float speed
= min( max( (float)atof(args
[1].c_str()), 0.1f
), 200.0f
); // speed range in km/h
964 uint eid
= (uint
)atoi(args
[0].c_str());
965 EIT eit
= findEntity (eid
);
966 CEntity
&entity
= (*eit
).second
;
968 entity
.Speed
= speed
/ 3.6f
;
969 if (entity
.Type
== CEntity::Self
)
971 MouseListener
->setSpeed(entity
.Speed
);
977 NLMISC_COMMAND(goto, "go to a given position", "<x> <y>")
979 // check args, if there s not the right number of parameter, return bad
980 if(args
.size() != 2) return false;
982 if (Self
== NULL
) return false;
984 CEntity
&entity
= *Self
;
988 x
= (float)atof(args
[0].c_str());
989 y
= (float)atof(args
[1].c_str());
992 if (entity
.MovePrimitive
!= NULL
&& entity
.VisualCollisionEntity
!= NULL
)
994 UGlobalPosition gPos
;
995 entity
.MovePrimitive
->setGlobalPosition(CVectorD(x
, y
, 0.0f
), 0);
996 // get the global position in pacs coordinates system
997 entity
.MovePrimitive
->getGlobalPosition(gPos
, 0);
998 // convert it in a vector 3d
999 entity
.Position
= GlobalRetriever
->getGlobalPosition(gPos
);
1000 // get the good z position
1001 gPos
.LocalPosition
.Estimation
.z
= 0.0f
;
1002 entity
.Position
.z
= GlobalRetriever
->getMeanHeight(gPos
);
1003 // snap to the ground
1004 if (!GlobalRetriever
->isInterior(gPos
))
1005 entity
.VisualCollisionEntity
->snapToGround( entity
.Position
);
1007 if (entity
.Type
== CEntity::Self
)
1009 MouseListener
->setPosition(entity
.Position
);
1016 NLMISC_COMMAND(entities
, "display all entities info", "")
1018 // check args, if there s not the right number of parameter, return bad
1019 if(args
.size() != 0) return false;
1021 for (EIT eit
= Entities
.begin (); eit
!= Entities
.end (); eit
++)
1023 CEntity
&e
= (*eit
).second
;
1024 log
.displayNL("%s %u (k%u) %s %d", (Self
==&e
)?"*":" ", e
.Id
, (*eit
).first
, e
.Name
.c_str(), e
.Type
);
1029 NLMISC_COMMAND(test_cls
, "test the collision service, disables collision test on self", "")
1031 _TestCLS
= !_TestCLS
;
1035 } /* namespace SBCLIENT */