// 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 .
//////////////
// Includes //
//////////////
#include "stdpch.h"
// very nice \\// :)
#include "nel/misc/bit_mem_stream.h"
#include "nel/misc/command.h"
#include "nel/misc/i18n.h"
#include "nel/misc/o_xml.h"
#include "nel/misc/async_file_manager.h"
#include "nel/3d/u_particle_system_instance.h"
#include "nel/3d/u_play_list_manager.h"
#include "nel/3d/u_animation_set.h"
#include "nel/3d/u_landscape.h"
#include "nel/3d/u_play_list.h"
#include "nel/3d/u_animation.h"
#include "nel/3d/u_scene.h"
#include "nel/3d/u_track.h"
#include "nel/ligo/primitive.h"
#include "game_share/player_visual_properties.h"
#include "game_share/generic_xml_msg_mngr.h"
#include "game_share/visual_slot_manager.h"
#include "game_share/mode_and_behaviour.h"
#include "game_share/ryzom_version.h"
#include "game_share/brick_types.h"
#include "game_share/time_weather_season/time_and_season.h"
#include "entity_animation_manager.h"
#include "ingame_database_manager.h"
#include "world_database_manager.h"
#include "string_manager_client.h"
#include "interface_v3/input_handler_manager.h"
#include "interface_v3/people_interraction.h"
#include "client_chat_manager.h"
#include "continent_manager.h"
#include "interface_v3/interface_manager.h"
#include "interface_v3/group_compas.h"
#include "init_main_loop.h"
#include "sheet_manager.h"
#include "sound_manager.h"
#include "interface_v3/group_editbox.h"
#include "debug_client.h"
#include "user_entity.h"
#include "time_client.h"
#include "net_manager.h"
#include "pacs_client.h"
#include "continent.h"
#include "ig_client.h"
#include "commands.h"
#include "entities.h"
#include "teleport.h"
#include "cdb_leaf.h"
#include "view.h"
#include "misc.h"
#include "demo.h"
#include "dummy_progress.h"
#include "interface_v3/sphrase_manager.h"
#include "interface_v3/sbrick_manager.h"
#include "interface_v3/inventory_manager.h"
#include "interface_v3/action_handler_help.h"
#include "projectile_manager.h"
#include "fx_manager.h"
#include "actions_client.h"
#include "attack_list.h"
#include "interface_v3/player_trade.h"
#include "interface_v3/ctrl_base_button.h"
#include "weather.h"
#include "forage_source_cl.h"
#include "connection.h"
#include "interface_v3/lua_object.h"
#include "interface_v3/lua_ihm.h"
#include "init.h"
#include "interface_v3/people_interraction.h"
#include "far_tp.h"
#include "zone_util.h"
//
// Only the define FINAL_VERSION can be defined on the project, not in this file
// to desactive some commands
//
////////////////
// Namespaces //
////////////////
using namespace NLMISC;
using namespace NLNET;
using namespace NL3D;
using namespace std;
/////////////
// Externs //
/////////////
extern CGenericXmlMsgHeaderManager GenericMsgHeaderMngr;
extern CEntityAnimationManager *EAM;
extern CClientChatManager ChatMngr;
extern ULandscape *Landscape;
extern UScene *Scene;
extern CLog g_log;
extern CEntityManager EntitiesMngr;
///////////////
// Variables //
///////////////
NLLIGO::CPrimitives *LDPrim = 0;
static std::vector ShapeAddedByCommand; // list of shapes added with the 'shape' command
///////////////
// FUNCTIONS //
///////////////
// Function to release all things allocated for commands.
void releaseCommands()
{
if(LDPrim)
{
delete LDPrim;
LDPrim = 0;
}
}
//////////////
// COMMANDS //
//////////////
// connect to the support chat
NLMISC_COMMAND(supportChat, "connect to the external support chat", "")
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
pIM->connectYuboChat();
return true;
}
// 'follow' : To Follow the target.
NLMISC_COMMAND(follow, "Follow the target", "")
{
// switch
if(UserEntity->follow())
UserEntity->disableFollow();
else
// enable follow, reseting the camera rotation
UserEntity->enableFollow(true);
return true;
}
NLMISC_COMMAND(where, "Ask information on the position", "")
{
// Check parameters.
if(args.size() == 0)
{ // Create the message and send.
const string msgName = "COMMAND:WHERE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("command 'where': unknown message named '%s'", msgName.c_str());
return true;
}
return false;
}
NLMISC_COMMAND(who, "Display all players currently in game","[]")
{
// Check parameters.
if(args.size() > 1)
return false;
CBitMemStream out;
if(!GenericMsgHeaderMngr.pushNameToStream("DEBUG:WHO", out))
{
nlwarning("Unknown message name DEBUG:WHO");
return false;
}
string opt;
if ( args.size() == 1 )
{
opt = args[0];
}
out.serial(opt);
NetMngr.push(out);
return true;
}
NLMISC_COMMAND(afk, "Set the player as 'away from keyboard'","[]")
{
string customText;
if( args.size() > 0 )
{
customText = args[0];
}
for(uint i = 1; i < args.size(); ++i )
{
customText += " ";
customText += args[i];
}
if (UserEntity != NULL)
UserEntity->setAFK(true,customText);
/*
CBitMemStream out;
if(!GenericMsgHeaderMngr.pushNameToStream("DEBUG:AFK", out))
{
nlwarning("Unknown message name DEBUG:AFK");
return false;
}
NetMngr.push(out);
*/
return true;
}
bool randomCheckCharset(std::string const& str)
{
std::string::const_iterator it, itEnd = str.end();
for (it=str.begin(); it!=itEnd; ++it)
if (*it<'0' || *it>'9')
return false;
return true;
}
// returns true if a<=b
bool randomLexicographicLess(std::string a, std::string b)
{
// Remove leading zeros
while (a.length()>1 && a[0]=='0')
a = a.substr(1);
while (b.length()>1 && b[0]=='0')
b = b.substr(1);
// Longest is the biggest
if (a.length()>b.length())
return false;
if (a.length()0) return false;
if (!negative && max<0) return false;
// Check number is not too big nor too small with a lexicographic compare
std::string smin = NLMISC::toString(std::max(min,-min));
std::string smax = NLMISC::toString(std::max(max,-max));
bool tooSmall = false, tooBig = false;
if (min>=0 && randomLexicographicLess(sAbsVal, smin))
tooSmall = true;
if (min<0 && randomLexicographicLess(smin, sAbsVal))
tooSmall = true;
if (max>=0 && randomLexicographicLess(smax, sAbsVal))
tooBig = true;
if (max<0 && randomLexicographicLess(sAbsVal, smax))
tooBig = true;
if (!tooSmall && !tooBig)
{
NLMISC::fromString(str, val);
return true;
}
else
return false;
}
NLMISC_COMMAND(random, "Roll a dice and say the result around","[] ")
{
// Check parameters.
if (args.size()<1 || args.size()>2)
return false;
sint16 min = 1;
sint16 max;
if (!randomFromString(args[0], max))
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
ucstring msg = CI18N::get("uiRandomBadParameter");
strFindReplace(msg, "%s", args[0] );
pIM->displaySystemInfo(msg);
return false;
}
if (args.size()==2)
{
if (!randomFromString(args[1], min))
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
ucstring msg = CI18N::get("uiRandomBadParameter");
strFindReplace(msg, "%s", args[0] );
pIM->displaySystemInfo(msg);
return false;
}
}
if (min>max)
std::swap(min, max);
if (UserEntity != NULL)
UserEntity->rollDice(min, max);
return true;
}
//-----------------------------------------------
// 'dumpShapePos' : Dump Last Added Shape Pos
//-----------------------------------------------
NLMISC_COMMAND(dumpShapePos, "Dump Last Added Shape Pos.", "")
{
#if FINAL_VERSION
if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;
#endif // FINAL_VERSION
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
CInterfaceManager *IM = CInterfaceManager::getInstance();
CVector pos = ShapeAddedByCommand.back().getPos();
IM->displaySystemInfo(ucstring(toString("Shape Pos = %f, %f, %f", pos.x, pos.y, pos.z)));
return true;
}
//-----------------------------------------------
// 'clearShape' : Remove all shapes added with the 'shape' command
//-----------------------------------------------
NLMISC_COMMAND(clearShape, "Remove all shapes added with the 'shape' command.", "")
{
#if FINAL_VERSION
/*if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
if (!Scene) return false;
for(uint k = 0; k < ShapeAddedByCommand.size(); ++k)
{
Scene->deleteInstance(ShapeAddedByCommand[k]);
}
ShapeAddedByCommand.clear();
return true;
}
//-----------------------------------------------------
// 'setShapeX' : Set X position for last created shape
//-----------------------------------------------------
NLMISC_COMMAND(setShapeX, "Set X position for last created shape.", "")
{
#if FINAL_VERSION
/*if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if (args.size() != 1) return false;
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
float coord;
bool valid_coord;
if (args[0][0] == '+')
valid_coord = fromString(args[0].substr(1), coord);
else
valid_coord = fromString(args[0], coord);
if (!valid_coord)
{
nlwarning("Can't get position");
return false;
}
CVector pos = ShapeAddedByCommand.back().getPos();
if (args[0][0] == '+')
pos.x += coord;
else
pos.x = coord;
ShapeAddedByCommand.back().setPos(pos);
return true;
}
//-----------------------------------------------------
// 'setShapeY' : Set Y position for last created shape
//-----------------------------------------------------
NLMISC_COMMAND(setShapeY, "Set Y position for last created shape.", "")
{
#if FINAL_VERSION
/*if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if (args.size() != 1) return false;
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
float coord;
bool valid_coord;
if (args[0][0] == '+')
valid_coord = fromString(args[0].substr(1), coord);
else
valid_coord = fromString(args[0], coord);
if (!valid_coord)
{
nlwarning("Can't get position");
return false;
}
CVector pos = ShapeAddedByCommand.back().getPos();
if (args[0][0] == '+')
pos.y += coord;
else
pos.y = coord;
ShapeAddedByCommand.back().setPos(pos);
return true;
}
//-----------------------------------------------------
// 'setShapeZ' : Set Z position for last created shape
//-----------------------------------------------------
NLMISC_COMMAND(setShapeZ, "Set Z position for last created shape.", "")
{
#if FINAL_VERSION
/*if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if (args.size() != 1) return false;
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
float coord;
bool valid_coord;
if (args[0][0] == '+')
valid_coord = fromString(args[0].substr(1), coord);
else
valid_coord = fromString(args[0], coord);
if (!valid_coord)
{
nlwarning("Can't get position");
return false;
}
CVector pos = ShapeAddedByCommand.back().getPos();
if (args[0][0] == '+')
pos.z += coord;
else
pos.z = coord;
ShapeAddedByCommand.back().setPos(pos);
return true;
}
//-----------------------------------------------------
// 'setShapeDir' : Set direction angle for last created shape
//-----------------------------------------------------
NLMISC_COMMAND(setShapeDir, "Set direction angle for last created shape.", "")
{
#if FINAL_VERSION
/*if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if (args.size() != 1) return false;
if (ShapeAddedByCommand.empty())
{
nlwarning("No shape created yet");
return false;
}
float angle;
if (!fromString(args[0], angle))
{
nlwarning("Can't get angle");
return false;
}
CMatrix dir;
dir.identity();
CVector vangle = CVector(sin(angle), cos(angle), 0.f);
CVector vi = vangle^CVector(0.f, 0.f, 1.f);
CVector vk = vi^vangle;
dir.setRot(vi, vangle, vk, true);
// Set Orientation : User Direction should be normalized.
ShapeAddedByCommand.back().setRotQuat(dir.getRot());
return true;
}
//-----------------------------------------------
// 'shape' : Add a shape in the scene.
//-----------------------------------------------
NLMISC_COMMAND(shape, "Add a shape in the scene.", "")
{
#if FINAL_VERSION
/* if (!hasPrivilegeDEV() &&
!hasPrivilegeSGM() &&
!hasPrivilegeGM() &&
!hasPrivilegeVG() &&
!hasPrivilegeSG() &&
!hasPrivilegeG() &&
!hasPrivilegeEM() &&
!hasPrivilegeEG())
return true;*/
#endif // FINAL_VERSION
if(args.size() < 1)
{
nlwarning("Command 'shape': need at least 1 parameter, try '/help shape' for more details.");
return false;
}
if (!Scene)
{
nlwarning("No scene available");
return false;
}
UInstance instance = Scene->createInstance(args[0]);
if(!instance.empty())
{
ShapeAddedByCommand.push_back(instance);
// Set the position
instance.setPos(UserEntity->pos());
instance.setClusterSystem(UserEntity->getClusterSystem()); // for simplicity, assume it is in the same
// cluster system than the user
// Compute the direction Matrix
CMatrix dir;
dir.identity();
CVector vi = UserEntity->dir()^CVector(0.f, 0.f, 1.f);
CVector vk = vi^UserEntity->dir();
dir.setRot(vi, UserEntity->dir(), vk, true);
// Set Orientation : User Direction should be normalized.
instance.setRotQuat(dir.getRot());
// if the shape is a particle system, additionnal parameters are user params
UParticleSystemInstance psi;
psi.cast (instance);
if (!psi.empty())
{
// set each user param that is present
for(uint k = 0; k < 4; ++k)
{
if (args.size() >= (k + 2))
{
float uparam;
if (fromString(args[k + 1], uparam))
{
psi.setUserParam(k, uparam);
}
else
{
nlwarning("Cant read param %d", k);
}
}
}
}
}
else
{
nlwarning("Command 'shape': cannot find the shape %s.", args[0].c_str());
}
// Command Well Done
return true;
}
NLMISC_COMMAND(bugReport, "Call the bug report tool with dump", "")
{
const char *brname[] = { "bug_report.exe", "bug_report_r.exe", "bug_report_rd.exe", "bug_report_df.exe", "bug_report_d.exe" };
string brn;
for (uint i = 0; i < sizeof(brname)/sizeof(brname[0]); i++)
{
if (CFile::fileExists (brname[i]))
{
brn = brname[i];
break;
}
}
if (brn.empty())
{
log.displayNL("bug_report*.exe not found");
return false;
}
string sys;
sys = "Language "+CI18N::getCurrentLanguageName().toString() +" ";
if (args.size()>0)
{
uint8 quality;
fromString(args[0], quality);
if (quality == 0)
quality = 80;
CBitmap btm;
Driver->getBuffer(btm);
string filename = CFile::findNewFile (getLogDirectory() + "screenshot.jpg");
COFile fs(filename);
btm.writeJPG(fs, quality);
sys += "AttachedFile "+filename+" ";
}
sys += "ClientVersion "RYZOM_VERSION" ";
// for now, set the same version than client one
sys += "ShardVersion "RYZOM_VERSION" ";
if (ClientCfg.Local)
sys += "ShardName OFFLINE ";
FILE *fp = fopen (std::string(getLogDirectory() + "bug_report.txt").c_str(), "wb");
if (fp != NULL)
{
string res = addSlashR(getDebugInformation());
// must put \r\n each line
fprintf(fp, "%s", res.c_str());
// // must put \r\n each line
// fprintf (fp, "UserId: %u\r\n", NetMngr.getUserId());
// fprintf (fp, "Player Name: '%s'.\r\n", UserEntity->getName().toString().c_str());
// fprintf (fp, "UserPosition: %.2f %.2f %.2f\r\n", UserEntity->pos().x, UserEntity->pos().y, UserEntity->pos().z);
// fprintf (fp, "ViewPosition: %.2f %.2f %.2f\r\n", View.viewPos().x, View.viewPos().y, View.viewPos().z);
// time_t ts; time( &ts );
// fprintf (fp, "LocalTime: %s\r\n", NLMISC::IDisplayer::dateToHumanString( ts ) );
// fprintf (fp, "ServerTick: %u\r\n", NetMngr.getCurrentServerTick());
// fprintf (fp, "ConnectState: %s\r\n", NetMngr.getConnectionStateCStr());
// fprintf (fp, "LocalAddress: %s\r\n", NetMngr.getAddress().asString().c_str());
fclose (fp);
sys += "DumpFilename bug_report.txt ";
}
nlinfo ("Calling for bug report : '%s %s'", brn.c_str(), sys.c_str());
launchProgram(brn, sys);
// give some cpu to the launched application
nlSleep (3000);
return true;
}
//
//
// This command is use to do all admin execution commands on you
//
// For example: "/a God 1" will set you in god mode
//
NLMISC_COMMAND(a, "Execute an admin command on you"," ")
{
if(args.size() == 0)
return false;
CBitMemStream out;
if (!GenericMsgHeaderMngr.pushNameToStream("COMMAND:ADMIN", out))
return false;
string cmd, arg;
cmd = args[0];
for (uint i = 1; i < args.size(); i++)
{
// temporary fix for utf-8
// servers commands are not decoded so convert them to ansi
std::string tmp = ucstring::makeFromUtf8(args[i]).toString();
if (!arg.empty())
arg += ' ';
if (tmp.find(' ') != std::string::npos)
{
arg += "\"" + tmp + "\"";
}
else
{
arg += tmp;
}
}
bool onTarget = false;
out.serial (onTarget);
out.serial (cmd);
out.serial (arg);
NetMngr.push (out);
return true;
}
//
//
// This command is use to do all admin execution commands on the target
//
// For example: "/b God 1" will set the target in god mod
//
NLMISC_COMMAND(b, "Execute an admin command on your target"," ")
{
if(args.size() == 0)
return false;
CBitMemStream out;
if (!GenericMsgHeaderMngr.pushNameToStream("COMMAND:ADMIN", out))
return false;
string cmd, arg;
cmd = args[0];
for (uint i = 1; i < args.size(); i++)
{
// temporary fix for utf-8
// servers commands are not decoded so convert them to ansi
std::string tmp = ucstring::makeFromUtf8(args[i]).toString();
if (!arg.empty())
arg += ' ';
if (tmp.find(' ') != std::string::npos)
{
arg += "\"" + tmp + "\"";
}
else
{
arg += tmp;
}
}
bool onTarget = true;
out.serial (onTarget);
out.serial (cmd);
out.serial (arg);
NetMngr.push (out);
return true;
}
//
//
// This command is used to do all admin execution commands on a character
//
// For example: "/c charName God 1" will set god mod on character if it's online, or keep
// command for wait character login
//
NLMISC_COMMAND(c, "Execute an admin command on character name"," ")
{
if(args.size() < 2)
return false;
CBitMemStream out;
if (!GenericMsgHeaderMngr.pushNameToStream("COMMAND:ADMIN_OFFLINE", out))
return false;
string characterName, cmd, arg;
characterName = args[0];
cmd = args[1];
for (uint i = 2; i < args.size(); i++)
{
// temporary fix for utf-8
// servers commands are not decoded so convert them to ansi
std::string tmp = ucstring::makeFromUtf8(args[i]).toString();
if (!arg.empty())
arg += ' ';
if (tmp.find(' ') != std::string::npos)
{
arg += "\"" + tmp + "\"";
}
else
{
arg += tmp;
}
}
out.serial (characterName);
out.serial (cmd);
out.serial (arg);
NetMngr.push (out);
return true;
}
NLMISC_COMMAND(boxes, "Show/Hide selection boxes", "[ : 0 to Hide, anything else to Show. Invert the current state if nothing specified.]")
{
#if FINAL_VERSION
if (!ClientCfg.ExtendedCommands) return false;
if( !ClientCfg.Local && !hasPrivilegeDEV() && !hasPrivilegeSGM() && !hasPrivilegeGM() )
return true;
#endif // FINAL_VERSION
// Invert Current State
if(args.size() == 0)
{
// Invert the current value.
ClientCfg.DrawBoxes = !ClientCfg.DrawBoxes;
return true;
}
// Set Current State
else if(args.size() == 1)
{
// Invert the current value.
fromString(args[0], ClientCfg.DrawBoxes);
return true;
}
// Bad parameters.
else
return false;
}
NLMISC_COMMAND(dump, "Command to create a file with the current state of the client", "[]")
{
if(args.size() > 1)
return false;
string dumpName;
if(args.size() == 1)
dumpName = args[0];
else
dumpName = "default";
dump(dumpName);
return true;
}
NLMISC_COMMAND(verbose, "Enable/Disable some Debug Information", "none or magic")
{
// Check parameters.
if(args.size() != 1)
{
// Help
CInterfaceManager *IM = CInterfaceManager::getInstance();
IM->displaySystemInfo(ucstring("This command need 1 parameter :"));
IM->displaySystemInfo(ucstring(" :"));
IM->displaySystemInfo(ucstring("- none(to remove all verboses)"));
IM->displaySystemInfo(ucstring("- magic(to add debug infos about magic)"));
IM->displaySystemInfo(ucstring("- anim (to add debug infos about animation)"));
}
else
{
std::string type = NLMISC::strlwr(args[0]);
if (type == "none")
Verbose = VerboseNone;
else if(type == "magic")
Verbose |= VerboseMagic;
else if(type == "anim")
Verbose |= VerboseAnim;
else
{
CInterfaceManager *IM = CInterfaceManager::getInstance();
IM->displaySystemInfo(ucstring("This command need 1 parameter :"));
IM->displaySystemInfo(ucstring(" :"));
IM->displaySystemInfo(ucstring("- none(to remove all verboses)"));
IM->displaySystemInfo(ucstring("- magic(to add debug infos about magic)"));
IM->displaySystemInfo(ucstring("- anim (to add debug infos about animation)"));
}
}
return true;
}
NLMISC_COMMAND(verboseAnimSelection, "Enable/Disable the animation log for the current selection", "")
{
// Check parameters.
if(args.size() != 0)
return false;
VerboseAnimSelection = !VerboseAnimSelection;
if(VerboseAnimSelection)
nlinfo("Enable VerboseAnimSelection");
else
nlinfo("Disable VerboseAnimSelection");
return true;
}
NLMISC_COMMAND(verboseAnimUser, "Enable/Disable the animation log for the user", "")
{
// Check parameters.
if(args.size() != 0)
return false;
VerboseAnimUser = !VerboseAnimUser;
if(VerboseAnimUser)
nlinfo("Enable VerboseAnimUser");
else
nlinfo("Disable VerboseAnimUser");
return true;
}
NLMISC_COMMAND(verboseDatabase, "Enable/Disable the log for the database", "")
{
// Check parameters.
if(args.size() != 0)
return false;
VerboseDatabase = !VerboseDatabase;
if(VerboseDatabase)
nlinfo("Enable VerboseDatabase");
else
nlinfo("Disable VerboseDatabase");
return true;
}
NLMISC_COMMAND(verbosePropertiesLoggingMode, "Set logging mode", "")
{
// Check parameters.
if(args.size() != 0)
return false;
CNetworkConnection::LoggingMode = !CNetworkConnection::LoggingMode;
if(CNetworkConnection::LoggingMode)
nlinfo("Enable LoggingMode");
else
nlinfo("Disable LoggingMode");
return true;
}
NLMISC_COMMAND(logEntities, "Write the position and orientation af all entities in the vision in the file 'entities.txt'", "")
{
// Check parameters
if(args.size() != 0)
return false;
// Log entities
EntitiesMngr.writeEntities();
// Command well done.
return true;
}
NLMISC_COMMAND(log, "Add/Del Positive/Negative Filters for logs", "Log System , Type , Filter ")
{
// check args, if there s not the right number of parameter, return bad
if(args.size() < 2 || args.size() > 3)
return false;
CLog *logSys;
// Debug log system.
if (string(args[0].c_str()) == "debug")
logSys = DebugLog;
// Info log system.
else if(string(args[0].c_str()) == "info")
logSys = InfoLog;
// Warning log system.
else if(string(args[0].c_str()) == "warning")
logSys = WarningLog;
// Assert log system.
else if(string(args[0].c_str()) == "assert")
logSys = AssertLog;
// Unknown Log System -> return false.
else
return false;
// Add a positive filter.
if (string(args[1].c_str()) == "pos")
logSys->addPositiveFilter(args[2].c_str());
// Add a negative filter.
else if(string(args[1].c_str()) == "neg")
logSys->addNegativeFilter(args[2].c_str());
// Removes a filter by name (in both filters).
else if(string(args[1].c_str()) == "del")
logSys->removeFilter(args[2].c_str());
// Reset both filters.
else if(string(args[1].c_str()) == "reset")
logSys->resetFilters();
// Unknown Filter -> return false.
else
return false;
// Command well done.
return true;
}
NLMISC_COMMAND(execScript, "Execute a script file (.cmd)","")
{
int size = (int)args.size();
if (size != 1)
return false;
CIFile iFile;
if (iFile.open(CPath::lookup(args[0], false)))
{
char line[512];
char *buffer;
// Read line by line and execute each line
sint inComment= 0;
bool eof = false;
while (!eof)
{
buffer = &line[0];
uint read = 0;
for(;;)
{
if (read == 512 -1)
{
*buffer = '\0';
break;
}
try
{
// read one byte
iFile.serialBuffer ((uint8 *)buffer, 1);
}
catch (EFile &)
{
*buffer = '\0';
eof = true;
break;
}
if (*buffer == '\n')
{
*buffer = '\0';
break;
}
// skip '\r' char
if (*buffer != '\r')
{
buffer++;
read++;
}
}
// execute line
if (strlen(line) > 0)
{
// if not a single comment
if(strncmp(line, "//", 2)!=0)
{
if(strncmp(line, "/*", 2)==0)
inComment++;
if(inComment<=0)
{
ucstring ucline(line);
CInterfaceManager::parseTokens(ucline);
ICommand::execute(ucline.toUtf8(), g_log);
}
if(strncmp(line, "*/", 2)==0)
inComment--;
}
}
// end?
if (iFile.eof())
eof = true;
}
iFile.close();
}
else
{
CInterfaceManager::getInstance()->displaySystemInfo(ucstring("Cannot open file"));
}
return true;
}
NLMISC_COMMAND(db, "Modify Database"," ")
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
int size = (int)args.size();
if (size == 2)
{
#if !FINAL_VERSION
// check if 2nd arg is a sheet name
if (args[1].empty()) return false;
sint64 value;
if (isalpha(args[1][0]))
{
CSheetId sheet(args[1]);
value = (sint64) sheet.asInt();
}
else
{
// Convert the string into an sint64.
fromString(args[1], value);
}
// Set the property.
CCDBNodeLeaf *node= pIM->getDbProp(args[0], false);
if(node)
node->setValue64(value);
else
pIM->displaySystemInfo(toString("DB %s don't exist.", args[0].c_str()));
#else
pIM->displaySystemInfo(ucstring("Can't write to DB when in Final Version."));
#endif
}
else if (size == 1)
{
CCDBNodeLeaf *node= pIM->getDbProp(args[0], false);
if(node)
{
sint64 prop = node->getValue64();
string str = toString(prop);
pIM->displaySystemInfo(ucstring(str));
nlinfo("%s", str.c_str());
}
else
pIM->displaySystemInfo(toString("DB %s don't exist.", args[0].c_str()));
return true;
}
else
return false;
return true;
}
static bool talkInChan(uint32 nb,std::vectorargs)
{
uint32 maxChans = CChatGroup::MaxDynChanPerPlayer;
if (nb>=maxChans)
{
return false;
}
if(args.size()>0)
{
std::string tmp="";
std::vector::const_iterator first(args.begin()),last(args.end());
for(;first!=last;++first)
{
tmp = tmp + (*first);
tmp = tmp+" ";
}
ucstring uctmp;
uctmp.fromUtf8(tmp);
PeopleInterraction.talkInDynamicChannel(nb, uctmp);
return true;
}
else
{
ChatMngr.updateChatModeAndButton(CChatGroup::dyn_chat, nb);
}
return false;
}
NLMISC_COMMAND(0,"talk in 0th dynamic chat channel"," ")
{
return talkInChan(0,args);
}
NLMISC_COMMAND(1,"talk in first dynamic chat channel"," ")
{
return talkInChan(1,args);
}
NLMISC_COMMAND(2,"talk in 2nd dynamic chat channel"," ")
{
return talkInChan(2,args);
}
NLMISC_COMMAND(3,"talk in 3rd dynamic chat channel"," ")
{
return talkInChan(3,args);
}
NLMISC_COMMAND(4,"talk in 4th dynamic chat channel"," ")
{
return talkInChan(4,args);
}
NLMISC_COMMAND(5,"talk in 5th dynamic chat channel"," ")
{
return talkInChan(5,args);
}
NLMISC_COMMAND(6,"talk in 6th dynamic chat channel"," ")
{
return talkInChan(6,args);
}
NLMISC_COMMAND(7,"talk in 7th dynamic chat channel"," ")
{
return talkInChan(7,args);
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////// COMMANDS after should NOT appear IN the FINAL VERSION ///////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
#if !FINAL_VERSION
NLMISC_COMMAND(ah, "Launch an action handler", " ")
{
if (args.size() == 0)
return false;
if (!ClientCfg.AllowDebugLua && strlwr(args[0]) == "lua")
{
return false; // not allowed!!
}
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if (args.size() == 1)
{
pIM->runActionHandler(args[0], NULL);
}
else
{
pIM->runActionHandler(args[0], NULL, args[1]);
}
return true;
}
static void setDynString(uint32 strID, const std::string &value)
{
STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance();
pSMC->receiveString(strID, ucstring(value));
CBitMemStream bm;
if (bm.isReading()) bm.invert();
bm.serial(strID);
bm.serial(strID);
bm.invert();
bm.seek(0, NLMISC::IStream::begin);
pSMC->receiveDynString(bm);
}
// for debug purposes, insert a string in the
NLMISC_COMMAND(setDynString, "set a dynamic string"," ")
{
if (args.size() != 2) return false;
uint32 strID;
fromString(args[0], strID);
setDynString(strID, args[1]);
return true;
}
class CAnimProgress : public IProgressCallback
{
public:
// Update the progress bar
virtual void progress (float value)
{
// can't do anything if no driver
if(Driver == NULL)
return;
// Get croped value
value = getCropedValue (value);
// Set matrix
Driver->setMatrixMode2D11();
// Display a progress bar background
Driver->drawQuad (PROGRESS_BAR_LEFT, 0.5f-PROGRESS_BAR_HEIGHT/2.0f, PROGRESS_BAR_LEFT+ PROGRESS_BAR_WIDTH, 0.5f+PROGRESS_BAR_HEIGHT/2.0f,
PROGRESS_BAR_BG_COLOR);
// Display a progress bar
Driver->drawQuad (PROGRESS_BAR_LEFT, 0.5f-PROGRESS_BAR_HEIGHT/2.0f, PROGRESS_BAR_LEFT+value*PROGRESS_BAR_WIDTH, 0.5f+PROGRESS_BAR_HEIGHT/2.0f,
PROGRESS_BAR_COLOR);
if(TextContext != NULL)
{
// Init the Pen.
TextContext->setKeep800x600Ratio(false);
TextContext->setColor(CRGBA(255,255,255));
TextContext->setFontSize(20);
TextContext->setHotSpot(UTextContext::MiddleMiddle);
// Display the Text.
TextContext->printfAt(0.5f, 0.5f, _ProgressMessage.c_str());
}
// Display to screen.
Driver->swapBuffers();
}
// New message
void newMessage(const std::string &message) {_ProgressMessage = message;}
private:
std::string _ProgressMessage;
};
NLMISC_COMMAND(reloadSearchPaths, "reload the search paths","")
{
if (!args.empty()) return false;
CPath::memoryUncompress();
CAnimProgress progress;
// remove all objects that may depend on an animation
CProjectileManager::getInstance().reset();
// Pathes
progress.newMessage("Reloading pathes");
progress.progress(0.0f);
progress.pushCropedValues(0.0f, 1.0f);
//
addSearchPaths(progress);
CPath::memoryCompress();
return true;
}
NLMISC_COMMAND(reloadAnim, "reload animations","")
{
CPath::memoryUncompress();
CAnimProgress dummy;
// remove all objects that may depend on an animation
CProjectileManager::getInstance().reset();
// Pathes
dummy.newMessage("Pathes");
dummy.progress(0.0f);
dummy.pushCropedValues(0.0f, 0.5f);
addSearchPaths(dummy);
if (ClientCfg.UpdatePackedSheet)
{
for(uint i = 0; i < ClientCfg.UpdatePackedSheetPath.size(); i++)
{
dummy.progress((float)i/(float)ClientCfg.UpdatePackedSheetPath.size());
dummy.pushCropedValues ((float)i/(float)ClientCfg.UpdatePackedSheetPath.size(), (float)(i+1)/(float)ClientCfg.UpdatePackedSheetPath.size());
CPath::addSearchPath(ClientCfg.UpdatePackedSheetPath[i], true, false, &dummy);
dummy.popCropedValues();
}
}
dummy.popCropedValues();
// Animations
dummy.newMessage("Anims");
dummy.progress(0.5f);
dummy.pushCropedValues(0.5f, 1.0f);
EAM->load(dummy, true);
dummy.popCropedValues();
// Reload Animations
EntitiesMngr.reloadAnims();
CPath::memoryCompress();
return true;
}
NLMISC_COMMAND(reloadAttack, "reload attack", "")
{
if (!args.empty()) return false;
// remove all objects that may depend on an animation
ClientSheetsStrings.memoryUncompress();
CProjectileManager::getInstance().reset();
EntitiesMngr.removeAllAttachedFX();
FXMngr.reset();
//
std::vector exts;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// replace attack list of creature in place (so pointers on character sheets remains valid)
CSheetManager sheetManager;
exts;
// exts.push_back("creature");
exts.push_back("race_stats");
NLMISC::IProgressCallback progress;
sheetManager.loadAllSheet(progress, true, false, false, true, &exts);
//
const CSheetManager::TEntitySheetMap &sm = SheetMngr.getSheets();
for(CSheetManager::TEntitySheetMap::const_iterator it = sm.begin(); it != sm.end(); ++it)
{
if (it->second.EntitySheet && it->second.EntitySheet->Type == CEntitySheet::FAUNA)
{
// find matching sheet in new sheetManager
const CEntitySheet *other = sheetManager.get(it->first);
if (other)
{
// replace data in place
((CCharacterSheet &) *it->second.EntitySheet).AttackLists = ((const CCharacterSheet &) *other).AttackLists;
}
}
else if(it->second.EntitySheet && it->second.EntitySheet->Type == CEntitySheet::RACE_STATS)
{
// find matching sheet in new sheetManager
const CEntitySheet *other = sheetManager.get(it->first);
if (other)
{
// replace data in place
((CRaceStatsSheet &) *it->second.EntitySheet).AttackLists = ((const CRaceStatsSheet &) *other).AttackLists;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAttackListManager::getInstance().release();
// form to reload all sheets of interest
exts.clear();
exts.push_back("attack_list");
exts.push_back("animation_fx_set");
exts.push_back("id_to_string_array");
CDummyProgress dp;
SheetMngr.loadAllSheet(dp, true, false, true, true, &exts);
CAttackListManager::getInstance().init();
//
ClientSheetsStrings.memoryCompress();
return true;
}
NLMISC_COMMAND(reloadSky, "reload new style sky", "")
{
if (!args.empty()) return false;
ContinentMngr.reloadSky();
return false;
}
NLMISC_COMMAND(missionReward, "debug"," ")
{
if (args.size() == 1)
{
uint8 index;
fromString(args[0], index);
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream("BOTCHAT:COMPLETE_MISSION", out))
{
out.serial(index);
NetMngr.push(out);
}
else
nlwarning(" : unknown message name : BOTCHAT:COMPLETE_MISSION");
return true;
}
return false;
}
NLMISC_COMMAND(missionProgress, "debug"," ")
{
if (args.size() == 1)
{
uint8 index;
fromString(args[0], index);
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream("BOTCHAT:PROGRESS_MISSION", out))
{
out.serial(index);
NetMngr.push(out);
}
else
nlwarning(" : unknown message name : BOTCHAT:PROGRESS_MISSION");
return true;
}
return false;
}
/*
NLMISC_COMMAND( displayDBModifs, "display server database modification in the chat window"," ")
{
if ( VerboseDatabase )
CInterfaceManager::getInstance()->getChatOutput()->addTextChild(ucstring("the database is already in verbose mode"),CRGBA(255,255,255,255));
else
{
CInterfaceManager::getInstance()->getChatOutput()->addTextChild(ucstring("database is now in verbose mode"),CRGBA(255,255,255,255));
VerboseDatabase = true;
}
return true;
}
NLMISC_COMMAND( hideDBModifs, "stop displaying server database modification in the chat window"," ")
{
if ( !VerboseDatabase )
CInterfaceManager::getInstance()->getChatOutput()->addTextChild(ucstring("the database is already not in verbose mode"),CRGBA(255,255,255,255));
else
{
CInterfaceManager::getInstance()->getChatOutput()->addTextChild(ucstring("database is not in verbose mode anymore"),CRGBA(255,255,255,255));
VerboseDatabase = false;
}
return true;
}
*/
/*
NLMISC_COMMAND(save_sentences, "save sentences"," ")
{
CSentenceDisplayer::saveSentences();
return true;
}
*/
NLMISC_COMMAND(getSheetId, "get_sheet_id","")
{
if (args.size() != 1)
return false;
CSheetId id(args[0]);
CInterfaceManager::getInstance()->displaySystemInfo(ucstring(toString(id.asInt())));
return true;
}
NLMISC_COMMAND(getSheetName, "get_sheet_name","")
{
if (args.size() != 1)
return false;
uint32 nId;
fromString(args[0], nId);
CSheetId id( nId );
string name = id.toString();
CInterfaceManager::getInstance()->displaySystemInfo(ucstring(name));
return true;
}
NLMISC_COMMAND(forgetAll, "forget all bricks", "")
{
// Check parameters.
if(args.size() != 0)
{
return false;
}
char buf[100];
for (uint i = 0;i<20;i++)
{
sprintf(buf,"SERVER:BRICK_FAMILY:%d:BRICKS",i);
CCDBNodeLeaf * node= CInterfaceManager::getInstance()->getDbProp(buf);
node->setValue64(0);
}
return true;
}
NLMISC_COMMAND(usePreprogMagic, "use the specified magic preprog sentence", "")
{
// Check parameters.
if(args.size() != 1)
{
return false;
}
// Create the message for the server to execute a phrase.
const string msgName = "SENTENCE:EXECUTE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream("SENTENCE:EXECUTE", out))
{
uint8 phrase;
fromString(args[0], phrase);
out.serial(phrase);
BRICK_TYPE::EBrickType type = BRICK_TYPE::MAGIC;
out.serialEnum( type );
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(usePreprogCombat, "use the specified combat preprog sentence", "")
{
// Check parameters.
if(args.size() != 1)
{
return false;
}
// Create the message for the server to execute a phrase.
const string msgName = "SENTENCE:EXECUTE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream("SENTENCE:EXECUTE", out))
{
uint8 phrase;
fromString(args[0], phrase);
out.serial(phrase);
BRICK_TYPE::EBrickType type = BRICK_TYPE::COMBAT;
out.serialEnum( type );
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(engage, "engage target in combat", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "COMBAT:ENGAGE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(defaultAttack, "use default attack on target", "")
{
// Default attack on the current selection.
UserEntity->attack();
// Well Done.
return true;
}
NLMISC_COMMAND(disengage, "disengage from combat", "")
{
// Disengage from combat.
UserEntity->disengage();
// Well Done.
return true;
}
NLMISC_COMMAND(leaveTeam, "leave team", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "TEAM:LEAVE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(joinTeam, "join the specified team", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "TEAM:JOIN";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(joinTeamProposal, "propose to current target to join the team", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "TEAM:JOIN_PROPOSAL";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(joinTeamDecline, "decline a join team proposal", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "TEAM:JOIN_PROPOSAL_DECLINE";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(kickTeammate, "kick someone from your team", "")
{
// Create the message for the server to execute a phrase.
const string msgName = "TEAM:KICK";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("mainLoop : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(cancelCurrentSentence, "cancel the sentence being executed", "")
{
// no parameter needed
// Create the message for the server to cancel the phrase being executed
const string msgName = "SENTENCE:CANCEL_CURRENT";
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream(msgName, out))
{
NetMngr.push(out);
}
else
nlwarning("command : unknown message name : '%s'", msgName.c_str());
return true;
}
NLMISC_COMMAND(cancelAllPhrases, "cancel all the phrases being executed", "")
{
// no parameter needed
UserEntity->cancelAllPhrases();
return true;
}
/*
NLMISC_COMMAND(drop,"drop an item to the ground","")
{
if( args.size() < 1 )
{
return false;
}
uint32 id;
fromString(args[0], id);
CEntityId itemId(RYZOMID::object,id);
sint32 x = (sint32)UserEntity->pos().x * 1000;
sint32 y = (sint32)UserEntity->pos().y * 1000;
sint32 z = (sint32)UserEntity->pos().z * 1000;
CBitMemStream bms;
string msgType = "ITEM:DROP";
if( GenericMsgHeaderMngr.pushNameToStream(msgType,bms) )
{
bms.serial( itemId );
bms.serial( x );
bms.serial( y );
bms.serial( z );
NetMngr.push( bms );
nldebug(" sending 'ITEM:DROP' message to server");
}
else
{
nlwarning(" unknown message name : ITEM:DROP");
}*
return true;
}
*/
NLMISC_COMMAND(pos, "Change the position of the user (in local only)", " OR 1 name of 'tp.teleport_list'. or a bot name")
{
CVectorD newPos;
// Named destination.
if(args.size() == 1)
{
string dest = args[0];
newPos = CTeleport::getPos(NLMISC::strlwr(dest));
if(newPos == CTeleport::Unknown)
{
//here we try to teleport to a bot destination
CBitMemStream out;
if(GenericMsgHeaderMngr.pushNameToStream("TP:BOT", out))
{
string str = args[0];
out.serial( str );
nldebug("/pos: TP:BOT sent");
NetMngr.push(out);
}
else
nlwarning("/pos: unknown message name : 'TP:BOT'");
return true;
}
}
// Teleport to anywhere.
else if(args.size() == 2 || args.size() == 3)
{
fromString(args[0], newPos.x);
fromString(args[1], newPos.y);
if(args.size() == 3)
fromString(args[2], newPos.z);
else
newPos.z = 0.0;
}
// Bad argument number.
else
return false;
/*
CANNOT USE ProgressBar here, because it does pumpEvents(), and
ICommand::execute() is typically called from a pumpEvents() too...
=> cause crash
*/
// Fade out the Game Sound
if(SoundMngr)
SoundMngr->fadeOutGameSound(ClientCfg.SoundTPFade);
// Remove the selection.
UserEntity->selection(CLFECOMMON::INVALID_SLOT);
// Remove the target.
UserEntity->targetSlot(CLFECOMMON::INVALID_SLOT);
// Change the position of the entity and in Pacs.
UserEntity->pos(newPos);
// Select the closest continent from the new position.
CDummyProgress progress;
ContinentMngr.select(newPos, progress);
// Teleport the User.
UserEntity->tp(newPos);
// First frame (for sound fade in)
extern bool FirstFrame;
FirstFrame = true;
return true;
}
NLMISC_COMMAND(removeEntity, "Remove an entity", "")
{
if (args.size() != 1) return false;
uint slot;
fromString(args[0], slot);
EntitiesMngr.remove(slot, true);
return true;
}
NLMISC_COMMAND(entity, "Create an entity on the user or just remove it if Form not valid", "