Skip to content

Commit

Permalink
Added GenericDocument + Reader API to AngelScript
Browse files Browse the repository at this point in the history
New script API:
* class GenericDocumentClass - loads/saves a document from OGRE resource system
* class GenericDocReaderClass - traverses document tokens
* enum TokenType (TOKEN_TYPE_*)
* enum GenericDocumentOptions (GENERIC_DOCUMENT_OPTION_*)
* function ImGui::AlignTextToFramePadding()

New features of demo_script.as:
* a "View document" button next to the vehicle name - it will tokenize the truck definition file and open a separate window with syntax-highlighted file contents.

Known issues:
* DearIMGUI windows opened by script can't be closed with X button. This is a global flaw in our DearIMGUI integration
* GenericDocument doesn't understand the truck title (first nonempty noncomment line).
* There are glitches in parsing naked strings - an extra bool is emitted instead.
* There are glitches in parsing keywords - a string is emitted instead.

Codechanges:
* GenericFileFormat: added RefCountingObject logic. Renamed Document to GenericDocument, added funcs {LoadFrom/SaveTo}Resource(). Renamed Reader to GenericDocReader, GetArg*() funcs renamed to GetTok*() - the Arg naming was to match existing parsers which is now moot. Added {Get/Is}TokComment(). Renamed GetType() to GetTokType().
* Actor + ActorAngelscript: added func getTruckFileResourceGroup() - name chosen to match existing getTruckFileName().
* ImGuiAngelscript - added binding of ImGui::AlignTextToFramePadding()
  • Loading branch information
ohlidalp committed Dec 9, 2022
1 parent b6a3516 commit 7cc2227
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 55 deletions.
95 changes: 89 additions & 6 deletions resources/scripts/demo_script.as
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Collect and show stats (i.e. frame count, total time)
* Read/Write cvars (RoR.cfg values, cli args, game state...)
* View and update game state (current vehicle...)
* Parse and display definition files with syntax highlighting.
There are 3 ways of invoking a script:
1. By defining it with terrain, see terrn2 fileformat, section '[Scripts]':
Expand All @@ -21,13 +22,13 @@
For introduction to game events, read
https://docs.rigsofrods.org/terrain-creation/scripting/.
For reference manual of the interface, see
https://github.com/RigsOfRods/rigs-of-rods/tree/master/doc/angelscript.
Scripting documentation:
https://developer.rigsofrods.org/d4/d07/group___script2_game.html
---------------------------------------------------------------------------
*/


/*
---------------------------------------------------------------------------
Global variables
Expand All @@ -38,6 +39,7 @@ CVarClass@ g_app_state = console.cVarFind("app_state"); // 0=bootstrap, 1=main
CVarClass@ g_sim_state = console.cVarFind("sim_state"); // 0=off, 1=running, 2=paused, 3=terrain editor, see SimState in Application.h
CVarClass@ g_mp_state = console.cVarFind("mp_state"); // 0=disabled, 1=connecting, 2=connected, see MpState in Application.h
CVarClass@ g_io_arcade_controls = console.cVarFind("io_arcade_controls"); // bool
GenericDocumentClass@ g_displayed_document = null;

/*
---------------------------------------------------------------------------
Expand Down Expand Up @@ -100,9 +102,31 @@ void frameStep(float dt)
ImGui::Text("Toggle fixed camera: " + inputs.getEventCommandTrimmed(EV_CAMERA_FREE_MODE_FIX));

BeamClass@ actor = game.getCurrentTruck();
if (actor != null)
if (@actor != null)
{
ImGui::AlignTextToFramePadding();
ImGui::Text("You are driving " + actor.getTruckName());
ImGui::SameLine();
if (@g_displayed_document == null)
{
if (ImGui::Button("View document"))
{
GenericDocumentClass@ doc = GenericDocumentClass();
int flags = GENERIC_DOCUMENT_OPTION_ALLOW_NAKED_STRINGS | GENERIC_DOCUMENT_OPTION_ALLOW_SLASH_COMMENTS;
if (doc.LoadFromResource(actor.getTruckFileName(), actor.getTruckFileResourceGroup(), flags))
{
@g_displayed_document = @doc;
}
}
}
else
{
if (ImGui::Button("Close document"))
{
@g_displayed_document = null;
}
}

ImGui::TextDisabled("Vehicle controls:");

ImGui::Text("Accelerate/Brake: "
Expand Down Expand Up @@ -157,4 +181,63 @@ void frameStep(float dt)
// Update global counters
g_total_frames++;
g_total_seconds += dt;
}

// Draw document window
if (@g_displayed_document != null)
{
drawDocumentWindow();
}
}

void drawDocumentWindow()
{
ImGui::PushID("document view");
ImGui::Begin("Document view", /*open:*/true, /*flags:*/0);

