1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "../interface_v3/group_map.h"
25 #include "tool_draw_prim.h"
26 #include "game_share/object.h"
29 #include "r2_config.h"
30 #include "object_factory_client.h"
31 #include "displayer_visual_group.h"
32 #include "tool_maintained_action.h"
34 #include "nel/misc/vectord.h"
35 #include "nel/misc/i18n.h"
36 #include "nel/misc/polygon.h"
39 #include "nel/gui/ctrl_quad.h"
45 using namespace NLMISC
;
51 // ***************************************************************
52 CToolDrawPrim::CToolDrawPrim(TPrimType primType
, CInstance
*extending
/*= NULL*/)
58 _PrimInitialized
= false;
60 _DistinctLastPoint
= true;
61 _ExtendedPrimitive
= extending
;
62 _Extending
= (_ExtendedPrimitive
!= NULL
);
64 _InaccessibleParts
= false;
66 _ForceShowPrims
= false;
70 // ***************************************************************
71 bool CToolDrawPrim::canTerminate() const
73 //H_AUTO(R2_CToolDrawPrim_canTerminate)
74 return ((_PrimType
== Road
&& _NumPoints
>= 1) || _NumPoints
>= 3) && _ValidPrim
;
77 // ***************************************************************
78 const char *CToolDrawPrim::getToolUIName() const
80 //H_AUTO(R2_CToolDrawPrim_getToolUIName)
81 return _PrimType
== Road
? "drawRoad" : "drawRegion";
85 // ***************************************************************
86 bool CToolDrawPrim::init(const CLuaObject
¶meters
)
88 //H_AUTO(R2_CToolDrawPrim_init)
90 _PrimLook
.init(parameters
["Look"]);
91 if (!parameters
["InvalidLook"].isNil())
93 _PrimLookInvalid
.init(parameters
["InvalidLook"]);
97 _PrimLookInvalid
= _PrimLook
;
99 if (!parameters
["CanCloseLook"].isNil())
101 _PrimLookCanClose
.init(parameters
["CanCloseLook"]);
105 _PrimLookCanClose
= _PrimLook
;
108 if (!parameters["InaccessibleLook"].isNil())
110 _PrimLookInaccessible.init(parameters["InaccessibleLook"]);
114 _PrimLookInaccessible = _PrimLook;
117 //TMP TMP hardcoded for first test
118 _PrimLookInaccessible
.init(getEditor().getEnv()["PrimRender"]["RoadLookInaccessible"]);
120 _InaccessiblePrim
.setLook(_PrimLookInaccessible
);
122 CLuaObject vertices
= parameters
["Vertices"];
123 if (vertices
.isTable())
125 // if start points where given, use them
126 ENUM_LUA_TABLE(vertices
, it
)
128 CLuaObject
&vertex
= it
.nextValue();
129 _Points
.push_back(NLMISC::CVector((float) vertex
["x"].toNumber(), (float) vertex
["y"].toNumber(), (float) vertex
["z"].toNumber()));
132 if (!parameters
["ExtendedPrimitiveId"].isNil())
134 std::string extendedPrimitiveId
= parameters
["ExtendedPrimitiveId"];
135 _ExtendedPrimitive
= getEditor().getInstanceFromId(extendedPrimitiveId
);
136 if (!_ExtendedPrimitive
)
139 nlwarning("Can't extend primitive with id %s", extendedPrimitiveId
.c_str());
144 _CancelFunc
= parameters
["OnCancel"];
145 _CookieKey
= parameters
["CookieKey"].toString();
146 _CookieValue
= parameters
["CookieValue"];
147 _PrimType
= parameters
["Type"].toString() == "Region" ? Region
: Road
;
148 _Prim
.setLook(_PrimLook
);
149 _NumPoints
= (uint
)_Points
.size();
151 if (!parameters
["ForceShowPrims"].isNil())
153 _ForceShowPrims
= parameters
["ForceShowPrims"].toBoolean();
156 if (!parameters
["SelectInstance"].isNil())
158 _SelectInstance
= parameters
["SelectInstance"].toBoolean();
162 _SelectInstance
= true;
165 CGroupMap
*worldMap
= getWorldMap();
168 worldMap
->addDeco(&_Prim
); // display primitive on the map
173 // ***************************************************************
174 void CToolDrawPrim::updateAfterRender()
176 //H_AUTO(R2_CToolDrawPrim_updateAfterRender)
179 // ***************************************************************
180 void CToolDrawPrim::removeFromWorldMap()
182 //H_AUTO(R2_CToolDrawPrim_removeFromWorldMap)
183 CGroupMap
*worldMap
= getWorldMap();
186 if (_Prim
.isAddedToWorldMap())
188 worldMap
->removeDeco(&_Prim
);
190 if (_InaccessiblePrim
.isAddedToWorldMap())
192 worldMap
->removeDeco(&_InaccessiblePrim
);
197 // ***************************************************************
198 void CToolDrawPrim::cancel()
200 //H_AUTO(R2_CToolDrawPrim_cancel)
201 if (_ExtendedPrimitive
)
203 CDisplayerVisualGroup
*dv
= dynamic_cast<CDisplayerVisualGroup
*>(_ExtendedPrimitive
->getDisplayerVisual());
206 dv
->setActiveRecurse(true);
209 removeFromWorldMap();
212 if (_CancelFunc
.isFunction())
214 _CancelFunc
.callNoThrow(0, 0);
219 // ***************************************************************
220 void CToolDrawPrim::setPrimLook(bool closed
, bool lastEdgeIsValid
, bool valid
)
222 //H_AUTO(R2_CToolDrawPrim_setPrimLook)
224 if (!valid
) look
= &_PrimLookInvalid
;
225 else if (closed
) look
= &_PrimLookCanClose
;
226 else look
= &_PrimLook
;
227 look
->LastEdgeIsValid
= lastEdgeIsValid
;
228 _Prim
.setLook(*look
);
231 // ***************************************************************
232 void CToolDrawPrim::updateBeforeRender()
234 //H_AUTO(R2_CToolDrawPrim_updateBeforeRender)
235 doUpdateBeforeRender();
236 CGroupMap
*worldMap
= getWorldMap();
237 if (worldMap
&& !_InaccessibleParts
&& _InaccessiblePrim
.isAddedToWorldMap())
239 worldMap
->removeDeco(&_InaccessiblePrim
); // display primitive on the map
243 // ***************************************************************
244 void CToolDrawPrim::doUpdateBeforeRender()
246 //H_AUTO(R2_CToolDrawPrim_doUpdateBeforeRender)
247 if (_Extending
&& !_ExtendedPrimitive
)
253 // Build vector for direction pointed by mouse in world
254 sint32 mouseX
, mouseY
;
255 getMousePos(mouseX
, mouseY
);
256 if (!isInScreen(mouseX
, mouseY
))
258 // mouse not in screen so don't display the last point
259 _Points
.resize(_NumPoints
);
260 _Prim
.setVertices(_Points
);
261 updateValidityFlag(false);
262 setPrimLook(false, true, _ValidPrim
);
263 _Prim
.addDecalsToRenderList();
267 sint32 autoPanDx
, autoPanDy
;
270 handleWorldMapAutoPan(autoPanDx
, autoPanDy
);
273 CTool::CWorldViewRay worldViewRay
;
275 computeWorldViewRay(mouseX
, mouseY
, worldViewRay
);
280 TRayIntersectionType interType
= computeLandscapeRayIntersection(worldViewRay
, inter
);
284 if (worldViewRay
.OnMiniMap
)
286 _Points
.resize(_NumPoints
);
287 _Prim
.setVertices(_Points
);
288 updateValidityFlag(false);
289 setPrimLook(false, true, _ValidPrim
);
290 _Prim
.addDecalsToRenderList();
291 setMouseCursor("curs_stop.tga");
294 // no collision, can't drop entity
295 wpPos
= worldViewRay
.Origin
+ 3.f
* worldViewRay
.Dir
;
299 _ValidPos
= isValid2DPos(inter
); // good pos to drop entity
308 // If there are at least 3 vertices and the primitive is a region, then see whether mouse
309 // is near the first vertex -> in this case, propose to the user to close the primitive
310 // If landscape is intersected too, then the vertex must be nearer then the first vertex
311 if (_PrimType
== Region
&& _NumPoints
>= 3)
313 bool canClose
= false;
314 CGroupMap
*gm
= isMouseOnWorldMap();
317 CViewBitmap
*bm
= _Prim
.getWorldMapVertexView(0);
320 canClose
= bm
->isIn(mouseX
, mouseY
);
325 if (!_Prim
.getVerticesShapeInstance().empty())
327 if (!_Prim
.getVerticesShapeInstance()[0].empty())
329 NLMISC::CAABBox vertexBBox
;
330 _Prim
.getVerticesShapeInstance()[0].getShapeAABBox(vertexBBox
);
331 vertexBBox
.setCenter(_Points
[0]);
332 if (vertexBBox
.intersect(worldViewRay
.Origin
, worldViewRay
.Origin
+ 200.f
* worldViewRay
.Dir
))
334 // if vertex nearer than landscape
335 bool landscapeNearest
= false;
336 if (interType
!= NoIntersection
)
338 // TODO nico : the test is not exact : should use intersection with the bbox to do
339 // length comparison over the same line ...
340 landscapeNearest
= (wpPos
- worldViewRay
.Origin
).norm() < (_Points
[0] - worldViewRay
.Origin
).norm();
342 canClose
= !landscapeNearest
;
349 _Points
.resize(_NumPoints
);
350 _Prim
.setVertices(_Points
);
351 updateValidityFlag(false);
352 setPrimLook(true, true, _ValidPrim
); // show a closed polygon
354 setMouseCursor("curs_pick.tga");
355 _Prim
.addDecalsToRenderList();
359 _Prim
.setEmissive(CRGBA::Black
);
361 _Points
.resize(_NumPoints
+ 1);
362 if (_Points
.size() >= 2)
364 _DistinctLastPoint
= (_Points
[_NumPoints
] != _Points
[_NumPoints
- 1]);
368 _DistinctLastPoint
= true;
371 _Points
.back() = wpPos
;
372 _Prim
.setVertices(_Points
);
373 updateValidityFlag(!_ValidPos
);
374 // updateValidityFlag(interType == NoIntersection);
375 setPrimLook(false, _ValidPos
, _ValidPrim
);
377 // change mouse depending on result
378 if (!isMouseOnUI() || isMouseOnWorldMap() || !_ValidPrim
)
380 setMouseCursor(_ValidPos
/* && !_InaccessibleParts */ ? "curs_create.tga" : "curs_stop.tga");
384 setMouseCursor("curs_create.tga");
386 _Prim
.addDecalsToRenderList();
387 CGroupMap
*worldMap
= getWorldMap();
388 //static CCtrlQuad *testQuad = NULL;
389 if (_InaccessibleParts
)
393 testQuad = new CCtrlQuad;
394 testQuad->setModulateGlobalColor(false);
395 float thickness = 256.f;
396 testQuad->setQuad(CVector(0.f, thickness, 0.f), CVector(thickness * 2.f, thickness, 0.f), thickness);
397 testQuad->setTexture("*accessibility_texture*");
398 worldMap->addCtrl(testQuad);
399 testQuad->setParent(worldMap);
401 if (worldMap
&& !_InaccessiblePrim
.isAddedToWorldMap())
403 worldMap
->addDeco(&_InaccessiblePrim
); // display primitive on the map
406 updateInaccessiblePrimRenderLook(_InaccessiblePrim
);
407 _InaccessiblePrim
.setVertices(_Points
);
408 _InaccessiblePrim
.addDecalsToRenderList();
411 static volatile bool testDecal
= false;
414 R2::CScenarioEntryPoints::CCompleteIsland
*id
= getEditor().getIslandCollision().getCurrIslandDesc();
418 m
.setScale(CVector(40000.f
, 40000.f
, 40000.f
));
419 m
.setPos(CVector(0.f
, -40000.f
, 0.f
));
420 _TestDecal
.setWorldMatrix(m
);
421 _TestDecal
.setCustomUVMatrix(true, getEditor().getIslandCollision().getWorldToAccessibilityTexMat(true));
422 _TestDecal
.setTexture("*accessibility_texture*");
423 _TestDecal
.addToRenderList();
428 // ***************************************************************
429 void CToolDrawPrim::updateInaccessiblePrimRenderLook(CPrimRender
&dest
)
431 //H_AUTO(R2_CToolDrawPrim_updateInaccessiblePrimRenderLook)
432 double duration
= CV_InaccessiblePosAnimDurationInMS
.get();
433 if (duration
<= 0) duration
= 0.1;
434 CRGBA color
= blend(CV_InaccessiblePosColor0
.get(),
435 CV_InaccessiblePosColor1
.get(),
436 0.5f
+ 0.5f
* (float) cos(NLMISC::Pi
* fmod((double) T1
, duration
) / duration
));
437 CPrimLook look
= dest
.getLook();
438 look
.EdgeLook
.WorldMapColor
= color
;
439 look
.EdgeLook
.DecalColor
= color
;
441 dest
.setCustomWorldMapEdgeUVMatrix(true, getEditor().getIslandCollision().getWorldToAccessibilityTexMat());
442 dest
.setCustomDecalEdgeUVMatrix(true, getEditor().getIslandCollision().getWorldToAccessibilityTexMat(true)); // cropped version for decal rendering
445 // ***************************************************************
446 bool CToolDrawPrim::onMouseLeftButtonClicked()
448 //H_AUTO(R2_CToolDrawPrim_onMouseLeftButtonClicked)
449 if (!checkRoomLeft())
451 displayNoMoreRoomLeftMsg();
454 if (_ValidPos
/*&& !_InaccessibleParts*/ && _DistinctLastPoint
)
460 startDoubleClickCheck();
465 // ***************************************************************
466 bool CToolDrawPrim::onMouseLeftButtonDown()
468 //H_AUTO(R2_CToolDrawPrim_onMouseLeftButtonDown)
469 if (_MustClose
&& _ValidPos
)
472 captureMouse(); // mouse will be released on "mouse up" by the default tool
475 if (!checkDoubleClick()) return false;
478 // the 2 last points must have a different position
480 CToolMaintainedAction
*tma
= dynamic_cast<CToolMaintainedAction
*>(getEditor().getCurrentTool());
483 tma
->markPreviousToolClickEnd();
490 // ***************************************************************
491 void CToolDrawPrim::commit()
493 //H_AUTO(R2_CToolDrawPrim_commit)
494 if (_Extending
&& !_ExtendedPrimitive
)
499 removeFromWorldMap();
500 _Points
.resize(_NumPoints
);
501 if (_Points
.empty()) return;
502 // send network command to create a new road
503 CObject
*desc
= NULL
;
506 getDMC().newAction(NLMISC::CI18N::get(_PrimType
== Road
? "uiR2EDCreateRouteAction" : "uiR2EDCreateZoneAction"));
510 getDMC().newAction(NLMISC::CI18N::get(_PrimType
== Road
? "uiR2EDExtendRouteAction" : "uiR2EDExtendZoneAction"));
514 // creating a new primitive, rather than growing an existing one
515 std::string className
= _PrimType
== Road
? "Road": "Region";
516 desc
= getDMC().newComponent(className
);
518 if (desc
|| _Extending
)
520 CObject
*points
= NULL
;
523 std::string instanceId
= getString(desc
, "InstanceId");
524 if (!instanceId
.empty())
526 // ask a flag to prompt user to enter name for this object when it is created
527 getEditor().setCookie(instanceId
, "AskName", true);
528 if (_CookieValue
.isValid())
530 if (!_CookieKey
.empty() && !_CookieValue
.isNil())
532 getEditor().setCookie(instanceId
, _CookieKey
, _CookieValue
);
536 getEditor().setCookie(instanceId
, "Select", _SelectInstance
);
538 static volatile bool wantDump
= false;
543 points
= desc
->getAttr("Points");
544 for(uint k
= 0; k
< _Points
.size(); ++k
)
546 CObject
*wp
= getDMC().newComponent(_PrimType
== Road
? "WayPoint" : "RegionVertex");
548 wp
->setObject("Position", buildVector(CVectorD(_Points
[k
])));
549 points
->insert("", wp
, -1);
555 string readableName
= NLMISC::CI18N::get(_PrimType
== Road
? "uiR2EDNameBotRoad" : "uiR2EDNameBotRegion");
556 readableName
= getEditor().genInstanceName(readableName
).toUtf8();
557 desc
->set("Name", readableName
);
558 // send creation command
559 // tmp : static npc counter
560 // add in component list of default feature
561 if (getEditor().getDefaultFeature())
563 getDMC().requestInsertNode(getEditor().getDefaultFeature(getEditor().getBaseAct())->getId(),
571 // NB : if display of primitives is 'hide all', then force to 'show all', else
572 // the new prim wouldn't be visible
575 if (getEditor().getEnv()["PrimDisplayVisible"].toBoolean() == false ||
576 getEditor().getEnv()["PrimDisplayContextualVisibility"].toBoolean() == true)
578 getEditor().callEnvMethod("notifyPrimDisplayShowAll", 0, 0);
585 CVectorD offset
= CVectorD::Null
;
586 CDisplayerVisualGroup
*dv
= dynamic_cast<CDisplayerVisualGroup
*>(_ExtendedPrimitive
->getDisplayerVisual());
589 offset
= - dv
->getWorldPos();
592 std::vector
<CDisplayerVisual
*> sons
;
595 for(uint k
= _StartNumPoints
- 1; k
> _Points
.size() - 1; --k
)
597 getDMC().requestEraseNode(sons
[k
]->getDisplayedInstance()->getId(), "", -1);
600 for (uint k
= 0; k
< std::min(_StartNumPoints
, uint(_Points
.size())); ++k
)
602 // change modified vertices
603 CVector newWorldPos
= CVectorD(_Points
[k
]).asVector();
604 if (newWorldPos
!= _InitialPoints
[k
])
606 CObject
*wp
= getDMC().newComponent(_PrimType
== Road
? "WayPoint" : "RegionVertex");
609 CObject
*newPos
= buildVector(offset
+ CVectorD(_Points
[k
]));
610 getDMC().requestSetNode(sons
[k
]->getDisplayedInstance()->getId(), "Position", newPos
);
615 // insert newly created vertices
616 for(uint k
= _StartNumPoints
; k
< _Points
.size(); ++k
)
618 CObject
*wp
= getDMC().newComponent(_PrimType
== Road
? "WayPoint" : "RegionVertex");
620 wp
->setObject("Position", buildVector(offset
+ CVectorD(_Points
[k
])));
621 getDMC().requestInsertNode(_ExtendedPrimitive
->getId(), "Points", -1, "", wp
);
626 dv
->setActiveRecurse(true);
631 getEditor().setCurrentTool(NULL
); // set the default tool
636 // ***************************************************************
637 bool CToolDrawPrim::onMouseRightButtonClicked()
639 //H_AUTO(R2_CToolDrawPrim_onMouseRightButtonClicked)
640 // TMP : exception for primitive drawing : right button finish the road
641 if (!checkRoomLeft())
643 displayNoMoreRoomLeftMsg();
646 if (_ValidPos
&& _DistinctLastPoint
)
658 // cancel the drawing
659 //getEditor().setCurrentTool(NULL);
663 // ***************************************************************
664 bool CToolDrawPrim::onDeleteCmd()
666 //H_AUTO(R2_CToolDrawPrim_onDeleteCmd)
667 CTool::TSmartPtr
hold(this);
672 getDMC().newAction(CI18N::get("uiR2EDDeleteRoad") + _ExtendedPrimitive
->getDisplayName());
673 // just erase the road
674 getDMC().requestEraseNode(_ExtendedPrimitive
->getId(), "", -1);
675 getDMC().getActionHistoric().endAction();
677 // cancel the drawing
678 getEditor().setCurrentTool(NULL
);
683 _Points
.resize(_NumPoints
);
689 // ***************************************************************
690 bool CToolDrawPrim::isValidPolyShape(bool ignoreLast
, std::list
<CPolygon
> &splitPoly
) const
692 //H_AUTO(R2_CToolDrawPrim_isValidPolyShape)
694 if(_Points
.size() <= 3)
698 // test that no self-intersection is found
699 static NLMISC::CPolygon2D poly2D
;
700 poly2D
.Vertices
.resize(_Points
.size());
701 for(uint k
= 0; k
< _Points
.size(); ++k
)
703 poly2D
.Vertices
[k
] = _Points
[k
];
705 if (poly2D
.selfIntersect()) return false;
707 poly
.Vertices
= _Points
;
708 if (poly
.Vertices
.back() == poly
.Vertices
[poly
.Vertices
.size() - 2] || ignoreLast
)
710 poly
.Vertices
.pop_back(); // duplicated point at the end will always fails the test, so remove it
712 bool validPrim
= poly
.toConvexPolygons(splitPoly
, CMatrix::Identity
);
715 std::reverse(poly
.Vertices
.begin(), poly
.Vertices
.end());
716 validPrim
= poly
.toConvexPolygons(splitPoly
, CMatrix::Identity
);
721 // ***************************************************************
722 bool CToolDrawPrim::testAccessibleEdges(bool ignoreLast
)
724 //H_AUTO(R2_CToolDrawPrim_testAccessibleEdges)
725 uint numPoints
= (uint
)_Points
.size();
726 if (_PrimType
== Road
)
728 numPoints
-= ignoreLast
? 2 : 1;
732 numPoints
-= ignoreLast
? 2 : 0;
734 for (sint k
= 0; k
< (sint
) numPoints
; ++k
)
736 if (!getEditor().getIslandCollision().isValidSegment(_Points
[k
], _Points
[(k
+ 1) % _Points
.size()]))
744 // ***************************************************************
745 void CToolDrawPrim::updateValidityFlag(bool ignoreLast
)
747 //H_AUTO(R2_CToolDrawPrim_updateValidityFlag)
749 if (_PrimType
!= Road
)
751 std::list
<CPolygon
> splitPoly
;
752 if (!isValidPolyShape(ignoreLast
, splitPoly
))
761 nlassert(0); // old code
762 if (_LastPoints.size() == _Points.size() && std::equal(_Points.begin(), _Points.end(), _LastPoints.begin()))
763 return; // not modified, so last flag remains valid
764 _InaccessibleParts = false;
765 _LastPoints = _Points;
766 // CHANGE: accessibility for edges not tested anymore ...
768 // bool accessible = testAccessibleEdges(ignoreLast);
769 // _InaccessibleParts = !accessible;
771 _InaccessibleParts = false;
772 sint testPointIndex = _Points.size() - (ignoreLast ? 2 : 1);
773 if (testPointIndex < 0)
778 // change : valid to create if current mouse pos is valid (test 0.5f meters around)
779 CVector2f testPos = _Points[testPointIndex];
780 _ValidPrim = getEditor().getIslandCollision().isValidPos(testPos);
782 if (!_ValidPrim) return;
783 // old test : all parts crossed by the edges needed to be accessible
787 // _ValidPrim = false;
789 if (_PrimType != Road)
791 std::list<CPolygon> splitPoly;
792 if (!isValidPolyShape(ignoreLast, splitPoly))
799 // ***************************************************************
800 void CToolDrawPrim::onActivate()
802 //H_AUTO(R2_CToolDrawPrim_onActivate)
803 setContextHelp(CI18N::get("uiR2EDToolDrawPrim"));
804 if (_ExtendedPrimitive
)
806 CDisplayerVisualGroup
*dv
= dynamic_cast<CDisplayerVisualGroup
*>(_ExtendedPrimitive
->getDisplayerVisual());
814 dv
->setActiveRecurse(false); // hide the primitive being extended, because we display
815 // our own version until we are finished
817 std::vector
<CDisplayerVisual
*> sons
;
819 for(uint k
= 0; k
< sons
.size(); ++k
)
821 _Points
.push_back(sons
[k
]->getWorldPos().asVector());
823 _NumPoints
= (uint
)_Points
.size();
824 _StartNumPoints
= _NumPoints
;
825 _InitialPoints
= _Points
;
829 // ***************************************************************
830 bool CToolDrawPrim::checkRoomLeft()
832 //H_AUTO(R2_CToolDrawPrim_checkRoomLeft)
833 return _NumPoints
< PrimMaxNumPoints
;
836 // ***************************************************************
837 void CToolDrawPrim::displayNoMoreRoomLeftMsg()
839 //H_AUTO(R2_CToolDrawPrim_displayNoMoreRoomLeftMsg)
840 getEditor().callEnvMethod("noMoreRoomLeftInPrimitveMsg", 0, 0);