// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
//
#include "editor.h"
#include "tool_create_entity.h"
//
#include "nel/misc/vectord.h"
#include "nel/misc/i18n.h"
//
#include "../client_sheets/character_sheet.h"
//
#include "../entity_cl.h"
#include "../player_r2_cl.h"
#include "../sheet_manager.h"
#include "../interface_v3/lua_ihm.h"
#include "../cdb_leaf.h"
#include "../interface_v3/interface_manager.h"
#include "dmc/palette.h"
#include "displayer_visual.h"
#include "r2_config.h"
#include "verbose_clock.h"
#include "entity_sorter.h"
//
#include "../interface_v3/ctrl_base_button.h"
//
#include "game_share/player_visual_properties.h"
#include "game_share/visual_slot_manager.h"
using namespace NLPACS;
using namespace NLMISC;
using namespace std;
namespace R2
{
// ***************************************************************
CToolCreateEntity::~CToolCreateEntity()
{
clearArray();
}
// ***************************************************************
void CToolCreateEntity::cancel()
{
CToolChoosePos::cancel();
clearArray();
}
// ***************************************************************
CToolCreateEntity::CToolCreateEntity(uint ghostSlot, const std::string &paletteId, bool arrayMode) : CToolChoosePos(ghostSlot)
{
_PaletteId = paletteId;
if (!arrayMode)
{
enableMultiPos();
}
_CreateState = CreateSingle;
_ArrayOrigin.set(0.f, 0.f, 0.f);
_ArrayEnd.set(0.f, 0.f, 0.f);
_ArrayDefaultAngle = 0.f;
if (arrayMode)
{
CObject *paletteNode = getEditor().getDMC().getPaletteElement(paletteId);
if (paletteNode)
{
std::string sheetClient = getString(paletteNode, "SheetClient");
if (isBotObjectSheet(CSheetId(sheetClient)))
{
_CreateState = ChooseArrayOrigin;
}
}
}
_ArrayWantedAction = ArrayActionNone;
}
// ***************************************************************
void CToolCreateEntity::updateInvalidCursorOnUI()
{
//H_AUTO(R2_CToolCreateEntity_updateInvalidCursorOnUI)
// set the default cursor unless the mouse is on the palette
const std::vector &groups = getUI().getGroupsUnderPointer();
for(uint k = 0; k < groups.size(); ++k)
{
if (groups[k]->getId() == "ui:interface:r2ed_palette") // hardcoded for now ...
{
setMouseCursor(_CursValid);
return;
}
}
setMouseCursor(DEFAULT_CURSOR);
}
// ***************************************************************
void CToolCreateEntity::commit(const NLMISC::CVector &createPosition, float createAngle)
{
//H_AUTO(R2_CToolCreateEntity_commit)
if (_CreateState == ChooseArrayOrigin)
{
if (!getEditor().verifyRoomLeft(0, 1))
{
getUI().executeLuaScript("r2:checkStaticQuota(1)");
return;
}
setContextHelp(CI18N::get("uiR2EDDrawArrayContextHelp"));
_CreateState = DrawArray;
_ArrayDefaultAngle = createAngle;
_ArrayOrigin = createPosition;
_ArrayEnd = createPosition;
updateArray(getGhost());
removeGhostSlot();
return;
}
CEntityCL *ghost = getGhost();
if (!ghost) return;
cloneEntityIntoScenario(ghost,
createPosition,
createAngle,
true, /* new action */
false /* create ghost */);
if (isMultiPos() && isShiftDown())
{
// prevent newly created ghost to be removed twice ...
removeGhostSlot();
// re set this tool to generate a new entity look
getUI().runActionHandler("r2ed_create_entity", NULL, "PaletteId=" + _PaletteId);
}
}
// ***************************************************************
bool CToolCreateEntity::isBotObjectSheet(const NLMISC::CSheetId &sheetId) const
{
//H_AUTO(R2_CToolCreateEntity_isBotObjectSheet)
const CCharacterSheet *charSheet = dynamic_cast(SheetMngr.get(sheetId));
if (charSheet)
{
std::string botobjectsPaletteRoot = "palette.entities.botobjects";
return (_PaletteId.substr(0, botobjectsPaletteRoot.size()) == botobjectsPaletteRoot);
}
return false;
}
// ***************************************************************
std::string CToolCreateEntity::cloneEntityIntoScenario(CEntityCL *clonee,
const NLMISC::CVector &createPosition,
float createAngle,
bool newAction,
bool createGhost
)
{
//H_AUTO(R2_CToolCreateEntity_cloneEntityIntoScenario)
if (!clonee) return "";
std::string instanceId;
bool isBotObject = isBotObjectSheet(clonee->sheetId());
if (!getEditor().verifyRoomLeft(isBotObject ? 0 : 1, isBotObject ? 1 : 0)) { return ""; }
std::string className;
// if class is given in the palette node, then use it. Default to 'Npc' else
CObject *paletteNode = getDMC().getPaletteElement(_PaletteId);
if (paletteNode && paletteNode->findIndex("Class") != -1)
{
className = getString(paletteNode, "Class");
}
if (className.empty())
{
className = "Npc";
}
ucstring readableName;
// retrieve name from the palette id
CLuaState &ls = getEditor().getLua();
getEditor().getEnv()["PaletteIdToTranslation"][_PaletteId].push();
if (ls.isString(-1))
{
readableName.fromUtf8(ls.toString(-1));
}
if (readableName.empty())
{
// if no name found then give a default one
readableName = CI18N::get(isBotObject ? "uiR2EDNameBotObject" : "uiR2EDNameNPC");
}
// except for creatures, posfix the name with a number
std::string creaturePaletteRoot = "palette.entities.creatures";
if (_PaletteId.substr(0, creaturePaletteRoot.size()) != creaturePaletteRoot)
{
readableName = getEditor().genInstanceName(readableName);
}
else
{
className = "NpcCreature";
// is Plant
std::string sheetClient = getString(paletteNode, "SheetClient");
getEditor().getLua().push(sheetClient);
if (getEditor().getEnv().callMethodByNameNoThrow("isNPCPlant", 1, 1))
{
CLuaObject result(getEditor().getLua());
bool isPlant = result.toBoolean();
if (isPlant)
className = "NpcPlant";
}
}
if (newAction)
{
getDMC().newAction(NLMISC::CI18N::get("uiR2EDCreateAction") + readableName);
}
// send network commands to create entity on server
std::auto_ptr desc(getDMC().newComponent(className));
if (desc.get())
{
// TMP FIX : if the created entity is a custom npc, then retrieve look from the clonee visual properties
if (className == "NpcCustom")
{
SPropVisualA vA;
SPropVisualB vB;
SPropVisualC vC;
const string propNameA = toString("SERVER:Entities:E%d:P%d", clonee->slot(), CLFECOMMON::PROPERTY_VPA);
const string propNameB = toString("SERVER:Entities:E%d:P%d", clonee->slot(), CLFECOMMON::PROPERTY_VPB);
const string propNameC = toString("SERVER:Entities:E%d:P%d", clonee->slot(), CLFECOMMON::PROPERTY_VPC);
CCDBNodeLeaf *leafA = CInterfaceManager::getInstance()->getDbProp(propNameA);
CCDBNodeLeaf *leafB = CInterfaceManager::getInstance()->getDbProp(propNameB);
CCDBNodeLeaf *leafC = CInterfaceManager::getInstance()->getDbProp(propNameC);
if (!leafA)
{
nlwarning("Can't find DB leaf %s", propNameA.c_str());
return "";
}
if (!leafB)
{
nlwarning("Can't find DB leaf %s", propNameB.c_str());
return "";
}
if (!leafC)
{
nlwarning("Can't find DB leaf %s", propNameC.c_str());
return "";
}
vA.PropertyA = leafA->getValue64();
vB.PropertyB = leafB->getValue64();
vC.PropertyC = leafC->getValue64();
nlassert(desc->isTable());
CObjectTable *props = (CObjectTable *) desc.get();
props->set("GabaritHeight", (double)vC.PropertySubData.CharacterHeight);
props->set("GabaritTorsoWidth", (double)vC.PropertySubData.TorsoWidth);
props->set("GabaritArmsWidth", (double)vC.PropertySubData.ArmsWidth);
props->set("GabaritLegsWidth", (double)vC.PropertySubData.LegsWidth);
props->set("GabaritBreastSize", (double)vC.PropertySubData.BreastSize);
props->set("HairColor", (double)vA.PropertySubData.HatColor);
props->set("Tattoo", (double)vC.PropertySubData.Tattoo);
props->set("EyesColor", (double)vC.PropertySubData.EyesColor);
props->set("MorphTarget1", (double)vC.PropertySubData.MorphTarget1);
props->set("MorphTarget2", (double)vC.PropertySubData.MorphTarget2);
props->set("MorphTarget3", (double)vC.PropertySubData.MorphTarget3);
props->set("MorphTarget4", (double)vC.PropertySubData.MorphTarget4);
props->set("MorphTarget5", (double)vC.PropertySubData.MorphTarget5);
props->set("MorphTarget6", (double)vC.PropertySubData.MorphTarget6);
props->set("MorphTarget7", (double)vC.PropertySubData.MorphTarget7);
props->set("MorphTarget8", (double)vC.PropertySubData.MorphTarget8);
props->set("Sex", (double)vA.PropertySubData.Sex);
CVisualSlotManager * vsManager = CVisualSlotManager::getInstance();
NLMISC::CSheetId * sheetId = NULL;
if(vA.PropertySubData.HatModel == 0)
{
props->set("HatModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.HatModel, SLOTTYPE::HEAD_SLOT);
if (sheetId)
{
props->set("HairType", (double)sheetId->asInt());
}
}
if(vA.PropertySubData.JacketModel == 0)
{
props->set("JacketModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.JacketModel, SLOTTYPE::CHEST_SLOT);
if (sheetId)
{
props->set("JacketModel", (double)sheetId->asInt());
}
}
if(vA.PropertySubData.TrouserModel == 0)
{
props->set("TrouserModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.TrouserModel, SLOTTYPE::LEGS_SLOT);
if (sheetId)
{
props->set("TrouserModel", (double)sheetId->asInt());
}
}
if(vB.PropertySubData.FeetModel == 0)
{
props->set("FeetModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vB.PropertySubData.FeetModel, SLOTTYPE::FEET_SLOT);
if (sheetId)
{
props->set("FeetModel", (double)sheetId->asInt());
}
}
if(vB.PropertySubData.HandsModel == 0)
{
props->set("HandsModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vB.PropertySubData.HandsModel, SLOTTYPE::HANDS_SLOT);
if (sheetId)
{
props->set("HandsModel", (double)sheetId->asInt());
}
}
if(vA.PropertySubData.ArmModel == 0)
{
props->set("ArmModel", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.ArmModel, SLOTTYPE::ARMS_SLOT);
if (sheetId)
{
props->set("ArmModel", (double)sheetId->asInt());
}
}
double weaponRH=0, weaponLH=0;
if(vA.PropertySubData.WeaponRightHand == 0)
{
props->set("WeaponRightHand", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.WeaponRightHand, SLOTTYPE::RIGHT_HAND_SLOT);
if (sheetId)
{
weaponRH = (double)sheetId->asInt();
}
props->set("WeaponRightHand", weaponRH);
}
if(vA.PropertySubData.WeaponLeftHand == 0)
{
props->set("WeaponLeftHand", 0);
}
else
{
sheetId = vsManager->index2Sheet((uint32)vA.PropertySubData.WeaponLeftHand, SLOTTYPE::LEFT_HAND_SLOT);
if (sheetId)
{
weaponLH = (double)sheetId->asInt();
}
props->set("WeaponLeftHand", weaponLH);
}
props->set("JacketColor", (double)vA.PropertySubData.JacketColor);
props->set("TrouserColor", (double)vA.PropertySubData.TrouserColor);
props->set("FeetColor", (double)vB.PropertySubData.FeetColor);
props->set("HandsColor", (double)vB.PropertySubData.HandsColor);
props->set("ArmColor", (double)vA.PropertySubData.ArmColor);
CPlayerR2CL * player = (CPlayerR2CL*)dynamic_cast(clonee);
if(player != NULL)
{
std::string race, gender, sheetClient;
switch(player->people())
{
case EGSPD::CPeople::Fyros:
sheetClient = "basic_fyros_";
race = "Fyros";
break;
case EGSPD::CPeople::Matis:
sheetClient = "basic_matis_";
race = "Matis";
break;
case EGSPD::CPeople::Tryker:
sheetClient = "basic_tryker_";
race = "Tryker";
break;
case EGSPD::CPeople::Zorai:
sheetClient = "basic_zorai_";
race = "Zorai";
break;
default:
nlwarning("CToolCreateEntity::commit unknown people");
}
switch(player->getGender())
{
case GSGENDER::female:
sheetClient = sheetClient+"female.creature";
gender = "female";
break;
case GSGENDER::male:
sheetClient = sheetClient+"male.creature";
gender = "male";
break;
default:
nlwarning("CToolCreateEntity::commit unknown gender");
}
props->set("SheetClient", sheetClient);
// random name
getEditor().getLua().push(race);
getEditor().getLua().push(gender);
if (getEditor().getEnv().callMethodByNameNoThrow("randomNPCName", 2, 1))
{
CLuaObject result(getEditor().getLua());
std::string name = result.toString();
props->set("Name", name);
}
}
getEditor().getLua().push(getString(paletteNode, "Equipment"));
getEditor().getLua().push(weaponRH);
getEditor().getLua().push(weaponLH);
getEditor().getLua().push(getString(paletteNode, "Sheet"));
getEditor().getLua().push(getString(paletteNode, "SheetModel"));
if (getEditor().getEnv().callMethodByNameNoThrow("searchSheet", 5, 1))
{
CLuaObject result(getEditor().getLua());
std::string sheet = result.toString();
props->set("Sheet", sheet);
}
else
{
nlwarning("SearchSheet failed : Palette Id = %s", _PaletteId.c_str());
return "";
}
}
else
{
desc->set("Name", readableName.toUtf8());
}
desc->set("Base", _PaletteId);
desc->setObject("Position", buildVector(CVectorD(createPosition)));
desc->set("Angle", createAngle);
//desc->set("Name", readableName.toUtf8());
instanceId = getString(desc.get(), "InstanceId");
if (!instanceId.empty())
{
if (!createGhost)
{
// selection asked when instance is created
getEditor().setCookie(instanceId, "Select", true);
}
else
{
getEditor().setCookie(instanceId, "GhostDuplicate", true);
}
}
// send creation command
// tmp : static npc counter
// add in component list of default feature
if (getEditor().getDefaultFeature())
{
std::string targetInstanceId;
// if object is a bot object, it is considered to be permanent content
// and should be created in the base act
CInstance *targetAct = isBotObject ? getEditor().getBaseAct() : getEditor().getCurrentAct();
if (!targetAct)
{
nlwarning("Can't find act when creating an entity");
}
else
{
if (_AutoGroup.getGroupingCandidate())
{
nlassert(!createGhost); // either autogroup or arraymode, both at the same time not supported
_AutoGroup.group(desc.get(), createPosition);
}
else
{
// create standalone
desc->setGhost(createGhost);
getDMC().requestInsertNode(getEditor().getDefaultFeature(targetAct)->getId(),
"Components",
-1,
"",
desc.get());
}
}
}
}
return instanceId;
}
// ***************************************************************
void CToolCreateEntity::onActivate()
{
//H_AUTO(R2_CToolCreateEntity_onActivate)
setContextHelp(CI18N::get("uiR2EDToolCreateEntity"));
}
// ***************************************************************
void CToolCreateEntity::updateBeforeRender()
{
//H_AUTO(R2_CToolCreateEntity_updateBeforeRender)
if (_CreateState != DrawArray)
{
CToolChoosePos::updateBeforeRender();
_AutoGroup.update(_CreatePosition, _PaletteId, _Valid && !isCtrlDown());
setContextHelp(CI18N::get(_AutoGroup.getGroupingCandidate() ? "uiR2EDToolCreateEntityAutoGroup" : "uiR2EDToolCreateEntity"));
return;
}
setContextHelp(CI18N::get("uiR2EDDrawArrayContextHelp"));
// update for array mode
bool valid = true;
sint32 mouseX, mouseY;
getMousePos(mouseX, mouseY);
if (!isInScreen(mouseX, mouseY) || (isMouseOnUI() && !isMouseOnWorldMap()))
{
valid = false;
_ArrayEnd = _ArrayOrigin;
}
//
CTool::CWorldViewRay worldViewRay;
computeWorldViewRay(mouseX, mouseY, worldViewRay);
//
CVector entityPos; // the pos where the ghost will be shown
CVector inter; // intersection of view ray with landscape
_ValidArray = true;
TRayIntersectionType rayIntersectionType = computeLandscapeRayIntersection(worldViewRay, inter);
switch(rayIntersectionType)
{
case NoIntersection:
_ValidArray = false;
_ArrayEnd = _ArrayOrigin;
break;
case ValidPacsPos:
case InvalidPacsPos:
_ArrayEnd = inter;
break;
}
for (uint k = 0; valid && k < _ArrayElements.size(); ++k)
{
if (_ArrayElements[k])
{
if (_ArrayElements[k]->getDisplayFlag(CDisplayerVisual::FlagBadPos))
{
_ValidArray = false;
}
}
}
CGroupMap *worldMap = getWorldMap();
if (worldMap) worldMap->setSelectionAxis(_ValidArray);
setMouseCursor(_ValidArray ? _CursValid.c_str() : _CursInvalid.c_str());
}
// ***************************************************************
void CToolCreateEntity::updateAfterRender()
{
//H_AUTO(R2_CToolCreateEntity_updateAfterRender)
if (_CreateState != DrawArray)
{
CToolChoosePos::updateAfterRender();
return;
}
switch(_ArrayWantedAction)
{
case ArrayActionNone:
break;
case ArrayActionValidate:
{
commitArray();
CTool::TSmartPtr hold(this);
getUI().runActionHandler("r2ed_create_entity", NULL, "PaletteId="+_PaletteId);
return;
}
break;
case ArrayActionCancel:
{
CTool::TSmartPtr hold(this);
cancel();
getEditor().setCurrentTool(NULL);
return;
}
break;
}
updateArray(NULL);
}
// ***************************************************************
bool CToolCreateEntity::onMouseLeftButtonClicked()
{
//H_AUTO(R2_CToolCreateEntity_onMouseLeftButtonClicked)
if (_CreateState != DrawArray)
{
return CToolChoosePos::onMouseLeftButtonClicked();
}
if (_ValidArray)
{
_ArrayWantedAction = ArrayActionValidate;
}
return true;
}
// ***************************************************************
bool CToolCreateEntity::onMouseRightButtonClicked()
{
//H_AUTO(R2_CToolCreateEntity_onMouseRightButtonClicked)
if (_CreateState != DrawArray)
{
return CToolChoosePos::onMouseRightButtonClicked();
}
_ArrayWantedAction = ArrayActionCancel;
return true;
}
// ***************************************************************
void CToolCreateEntity::clearArray()
{
//H_AUTO(R2_CToolCreateEntity_clearArray)
for (uint k = 0; k < _ArrayElements.size(); ++k)
{
if (_ArrayElements[k])
{
getEditor().getDMC().requestEraseNode(_ArrayElements[k]->getDisplayedInstance()->getId(), "", -1);
}
}
}
// ***************************************************************
void CToolCreateEntity::updateArray(CEntityCL *clonee)
{
//H_AUTO(R2_CToolCreateEntity_updateArray)
if (!clonee)
{
nlassert(!_ArrayElements.empty());
nlassert(_ArrayElements[0] != NULL);
clonee = _ArrayElements[0]->getEntity();
if (!clonee)
{
return;
}
}
CVector extent = _ArrayEnd - _ArrayOrigin;
uint arraySize = 1;
float arrayStepLength = 1.f;
if (!_ArrayElements.empty() && _ArrayElements[0])
{
arrayStepLength = 2.f * _ArrayElements[0]->getSelectionDecalRadius();
arraySize = (uint) floorf(extent.norm() / arrayStepLength) + 1;
arraySize = std::min(arraySize, (uint) 16);
}
while (!getEditor().verifyRoomLeft(0, arraySize))
{
-- arraySize;
if (arraySize == 0)
{
TSmartPtr hold(this);
cancel();
return;
}
}
_ArrayElements.resize(std::max(arraySize, uint(_ArrayElements.size())));
CVector delta = arrayStepLength * extent.normed();
float angle = _ArrayDefaultAngle;
if (arraySize > 1)
{
angle = - (float) atan2(extent.x, extent.y);
}
bool newEntityCreated = false;
uint numCreatedEntity = 0;
for (uint k = 0; k < _ArrayElements.size(); ++k)
{
CVector pos = _ArrayOrigin + (float) k * delta;
if (!_ArrayElements[k])
{
if (k < arraySize)
{
nlwarning("NEW ENTITY");
// create new element
std::string instanceId = cloneEntityIntoScenario(clonee,
pos,
angle,
false, /*new action*/
true /*create ghost*/);
CInstance *inst = getEditor().getInstanceFromId(instanceId);
if (inst)
{
_ArrayElements[k] = dynamic_cast(inst->getDisplayerVisual());
if (_ArrayElements[k])
{
_ArrayElements[k]->setDisplayMode(CDisplayerVisual::DisplayModeArray);
}
}
newEntityCreated = true;
++ numCreatedEntity;
}
}
if (_ArrayElements[k])
{
bool active = k < arraySize;
// do a kind of 'reserve' on the list of entities : don't delete entities in excess, but hide them instead
if (active != _ArrayElements[k]->getActive())
{
_ArrayElements[k]->setActive(active);
if (active)
{
newEntityCreated = true;
_ArrayElements[k]->setDisplayMode(CDisplayerVisual::DisplayModeArray);
}
}
if (active)
{
// update pos & angle
TInstanceId instanceId = _ArrayElements[k]->getDisplayedInstance()->getId();
CVector worldPos = _ArrayElements[k]->getWorldPos();
if (pos != worldPos)
{
CObject *newPos = buildVector(pos, _ArrayElements[k]->getDisplayedInstance()->getPosInstanceId());
getEditor().getDMC().requestSetNode(instanceId, "Position", newPos);
delete newPos;
}
if (angle != _ArrayElements[k]->getAngle())
{
CObjectNumber *angleObject = new CObjectNumber(angle);
getEditor().getDMC().requestSetNode(instanceId, "Angle", angleObject);
delete angleObject;
}
}
}
}
if (newEntityCreated)
{
nlwarning("Num created entity = %d", numCreatedEntity);
getEditor().getEntitySorter()->clipEntitiesByDist();
}
}
// ***************************************************************
void CToolCreateEntity::commitArray()
{
//H_AUTO(R2_CToolCreateEntity_commitArray)
for (uint k = 0; k < _ArrayElements.size(); ++k)
{
if (_ArrayElements[k])
{
cloneEntityIntoScenario(_ArrayElements[k]->getEntity(),
_ArrayElements[k]->getWorldPos(),
_ArrayElements[k]->getAngle(),
k == 0, /*new action*/
false /*create ghost*/);
}
}
clearArray();
getEditor().getDMC().flushActions();
}
// ***************************************************************
bool CToolCreateEntity::stopAfterCommit() const
{
//H_AUTO(R2_CToolCreateEntity_stopAfterCommit)
return _CreateState == CreateSingle;
}
// ***************************************************************
class CAHR2EDToggleDrawArray : public IActionHandler
{
virtual void execute(CCtrlBase *pCaller, const std::string &/* sParams */)
{
CInterfaceManager *im = CInterfaceManager::getInstance();
CCtrlBaseButton *but = dynamic_cast(pCaller);
if (but)
{
im->getDbProp("UI:TEMP:R2_DRAW_ARRAY")->setValueBool(but->getPushed());
CToolCreateEntity *tce = dynamic_cast(getEditor().getCurrentTool());
if (tce)
{
im->runActionHandler("r2ed_create_entity", NULL, "PaletteId=" + tce->getPaletteId());
}
}
}
};
REGISTER_ACTION_HANDLER(CAHR2EDToggleDrawArray, "r2ed_toggle_draw_array");
} // R2