GenericDocReaderClass reader(g_displayed_document);
while (!reader.EndOfFile())
{
switch (reader.GetTokType())
{
// These tokens are always at start of line
case TOKEN_TYPE_KEYWORD:
ImGui::TextColored(color(1.f, 1.f, 0.f, 1.f), reader.GetTokKeyword());
break;
case TOKEN_TYPE_COMMENT:
ImGui::TextDisabled(";" + reader.GetTokComment());
break;

// Linebreak is implicit in DearIMGUI, no action needed
case TOKEN_TYPE_LINEBREAK:
break;

// Other tokens come anywhere - delimiting logic is needed
default:
if (reader.GetTokType(-1) != TOKEN_TYPE_LINEBREAK)
{
ImGui::SameLine();
string delimiter = (reader.GetTokType(-1) == TOKEN_TYPE_KEYWORD) ? " " : ", ";
ImGui::Text(delimiter);
ImGui::SameLine();
}

switch (reader.GetTokType())
{
case TOKEN_TYPE_STRING:
ImGui::TextColored(color(0.f, 1.f, 1.f, 1.f), "\"" + reader.GetTokString() + "\"");
break;
case TOKEN_TYPE_NUMBER:
ImGui::Text("" + reader.GetTokFloat());
break;
case TOKEN_TYPE_BOOL:
ImGui::TextColored(color(1.f, 0.f, 1.f, 1.f), ""+reader.GetTokBool());
break;
}
}

reader.MoveNext();
}

ImGui::End();
ImGui::PopID(); //"document view"
}
1 change: 1 addition & 0 deletions source/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ if (USE_ANGELSCRIPT)
scripting/bindings/ActorAngelscript.cpp
scripting/bindings/ConsoleAngelscript.cpp
scripting/bindings/GameScriptAngelscript.cpp
scripting/bindings/GenericFileFormatAngelscript.cpp
scripting/bindings/ImGuiAngelscript.cpp
scripting/bindings/InputEngineAngelscript.cpp
scripting/bindings/LocalStorageAngelscript.cpp
Expand Down
5 changes: 5 additions & 0 deletions source/main/physics/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4650,3 +4650,8 @@ void Actor::UpdatePropAnimInputEvents()
state.event_active_prev = ev_active;
}
}

std::string Actor::getTruckFileResourceGroup()
{
return m_gfx_actor->GetResourceGroup();
}
1 change: 1 addition & 0 deletions source/main/physics/Actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class Actor : public ZeroedMemoryAllocator
/// @{
std::string getTruckName() { return ar_design_name; }
std::string getTruckFileName() { return ar_filename; }
std::string getTruckFileResourceGroup();
int getTruckType() { return ar_driveable; }
Ogre::String getSectionConfig() { return m_section_config; }
CacheEntry* getUsedSkin() { return m_used_skin_entry; }
Expand Down
1 change: 1 addition & 0 deletions source/main/scripting/ScriptEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ void ScriptEngine::init()
RegisterTerrain(engine); // TerrainClass
RegisterGameScript(engine); // GameScriptClass
RegisterScriptEvents(engine); // scriptEvents
RegisterGenericFileFormat(engine); // TokenType, GenericDocumentClass, GenericDocReaderClass

// now the global instances
result = engine->RegisterGlobalProperty("GameScriptClass game", &m_game_script); ROR_ASSERT(result>=0);
Expand Down
1 change: 1 addition & 0 deletions source/main/scripting/bindings/ActorAngelscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void RoR::RegisterActor(asIScriptEngine *engine)
result = engine->RegisterObjectMethod("BeamClass", "void scaleTruck(float)", asMETHOD(Actor,scaleTruck), asCALL_THISCALL); ROR_ASSERT(result>=0);
result = engine->RegisterObjectMethod("BeamClass", "string getTruckName()", asMETHOD(Actor,getTruckName), asCALL_THISCALL); ROR_ASSERT(result>=0);
result = engine->RegisterObjectMethod("BeamClass", "string getTruckFileName()", asMETHOD(Actor,getTruckFileName), asCALL_THISCALL); ROR_ASSERT(result>=0);
result = engine->RegisterObjectMethod("BeamClass", "string getTruckFileResourceGroup()", asMETHOD(Actor, getTruckFileResourceGroup), asCALL_THISCALL); ROR_ASSERT(result >= 0);
result = engine->RegisterObjectMethod("BeamClass", "string getSectionConfig()", asMETHOD(Actor, getSectionConfig), asCALL_THISCALL); ROR_ASSERT(result >= 0);
result = engine->RegisterObjectMethod("BeamClass", "int getTruckType()", asMETHOD(Actor,getTruckType), asCALL_THISCALL); ROR_ASSERT(result>=0);
result = engine->RegisterObjectMethod("BeamClass", "void reset(bool)", asMETHOD(Actor,reset), asCALL_THISCALL); ROR_ASSERT(result>=0);
Expand Down
3 changes: 3 additions & 0 deletions source/main/scripting/bindings/AngelScriptBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ void RegisterTerrain(AngelScript::asIScriptEngine* engine);
/// defined in ProceduralRoadAngelscript.cpp
void RegisterProceduralRoad(AngelScript::asIScriptEngine* engine);

/// defined in GenericFileFormatAngelscript.cpp
void RegisterGenericFileFormat(AngelScript::asIScriptEngine* engine);


/// @} //addtogroup Scripting

Expand Down
113 changes: 113 additions & 0 deletions source/main/scripting/bindings/GenericFileFormatAngelscript.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
This source file is part of Rigs of Rods
Copyright 2005-2012 Pierre-Michel Ricordel
Copyright 2007-2012 Thomas Fischer
Copyright 2013-2022 Petr Ohlidal
For more information, see http://www.rigsofrods.org/
Rigs of Rods is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License version 3, as
published by the Free Software Foundation.
Rigs of Rods 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
*/

/// @file
/// @author Petr Ohlidal
/// @date 12-2022

#include "AngelScriptBindings.h"
#include "GenericFileFormat.h"

using namespace RoR;
using namespace AngelScript;

// Wrappers
static std::string GenericDocReader_GetTokString(GenericDocReader* reader, uint32_t offset)
{
const char* val = reader->GetTokString(offset);
return (val) ? val : "";
}

static std::string GenericDocReader_GetTokKeyword(GenericDocReader* reader, uint32_t offset)
{
const char* val = reader->GetTokKeyword(offset);
return (val) ? val : "";
}

static std::string GenericDocReader_GetTokComment(GenericDocReader* reader, uint32_t offset)
{
const char* val = reader->GetTokComment(offset);
return (val) ? val : "";
}

// Factories
static GenericDocument* GenericDocumentFactory()
{
return new GenericDocument();
}

static GenericDocReader* GenericDocReaderFactory(GenericDocumentPtr doc)
{
return new GenericDocReader(doc);
}

void RoR::RegisterGenericFileFormat(asIScriptEngine* engine)
{
// enum TokenType
engine->RegisterEnum("TokenType");
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_NONE", (int)TokenType::NONE);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_LINEBREAK", (int)TokenType::LINEBREAK);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_COMMENT", (int)TokenType::COMMENT);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_STRING", (int)TokenType::STRING);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_NUMBER", (int)TokenType::NUMBER);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_BOOL", (int)TokenType::BOOL);
engine->RegisterEnumValue("TokenType", "TOKEN_TYPE_KEYWORD", (int)TokenType::KEYWORD);


// GenericDocument constants
engine->RegisterEnum("GenericDocumentOptions");
engine->RegisterEnumValue("GenericDocumentOptions", "GENERIC_DOCUMENT_OPTION_ALLOW_NAKED_STRINGS", GenericDocument::OPTION_ALLOW_NAKED_STRINGS);
engine->RegisterEnumValue("GenericDocumentOptions", "GENERIC_DOCUMENT_OPTION_ALLOW_SLASH_COMMENTS", GenericDocument::OPTION_ALLOW_SLASH_COMMENTS);


// class GenericDocument
GenericDocument::RegisterRefCountingObject(engine, "GenericDocumentClass");
GenericDocumentPtr::RegisterRefCountingObjectPtr(engine, "GenericDocumentClassPtr", "GenericDocumentClass");
engine->RegisterObjectBehaviour("GenericDocumentClass", asBEHAVE_FACTORY, "GenericDocumentClass@ f()", asFUNCTION(GenericDocumentFactory), asCALL_CDECL);

engine->RegisterObjectMethod("GenericDocumentClass", "bool LoadFromResource(string,string,int)", asMETHOD(GenericDocument, LoadFromResource), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocumentClass", "bool SaveToResource(string,string)", asMETHOD(GenericDocument, SaveToResource), asCALL_THISCALL);


// class GenericDocReader
GenericDocReader::RegisterRefCountingObject(engine, "GenericDocReaderClass");
GenericDocReaderPtr::RegisterRefCountingObjectPtr(engine, "GenericDocReaderClassPtr", "GenericDocReaderClass");
engine->RegisterObjectBehaviour("GenericDocReaderClass", asBEHAVE_FACTORY, "GenericDocReaderClass@ f(GenericDocumentClassPtr @)", asFUNCTION(GenericDocReaderFactory), asCALL_CDECL);

engine->RegisterObjectMethod("GenericDocReaderClass", "bool MoveNext()", asMETHOD(GenericDocReader, MoveNext), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool SeekNextLine()", asMETHOD(GenericDocReader, SeekNextLine), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "uint CountLineArgs()", asMETHOD(GenericDocReader, CountLineArgs), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool EndOfFile(int offset = 0)", asMETHOD(GenericDocReader, EndOfFile), asCALL_THISCALL);

engine->RegisterObjectMethod("GenericDocReaderClass", "TokenType GetTokType(int offset = 0)", asMETHOD(GenericDocReader, GetTokType), asCALL_THISCALL);

engine->RegisterObjectMethod("GenericDocReaderClass", "string GetTokString(int offset = 0)", asFUNCTION(GenericDocReader_GetTokString), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("GenericDocReaderClass", "float GetTokFloat(int offset = 0)", asMETHOD(GenericDocReader, GetTokFloat), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool GetTokBool(int offset = 0)", asMETHOD(GenericDocReader, GetTokBool), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "string GetTokKeyword(int offset = 0)", asFUNCTION(GenericDocReader_GetTokKeyword), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("GenericDocReaderClass", "string GetTokComment(int offset = 0)", asFUNCTION(GenericDocReader_GetTokComment), asCALL_CDECL_OBJFIRST);

engine->RegisterObjectMethod("GenericDocReaderClass", "bool IsTokString(int offset = 0)", asMETHOD(GenericDocReader, IsTokString), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool IsTokFloat(int offset = 0)", asMETHOD(GenericDocReader, IsTokFloat), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool IsTokBool(int offset = 0)", asMETHOD(GenericDocReader, IsTokBool), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool IsTokKeyword(int offset = 0)", asMETHOD(GenericDocReader, IsTokKeyword), asCALL_THISCALL);
engine->RegisterObjectMethod("GenericDocReaderClass", "bool IsTokComment(int offset = 0)", asMETHOD(GenericDocReader, IsTokComment), asCALL_THISCALL);
}
2 changes: 1 addition & 1 deletion source/main/scripting/bindings/ImGuiAngelscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void RoR::RegisterImGuiBindings(AngelScript::asIScriptEngine* engine)
engine->RegisterGlobalFunction("vector2 GetCursorStartPos()", asFUNCTIONPR([]() { auto v = ImGui::GetCursorStartPos(); return Vector2(v.x, v.y); }, (), Vector2), asCALL_CDECL);
engine->RegisterGlobalFunction("vector2 GetCursorScreenPos()", asFUNCTIONPR([]() { auto v = ImGui::GetCursorScreenPos(); return Vector2(v.x, v.y); }, (), Vector2), asCALL_CDECL);
engine->RegisterGlobalFunction("void SetCursorScreenPos(vector2)", asFUNCTIONPR([](Vector2 v) { ImGui::SetCursorScreenPos(ImVec2(v.x, v.y)); }, (Vector2), void), asCALL_CDECL);
// engine->RegisterGlobalFunction("void AlignTextToFramePadding()", asFUNCTIONPR(ImGui::AlignTextToFramePadding, (), void), asCALL_CDECL);
engine->RegisterGlobalFunction("void AlignTextToFramePadding()", asFUNCTIONPR(ImGui::AlignTextToFramePadding, (), void), asCALL_CDECL);
engine->RegisterGlobalFunction("float GetTextLineHeight()", asFUNCTIONPR(ImGui::GetTextLineHeight, (), float), asCALL_CDECL);
engine->RegisterGlobalFunction("float GetTextLineHeightWithSpacing()", asFUNCTIONPR(ImGui::GetTextLineHeightWithSpacing, (), float), asCALL_CDECL);
// engine->RegisterGlobalFunction("float GetFrameHeight()", asFUNCTIONPR(ImGui::GetFrameHeight, (), float), asCALL_CDECL);
Expand Down
Loading

0 comments on commit 7cc2227

Please sign in to comment.