Compare commits
12 Commits
4d0fb8f60f
...
62e14cf075
| Author | SHA1 | Date | |
|---|---|---|---|
| 62e14cf075 | |||
| 4249a0238b | |||
| cfd9ed8708 | |||
| 1977a12d8b | |||
| 190318e5c4 | |||
| 1aa002d8ba | |||
| 1bc0f298fc | |||
| e6463fc264 | |||
| 7621607152 | |||
| 82ac145e87 | |||
| ff780c34b3 | |||
| 5c03f0cd2c |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -4,3 +4,4 @@
|
||||
*.vroid filter=lfs diff=lfs merge=lfs -text
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
819
Bootstrap.cpp
819
Bootstrap.cpp
File diff suppressed because it is too large
Load Diff
@@ -71,6 +71,8 @@ add_subdirectory(src/gamedata)
|
||||
add_subdirectory(src/miniaudio)
|
||||
add_subdirectory(src/sound)
|
||||
add_subdirectory(audio/gui)
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(lua-scripts)
|
||||
|
||||
# add the source files as usual
|
||||
add_executable(0_Bootstrap Bootstrap.cpp)
|
||||
@@ -117,6 +119,19 @@ if(OGRE_STATIC)
|
||||
target_link_options(Procedural PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(Procedural stage_files import_buildings)
|
||||
file(GLOB FONTS_SRC ${CMAKE_SOURCE_DIR}/assets/fonts/*.ttf)
|
||||
set(FONT_OUTPUT_FILES)
|
||||
foreach(FONT_FILE ${FONTS_SRC})
|
||||
get_filename_component(FILE_NAME ${FONT_FILE} NAME_WE)
|
||||
set(FONT_OUTPUT_FILE ${CMAKE_BINARY_DIR}/resources/fonts/${FILE_NAME}.ttf)
|
||||
add_custom_command(
|
||||
OUTPUT ${FONT_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/resources/fonts
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${FONT_FILE} ${FONT_OUTPUT_FILE}
|
||||
DEPENDS ${FONT_FILE})
|
||||
list(APPEND FONT_OUTPUT_FILES ${FONT_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(fonts ALL DEPENDS ${FONT_OUTPUT_FILES})
|
||||
file(GLOB BUILDINGS_SRC ${CMAKE_SOURCE_DIR}/assets/blender/buildings/*.blend)
|
||||
set(BUILDING_OUTPUT_FILES)
|
||||
foreach(BUILDING_FILE ${BUILDINGS_SRC})
|
||||
@@ -178,19 +193,14 @@ if(OGRE_STATIC)
|
||||
target_link_libraries(Editor fix::assimp pugixml)
|
||||
endif()
|
||||
|
||||
file(GLOB LUA_SCRIPTS_SRC ${CMAKE_SOURCE_DIR}/lua-scripts/*.lua)
|
||||
set(LUA_SCRIPTS_OUTPUT)
|
||||
foreach(LUA_SCRIPT_FILE ${LUA_SCRIPTS_SRC})
|
||||
get_filename_component(FILE_NAME ${LUA_SCRIPT_FILE} NAME_WE)
|
||||
set(LUA_SCRIPT_OUTPUT_FILE ${CMAKE_BINARY_DIR}/lua-scripts/${FILE_NAME}.lua)
|
||||
add_custom_command(OUTPUT ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${LUA_SCRIPT_FILE} ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
DEPENDS ${LUA_SCRIPT_FILE})
|
||||
list(APPEND LUA_SCRIPTS_OUTPUT ${LUA_SCRIPT_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(stage_lua_scripts ALL DEPENDS ${LUA_SCRIPTS_OUTPUT})
|
||||
add_dependencies(TerrainTest stage_lua_scripts stage_files)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/stories
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/lua-scripts/stories ${CMAKE_BINARY_DIR}/stories
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/lua-scripts/stories
|
||||
)
|
||||
add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR}/stories)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
@@ -272,9 +282,8 @@ endforeach()
|
||||
|
||||
file(GLOB VRM_FILES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/vroid/*.vrm)
|
||||
file(GLOB MIXAMO_FILES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/blender/mixamo/**/*.fbx)
|
||||
file(GLOB EDITED_BLENDS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/blender/edited-*.blend)
|
||||
set(VRM_SOURCE)
|
||||
foreach(VRM_FILE ${VRM_FILES} ${MIXAMO_FILES} ${EDITED_BLENDS})
|
||||
foreach(VRM_FILE ${VRM_FILES} ${MIXAMO_FILES})
|
||||
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${VRM_FILE}")
|
||||
set(INPUT_FILE "${CMAKE_SOURCE_DIR}/${VRM_FILE}")
|
||||
add_custom_command(OUTPUT "${OUTPUT_FILE}"
|
||||
@@ -297,13 +306,38 @@ add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS}
|
||||
${VRM_SOURCE}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
set(CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/male/normal-male.glb)
|
||||
set(EDITED_BLENDS_LIST
|
||||
male
|
||||
female
|
||||
)
|
||||
set(EDITED_BLEND_TARGETS)
|
||||
set(CHARACTER_GLBS)
|
||||
foreach(EDITED_BLEND ${EDITED_BLENDS_LIST})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-E copy ${CMAKE_SOURCE_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
COMMAND ${BLENDER} -b -Y
|
||||
${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
-P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/copy_animations.py --
|
||||
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend ${EDITED_BLEND}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend)
|
||||
list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(edited-blends ALL DEPENDS ${EDITED_BLEND_TARGETS})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CHARACTER_GLBS}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
|
||||
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLENDS}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
#add_custom_command(
|
||||
# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene
|
||||
@@ -326,7 +360,8 @@ add_custom_command(
|
||||
|
||||
add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT}
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png)
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
|
||||
edited-blends)
|
||||
|
||||
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
|
||||
|
||||
|
||||
441
Game.cpp
441
Game.cpp
@@ -167,6 +167,123 @@ public:
|
||||
mSkyBoxGenParameters.skyBoxDistance = distance;
|
||||
}
|
||||
};
|
||||
class App;
|
||||
class KeyboardListener : public OgreBites::InputListener {
|
||||
App *mApp;
|
||||
|
||||
uint32_t control;
|
||||
ECS::Vector2 mouse;
|
||||
float wheel_y;
|
||||
bool mouse_moved, wheel_moved;
|
||||
float mInitDelay;
|
||||
|
||||
public:
|
||||
Ogre::Timer fps_timer;
|
||||
bool fast;
|
||||
KeyboardListener(App *app)
|
||||
: OgreBites::InputListener()
|
||||
, mApp(app)
|
||||
, fast(false)
|
||||
, control(0)
|
||||
, mouse({ 0, 0 })
|
||||
, wheel_y(0.0f)
|
||||
, mouse_moved(false)
|
||||
, wheel_moved(false)
|
||||
, mInitDelay(1.0)
|
||||
{
|
||||
}
|
||||
bool isGuiEnabled()
|
||||
{
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return false;
|
||||
return (ECS::get().get<ECS::GUI>().enabled);
|
||||
}
|
||||
void setGuiEnabled(bool value)
|
||||
{
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return;
|
||||
ECS::get().get_mut<ECS::GUI>().enabled = value;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
bool keyPressed(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
bool updated = false;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
|
||||
OgreAssert(ECS::get().has<ECS::GUI>(), "");
|
||||
setGuiEnabled(true);
|
||||
if (ECS::get().has<ECS::GUI>())
|
||||
ECS::get<ECS::GUI>().setWindowGrab(false);
|
||||
return true;
|
||||
}
|
||||
OgreBites::Keycode key = evt.keysym.sym;
|
||||
if (key == 'w')
|
||||
control |= 1;
|
||||
else if (key == 'a')
|
||||
control |= 2;
|
||||
else if (key == 's')
|
||||
control |= 4;
|
||||
else if (key == 'd')
|
||||
control |= 8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control |= 16;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == OgreBites::SDLK_LSHIFT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
OgreBites::Keycode key = evt.keysym.sym;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (key == 'w')
|
||||
control &= ~1;
|
||||
else if (key == 'a')
|
||||
control &= ~2;
|
||||
else if (key == 's')
|
||||
control &= ~4;
|
||||
else if (key == 'd')
|
||||
control &= ~8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control &= ~16;
|
||||
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
|
||||
key == OgreBites::SDLK_LSHIFT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override
|
||||
{
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
mouse.x = evt.xrel;
|
||||
mouse.y = evt.yrel;
|
||||
mouse_moved = true;
|
||||
/* no special mouse handling */
|
||||
return true;
|
||||
}
|
||||
bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override
|
||||
{
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
/* no special mouse wheel handling */
|
||||
wheel_y = evt.y;
|
||||
wheel_moved = true;
|
||||
return true;
|
||||
}
|
||||
bool mousePressed(const OgreBites::MouseButtonEvent &evt) override
|
||||
{
|
||||
std::cout << "Mouse press " << (int)evt.button << " "
|
||||
<< (int)evt.clicks << "\n";
|
||||
if ((int)evt.button == 1)
|
||||
control |= 256;
|
||||
else
|
||||
control &= ~256;
|
||||
return false;
|
||||
}
|
||||
void frameRendered(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
@@ -177,171 +294,8 @@ class App : public OgreBites::ApplicationContext {
|
||||
Ogre::Viewport *mViewport;
|
||||
SkyBoxRenderer *sky;
|
||||
bool mGrab;
|
||||
class KeyboardListener : public OgreBites::InputListener,
|
||||
public Ogre::FrameListener {
|
||||
App *mApp;
|
||||
|
||||
uint32_t control;
|
||||
ECS::Vector2 mouse{ 0, 0 };
|
||||
float wheel_y;
|
||||
bool mouse_moved = false, wheel_moved = false;
|
||||
float mInitDelay;
|
||||
|
||||
public:
|
||||
Ogre::Timer fps_timer;
|
||||
bool fast;
|
||||
KeyboardListener(App *app)
|
||||
: OgreBites::InputListener()
|
||||
, Ogre::FrameListener()
|
||||
, mApp(app)
|
||||
, fast(false)
|
||||
, control(0)
|
||||
, mInitDelay(1.0)
|
||||
{
|
||||
}
|
||||
bool isGuiEnabled()
|
||||
{
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return false;
|
||||
return (ECS::get().get<ECS::GUI>().enabled);
|
||||
}
|
||||
void setGuiEnabled(bool value)
|
||||
{
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return;
|
||||
ECS::get().get_mut<ECS::GUI>().enabled = value;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
bool keyPressed(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
bool updated = false;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
|
||||
OgreAssert(ECS::get().has<ECS::GUI>(), "");
|
||||
setGuiEnabled(true);
|
||||
if (ECS::get().has<ECS::GUI>())
|
||||
ECS::get<ECS::GUI>().setWindowGrab(
|
||||
false);
|
||||
return true;
|
||||
}
|
||||
OgreBites::Keycode key = evt.keysym.sym;
|
||||
if (key == 'w')
|
||||
control |= 1;
|
||||
else if (key == 'a')
|
||||
control |= 2;
|
||||
else if (key == 's')
|
||||
control |= 4;
|
||||
else if (key == 'd')
|
||||
control |= 8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control |= 16;
|
||||
if (key == 'w' || key == 'a' || key == 's' ||
|
||||
key == 'd' || key == OgreBites::SDLK_LSHIFT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
|
||||
{
|
||||
OgreBites::Keycode key = evt.keysym.sym;
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
if (key == 'w')
|
||||
control &= ~1;
|
||||
else if (key == 'a')
|
||||
control &= ~2;
|
||||
else if (key == 's')
|
||||
control &= ~4;
|
||||
else if (key == 'd')
|
||||
control &= ~8;
|
||||
else if (key == OgreBites::SDLK_LSHIFT)
|
||||
control &= ~16;
|
||||
if (key == 'w' || key == 'a' || key == 's' ||
|
||||
key == 'd' || key == OgreBites::SDLK_LSHIFT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override
|
||||
{
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
mouse.x = evt.xrel;
|
||||
mouse.y = evt.yrel;
|
||||
mouse_moved = true;
|
||||
/* no special mouse handling */
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override
|
||||
{
|
||||
if (isGuiEnabled())
|
||||
return false;
|
||||
/* no special mouse wheel handling */
|
||||
wheel_y = evt.y;
|
||||
wheel_moved = true;
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
mousePressed(const OgreBites::MouseButtonEvent &evt) override
|
||||
{
|
||||
std::cout << "Mouse press " << (int)evt.button << " "
|
||||
<< (int)evt.clicks << "\n";
|
||||
if ((int)evt.button == 1)
|
||||
control |= 256;
|
||||
else
|
||||
control &= ~256;
|
||||
return false;
|
||||
}
|
||||
void update(float delta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void frameRendered(const Ogre::FrameEvent &evt) override
|
||||
{
|
||||
if (fps_timer.getMilliseconds() > 1000.0f) {
|
||||
std::cout << "FPS: "
|
||||
<< mApp->getRenderWindow()
|
||||
->getStatistics()
|
||||
.lastFPS
|
||||
<< " ";
|
||||
std::cout << "Draw calls: "
|
||||
<< mApp->getRenderWindow()
|
||||
->getStatistics()
|
||||
.batchCount
|
||||
<< " ";
|
||||
fps_timer.reset();
|
||||
std::cout << "Drops: "
|
||||
<< mApp->getRenderWindow()
|
||||
->getStatistics()
|
||||
.vBlankMissCount
|
||||
<< "\n";
|
||||
fps_timer.reset();
|
||||
}
|
||||
update(evt.timeSinceLastFrame);
|
||||
if (!isGuiEnabled() && mApp->isTerrainReady()) {
|
||||
OgreAssert(mApp->isTerrainReady(),
|
||||
"terrain is not ready");
|
||||
}
|
||||
if (!isGuiEnabled()) {
|
||||
mApp->updateWorld(evt.timeSinceLastFrame);
|
||||
if (mInitDelay >= 0.0f)
|
||||
mInitDelay -= evt.timeSinceLastFrame;
|
||||
}
|
||||
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
|
||||
ECS::Input &input =
|
||||
ECS::get().get_mut<ECS::Input>();
|
||||
input.control = control;
|
||||
input.mouse = mouse;
|
||||
mouse.x = 0;
|
||||
mouse.y = 0;
|
||||
input.wheel_y = wheel_y;
|
||||
wheel_y = 0;
|
||||
input.mouse_moved = mouse_moved;
|
||||
input.wheel_moved = wheel_moved;
|
||||
}
|
||||
}
|
||||
};
|
||||
KeyboardListener mKbd;
|
||||
bool enabldDbgDraw;
|
||||
|
||||
public:
|
||||
App()
|
||||
@@ -350,6 +304,7 @@ public:
|
||||
, mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mGrab(false)
|
||||
, enabldDbgDraw(false)
|
||||
{
|
||||
}
|
||||
virtual ~App()
|
||||
@@ -376,6 +331,11 @@ public:
|
||||
{
|
||||
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
|
||||
"Water", true);
|
||||
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
|
||||
"LuaScripts", false);
|
||||
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
|
||||
"./lua-scripts", "FileSystem", "LuaScripts", true,
|
||||
true);
|
||||
OgreBites::ApplicationContext::locateResources();
|
||||
}
|
||||
void loadResources() override
|
||||
@@ -439,11 +399,13 @@ public:
|
||||
.initialiseAllResourceGroups();
|
||||
// OgreBites::ApplicationContext::loadResources();
|
||||
// setupCursor();
|
||||
std::cout << "Create content" << "\n";
|
||||
createContent();
|
||||
std::cout << "Setup input" << "\n";
|
||||
setupInput();
|
||||
std::cout << "Create content" << "\n";
|
||||
createContent();
|
||||
std::cout << "Setup done" << "\n";
|
||||
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
|
||||
btIDebugDraw::DBG_DrawContactPoints);
|
||||
}
|
||||
Ogre::SceneManager *getSceneManager()
|
||||
{
|
||||
@@ -466,17 +428,22 @@ public:
|
||||
}
|
||||
void updateWorld(float delta)
|
||||
{
|
||||
mDynWorld->getBtWorld()->stepSimulation(delta, 4);
|
||||
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
return;
|
||||
/* Update window grab */
|
||||
if (ECS::get().has<ECS::GUI>() &&
|
||||
ECS::get().get<ECS::GUI>().grabChanged) {
|
||||
setWindowGrab(ECS::get().get<ECS::GUI>().grab);
|
||||
ECS::get().get_mut<ECS::GUI>().grabChanged = false;
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
if (gui.grabChanged) {
|
||||
setWindowGrab(gui.grab);
|
||||
gui.grabChanged = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
|
||||
ECS::update(delta);
|
||||
// mDbgDraw->update();
|
||||
|
||||
/*
|
||||
if (enabldDbgDraw)
|
||||
mDbgDraw->update();
|
||||
*/
|
||||
}
|
||||
class InputListenerChainFlexible : public OgreBites::InputListener {
|
||||
protected:
|
||||
@@ -609,7 +576,30 @@ public:
|
||||
flecs::entity find_wait_gui;
|
||||
void setupInput()
|
||||
{
|
||||
ECS::App &p = ECS::get().get_mut<ECS::App>();
|
||||
}
|
||||
void createContent()
|
||||
{
|
||||
int i;
|
||||
sky = new SkyBoxRenderer(getSceneManager());
|
||||
bool drawFirst = true;
|
||||
uint8_t renderQueue = drawFirst ?
|
||||
Ogre::RENDER_QUEUE_SKIES_EARLY :
|
||||
Ogre::RENDER_QUEUE_SKIES_LATE;
|
||||
sky->create("Skybox/Dynamic", 450, renderQueue,
|
||||
Ogre::Quaternion::IDENTITY,
|
||||
Ogre::ResourceGroupManager::
|
||||
AUTODETECT_RESOURCE_GROUP_NAME);
|
||||
sky->setEnabled(true);
|
||||
|
||||
Ogre::MaterialPtr m =
|
||||
Ogre::MaterialManager::getSingleton().getByName(
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera,
|
||||
getRenderWindow());
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
ECS::get()
|
||||
.observer<ECS::GUI>("UpdateGrab")
|
||||
.event(flecs::OnSet)
|
||||
@@ -619,11 +609,6 @@ public:
|
||||
std::cout << "grab: " << gui.grab << "\n";
|
||||
std::cout << "GUI enabled: " << gui.enabled
|
||||
<< "\n";
|
||||
std::cout << "grabbed: " << isWindowGrab()
|
||||
<< "\n";
|
||||
std::cout
|
||||
<< "UI active: " << mKbd.isGuiEnabled()
|
||||
<< "\n";
|
||||
});
|
||||
ECS::get()
|
||||
.observer<ECS::App>("UpdateInputListener")
|
||||
@@ -632,10 +617,10 @@ public:
|
||||
if (app.mInput)
|
||||
removeInputListener(app.mInput);
|
||||
delete app.mInput;
|
||||
app.mInput = new OgreBites::InputListenerChain(
|
||||
app.listeners);
|
||||
app.mInput =
|
||||
OGRE_NEW OgreBites::InputListenerChain(
|
||||
app.listeners);
|
||||
addInputListener(app.mInput);
|
||||
std::cout << "Update listeners\n";
|
||||
});
|
||||
#if 0
|
||||
ECS::get()
|
||||
@@ -707,52 +692,10 @@ public:
|
||||
<< "\n";
|
||||
});
|
||||
#endif
|
||||
|
||||
p.listeners.clear();
|
||||
p.listeners.push_back(&mKbd);
|
||||
ECS::get().modified<ECS::App>();
|
||||
}
|
||||
void createContent()
|
||||
{
|
||||
int i;
|
||||
std::cout << "addFrameListener\n";
|
||||
getRoot()->addFrameListener(&mKbd);
|
||||
sky = new SkyBoxRenderer(getSceneManager());
|
||||
bool drawFirst = true;
|
||||
uint8_t renderQueue = drawFirst ?
|
||||
Ogre::RENDER_QUEUE_SKIES_EARLY :
|
||||
Ogre::RENDER_QUEUE_SKIES_LATE;
|
||||
sky->create("Skybox/Dynamic", 450, renderQueue,
|
||||
Ogre::Quaternion::IDENTITY,
|
||||
Ogre::ResourceGroupManager::
|
||||
AUTODETECT_RESOURCE_GROUP_NAME);
|
||||
sky->setEnabled(true);
|
||||
|
||||
Ogre::MaterialPtr m =
|
||||
Ogre::MaterialManager::getSingleton().getByName(
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera);
|
||||
ECS::get()
|
||||
.system<const ECS::GUI, ECS::App>("SetInputListener")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](const ECS::GUI &gui, ECS::App &app) {
|
||||
if (gui.mGuiInpitListener &&
|
||||
app.listeners.size() == 1) {
|
||||
app.listeners.clear();
|
||||
app.listeners.push_back(
|
||||
gui.mGuiInpitListener);
|
||||
app.listeners.push_back(&mKbd);
|
||||
ECS::modified<ECS::App>();
|
||||
}
|
||||
});
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
ECS::get().set<ECS::App>(
|
||||
{ static_cast<OgreBites::ApplicationContext *>(this),
|
||||
{ initialiseImGui(),
|
||||
nullptr,
|
||||
{} });
|
||||
{ getImGuiInputListener(), &mKbd } });
|
||||
Sound::setup();
|
||||
Sound::ding();
|
||||
}
|
||||
@@ -792,7 +735,51 @@ public:
|
||||
ECS::get().lookup("ECS::CharacterModule::player");
|
||||
return player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
enabldDbgDraw = enable;
|
||||
}
|
||||
bool isEnabledDbgDraw() const
|
||||
{
|
||||
return enabldDbgDraw;
|
||||
}
|
||||
};
|
||||
void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
if (fps_timer.getMilliseconds() > 1000.0f) {
|
||||
std::cout << "FPS: "
|
||||
<< mApp->getRenderWindow()->getStatistics().lastFPS
|
||||
<< " ";
|
||||
std::cout << "Draw calls: "
|
||||
<< mApp->getRenderWindow()->getStatistics().batchCount
|
||||
<< " ";
|
||||
fps_timer.reset();
|
||||
std::cout << "Drops: "
|
||||
<< mApp->getRenderWindow()
|
||||
->getStatistics()
|
||||
.vBlankMissCount
|
||||
<< "\n";
|
||||
fps_timer.reset();
|
||||
}
|
||||
if (!isGuiEnabled() ||
|
||||
(isGuiEnabled() && ECS::get<ECS::GUI>().narrationBox)) {
|
||||
mApp->updateWorld(evt.timeSinceLastFrame);
|
||||
if (mInitDelay >= 0.0f)
|
||||
mInitDelay -= evt.timeSinceLastFrame;
|
||||
}
|
||||
|
||||
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
|
||||
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
|
||||
input.control = control;
|
||||
input.mouse = mouse;
|
||||
mouse.x = 0;
|
||||
mouse.y = 0;
|
||||
input.wheel_y = wheel_y;
|
||||
wheel_y = 0;
|
||||
input.mouse_moved = mouse_moved;
|
||||
input.wheel_moved = wheel_moved;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -803,7 +790,7 @@ int main()
|
||||
// register for input events
|
||||
// KeyHandler keyHandler;
|
||||
// ctx.addInputListener(&keyHandler);
|
||||
|
||||
ctx.enableDbgDraw(false);
|
||||
ctx.getRoot()->startRendering();
|
||||
ctx.setWindowGrab(false);
|
||||
ctx.closeApp();
|
||||
|
||||
@@ -141,7 +141,7 @@ public:
|
||||
cs->calculateLocalInertia(mass, inertia);
|
||||
} break;
|
||||
case Ogre::Bullet::CT_CAPSULE: {
|
||||
cs = new btCompoundShape();
|
||||
cs = new btCompoundShape(false);
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
shape = new btCapsuleShape(radius,
|
||||
@@ -377,7 +377,7 @@ void CharacterController::setupBody()
|
||||
// mRigidBody = world->addCharacter(mBodyEnt, 0);
|
||||
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
|
||||
mGhostObject = new btPairCachingGhostObject();
|
||||
mCollisionShape = new btCompoundShape;
|
||||
mCollisionShape = new btCompoundShape(false);
|
||||
mGhostObject->setCollisionShape(mCollisionShape);
|
||||
|
||||
{
|
||||
|
||||
BIN
assets/blender/altar.blend
LFS
BIN
assets/blender/altar.blend
LFS
Binary file not shown.
BIN
assets/blender/buildings/altar.blend
LFS
Normal file
BIN
assets/blender/buildings/altar.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/edited-normal-female.blend
LFS
Normal file
BIN
assets/blender/edited-normal-female.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/edited-normal-male-complete.blend
LFS
Normal file
BIN
assets/blender/edited-normal-male-complete.blend
LFS
Normal file
Binary file not shown.
Binary file not shown.
BIN
assets/blender/mixamo/female/swimming.fbx
LFS
Normal file
BIN
assets/blender/mixamo/female/swimming.fbx
LFS
Normal file
Binary file not shown.
BIN
assets/blender/mixamo/female/treading_water.fbx
LFS
Normal file
BIN
assets/blender/mixamo/female/treading_water.fbx
LFS
Normal file
Binary file not shown.
BIN
assets/blender/mixamo/male/swimming.fbx
LFS
Normal file
BIN
assets/blender/mixamo/male/swimming.fbx
LFS
Normal file
Binary file not shown.
BIN
assets/blender/mixamo/male/treading_water.fbx
LFS
Normal file
BIN
assets/blender/mixamo/male/treading_water.fbx
LFS
Normal file
Binary file not shown.
64
assets/blender/scripts/copy_animations.py
Normal file
64
assets/blender/scripts/copy_animations.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import bpy
|
||||
import sys
|
||||
import os
|
||||
|
||||
argv = sys.argv
|
||||
argv = argv[argv.index("--") + 1:]
|
||||
|
||||
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
|
||||
|
||||
source_filepath = argv[0]
|
||||
armature_name = argv[1]
|
||||
|
||||
print("starting...")
|
||||
|
||||
def copy_actions_and_create_nla_tracks(source_filepath, target_object_name):
|
||||
"""
|
||||
Copies actions from a source Blender file that do not exist in the current
|
||||
file, creates NLA tracks for them on a target object, and saves the file.
|
||||
|
||||
Args:
|
||||
source_filepath (str): The full path to the source Blender file.
|
||||
target_object_name (str): The name of the object in the current file
|
||||
to which the actions and NLA tracks will be applied.
|
||||
"""
|
||||
|
||||
# 1. Link or Append Actions from the Source File
|
||||
with bpy.data.libraries.load(source_filepath, link=False) as (data_from, data_to):
|
||||
source_action_names = data_from.actions
|
||||
current_action_names = {action.name for action in bpy.data.actions}
|
||||
actions_to_append = [name for name in source_action_names if name not in current_action_names]
|
||||
data_to.actions = actions_to_append
|
||||
print(actions_to_append)
|
||||
|
||||
# Get the target object
|
||||
target_object = bpy.data.objects.get(target_object_name)
|
||||
if not target_object:
|
||||
print(f"Error: Object '{target_object_name}' not found in the current file.")
|
||||
return
|
||||
|
||||
# Ensure the object has an NLA editor
|
||||
if not target_object.animation_data:
|
||||
target_object.animation_data_create()
|
||||
|
||||
# 2. Iterate through newly imported actions and create NLA tracks
|
||||
for action in data_to.actions:
|
||||
# Check if an action with the same name already exists in the current file
|
||||
# Add the action to the NLA editor as a strip
|
||||
nla_track = target_object.animation_data.nla_tracks.new()
|
||||
nla_track.name = f"NLA_Track_{action.name}"
|
||||
nla_track.strips.new(name=action.name, start=1, action=action)
|
||||
print(f"Created NLA track for action: {action.name}")
|
||||
|
||||
# 3. Save the current Blender file
|
||||
bpy.ops.wm.save_as_mainfile(filepath=bpy.context.blend_data.filepath)
|
||||
print(f"File saved: {bpy.context.blend_data.filepath}")
|
||||
|
||||
# --- Usage Example ---
|
||||
if __name__ == "__main__":
|
||||
# Replace with your actual source file path and target object name
|
||||
source_file = source_filepath
|
||||
target_obj = armature_name
|
||||
|
||||
copy_actions_and_create_nla_tracks(source_file, target_obj)
|
||||
|
||||
@@ -9,7 +9,7 @@ from mathutils import Vector, Matrix
|
||||
from math import radians, pi
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from settings import ExportMappingFemale, ExportMappingMale, ExportMappingMaleBabyShape, ExportMappingMaleEdited
|
||||
from settings import ExportMappingFemale, ExportMappingMale, ExportMappingMaleBabyShape, ExportMappingMaleEdited, ExportMappingFemaleEdited
|
||||
|
||||
basepath = os.getcwd()
|
||||
def check_bone(bname):
|
||||
@@ -179,7 +179,7 @@ def extra_linear(angle, offset):
|
||||
ret += offt
|
||||
return ret
|
||||
|
||||
for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBabyShape(), ExportMappingMaleEdited()]:
|
||||
for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBabyShape(), ExportMappingMaleEdited(), ExportMappingFemaleEdited()]:
|
||||
if not os.path.exists(mapping.blend_path):
|
||||
print("Skipping mapping: " + mapping.blend_path)
|
||||
continue
|
||||
|
||||
@@ -6,7 +6,7 @@ class VRMDataFemale:
|
||||
editfile = "modelling-vroid-normal-female.blend"
|
||||
armature_name = "female"
|
||||
mixamo_animation_path = basepath + "/assets/blender/mixamo/female/"
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn"]
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water"]
|
||||
fbx_scale = 0.89
|
||||
class VRMDataMale:
|
||||
path = "buch1.vrm"
|
||||
@@ -14,7 +14,7 @@ class VRMDataMale:
|
||||
editfile = "modelling-vroid-normal-male.blend"
|
||||
armature_name = "male"
|
||||
mixamo_animation_path = basepath + "/assets/blender/mixamo/male/"
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn"]
|
||||
mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water"]
|
||||
fbx_scale = 1.0
|
||||
class VRMDataMaleBabyShape:
|
||||
path = "buch1-chibi.vrm"
|
||||
@@ -54,6 +54,20 @@ class ExportMappingMale:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingMaleEdited:
|
||||
blend_path = "assets/blender/" + "edited-normal-male.blend"
|
||||
gltf_path = "characters/male/normal-male.gltf"
|
||||
ogre_scene = "characters/male/vroid-normal-male.scene"
|
||||
inner_path = "Object"
|
||||
objs = ["male", "Body", "Hair", "Face", "BackHair"]
|
||||
armature_name = "male"
|
||||
outfile = "tmp-male.blend"
|
||||
default_action = 'default'
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
for fobj in self.objs:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingMaleClothedEdited:
|
||||
blend_path = "assets/blender/" + "edited-normal-male.blend"
|
||||
gltf_path = "characters/male/normal-male.gltf"
|
||||
ogre_scene = "characters/male/vroid-normal-male.scene"
|
||||
@@ -67,6 +81,21 @@ class ExportMappingMaleEdited:
|
||||
for fobj in self.objs:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingFemaleEdited:
|
||||
blend_path = "assets/blender/" + "edited-normal-female.blend"
|
||||
gltf_path = "characters/female/normal-female.gltf"
|
||||
ogre_scene = "characters/female/vroid-normal-female.scene"
|
||||
inner_path = "Object"
|
||||
# objs = ["male", "Body", "Hair", "Face", "BackHair", "Tops", "Bottoms", "Shoes", "Accessory"]
|
||||
objs = ["female", "Body", "Hair", "Face", "BackHair", "Tops", "Bottoms", "Shoes", "Accessory"]
|
||||
armature_name = "female"
|
||||
outfile = "tmp-female.blend"
|
||||
default_action = 'default'
|
||||
def __init__(self):
|
||||
self.files = []
|
||||
for fobj in self.objs:
|
||||
self.files.append({"name": fobj})
|
||||
|
||||
class ExportMappingMaleBabyShape:
|
||||
blend_path = "assets/blender/shapes/male/" + "vrm-vroid-normal-male-chibi.blend"
|
||||
gltf_path = "assets/blender/" + "characters/shapes/male/chibi/vroid-normal-male-chibi.gltf"
|
||||
|
||||
BIN
assets/blender/vehicles/boat-sails.blend
LFS
Normal file
BIN
assets/blender/vehicles/boat-sails.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/vehicles/boat.blend
LFS
Normal file
BIN
assets/blender/vehicles/boat.blend
LFS
Normal file
Binary file not shown.
BIN
assets/blender/vehicles/boat.png
Normal file
BIN
assets/blender/vehicles/boat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
BIN
assets/fonts/Jupiteroid-Bold.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-Bold.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Jupiteroid-BoldItalic.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-BoldItalic.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Jupiteroid-Italic.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-Italic.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Jupiteroid-Light.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-Light.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Jupiteroid-LightItalic.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-LightItalic.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Jupiteroid-Regular.ttf
LFS
Normal file
BIN
assets/fonts/Jupiteroid-Regular.ttf
LFS
Normal file
Binary file not shown.
BIN
assets/fonts/Kenney Bold.ttf
LFS
Normal file
BIN
assets/fonts/Kenney Bold.ttf
LFS
Normal file
Binary file not shown.
24
lua-scripts/CMakeLists.txt
Normal file
24
lua-scripts/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
project(lua-scripts)
|
||||
set(LUA_SCRIPTS_SRC
|
||||
data.lua
|
||||
)
|
||||
set(LUA_SCRIPTS_OUTPUT)
|
||||
set(LUA_PACKAGES narrator stories)
|
||||
foreach(LUA_SCRIPT_FILE ${LUA_SCRIPTS_SRC})
|
||||
get_filename_component(FILE_NAME ${LUA_SCRIPT_FILE} NAME_WE)
|
||||
set(LUA_SCRIPT_OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.lua)
|
||||
add_custom_command(OUTPUT ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${LUA_SCRIPT_FILE} ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
DEPENDS ${LUA_SCRIPT_FILE})
|
||||
list(APPEND LUA_SCRIPTS_OUTPUT ${LUA_SCRIPT_OUTPUT_FILE})
|
||||
endforeach()
|
||||
set(LUA_PACKAGES_OUTPUT)
|
||||
foreach(LUA_PACKAGE ${LUA_PACKAGES})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LUA_PACKAGE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${LUA_PACKAGE}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${LUA_PACKAGE}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${LUA_PACKAGE})
|
||||
list(APPEND LUA_PACKAGES_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${LUA_PACKAGE})
|
||||
endforeach()
|
||||
add_custom_target(stage_lua_scripts ALL DEPENDS ${LUA_SCRIPTS_OUTPUT} ${LUA_PACKAGES_OUTPUT})
|
||||
@@ -36,4 +36,237 @@ function foo()
|
||||
end
|
||||
v = Vector3(0, 1, 2)
|
||||
end
|
||||
foo()
|
||||
--[[
|
||||
narration = {
|
||||
position = 1,
|
||||
narration_start = {
|
||||
"The party was hot, girls were sexy the wine, beer and whisky were in \
|
||||
enormous numbers. It was anniversary since you set sail with your friends. \
|
||||
The sea was calm and everything promised you another great night.",
|
||||
"The whole year with your friends on your ship over vast seas of the world in decay was almost over.\
|
||||
It was so good year full of adventure, romance, indecency and luxury.",
|
||||
"Your trusted friends decided that you have too much, they have too much of you and you owe them a lot.",
|
||||
"They found you drunk in your room and moved to the deck and used ropes to \
|
||||
restrain you and attach a bucket of stones or something else heavy to your body.",
|
||||
"After a few hours passed two strong people pulled you to the side and \
|
||||
dropped you into the sea. Last thing you heard before you hit the water was happy laugher..."
|
||||
},
|
||||
progress = function(this)
|
||||
if #this.narration_start < this.position then
|
||||
return ""
|
||||
end
|
||||
local ret = this.narration_start[this.position]
|
||||
this.position = this.position + 1
|
||||
return ret
|
||||
end,
|
||||
}
|
||||
]]--
|
||||
local narrator = require('narrator.narrator')
|
||||
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
for k,v in pairs(o) do
|
||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||
s = s .. '['..k..'] = ' .. dump(v) .. ','
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
return tostring(o)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
function _narration()
|
||||
local ret = ""
|
||||
local choices = {}
|
||||
if story:can_continue() then
|
||||
print("can continue")
|
||||
local paragraph = story:continue(1)
|
||||
print(dump(paragraph))
|
||||
local text = paragraph.text
|
||||
if paragraph.tags then
|
||||
text = text .. ' #' .. table.concat(paragraph.tags, ' #')
|
||||
for i, tag in ipairs(paragraph.tags) do
|
||||
if tag == "mc_is_free" then
|
||||
ecs_character_set("player", "gravity", true)
|
||||
ecs_character_set("player", "buoyancy", true)
|
||||
end
|
||||
end
|
||||
end
|
||||
ret = text
|
||||
if story:can_choose() then
|
||||
local ch = story:get_choices()
|
||||
for i, choice in ipairs(ch) do
|
||||
table.insert(choices, choice.text)
|
||||
print(i, dump(choice))
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Ascend" then
|
||||
story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Continue" then
|
||||
story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
end
|
||||
else
|
||||
print("can NOT continue")
|
||||
end
|
||||
print(ret)
|
||||
if (#choices > 0) then
|
||||
print("choices!!!")
|
||||
narrate(ret, choices)
|
||||
else
|
||||
narrate(ret)
|
||||
end
|
||||
end
|
||||
]]--
|
||||
function Quest(name, book)
|
||||
local quest = {
|
||||
name = name,
|
||||
active = false,
|
||||
activate = function(this)
|
||||
this.active = true
|
||||
this.story:begin()
|
||||
this:_narration()
|
||||
end,
|
||||
event = function(this, event)
|
||||
if not this.active then
|
||||
return
|
||||
end
|
||||
if event == "narration_progress" then
|
||||
this:_narration()
|
||||
elseif event == "narration_answered" then
|
||||
local answer = narration_get_answer()
|
||||
this.story:choose(answer)
|
||||
print("answered:", answer)
|
||||
this:_narration()
|
||||
end
|
||||
end,
|
||||
_handle_tag = function(this, tag)
|
||||
if tag == "mc_is_free" then
|
||||
ecs_character_set("player", "gravity", true)
|
||||
ecs_character_set("player", "buoyancy", true)
|
||||
end
|
||||
end,
|
||||
_narration = function(this)
|
||||
local ret = ""
|
||||
local choices = {}
|
||||
local have_choice = false
|
||||
local have_paragraph = false
|
||||
if not this.active then
|
||||
return
|
||||
end
|
||||
if this.story:can_continue() then
|
||||
print("can continue")
|
||||
have_paragraph = true
|
||||
local paragraph = this.story:continue(1)
|
||||
print(dump(paragraph))
|
||||
local text = paragraph.text
|
||||
if paragraph.tags then
|
||||
text = text .. ' #' .. table.concat(paragraph.tags, ' #')
|
||||
for i, tag in ipairs(paragraph.tags) do
|
||||
this:_handle_tag(tag)
|
||||
end
|
||||
end
|
||||
ret = text
|
||||
if this.story:can_choose() then
|
||||
have_choice = true
|
||||
local ch = this.story:get_choices()
|
||||
for i, choice in ipairs(ch) do
|
||||
table.insert(choices, choice.text)
|
||||
print(i, dump(choice))
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Ascend" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Continue" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
end
|
||||
else
|
||||
print("can NOT continue")
|
||||
end
|
||||
if not have_choice and not have_paragraph then
|
||||
this:complete()
|
||||
this.active = false
|
||||
end
|
||||
print(ret)
|
||||
if (#choices > 0) then
|
||||
print("choices!!!")
|
||||
narrate(ret, choices)
|
||||
else
|
||||
narrate(ret)
|
||||
end
|
||||
end,
|
||||
complete = function(this)
|
||||
print(this.name .. 'complete')
|
||||
end,
|
||||
story = narrator.init_story(book)
|
||||
}
|
||||
|
||||
-- Begin the story
|
||||
|
||||
return quest
|
||||
end
|
||||
function StartGameQuest()
|
||||
-- Parse a book from the Ink file.
|
||||
local book = narrator.parse_file('stories.initiation')
|
||||
local quest = Quest('start game', book)
|
||||
quest.base = {}
|
||||
quest.base.activate = quest.activate
|
||||
quest.base.complete = quest.complete
|
||||
quest.boat = false
|
||||
quest.activate = function(this)
|
||||
print('activate...')
|
||||
local mc_is_free = function()
|
||||
ecs_character_params_set("player", "gravity", true)
|
||||
ecs_character_params_set("player", "buoyancy", true)
|
||||
end
|
||||
this.story:bind('mc_is_free', mc_is_free)
|
||||
this.base.activate(this)
|
||||
end
|
||||
quest.complete = function(this)
|
||||
this.base.complete(this)
|
||||
this.active = false
|
||||
if not this.boat then
|
||||
local boat = ecs_vehicle_set("boat", 0, 0, -20, 1.75)
|
||||
local trigger = ecs_child_character_trigger(boat, "entered_boat", 0, 0, 0, 3, 3)
|
||||
local npc = ecs_npc_set("normal-female.glb", 0, 2, -20, 1.75)
|
||||
this.boat = true
|
||||
end
|
||||
end
|
||||
return quest
|
||||
end
|
||||
quests = {}
|
||||
setup_handler(function(event)
|
||||
print(event)
|
||||
for k, v in pairs(quests) do
|
||||
if v.active then
|
||||
v:event(event)
|
||||
end
|
||||
end
|
||||
if event == "startup" then
|
||||
main_menu()
|
||||
elseif event == "narration_progress" then
|
||||
_narration()
|
||||
elseif event == "narration_answered" then
|
||||
local answer = narration_get_answer()
|
||||
story:choose(answer)
|
||||
print("answered:", answer)
|
||||
_narration()
|
||||
elseif event == "new_game" then
|
||||
ecs_character_params_set("player", "gravity", true)
|
||||
ecs_character_params_set("player", "buoyancy", false)
|
||||
local quest = StartGameQuest()
|
||||
quests[quest.name] = quest
|
||||
for k, v in pairs(quests) do
|
||||
print(k, v.active)
|
||||
end
|
||||
quest:activate()
|
||||
end
|
||||
end)
|
||||
|
||||
37
lua-scripts/narrator/annotations.lua
Normal file
37
lua-scripts/narrator/annotations.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
---@class Narrator.Book.Version
|
||||
---@field engine number
|
||||
---@field tree number
|
||||
|
||||
---@class Narrator.Book
|
||||
---@field version Narrator.Book.Version
|
||||
---@field inclusions string[]
|
||||
---@field lists table
|
||||
---@field constants table
|
||||
---@field variables table
|
||||
---@field params table
|
||||
---@field tree table
|
||||
|
||||
---@class Narrator.ParsingParams
|
||||
---@field save boolean Save a parsed book to the lua file
|
||||
|
||||
---@class Narrator.Paragraph
|
||||
---@field text string
|
||||
---@field tags string[]|nil
|
||||
|
||||
---@class Narrator.Choice
|
||||
---@field text string
|
||||
---@field tags string[]|nil
|
||||
|
||||
---@class Narrator.State
|
||||
---@field version number
|
||||
---@field temp table
|
||||
---@field seeds table
|
||||
---@field variables table
|
||||
---@field params table|nil
|
||||
---@field visits table
|
||||
---@field current_path table
|
||||
---@field paragraphs table
|
||||
---@field choices table
|
||||
---@field output table
|
||||
---@field tunnels table|nil
|
||||
---@field path table
|
||||
32
lua-scripts/narrator/enums.lua
Normal file
32
lua-scripts/narrator/enums.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
local enums = {
|
||||
|
||||
---Bump it when the state structure is changed
|
||||
engine_version = 2,
|
||||
|
||||
---@enum Narrator.ItemType
|
||||
item = {
|
||||
text = 1,
|
||||
alts = 2,
|
||||
choice = 3,
|
||||
condition = 4,
|
||||
variable = 5
|
||||
},
|
||||
|
||||
---@enum Narrator.Sequence
|
||||
sequence = {
|
||||
cycle = 1,
|
||||
stopping = 2,
|
||||
once = 3
|
||||
},
|
||||
|
||||
---@enum Narrator.ReadMode
|
||||
read_mode = {
|
||||
text = 1,
|
||||
choices = 2,
|
||||
gathers = 3,
|
||||
quit = 4
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return enums
|
||||
68
lua-scripts/narrator/libs/classic.lua
Executable file
68
lua-scripts/narrator/libs/classic.lua
Executable file
@@ -0,0 +1,68 @@
|
||||
--
|
||||
-- classic
|
||||
--
|
||||
-- Copyright (c) 2014, rxi
|
||||
--
|
||||
-- This module is free software; you can redistribute it and/or modify it under
|
||||
-- the terms of the MIT license. See LICENSE for details.
|
||||
--
|
||||
|
||||
|
||||
local Object = {}
|
||||
Object.__index = Object
|
||||
|
||||
|
||||
function Object:new()
|
||||
end
|
||||
|
||||
|
||||
function Object:extend()
|
||||
local cls = {}
|
||||
for k, v in pairs(self) do
|
||||
if k:find("__") == 1 then
|
||||
cls[k] = v
|
||||
end
|
||||
end
|
||||
cls.__index = cls
|
||||
cls.super = self
|
||||
setmetatable(cls, self)
|
||||
return cls
|
||||
end
|
||||
|
||||
|
||||
function Object:implement(...)
|
||||
for _, cls in pairs({...}) do
|
||||
for k, v in pairs(cls) do
|
||||
if self[k] == nil and type(v) == "function" then
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Object:is(T)
|
||||
local mt = getmetatable(self)
|
||||
while mt do
|
||||
if mt == T then
|
||||
return true
|
||||
end
|
||||
mt = getmetatable(mt)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function Object:__tostring()
|
||||
return "Object"
|
||||
end
|
||||
|
||||
|
||||
function Object:__call(...)
|
||||
local obj = setmetatable({}, self)
|
||||
obj:new(...)
|
||||
return obj
|
||||
end
|
||||
|
||||
|
||||
return Object
|
||||
780
lua-scripts/narrator/libs/lume.lua
Executable file
780
lua-scripts/narrator/libs/lume.lua
Executable file
@@ -0,0 +1,780 @@
|
||||
--
|
||||
-- lume
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local lume = { _version = "2.3.0" }
|
||||
|
||||
local pairs, ipairs = pairs, ipairs
|
||||
local type, assert, unpack = type, assert, unpack or table.unpack
|
||||
local tostring, tonumber = tostring, tonumber
|
||||
local math_floor = math.floor
|
||||
local math_ceil = math.ceil
|
||||
local math_atan2 = math.atan2 or math.atan
|
||||
local math_sqrt = math.sqrt
|
||||
local math_abs = math.abs
|
||||
|
||||
local noop = function()
|
||||
end
|
||||
|
||||
local identity = function(x)
|
||||
return x
|
||||
end
|
||||
|
||||
local patternescape = function(str)
|
||||
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
|
||||
end
|
||||
|
||||
local absindex = function(len, i)
|
||||
return i < 0 and (len + i + 1) or i
|
||||
end
|
||||
|
||||
local iscallable = function(x)
|
||||
if type(x) == "function" then return true end
|
||||
local mt = getmetatable(x)
|
||||
return mt and mt.__call ~= nil
|
||||
end
|
||||
|
||||
local getiter = function(x)
|
||||
if lume.isarray(x) then
|
||||
return ipairs
|
||||
elseif type(x) == "table" then
|
||||
return pairs
|
||||
end
|
||||
error("expected table", 3)
|
||||
end
|
||||
|
||||
local iteratee = function(x)
|
||||
if x == nil then return identity end
|
||||
if iscallable(x) then return x end
|
||||
if type(x) == "table" then
|
||||
return function(z)
|
||||
for k, v in pairs(x) do
|
||||
if z[k] ~= v then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return function(z) return z[x] end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function lume.clamp(x, min, max)
|
||||
return x < min and min or (x > max and max or x)
|
||||
end
|
||||
|
||||
|
||||
function lume.round(x, increment)
|
||||
if increment then return lume.round(x / increment) * increment end
|
||||
return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
|
||||
end
|
||||
|
||||
|
||||
function lume.sign(x)
|
||||
return x < 0 and -1 or 1
|
||||
end
|
||||
|
||||
|
||||
function lume.lerp(a, b, amount)
|
||||
return a + (b - a) * lume.clamp(amount, 0, 1)
|
||||
end
|
||||
|
||||
|
||||
function lume.smooth(a, b, amount)
|
||||
local t = lume.clamp(amount, 0, 1)
|
||||
local m = t * t * (3 - 2 * t)
|
||||
return a + (b - a) * m
|
||||
end
|
||||
|
||||
|
||||
function lume.pingpong(x)
|
||||
return 1 - math_abs(1 - x % 2)
|
||||
end
|
||||
|
||||
|
||||
function lume.distance(x1, y1, x2, y2, squared)
|
||||
local dx = x1 - x2
|
||||
local dy = y1 - y2
|
||||
local s = dx * dx + dy * dy
|
||||
return squared and s or math_sqrt(s)
|
||||
end
|
||||
|
||||
|
||||
function lume.angle(x1, y1, x2, y2)
|
||||
return math_atan2(y2 - y1, x2 - x1)
|
||||
end
|
||||
|
||||
|
||||
function lume.vector(angle, magnitude)
|
||||
return math.cos(angle) * magnitude, math.sin(angle) * magnitude
|
||||
end
|
||||
|
||||
|
||||
function lume.random(a, b)
|
||||
if not a then a, b = 0, 1 end
|
||||
if not b then b = 0 end
|
||||
return a + math.random() * (b - a)
|
||||
end
|
||||
|
||||
|
||||
function lume.randomchoice(t)
|
||||
return t[math.random(#t)]
|
||||
end
|
||||
|
||||
|
||||
function lume.weightedchoice(t)
|
||||
local sum = 0
|
||||
for _, v in pairs(t) do
|
||||
assert(v >= 0, "weight value less than zero")
|
||||
sum = sum + v
|
||||
end
|
||||
assert(sum ~= 0, "all weights are zero")
|
||||
local rnd = lume.random(sum)
|
||||
for k, v in pairs(t) do
|
||||
if rnd < v then return k end
|
||||
rnd = rnd - v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.isarray(x)
|
||||
return type(x) == "table" and x[1] ~= nil
|
||||
end
|
||||
|
||||
|
||||
function lume.push(t, ...)
|
||||
local n = select("#", ...)
|
||||
for i = 1, n do
|
||||
t[#t + 1] = select(i, ...)
|
||||
end
|
||||
return ...
|
||||
end
|
||||
|
||||
|
||||
function lume.remove(t, x)
|
||||
local iter = getiter(t)
|
||||
for i, v in iter(t) do
|
||||
if v == x then
|
||||
if lume.isarray(t) then
|
||||
table.remove(t, i)
|
||||
break
|
||||
else
|
||||
t[i] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
|
||||
function lume.clear(t)
|
||||
local iter = getiter(t)
|
||||
for k in iter(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function lume.extend(t, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local x = select(i, ...)
|
||||
if x then
|
||||
for k, v in pairs(x) do
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function lume.shuffle(t)
|
||||
local rtn = {}
|
||||
for i = 1, #t do
|
||||
local r = math.random(i)
|
||||
if r ~= i then
|
||||
rtn[i] = rtn[r]
|
||||
end
|
||||
rtn[r] = t[i]
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.sort(t, comp)
|
||||
local rtn = lume.clone(t)
|
||||
if comp then
|
||||
if type(comp) == "string" then
|
||||
table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
|
||||
else
|
||||
table.sort(rtn, comp)
|
||||
end
|
||||
else
|
||||
table.sort(rtn)
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.array(...)
|
||||
local t = {}
|
||||
for x in ... do t[#t + 1] = x end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function lume.each(t, fn, ...)
|
||||
local iter = getiter(t)
|
||||
if type(fn) == "string" then
|
||||
for _, v in iter(t) do v[fn](v, ...) end
|
||||
else
|
||||
for _, v in iter(t) do fn(v, ...) end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
function lume.map(t, fn)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
local rtn = {}
|
||||
for k, v in iter(t) do rtn[k] = fn(v) end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.all(t, fn)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
for _, v in iter(t) do
|
||||
if not fn(v) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function lume.any(t, fn)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
for _, v in iter(t) do
|
||||
if fn(v) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function lume.reduce(t, fn, first)
|
||||
local started = first ~= nil
|
||||
local acc = first
|
||||
local iter = getiter(t)
|
||||
for _, v in iter(t) do
|
||||
if started then
|
||||
acc = fn(acc, v)
|
||||
else
|
||||
acc = v
|
||||
started = true
|
||||
end
|
||||
end
|
||||
assert(started, "reduce of an empty table with no first value")
|
||||
return acc
|
||||
end
|
||||
|
||||
|
||||
function lume.unique(t)
|
||||
local rtn = {}
|
||||
for k in pairs(lume.invert(t)) do
|
||||
rtn[#rtn + 1] = k
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.filter(t, fn, retainkeys)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
local rtn = {}
|
||||
if retainkeys then
|
||||
for k, v in iter(t) do
|
||||
if fn(v) then rtn[k] = v end
|
||||
end
|
||||
else
|
||||
for _, v in iter(t) do
|
||||
if fn(v) then rtn[#rtn + 1] = v end
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.reject(t, fn, retainkeys)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
local rtn = {}
|
||||
if retainkeys then
|
||||
for k, v in iter(t) do
|
||||
if not fn(v) then rtn[k] = v end
|
||||
end
|
||||
else
|
||||
for _, v in iter(t) do
|
||||
if not fn(v) then rtn[#rtn + 1] = v end
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.merge(...)
|
||||
local rtn = {}
|
||||
for i = 1, select("#", ...) do
|
||||
local t = select(i, ...)
|
||||
local iter = getiter(t)
|
||||
for k, v in iter(t) do
|
||||
rtn[k] = v
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.concat(...)
|
||||
local rtn = {}
|
||||
for i = 1, select("#", ...) do
|
||||
local t = select(i, ...)
|
||||
if t ~= nil then
|
||||
local iter = getiter(t)
|
||||
for _, v in iter(t) do
|
||||
rtn[#rtn + 1] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.find(t, value)
|
||||
local iter = getiter(t)
|
||||
for k, v in iter(t) do
|
||||
if v == value then return k end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
function lume.match(t, fn)
|
||||
fn = iteratee(fn)
|
||||
local iter = getiter(t)
|
||||
for k, v in iter(t) do
|
||||
if fn(v) then return v, k end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
function lume.count(t, fn)
|
||||
local count = 0
|
||||
local iter = getiter(t)
|
||||
if fn then
|
||||
fn = iteratee(fn)
|
||||
for _, v in iter(t) do
|
||||
if fn(v) then count = count + 1 end
|
||||
end
|
||||
else
|
||||
if lume.isarray(t) then
|
||||
return #t
|
||||
end
|
||||
for _ in iter(t) do count = count + 1 end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
function lume.slice(t, i, j)
|
||||
i = i and absindex(#t, i) or 1
|
||||
j = j and absindex(#t, j) or #t
|
||||
local rtn = {}
|
||||
for x = i < 1 and 1 or i, j > #t and #t or j do
|
||||
rtn[#rtn + 1] = t[x]
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.first(t, n)
|
||||
if not n then return t[1] end
|
||||
return lume.slice(t, 1, n)
|
||||
end
|
||||
|
||||
|
||||
function lume.last(t, n)
|
||||
if not n then return t[#t] end
|
||||
return lume.slice(t, -n, -1)
|
||||
end
|
||||
|
||||
|
||||
function lume.invert(t)
|
||||
local rtn = {}
|
||||
for k, v in pairs(t) do rtn[v] = k end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.pick(t, ...)
|
||||
local rtn = {}
|
||||
for i = 1, select("#", ...) do
|
||||
local k = select(i, ...)
|
||||
rtn[k] = t[k]
|
||||
end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.keys(t)
|
||||
local rtn = {}
|
||||
local iter = getiter(t)
|
||||
for k in iter(t) do rtn[#rtn + 1] = k end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.clone(t)
|
||||
local rtn = {}
|
||||
for k, v in pairs(t) do rtn[k] = v end
|
||||
return rtn
|
||||
end
|
||||
|
||||
|
||||
function lume.fn(fn, ...)
|
||||
assert(iscallable(fn), "expected a function as the first argument")
|
||||
local args = { ... }
|
||||
return function(...)
|
||||
local a = lume.concat(args, { ... })
|
||||
return fn(unpack(a))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.once(fn, ...)
|
||||
local f = lume.fn(fn, ...)
|
||||
local done = false
|
||||
return function(...)
|
||||
if done then return end
|
||||
done = true
|
||||
return f(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local memoize_fnkey = {}
|
||||
local memoize_nil = {}
|
||||
|
||||
function lume.memoize(fn)
|
||||
local cache = {}
|
||||
return function(...)
|
||||
local c = cache
|
||||
for i = 1, select("#", ...) do
|
||||
local a = select(i, ...) or memoize_nil
|
||||
c[a] = c[a] or {}
|
||||
c = c[a]
|
||||
end
|
||||
c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
|
||||
return unpack(c[memoize_fnkey])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.combine(...)
|
||||
local n = select('#', ...)
|
||||
if n == 0 then return noop end
|
||||
if n == 1 then
|
||||
local fn = select(1, ...)
|
||||
if not fn then return noop end
|
||||
assert(iscallable(fn), "expected a function or nil")
|
||||
return fn
|
||||
end
|
||||
local funcs = {}
|
||||
for i = 1, n do
|
||||
local fn = select(i, ...)
|
||||
if fn ~= nil then
|
||||
assert(iscallable(fn), "expected a function or nil")
|
||||
funcs[#funcs + 1] = fn
|
||||
end
|
||||
end
|
||||
return function(...)
|
||||
for _, f in ipairs(funcs) do f(...) end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.call(fn, ...)
|
||||
if fn then
|
||||
return fn(...)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.time(fn, ...)
|
||||
local start = os.clock()
|
||||
local rtn = {fn(...)}
|
||||
return (os.clock() - start), unpack(rtn)
|
||||
end
|
||||
|
||||
|
||||
local lambda_cache = {}
|
||||
|
||||
function lume.lambda(str)
|
||||
if not lambda_cache[str] then
|
||||
local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
|
||||
assert(args and body, "bad string lambda")
|
||||
local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
|
||||
lambda_cache[str] = lume.dostring(s)
|
||||
end
|
||||
return lambda_cache[str]
|
||||
end
|
||||
|
||||
|
||||
local serialize
|
||||
|
||||
local serialize_map = {
|
||||
[ "boolean" ] = tostring,
|
||||
[ "nil" ] = tostring,
|
||||
[ "string" ] = function(v) return string.format("%q", v) end,
|
||||
[ "number" ] = function(v)
|
||||
if v ~= v then return "0/0" -- nan
|
||||
elseif v == 1 / 0 then return "1/0" -- inf
|
||||
elseif v == -1 / 0 then return "-1/0" end -- -inf
|
||||
return tostring(v)
|
||||
end,
|
||||
[ "table" ] = function(t, stk)
|
||||
stk = stk or {}
|
||||
if stk[t] then error("circular reference") end
|
||||
local rtn = {}
|
||||
stk[t] = true
|
||||
for k, v in pairs(t) do
|
||||
rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
|
||||
end
|
||||
stk[t] = nil
|
||||
return "{" .. table.concat(rtn, ",") .. "}"
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(serialize_map, {
|
||||
__index = function(_, k) error("unsupported serialize type: " .. k) end
|
||||
})
|
||||
|
||||
serialize = function(x, stk)
|
||||
return serialize_map[type(x)](x, stk)
|
||||
end
|
||||
|
||||
function lume.serialize(x)
|
||||
return serialize(x)
|
||||
end
|
||||
|
||||
|
||||
function lume.deserialize(str)
|
||||
return lume.dostring("return " .. str)
|
||||
end
|
||||
|
||||
|
||||
function lume.split(str, sep)
|
||||
if not sep then
|
||||
return lume.array(str:gmatch("([%S]+)"))
|
||||
else
|
||||
assert(sep ~= "", "empty separator")
|
||||
local psep = patternescape(sep)
|
||||
return lume.array((str..sep):gmatch("(.-)("..psep..")"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function lume.trim(str, chars)
|
||||
if not chars then return str:match("^[%s]*(.-)[%s]*$") end
|
||||
chars = patternescape(chars)
|
||||
return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
|
||||
end
|
||||
|
||||
|
||||
function lume.wordwrap(str, limit)
|
||||
limit = limit or 72
|
||||
local check
|
||||
if type(limit) == "number" then
|
||||
check = function(s) return #s >= limit end
|
||||
else
|
||||
check = limit
|
||||
end
|
||||
local rtn = {}
|
||||
local line = ""
|
||||
for word, spaces in str:gmatch("(%S+)(%s*)") do
|
||||
local s = line .. word
|
||||
if check(s) then
|
||||
table.insert(rtn, line .. "\n")
|
||||
line = word
|
||||
else
|
||||
line = s
|
||||
end
|
||||
for c in spaces:gmatch(".") do
|
||||
if c == "\n" then
|
||||
table.insert(rtn, line .. "\n")
|
||||
line = ""
|
||||
else
|
||||
line = line .. c
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(rtn, line)
|
||||
return table.concat(rtn)
|
||||
end
|
||||
|
||||
|
||||
function lume.format(str, vars)
|
||||
if not vars then return str end
|
||||
local f = function(x)
|
||||
return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
|
||||
end
|
||||
return (str:gsub("{(.-)}", f))
|
||||
end
|
||||
|
||||
|
||||
function lume.trace(...)
|
||||
local info = debug.getinfo(2, "Sl")
|
||||
local t = { info.short_src .. ":" .. info.currentline .. ":" }
|
||||
for i = 1, select("#", ...) do
|
||||
local x = select(i, ...)
|
||||
if type(x) == "number" then
|
||||
x = string.format("%g", lume.round(x, .01))
|
||||
end
|
||||
t[#t + 1] = tostring(x)
|
||||
end
|
||||
print(table.concat(t, " "))
|
||||
end
|
||||
|
||||
|
||||
function lume.dostring(str)
|
||||
return assert((loadstring or load)(str))()
|
||||
end
|
||||
|
||||
|
||||
function lume.uuid()
|
||||
local fn = function(x)
|
||||
local r = math.random(16) - 1
|
||||
r = (x == "x") and (r + 1) or (r % 4) + 9
|
||||
return ("0123456789abcdef"):sub(r, r)
|
||||
end
|
||||
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
|
||||
end
|
||||
|
||||
|
||||
function lume.hotswap(modname)
|
||||
local oldglobal = lume.clone(_G)
|
||||
local updated = {}
|
||||
local function update(old, new)
|
||||
if updated[old] then return end
|
||||
updated[old] = true
|
||||
local oldmt, newmt = getmetatable(old), getmetatable(new)
|
||||
if oldmt and newmt then update(oldmt, newmt) end
|
||||
for k, v in pairs(new) do
|
||||
if type(v) == "table" then update(old[k], v) else old[k] = v end
|
||||
end
|
||||
end
|
||||
local err = nil
|
||||
local function onerror(e)
|
||||
for k in pairs(_G) do _G[k] = oldglobal[k] end
|
||||
err = lume.trim(e)
|
||||
end
|
||||
local ok, oldmod = pcall(require, modname)
|
||||
oldmod = ok and oldmod or nil
|
||||
xpcall(function()
|
||||
package.loaded[modname] = nil
|
||||
local newmod = require(modname)
|
||||
if type(oldmod) == "table" then update(oldmod, newmod) end
|
||||
for k, v in pairs(oldglobal) do
|
||||
if v ~= _G[k] and type(v) == "table" then
|
||||
update(v, _G[k])
|
||||
_G[k] = v
|
||||
end
|
||||
end
|
||||
end, onerror)
|
||||
package.loaded[modname] = oldmod
|
||||
if err then return nil, err end
|
||||
return oldmod
|
||||
end
|
||||
|
||||
|
||||
local ripairs_iter = function(t, i)
|
||||
i = i - 1
|
||||
local v = t[i]
|
||||
if v ~= nil then
|
||||
return i, v
|
||||
end
|
||||
end
|
||||
|
||||
function lume.ripairs(t)
|
||||
return ripairs_iter, t, (#t + 1)
|
||||
end
|
||||
|
||||
|
||||
function lume.color(str, mul)
|
||||
mul = mul or 1
|
||||
local r, g, b, a
|
||||
r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
|
||||
if r then
|
||||
r = tonumber(r, 16) / 0xff
|
||||
g = tonumber(g, 16) / 0xff
|
||||
b = tonumber(b, 16) / 0xff
|
||||
a = 1
|
||||
elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
|
||||
local f = str:gmatch("[%d.]+")
|
||||
r = (f() or 0) / 0xff
|
||||
g = (f() or 0) / 0xff
|
||||
b = (f() or 0) / 0xff
|
||||
a = f() or 1
|
||||
else
|
||||
error(("bad color string '%s'"):format(str))
|
||||
end
|
||||
return r * mul, g * mul, b * mul, a * mul
|
||||
end
|
||||
|
||||
|
||||
local chain_mt = {}
|
||||
chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
|
||||
function(fn)
|
||||
return function(self, ...)
|
||||
self._value = fn(self._value, ...)
|
||||
return self
|
||||
end
|
||||
end)
|
||||
chain_mt.__index.result = function(x) return x._value end
|
||||
|
||||
function lume.chain(value)
|
||||
return setmetatable({ _value = value }, chain_mt)
|
||||
end
|
||||
|
||||
setmetatable(lume, {
|
||||
__call = function(_, ...)
|
||||
return lume.chain(...)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
return lume
|
||||
401
lua-scripts/narrator/list/mt.lua
Normal file
401
lua-scripts/narrator/list/mt.lua
Normal file
@@ -0,0 +1,401 @@
|
||||
--
|
||||
-- Dependencies
|
||||
|
||||
local lume = require('narrator.libs.lume')
|
||||
|
||||
--
|
||||
-- Metatable
|
||||
|
||||
local mt = { lists = { } }
|
||||
|
||||
function mt.__tostring(self)
|
||||
local pool = { }
|
||||
|
||||
local list_keys = { }
|
||||
for key, _ in pairs(self) do
|
||||
table.insert(list_keys, key)
|
||||
end
|
||||
table.sort(list_keys)
|
||||
|
||||
for i = 1, #list_keys do
|
||||
local list_name = list_keys[i]
|
||||
local list_items = self[list_name]
|
||||
for index = 1, #mt.lists[list_name] do
|
||||
pool[index] = pool[index] or { }
|
||||
local item_name = mt.lists[list_name][index]
|
||||
if list_items[item_name] == true then
|
||||
table.insert(pool[index], 1, item_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local items = { }
|
||||
|
||||
for _, titles in ipairs(pool) do
|
||||
for _, title in ipairs(titles) do
|
||||
table.insert(items, title)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(items, ', ')
|
||||
end
|
||||
|
||||
--
|
||||
-- Operators
|
||||
|
||||
function mt.__add(lhs, rhs) -- +
|
||||
if type(rhs) == 'table' then
|
||||
return mt.__add_list(lhs, rhs)
|
||||
elseif type(rhs) == 'number' then
|
||||
return mt.__shift_by_number(lhs, rhs)
|
||||
else
|
||||
error('Attempt to sum the list with ' .. type(rhs))
|
||||
end
|
||||
end
|
||||
|
||||
function mt.__sub(lhs, rhs) -- -
|
||||
if type(rhs) == 'table' then
|
||||
return mt.__subList(lhs, rhs)
|
||||
elseif type(rhs) == 'number' then
|
||||
return mt.__shift_by_number(lhs, -rhs)
|
||||
else
|
||||
error('Attempt to sub the list with ' .. type(rhs))
|
||||
end
|
||||
end
|
||||
|
||||
function mt.__mod(lhs, rhs) -- % (contain)
|
||||
if type(rhs) ~= 'table' then
|
||||
error('Attempt to check content of the list for ' .. type(rhs))
|
||||
end
|
||||
|
||||
for list_name, list_items in pairs(rhs) do
|
||||
if lhs[list_name] == nil then return false end
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if (lhs[list_name][item_name] or false) ~= item_value then return false end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function mt.__pow(lhs, rhs) -- ^ (intersection)
|
||||
if type(rhs) ~= 'table' then
|
||||
error('Attempt to interselect the list with ' .. type(rhs))
|
||||
end
|
||||
|
||||
local intersection = { }
|
||||
|
||||
for list_name, list_items in pairs(lhs) do
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
local left = lhs[list_name][item_name]
|
||||
local right = (rhs[list_name] or { })[item_name]
|
||||
if left == true and right == true then
|
||||
intersection[list_name] = intersection[list_name] or { }
|
||||
intersection[list_name][item_name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(intersection, mt)
|
||||
return intersection
|
||||
end
|
||||
|
||||
function mt.__len(self) -- #
|
||||
local len = 0
|
||||
|
||||
for list_name, list_items in pairs(self) do
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if item_value == true then len = len + 1 end
|
||||
end
|
||||
end
|
||||
|
||||
return len
|
||||
end
|
||||
|
||||
function mt.__eq(lhs, rhs) -- ==
|
||||
if type(rhs) ~= 'table' then
|
||||
error('Attempt to compare the list with ' .. type(rhs))
|
||||
end
|
||||
|
||||
local function keys_count(object)
|
||||
local count = 0
|
||||
for _, _ in pairs(object) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local left_lists_count = keys_count(lhs)
|
||||
local right_lists_count = keys_count(rhs)
|
||||
if left_lists_count ~= right_lists_count then
|
||||
return false
|
||||
end
|
||||
|
||||
for list_name, left_items in pairs(lhs) do
|
||||
local right_items = rhs[list_name]
|
||||
if right_items == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
local left_items_count = keys_count(left_items)
|
||||
local right_items_count = keys_count(right_items)
|
||||
|
||||
if left_items_count ~= right_items_count then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return mt.__mod(lhs, rhs)
|
||||
end
|
||||
|
||||
function mt.__lt(lhs, rhs) -- <
|
||||
if type(rhs) ~= 'table' then
|
||||
error('Attempt to compare the list with ' .. type(rhs))
|
||||
end
|
||||
|
||||
-- LEFT < RIGHT means "the smallest value in RIGHT is bigger than the largest values in LEFT"
|
||||
|
||||
local minLeft = mt.min_value_of(lhs, true)
|
||||
local maxRight = mt.max_value_of(rhs, true)
|
||||
|
||||
return minLeft < maxRight
|
||||
end
|
||||
|
||||
function mt.__le(lhs, rhs) -- <=
|
||||
if type(rhs) ~= 'table' then
|
||||
error('Attempt to compare the list with ' .. type(rhs))
|
||||
end
|
||||
|
||||
-- LEFT => RIGHT means "the smallest value in RIGHT is at least the smallest value in LEFT,
|
||||
-- and the largest value in RIGHT is at least the largest value in LEFT".
|
||||
|
||||
local minRight = mt.min_value_of(rhs, true)
|
||||
local minLeft = mt.min_value_of(lhs, true)
|
||||
local maxRight = mt.max_value_of(rhs, true)
|
||||
local maxLeft = mt.max_value_of(lhs, true)
|
||||
|
||||
return minRight >= minLeft and maxRight >= maxLeft
|
||||
end
|
||||
|
||||
--
|
||||
-- Custom operators
|
||||
|
||||
function mt.__add_list(lhs, rhs)
|
||||
local result = lume.clone(lhs)
|
||||
|
||||
for list_name, list_items in pairs(rhs) do
|
||||
result[list_name] = result[list_name] or { }
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
result[list_name][item_name] = item_value
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function mt.__subList(lhs, rhs)
|
||||
local result = lume.clone(lhs)
|
||||
|
||||
for list_name, list_items in pairs(rhs) do
|
||||
if lhs[list_name] ~= nil then
|
||||
for item_name, _ in pairs(list_items) do
|
||||
lhs[list_name][item_name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return mt.remove_empties_in_list(result)
|
||||
end
|
||||
|
||||
function mt.__shift_by_number(list, number)
|
||||
local result = { }
|
||||
|
||||
for list_name, list_items in pairs(list) do
|
||||
result[list_name] = { }
|
||||
for index, item_name in ipairs(mt.lists[list_name]) do
|
||||
if list_items[item_name] == true then
|
||||
local nextItem = mt.lists[list_name][index + number]
|
||||
if nextItem ~= nil then
|
||||
result[list_name][nextItem] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return mt.remove_empties_in_list(result)
|
||||
end
|
||||
|
||||
--
|
||||
-- Helpers
|
||||
|
||||
function mt.remove_empties_in_list(list)
|
||||
local result = lume.clone(list)
|
||||
|
||||
for list_name, list_items in pairs(list) do
|
||||
if next(list_items) == nil then
|
||||
result[list_name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function mt.min_value_of(list, raw)
|
||||
local min_index = 0
|
||||
local min_value = { }
|
||||
|
||||
local list_keys = { }
|
||||
for key, _ in pairs(list) do
|
||||
table.insert(list_keys, key)
|
||||
end
|
||||
table.sort(list_keys)
|
||||
|
||||
for i = 1, #list_keys do
|
||||
local list_name = list_keys[i]
|
||||
local list_items = list[list_name]
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if item_value == true then
|
||||
local index = lume.find(mt.lists[list_name], item_name)
|
||||
if index and index < min_index or min_index == 0 then
|
||||
min_index = index
|
||||
min_value = { [list_name] = { [item_name] = true } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return raw and min_index or min_value
|
||||
end
|
||||
|
||||
function mt.max_value_of(list, raw)
|
||||
local max_index = 0
|
||||
local max_value = { }
|
||||
|
||||
local list_keys = { }
|
||||
for key, _ in pairs(list) do
|
||||
table.insert(list_keys, key)
|
||||
end
|
||||
table.sort(list_keys)
|
||||
|
||||
for i = 1, #list_keys do
|
||||
local list_name = list_keys[i]
|
||||
local list_items = list[list_name]
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if item_value == true then
|
||||
local index = lume.find(mt.lists[list_name], item_name)
|
||||
if index and index > max_index or max_index == 0 then
|
||||
max_index = index
|
||||
max_value = { [list_name] = { [item_name] = true } }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return raw and max_index or max_value
|
||||
end
|
||||
|
||||
function mt.random_value_of(list)
|
||||
local items = { }
|
||||
|
||||
local list_keys = { }
|
||||
for key, _ in pairs(list) do
|
||||
table.insert(list_keys, key)
|
||||
end
|
||||
table.sort(list_keys)
|
||||
|
||||
for i = 1, #list_keys do
|
||||
local list_name = list_keys[i]
|
||||
local list_items = list[list_name]
|
||||
local items_keys = { }
|
||||
for key, _ in pairs(list_items) do
|
||||
table.insert(items_keys, key)
|
||||
end
|
||||
table.sort(items_keys)
|
||||
|
||||
for i = 1, #items_keys do
|
||||
local item_name = items_keys[i]
|
||||
local item_value = list_items[item_name]
|
||||
if item_value == true then
|
||||
local result = { [list_name] = { [item_name] = true } }
|
||||
table.insert(items, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local random_index = math.random(1, #items)
|
||||
return items[random_index]
|
||||
end
|
||||
|
||||
function mt.first_raw_value_of(list)
|
||||
local result = 0
|
||||
|
||||
for list_name, list_items in pairs(list) do
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if item_value == true then
|
||||
local index = lume.find(mt.lists[list_name], item_name)
|
||||
if index then
|
||||
result = index
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function mt.posible_values_of(list)
|
||||
local result = { }
|
||||
|
||||
for list_name, list_items in pairs(list) do
|
||||
local subList = { }
|
||||
for _, item_name in ipairs(mt.lists[list_name]) do
|
||||
subList[item_name] = true
|
||||
end
|
||||
result[list_name] = subList
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function mt.range_of(list, min, max)
|
||||
if type(min) ~= 'table' and type(min) ~= 'number' then
|
||||
error('Attempt to get a range with incorrect min value of type ' .. type(min))
|
||||
end
|
||||
if type(max) ~= 'table' and type(max) ~= 'number' then
|
||||
error('Attempt to get a range with incorrect max value of type ' .. type(max))
|
||||
end
|
||||
|
||||
local result = { }
|
||||
local allList = mt.posible_values_of(list)
|
||||
local min_index = type(min) == 'number' and min or mt.first_raw_value_of(min)
|
||||
local max_index = type(max) == 'number' and max or mt.first_raw_value_of(max)
|
||||
|
||||
for list_name, list_items in pairs(allList) do
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
local index = lume.find(mt.lists[list_name], item_name)
|
||||
if index and index >= min_index and index <= max_index and list[list_name][item_name] == true then
|
||||
result[list_name] = result[list_name] or { }
|
||||
result[list_name][item_name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function mt.invert(list)
|
||||
local result = mt.posible_values_of(list)
|
||||
|
||||
for list_name, list_items in pairs(list) do
|
||||
for item_name, item_value in pairs(list_items) do
|
||||
if item_value == true then
|
||||
result[list_name][item_name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return mt
|
||||
150
lua-scripts/narrator/narrator.lua
Normal file
150
lua-scripts/narrator/narrator.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
local lume = require('narrator.libs.lume')
|
||||
local enums = require('narrator.enums')
|
||||
local parser = require('narrator.parser')
|
||||
local Story = require('narrator.story')
|
||||
|
||||
--
|
||||
-- Local
|
||||
|
||||
local folder_separator = package.config:sub(1, 1)
|
||||
|
||||
---Clear path from '.lua' and '.ink' extensions and replace '.' to '/' or '\'
|
||||
---@param path string
|
||||
---@return string normalized_path
|
||||
local function normalize_path(path)
|
||||
local path = path:gsub('.lua$', '')
|
||||
local path = path:gsub('.ink$', '')
|
||||
|
||||
if path:match('%.') and not path:match(folder_separator) then
|
||||
path = path:gsub('%.', folder_separator)
|
||||
end
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
---Parse an .ink file to the content string.
|
||||
---@param path string
|
||||
---@return string content
|
||||
local function read_ink_file(path)
|
||||
local path = normalize_path(path) .. '.ink'
|
||||
|
||||
local file = io.open(path, 'r')
|
||||
assert(file, 'File doesn\'t exist: ' .. path)
|
||||
|
||||
local content = file:read('*all')
|
||||
file:close()
|
||||
|
||||
return content
|
||||
end
|
||||
|
||||
---Save a book to the lua module
|
||||
---@param book Narrator.Book
|
||||
---@param path string
|
||||
---@return boolean success
|
||||
local function save_book(book, path)
|
||||
local path = normalize_path(path) .. '.lua'
|
||||
|
||||
local data = lume.serialize(book)
|
||||
data = data:gsub('%[%d+%]=', '')
|
||||
data = data:gsub('[\'[%w_]+\']', function(match) return
|
||||
match:sub(3, #match - 2)
|
||||
end)
|
||||
|
||||
local file = io.open(path, 'w')
|
||||
if file == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
file:write('return ' .. data)
|
||||
file:close()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---Merge a chapter to the book
|
||||
---@param book Narrator.Book
|
||||
---@param chapter Narrator.Book
|
||||
---@return Narrator.Book
|
||||
local function merge_chapter_to_book(book, chapter)
|
||||
-- Check a engine version compatibility
|
||||
if chapter.version.engine and chapter.version.engine ~= enums.engine_version then
|
||||
assert('Version ' .. chapter.version.engine .. ' of book isn\'t equal to the version ' .. enums.engine_version .. ' of Narrator.')
|
||||
end
|
||||
|
||||
--Merge the root knot and it's stitch
|
||||
book.tree._._ = lume.concat(chapter.tree._._, book.tree._._)
|
||||
chapter.tree._._ = nil
|
||||
book.tree._ = lume.merge(chapter.tree._, book.tree._)
|
||||
chapter.tree._ = nil
|
||||
|
||||
--Merge a chapter to the book
|
||||
book.tree = lume.merge(book.tree or { }, chapter.tree or { })
|
||||
book.constants = lume.merge(book.constants or { }, chapter.constants or { })
|
||||
book.lists = lume.merge(book.lists or { }, chapter.lists or { })
|
||||
book.variables = lume.merge(book.variables or { }, chapter.variables or { })
|
||||
book.params = lume.merge(book.params or { }, chapter.params or { })
|
||||
|
||||
return book
|
||||
end
|
||||
|
||||
--
|
||||
-- Public
|
||||
|
||||
local narrator = { }
|
||||
|
||||
---Parse a book from an Ink file
|
||||
---Use it during development, but prefer already parsed and stored books in production
|
||||
---Requires `lpeg` and `io`.
|
||||
---@param path string
|
||||
---@param params Narrator.ParsingParams|nil
|
||||
---@return Narrator.Book
|
||||
function narrator.parse_file(path, params)
|
||||
local params = params or { save = false }
|
||||
assert(parser, 'Can\'t parse anything without lpeg, sorry.')
|
||||
|
||||
local content = read_ink_file(path)
|
||||
local book = parser.parse(content)
|
||||
|
||||
for _, inclusion in ipairs(book.inclusions) do
|
||||
local folder_path = normalize_path(path):match('(.*' .. folder_separator .. ')')
|
||||
local inclusion_path = folder_path .. normalize_path(inclusion) .. '.ink'
|
||||
local chapter = narrator.parse_file(inclusion_path)
|
||||
|
||||
merge_chapter_to_book(book, chapter)
|
||||
end
|
||||
|
||||
if params.save then
|
||||
save_book(book, path)
|
||||
end
|
||||
|
||||
return book
|
||||
end
|
||||
|
||||
---Parse a book from the ink content string
|
||||
---Use it during development, but prefer already parsed and stored books in production
|
||||
---Requires `lpeg`
|
||||
---@param content string
|
||||
---@param inclusions string[]
|
||||
---@return Narrator.Book
|
||||
function narrator.parse_content(content, inclusions)
|
||||
local inclusions = inclusions or { }
|
||||
assert(parser, 'Can\'t parse anything without a parser.')
|
||||
|
||||
local book = parser.parse(content)
|
||||
|
||||
for _, inclusion in ipairs(inclusions) do
|
||||
local chapter = parser.parse(inclusion)
|
||||
merge_chapter_to_book(book, chapter)
|
||||
end
|
||||
|
||||
return book
|
||||
end
|
||||
|
||||
---Init a story based on the book
|
||||
---@param book Narrator.Book
|
||||
---@return Narrator.Story
|
||||
function narrator.init_story(book)
|
||||
return Story(book)
|
||||
end
|
||||
|
||||
return narrator
|
||||
789
lua-scripts/narrator/parser.lua
Normal file
789
lua-scripts/narrator/parser.lua
Normal file
@@ -0,0 +1,789 @@
|
||||
local lume = require('narrator.libs.lume')
|
||||
local enums = require('narrator.enums')
|
||||
|
||||
--
|
||||
-- LPeg
|
||||
|
||||
-- To allow to build in Defold
|
||||
local lpeg_name = 'lpeg'
|
||||
|
||||
if not pcall(require, lpeg_name) then
|
||||
return false
|
||||
end
|
||||
|
||||
local lpeg = require(lpeg_name)
|
||||
|
||||
local S, C, P, V = lpeg.S, lpeg.C, lpeg.P, lpeg.V
|
||||
local Cb, Ct, Cc, Cg = lpeg.Cb, lpeg.Ct, lpeg.Cc, lpeg.Cg
|
||||
local Cmt = lpeg.Cmt
|
||||
|
||||
lpeg.locale(lpeg)
|
||||
|
||||
--
|
||||
-- Parser
|
||||
|
||||
local parser = { }
|
||||
local constructor = { }
|
||||
|
||||
---Parse ink content string
|
||||
---@param content string
|
||||
---@return Narrator.Book
|
||||
function parser.parse(content)
|
||||
|
||||
--
|
||||
-- Basic patterns
|
||||
|
||||
local function get_length(array) return
|
||||
#array
|
||||
end
|
||||
|
||||
local eof = -1
|
||||
local sp = S(' \t') ^ 0
|
||||
local ws = S(' \t\r\n') ^ 0
|
||||
local nl = S('\r\n') ^ 1
|
||||
local none = Cc(nil)
|
||||
|
||||
local divert_sign = P'->'
|
||||
local gather_mark = sp * C('-' - divert_sign)
|
||||
local gather_level = Cg(Ct(gather_mark ^ 1) / get_length + none, 'level')
|
||||
|
||||
local sticky_marks = Cg(Ct((sp * C('+')) ^ 1) / get_length, 'level') * Cg(Cc(true), 'sticky')
|
||||
local choice_marks = Cg(Ct((sp * C('*')) ^ 1) / get_length, 'level') * Cg(Cc(false), 'sticky')
|
||||
local choice_level = sticky_marks + choice_marks
|
||||
|
||||
local id = (lpeg.alpha + '_') * (lpeg.alnum + '_') ^ 0
|
||||
local label = Cg('(' * sp * C(id) * sp * ')', 'label')
|
||||
local address = id * ('.' * id) ^ -2
|
||||
|
||||
---Something for tunnels
|
||||
local function check_tunnel(s, i, a)
|
||||
local r = lpeg.match (sp * divert_sign, s, i)
|
||||
return i, r ~= nil
|
||||
end
|
||||
|
||||
-- TODO: Clean divert expression to divert and tunnel
|
||||
local divert = divert_sign * sp * Cg(address, 'path') -- base search for divert symbol and path to follow
|
||||
local check_tunnel = Cg(Cmt(Cb('path'), check_tunnel), 'tunnel') -- a weird way to to check tunnel
|
||||
local opt_tunnel_sign = (sp * divert_sign * sp * (#nl + #S'#') ) ^ -1 -- tunnel sign in end of string, keep newline not consumed
|
||||
divert = Cg(Ct(divert * sp * check_tunnel * opt_tunnel_sign), 'divert')
|
||||
|
||||
local divert_to_nothing = divert_sign * none
|
||||
local exit_tunnel = Cg(divert_sign * divert_sign, 'exit')
|
||||
local tag = '#' * sp * V'text'
|
||||
local tags = Cg(Ct(tag * (sp * tag) ^ 0), 'tags')
|
||||
|
||||
local todo = sp * 'TODO:' * (1 - nl) ^ 0
|
||||
local comment_line = sp * '//' * sp * (1 - nl) ^ 0
|
||||
local comment_multi = sp * '/*' * ((P(1) - '*/') ^ 0) * '*/'
|
||||
local comment = comment_line + comment_multi
|
||||
|
||||
local multiline_end = ws * '}'
|
||||
|
||||
--
|
||||
-- Dynamic patterns and evaluation helpers
|
||||
|
||||
local function item_type(type)
|
||||
return Cg(Cc(type), 'type')
|
||||
end
|
||||
|
||||
local function balanced_multiline_item(is_restricted)
|
||||
local is_restricted = is_restricted ~= nil and is_restricted or false
|
||||
local paragraph = is_restricted and V'restricted_paragraph' or V'paragraph'
|
||||
return sp * paragraph ^ -1 * sp * V'multiline_item' * sp * paragraph ^ -1 * ws
|
||||
end
|
||||
|
||||
local function sentence_before(excluded, tailed)
|
||||
local tailed = tailed or false
|
||||
local character = P(1 - S(' \t')) - excluded
|
||||
local pattern = (sp * character ^ 1) ^ 1
|
||||
local with_tail = C(pattern * sp)
|
||||
local without_tail = C(pattern) * sp
|
||||
local without_tail_always = C(pattern) * sp * #(tags + nl)
|
||||
return without_tail_always + (tailed and with_tail or without_tail)
|
||||
end
|
||||
|
||||
local function unwrap_assignment(assignment)
|
||||
local unwrapped = assignment
|
||||
unwrapped = unwrapped:gsub('([%w_]*)%s*([%+%-])[%+%-]', '%1 = %1 %2 1')
|
||||
unwrapped = unwrapped:gsub('([%w_]*)%s*([%+%-])=%s*(.*)', '%1 = %1 %2 %3')
|
||||
local name, value = unwrapped:match('([%w_]*)%s*=%s*(.*)')
|
||||
return name or '', value or assignment
|
||||
end
|
||||
|
||||
local function check_special_escape(s, i, a)
|
||||
if string.sub(s, i - 2, i - 2) == '\\' then
|
||||
return
|
||||
end
|
||||
|
||||
return i
|
||||
end
|
||||
|
||||
--
|
||||
-- Grammar rules
|
||||
|
||||
local ink_grammar = P({ 'root',
|
||||
|
||||
-- Root
|
||||
|
||||
root = ws * V'items' + eof,
|
||||
items = Ct(V'item' ^ 0),
|
||||
|
||||
item = balanced_multiline_item() + V'singleline_item',
|
||||
singleline_item = sp * (V'global' + V'statement' + V'paragraph' + V'gatherPoint') * ws,
|
||||
multiline_item = ('{' * sp * (V'sequence' + V'switch') * sp * multiline_end) - V'inline_condition',
|
||||
|
||||
-- Gather points
|
||||
gatherPoint = Ct(gather_level * sp * nl * item_type('gather')),
|
||||
|
||||
-- Global declarations
|
||||
|
||||
global =
|
||||
Ct(V'inclusion' * item_type('inclusion')) +
|
||||
Ct(V'list' * item_type('list')) +
|
||||
Ct(V'constant' * item_type('constant')) +
|
||||
Ct(V'variable' * item_type('variable'))
|
||||
,
|
||||
|
||||
inclusion = 'INCLUDE ' * sp * Cg(sentence_before(nl + comment), 'filename'),
|
||||
list = 'LIST ' * sp * V'assignment_pair',
|
||||
constant = 'CONST ' * sp * V'assignment_pair',
|
||||
variable = 'VAR ' * sp * V'assignment_pair',
|
||||
|
||||
-- Statements
|
||||
|
||||
statement =
|
||||
Ct(V'return_from_func' * item_type('return')) +
|
||||
Ct(V'assignment' * item_type('assignment')) +
|
||||
Ct(V'func' * item_type('func')) +
|
||||
Ct(V'knot' * item_type('knot')) +
|
||||
Ct(V'stitch' * item_type('stitch')) +
|
||||
Ct(V'choice' * item_type('choice')) +
|
||||
comment + todo
|
||||
,
|
||||
|
||||
section_name = C(id) * sp * P'=' ^ 0,
|
||||
knot = P'==' * (P'=' ^ 0) * sp * Cg(V'section_name', 'knot'),
|
||||
stitch = '=' * sp * Cg(V'section_name', 'stitch'),
|
||||
|
||||
func_param = sp * C(id) * sp * S','^0,
|
||||
func_params = P'(' * Cg(Ct(V'func_param'^0), 'params') * P')',
|
||||
function_name = P'function' * sp * Cg(id, 'name') * sp * V'func_params' * sp * P'=' ^ 0,
|
||||
func = P'==' * (P'=' ^ 0) * sp * Cg(Ct(V'function_name'), 'func'),
|
||||
|
||||
return_from_func = sp * '~' * sp * P('return') * sp * Cg((P(1) - nl)^0, 'value') * nl ^ 0,
|
||||
|
||||
assignment = gather_level * sp * '~' * sp * V'assignment_temp' * sp * V'assignment_pair',
|
||||
assignment_temp = Cg('temp' * Cc(true) + Cc(false), 'temp'),
|
||||
assignment_pair = Cg(sentence_before(nl + comment) / unwrap_assignment, 'name') * Cg(Cb('name') / 2, 'value'),
|
||||
|
||||
choice_condition = Cg(V'expression' + none, 'condition'),
|
||||
choice_fallback = choice_level * sp * V'label_optional' * sp * V'choice_condition' * sp * (divert + divert_to_nothing) * sp * V'tags_optional',
|
||||
choice_normal = choice_level * sp * V'label_optional' * sp * V'choice_condition' * sp * Cg(V'text', 'text') * divert ^ -1 * sp * V'tags_optional',
|
||||
choice = V'choice_fallback' + V'choice_normal',
|
||||
|
||||
-- Paragraph
|
||||
|
||||
paragraph = Ct(gather_level * sp * (V'paragraph_label' + V'paragraph_text' + V'paragraph_tags') * item_type('paragraph')),
|
||||
paragraph_label = label * sp * Cg(V'text_optional', 'parts') * sp * V'tags_optional',
|
||||
paragraph_text = V'label_optional' * sp * Cg(V'text_complex', 'parts') * sp * V'tags_optional',
|
||||
paragraph_tags = V'label_optional' * sp * Cg(V'text_optional', 'parts') * sp * tags,
|
||||
|
||||
label_optional = label + none,
|
||||
text_optional = V'text_complex' + none,
|
||||
tags_optional = tags + none,
|
||||
|
||||
text_complex = Ct((Ct(
|
||||
Cg(V'inline_condition', 'condition') +
|
||||
Cg(V'inline_sequence', 'sequence') +
|
||||
Cg(V'expression', 'expression') +
|
||||
Cg(V'text' + ' ', 'text') * (exit_tunnel ^ -1) * (divert ^ -1) + exit_tunnel + divert
|
||||
) - V'multiline_item') ^ 1),
|
||||
|
||||
special_check_escape = Cmt(S("{|}"), check_special_escape),
|
||||
|
||||
text = sentence_before(nl + exit_tunnel + divert + comment + tag + V'special_check_escape', true) - V'statement',
|
||||
-- Inline expressions, conditions, sequences
|
||||
|
||||
expression = '{' * sp * sentence_before('}' + nl) * sp * '}',
|
||||
|
||||
inline_condition = '{' * sp * Ct(V'inline_if_else' + V'inline_if') * sp * '}',
|
||||
inline_if = Cg(sentence_before(S':}' + nl), 'condition') * sp * ':' * sp * Cg(V'text_complex', 'success'),
|
||||
inline_if_else = (V'inline_if') * sp * '|' * sp * Cg(V'text_complex', 'failure'),
|
||||
|
||||
inline_alt_empty = Ct(Ct(Cg(sp * Cc'', 'text') * sp * divert ^ -1)),
|
||||
inline_alt = V'text_complex' + V'inline_alt_empty',
|
||||
inline_alts = Ct(((sp * V'inline_alt' * sp * '|') ^ 1) * sp * V'inline_alt'),
|
||||
inline_sequence = '{' * sp * (
|
||||
'!' * sp * Ct(Cg(V'inline_alts', 'alts') * Cg(Cc('once'), 'sequence')) +
|
||||
'&' * sp * Ct(Cg(V'inline_alts', 'alts') * Cg(Cc('cycle'), 'sequence')) +
|
||||
'~' * sp * Ct(Cg(V'inline_alts', 'alts') * Cg(Cc('stopping'), 'sequence') * Cg(Cc(true), 'shuffle')) +
|
||||
Ct(Cg(V'inline_alts', 'alts') * Cg(Cc('stopping'), 'sequence'))
|
||||
) * sp * '}',
|
||||
|
||||
-- Multiline conditions and switches
|
||||
|
||||
switch = Ct((V'switch_comparative' + V'switch_conditional') * item_type('switch')),
|
||||
|
||||
switch_comparative = Cg(V'switch_condition', 'expression') * ws * Cg(Ct((sp * V'switch_case') ^ 1), 'cases'),
|
||||
switch_conditional = Cg(Ct(V'switch_cases_headed' + V'switch_cases_only'), 'cases'),
|
||||
|
||||
switch_cases_headed = V'switch_if' * ((sp * V'switch_case') ^ 0),
|
||||
switch_cases_only = ws * ((sp * V'switch_case') ^ 1),
|
||||
|
||||
switch_if = Ct(Cg(V'switch_condition', 'condition') * ws * Cg(Ct(V'switch_items'), 'node')),
|
||||
switch_case = ('-' - divert_sign) * sp * V'switch_if',
|
||||
switch_condition = sentence_before(':' + nl) * sp * ':' * sp * comment ^ -1,
|
||||
switch_items = (V'restricted_item' - V'switch_case') ^ 1,
|
||||
|
||||
-- Multiline sequences
|
||||
|
||||
sequence = Ct((V'sequence_params' * sp * nl * sp * V'sequence_alts') * item_type('sequence')),
|
||||
|
||||
sequence_params = (
|
||||
V'sequence_shuffle_optional' * sp * V'sequence_type' +
|
||||
V'sequence_shuffle' * sp * V'sequence_type' +
|
||||
V'sequence_shuffle' * sp * V'sequence_type_optional'
|
||||
) * sp * ':' * sp * comment ^ -1,
|
||||
|
||||
sequence_shuffle_optional = V'sequence_shuffle' + Cg(Cc(false), 'shuffle'),
|
||||
sequence_shuffle = Cg(P'shuffle' / function() return true end, 'shuffle'),
|
||||
|
||||
sequence_type_optional = V'sequence_type' + Cg(Cc'cycle', 'sequence'),
|
||||
sequence_type = Cg(P'cycle' + 'stopping' + 'once', 'sequence'),
|
||||
|
||||
sequence_alts = Cg(Ct((sp * V'sequence_alt') ^ 1), 'alts'),
|
||||
sequence_alt = ('-' - divert_sign) * ws * Ct(V'sequence_items'),
|
||||
sequence_items = (V'restricted_item' - V'sequence_alt') ^ 1,
|
||||
|
||||
-- Restricted items inside multiline items
|
||||
|
||||
restricted_item = balanced_multiline_item(true) + V'restricted_singleline_item',
|
||||
restricted_singleline_item = sp * (V'global' + V'restricted_statement' + V'restricted_paragraph' - multiline_end) * ws,
|
||||
|
||||
restricted_statement = Ct(
|
||||
V'choice' * item_type('choice') +
|
||||
V'assignment' * item_type('assignment')
|
||||
) + comment + todo,
|
||||
|
||||
restricted_paragraph = Ct((
|
||||
Cg(V'text_complex', 'parts') * sp * V'tags_optional' +
|
||||
Cg(V'text_optional', 'parts') * sp * tags
|
||||
) * item_type('paragraph'))
|
||||
|
||||
})
|
||||
|
||||
--
|
||||
-- Result
|
||||
|
||||
local parsed_items = ink_grammar:match(content)
|
||||
local book = constructor.construct_book(parsed_items)
|
||||
return book
|
||||
end
|
||||
|
||||
--
|
||||
-- A book construction
|
||||
|
||||
function constructor.unescape(text)
|
||||
local result = text
|
||||
|
||||
result = result:gsub('\\|', '|')
|
||||
result = result:gsub('\\{', '{')
|
||||
result = result:gsub('\\}', '}')
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function constructor.construct_book(items)
|
||||
|
||||
local construction = {
|
||||
current_knot = '_',
|
||||
current_stitch = '_',
|
||||
variables_to_compute = { }
|
||||
}
|
||||
|
||||
construction.book = {
|
||||
inclusions = { },
|
||||
lists = { },
|
||||
constants = { },
|
||||
variables = { },
|
||||
params = { },
|
||||
tree = { _ = { _ = { } } }
|
||||
}
|
||||
|
||||
construction.book.version = {
|
||||
engine = enums.engine_version,
|
||||
tree = 1
|
||||
}
|
||||
|
||||
construction.nodes_chain = {
|
||||
construction.book.tree[construction.current_knot][construction.current_stitch]
|
||||
}
|
||||
|
||||
constructor.add_node(construction, items)
|
||||
constructor.clear(construction.book.tree)
|
||||
constructor.compute_variables(construction)
|
||||
|
||||
return construction.book
|
||||
end
|
||||
|
||||
function constructor:add_node(items, is_restricted)
|
||||
local is_restricted = is_restricted ~= nil and is_restricted or false
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
if is_restricted then
|
||||
-- Are not allowed inside multiline blocks by Ink rules:
|
||||
-- a) nesting levels
|
||||
-- b) choices without diverts
|
||||
|
||||
item.level = nil
|
||||
if item.type == 'choice' and item.divert == nil then
|
||||
item.type = nil
|
||||
end
|
||||
end
|
||||
|
||||
if item.type == 'inclusion' then
|
||||
-- filename
|
||||
constructor.add_inclusion(self, item.filename)
|
||||
elseif item.type == 'list' then
|
||||
-- name, value
|
||||
constructor.add_list(self, item.name, item.value)
|
||||
elseif item.type == 'constant' then
|
||||
-- name, value
|
||||
constructor.add_constant(self, item.name, item.value)
|
||||
elseif item.type == 'variable' then
|
||||
-- name, value
|
||||
constructor.add_variable(self, item.name, item.value)
|
||||
elseif item.type == 'func' then
|
||||
-- function
|
||||
constructor.add_function(self, item.func.name, item.func.params)
|
||||
elseif item.type == 'knot' then
|
||||
-- knot
|
||||
constructor.add_knot(self, item.knot)
|
||||
elseif item.type == 'stitch' then
|
||||
-- stitch
|
||||
constructor.add_stitch(self, item.stitch)
|
||||
elseif item.type == 'switch' then
|
||||
-- expression, cases
|
||||
constructor.add_switch(self, item.expression, item.cases)
|
||||
elseif item.type == 'sequence' then
|
||||
-- sequence, shuffle, alts
|
||||
constructor.add_sequence(self, item.sequence, item.shuffle, item.alts)
|
||||
elseif item.type == 'assignment' then
|
||||
-- level, name, value, temp
|
||||
constructor.add_assignment(self, item.level, item.name, item.value, item.temp)
|
||||
elseif item.type == 'return' then
|
||||
constructor.add_return(self, item.value)
|
||||
elseif item.type == 'paragraph' then
|
||||
-- level, label, parts, tags
|
||||
constructor.add_paragraph(self, item.level, item.label, item.parts, item.tags)
|
||||
elseif item.type == 'gather' then
|
||||
constructor.add_paragraph(self, item.level, "", nil, item.tags)
|
||||
elseif item.type == 'choice' then
|
||||
-- level, sticky, label, condition, text, divert, tags
|
||||
constructor.add_choice(self, item.level, item.sticky, item.label, item.condition, item.text, item.divert, item.tags)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function constructor:add_inclusion(filename)
|
||||
table.insert(self.book.inclusions, filename)
|
||||
end
|
||||
|
||||
function constructor:add_list(name, value)
|
||||
local items = lume.array(value:gmatch('[%w_%.]+'))
|
||||
self.book.lists[name] = items
|
||||
|
||||
local switched = lume.array(value:gmatch('%b()'))
|
||||
switched = lume.map(switched, function(item) return item:sub(2, #item - 1) end)
|
||||
self.book.variables[name] = { [name] = { } }
|
||||
lume.each(switched, function(item) self.book.variables[name][name][item] = true end)
|
||||
end
|
||||
|
||||
function constructor:add_constant(constant, value)
|
||||
local value = lume.deserialize(value)
|
||||
self.book.constants[constant] = value
|
||||
end
|
||||
|
||||
function constructor:add_variable(variable, value)
|
||||
self.variables_to_compute[variable] = value
|
||||
end
|
||||
|
||||
function constructor:add_function(fname, params)
|
||||
local node = { }
|
||||
self.book.tree[fname] = { ['_'] = node }
|
||||
self.book.params[fname] = params
|
||||
self.nodes_chain = { node }
|
||||
end
|
||||
|
||||
function constructor:add_knot(knot)
|
||||
self.current_knot = knot
|
||||
self.current_stitch = '_'
|
||||
|
||||
local node = { }
|
||||
self.book.tree[self.current_knot] = { [self.current_stitch] = node }
|
||||
self.nodes_chain = { node }
|
||||
end
|
||||
|
||||
function constructor:add_stitch(stitch)
|
||||
-- If a root stitch is empty we need to add a divert to the first stitch in the ink file.
|
||||
if self.current_stitch == '_' then
|
||||
local root_stitch_node = self.book.tree[self.current_knot]._
|
||||
if #root_stitch_node == 0 then
|
||||
local divertItem = { divert = { path = stitch } }
|
||||
table.insert(root_stitch_node, divertItem)
|
||||
end
|
||||
end
|
||||
|
||||
self.current_stitch = stitch
|
||||
|
||||
local node = { }
|
||||
self.book.tree[self.current_knot][self.current_stitch] = node
|
||||
self.nodes_chain = { node }
|
||||
end
|
||||
|
||||
function constructor:add_switch(expression, cases)
|
||||
if expression then
|
||||
-- Convert switch cases to comparing conditions with expression
|
||||
for _, case in ipairs(cases) do
|
||||
if case.condition ~= 'else' then
|
||||
case.condition = expression .. '==' .. case.condition
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local item = {
|
||||
condition = { },
|
||||
success = { }
|
||||
}
|
||||
|
||||
for _, case in ipairs(cases) do
|
||||
if case.condition == 'else' then
|
||||
local failure_node = { }
|
||||
table.insert(self.nodes_chain, failure_node)
|
||||
constructor.add_node(self, case.node, true)
|
||||
table.remove(self.nodes_chain)
|
||||
item.failure = failure_node
|
||||
else
|
||||
local success_node = { }
|
||||
table.insert(self.nodes_chain, success_node)
|
||||
constructor.add_node(self, case.node, true)
|
||||
table.remove(self.nodes_chain)
|
||||
table.insert(item.success, success_node)
|
||||
table.insert(item.condition, case.condition)
|
||||
end
|
||||
end
|
||||
|
||||
constructor.add_item(self, nil, item)
|
||||
end
|
||||
|
||||
function constructor:add_sequence(sequence, shuffle, alts)
|
||||
local item = {
|
||||
sequence = sequence,
|
||||
shuffle = shuffle and true or nil,
|
||||
alts = { }
|
||||
}
|
||||
|
||||
for _, alt in ipairs(alts) do
|
||||
local alt_node = { }
|
||||
table.insert(self.nodes_chain, alt_node)
|
||||
constructor.add_node(self, alt, true)
|
||||
table.remove(self.nodes_chain)
|
||||
table.insert(item.alts, alt_node)
|
||||
end
|
||||
|
||||
constructor.add_item(self, nil, item)
|
||||
end
|
||||
|
||||
function constructor:add_return(value)
|
||||
local item = {
|
||||
return_value = value
|
||||
}
|
||||
|
||||
constructor.add_item(self, nil, item)
|
||||
end
|
||||
|
||||
function constructor:add_assignment(level, name, value, temp)
|
||||
local item = {
|
||||
temp = temp or nil,
|
||||
var = name,
|
||||
value = value
|
||||
}
|
||||
|
||||
constructor.add_item(self, level, item)
|
||||
end
|
||||
|
||||
function constructor:add_paragraph(level, label, parts, tags)
|
||||
local items = constructor.convert_paragraph_parts_to_items(parts, true)
|
||||
items = items or { }
|
||||
|
||||
-- If the paragraph has a label or tags we need to place them as the first text item.
|
||||
if label ~= nil or tags ~= nil then
|
||||
local first_item
|
||||
|
||||
if #items > 0 and items[1].condition == nil then
|
||||
first_item = items[1]
|
||||
else
|
||||
first_item = { }
|
||||
table.insert(items, first_item)
|
||||
end
|
||||
|
||||
first_item.label = label
|
||||
first_item.tags = tags
|
||||
end
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
constructor.add_item(self, level, item)
|
||||
end
|
||||
end
|
||||
|
||||
function constructor.convert_paragraph_parts_to_items(parts, is_root)
|
||||
if parts == nil then return nil end
|
||||
|
||||
local is_root = is_root ~= nil and is_root or false
|
||||
local items = { }
|
||||
local item
|
||||
|
||||
for index, part in ipairs(parts) do
|
||||
|
||||
if part.condition then -- Inline condition part
|
||||
|
||||
item = {
|
||||
condition = part.condition.condition,
|
||||
success = constructor.convert_paragraph_parts_to_items(part.condition.success),
|
||||
failure = constructor.convert_paragraph_parts_to_items(part.condition.failure)
|
||||
}
|
||||
|
||||
table.insert(items, item)
|
||||
item = nil
|
||||
|
||||
elseif part.sequence then -- Inline sequence part
|
||||
|
||||
item = {
|
||||
sequence = part.sequence.sequence,
|
||||
shuffle = part.sequence.shuffle and true or nil,
|
||||
alts = { }
|
||||
}
|
||||
|
||||
for _, alt in ipairs(part.sequence.alts) do
|
||||
table.insert(item.alts, constructor.convert_paragraph_parts_to_items(alt))
|
||||
end
|
||||
|
||||
table.insert(items, item)
|
||||
item = nil
|
||||
|
||||
else -- Text, expression and divert may be
|
||||
|
||||
local is_divert_only = part.divert ~= nil and part.text == nil
|
||||
|
||||
if item == nil then
|
||||
item = { text = (is_root or is_divert_only) and '' or '<>' }
|
||||
end
|
||||
|
||||
if part.text then
|
||||
item.text = item.text .. part.text:gsub('%s+', ' ')
|
||||
item.text = constructor.unescape(item.text)
|
||||
elseif part.expression then
|
||||
item.text = item.text .. '#' .. part.expression .. '#'
|
||||
end
|
||||
|
||||
if part.divert or part.exit then
|
||||
item.exit = part.exit and true or nil
|
||||
item.divert = part.divert
|
||||
item.text = #item.text > 0 and (item.text .. '<>') or nil
|
||||
table.insert(items, item)
|
||||
item = nil
|
||||
else
|
||||
local next = parts[index + 1]
|
||||
local next_is_block = next and not (next.text or next.expression)
|
||||
|
||||
if not next or next_is_block then
|
||||
if not is_root or next_is_block then
|
||||
item.text = item.text .. '<>'
|
||||
end
|
||||
table.insert(items, item)
|
||||
item = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
if is_root then
|
||||
-- Add a safe prefix and suffix for correct conditions gluing
|
||||
|
||||
local first_item = items[1]
|
||||
if first_item.text == nil and first_item.divert == nil and first_item.exit == nil then
|
||||
table.insert(items, 1, { text = '' } )
|
||||
end
|
||||
|
||||
local last_item = items[#items]
|
||||
if last_item.text == nil and last_item.divert == nil and last_item.exit == nil then
|
||||
table.insert(items, { text = '' } )
|
||||
elseif last_item.text ~= nil and last_item.divert == nil then
|
||||
last_item.text = last_item.text:gsub('(.-)%s*$', '%1')
|
||||
end
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
function constructor:add_choice(level, sticky, label, condition, sentence, divert, tags)
|
||||
local item = {
|
||||
sticky = sticky or nil,
|
||||
condition = condition,
|
||||
label = label,
|
||||
divert = divert,
|
||||
tags = tags
|
||||
}
|
||||
|
||||
if sentence == nil then
|
||||
item.choice = 0
|
||||
else
|
||||
local prefix, divider, suffix = sentence:match('(.*)%[(.*)%](.*)')
|
||||
prefix = prefix or sentence
|
||||
divider = divider or ''
|
||||
suffix = suffix or ''
|
||||
|
||||
local text = (prefix .. suffix):gsub('%s+', ' ')
|
||||
local choice = (prefix .. divider):gsub('%s+', ' '):gsub('^%s*(.-)%s*$', '%1')
|
||||
|
||||
if divert and #text > 0 and text:match('%S+') then
|
||||
text = text .. '<>'
|
||||
else
|
||||
text = text:gsub('^%s*(.-)%s*$', '%1')
|
||||
end
|
||||
|
||||
item.text = constructor.unescape(text)
|
||||
item.choice = constructor.unescape(choice)
|
||||
end
|
||||
|
||||
constructor.add_item(self, level, item)
|
||||
|
||||
if divert == nil then
|
||||
item.node = { }
|
||||
table.insert(self.nodes_chain, item.node)
|
||||
end
|
||||
end
|
||||
|
||||
function constructor:add_item(level, item)
|
||||
local level = (level ~= nil and level > 0) and level or #self.nodes_chain
|
||||
while #self.nodes_chain > level do
|
||||
table.remove(self.nodes_chain)
|
||||
end
|
||||
|
||||
local node = self.nodes_chain[#self.nodes_chain]
|
||||
table.insert(node, item)
|
||||
end
|
||||
|
||||
function constructor:compute_variable(variable, value)
|
||||
local constant = self.book.constants[value]
|
||||
if constant then
|
||||
self.book.variables[variable] = constant
|
||||
return
|
||||
end
|
||||
|
||||
local list_expression = value:match('%(([%s%w%.,_]*)%)')
|
||||
local item_expressions = list_expression and lume.array(list_expression:gmatch('[%w_%.]+')) or { value }
|
||||
local list_variable = list_expression and { } or nil
|
||||
|
||||
for _, item_expression in ipairs(item_expressions) do
|
||||
local list_part, item_part = item_expression:match('([%w_]+)%.([%w_]+)')
|
||||
item_part = item_part or item_expression
|
||||
|
||||
for list_name, list_items in pairs(self.book.lists) do
|
||||
local list_is_valid = list_part == nil or list_part == list_name
|
||||
local item_is_found = lume.find(list_items, item_part)
|
||||
|
||||
if list_is_valid and item_is_found then
|
||||
list_variable = list_variable or { }
|
||||
list_variable[list_name] = list_variable[list_name] or { }
|
||||
list_variable[list_name][item_part] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if list_variable then
|
||||
self.book.variables[variable] = list_variable
|
||||
else
|
||||
self.book.variables[variable] = lume.deserialize(value)
|
||||
end
|
||||
end
|
||||
|
||||
function constructor:compute_variables()
|
||||
for variable, value in pairs(self.variables_to_compute) do
|
||||
constructor.compute_variable(self, variable, value)
|
||||
end
|
||||
end
|
||||
|
||||
function constructor.clear(tree)
|
||||
for knot, node in pairs(tree) do
|
||||
for stitch, node in pairs(node) do
|
||||
constructor.clear_node(node)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function constructor.clear_node(node)
|
||||
for index, item in ipairs(node) do
|
||||
|
||||
-- Simplify text only items
|
||||
if item.text ~= nil and lume.count(item) == 1 then
|
||||
node[index] = item.text
|
||||
end
|
||||
|
||||
if item.node ~= nil then
|
||||
-- Clear choice nodes
|
||||
if #item.node == 0 then
|
||||
item.node = nil
|
||||
else
|
||||
constructor.clear_node(item.node)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if item.success ~= nil then
|
||||
-- Simplify single condition
|
||||
if type(item.condition) == 'table' and #item.condition == 1 then
|
||||
item.condition = item.condition[1]
|
||||
end
|
||||
|
||||
-- Clear success nodes
|
||||
if item.success[1] ~= nil and item.success[1][1] ~= nil then
|
||||
for index, success_node in ipairs(item.success) do
|
||||
constructor.clear_node(success_node)
|
||||
if #success_node == 1 and type(success_node[1]) == 'string' then
|
||||
item.success[index] = success_node[1]
|
||||
end
|
||||
end
|
||||
|
||||
if #item.success == 1 then
|
||||
item.success = item.success[1]
|
||||
end
|
||||
else
|
||||
constructor.clear_node(item.success)
|
||||
if #item.success == 1 and type(item.success[1]) == 'string' then
|
||||
item.success = item.success[1]
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear failure nodes
|
||||
if item.failure ~= nil then
|
||||
constructor.clear_node(item.failure)
|
||||
if #item.failure == 1 and type(item.failure[1]) == 'string' then
|
||||
item.failure = item.failure[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if item.alts ~= nil then
|
||||
for index, alt_node in ipairs(item.alts) do
|
||||
constructor.clear_node(alt_node)
|
||||
if #alt_node == 1 and type(alt_node[1]) == 'string' then
|
||||
item.alts[index] = alt_node[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return parser
|
||||
1253
lua-scripts/narrator/story.lua
Normal file
1253
lua-scripts/narrator/story.lua
Normal file
File diff suppressed because it is too large
Load Diff
0
lua-scripts/stories/debug.ink
Normal file
0
lua-scripts/stories/debug.ink
Normal file
19
lua-scripts/stories/game.ink
Normal file
19
lua-scripts/stories/game.ink
Normal file
@@ -0,0 +1,19 @@
|
||||
- I looked at Monsieur Fogg
|
||||
* ... and I could contain myself no longer.
|
||||
'What is the purpose of our journey, Monsieur?'
|
||||
'A wager,' he replied.
|
||||
* * 'A wager!'[] I returned.
|
||||
He nodded.
|
||||
* * * 'But surely that is foolishness!'
|
||||
* * * 'A most serious matter then!'
|
||||
- - - He nodded again.
|
||||
* * * 'But can we win?'
|
||||
'That is what we will endeavour to find out,' he answered.
|
||||
* * * 'A modest wager, I trust?'
|
||||
'Twenty thousand pounds,' he replied, quite flatly.
|
||||
* * * I asked nothing further of him then[.], and after a final, polite cough, he offered nothing more to me. <>
|
||||
* * 'Ah[.'],' I replied, uncertain what I thought.
|
||||
- - After that, <>
|
||||
* ... but I said nothing[] and <>
|
||||
- we passed the day in silence.
|
||||
- -> END
|
||||
1
lua-scripts/stories/game.lua
Normal file
1
lua-scripts/stories/game.lua
Normal file
@@ -0,0 +1 @@
|
||||
return {inclusions={},constants={},version={tree=1,engine=1},tree={_={_={"I looked at Monsieur Fogg",{text="... and I could contain myself no longer.",choice="... and I could contain myself no longer.",node={"'What is the purpose of our journey, Monsieur?'","'A wager,' he replied.",{text="'A wager!' I returned.",choice="'A wager!'",node={"He nodded.",{text="'But surely that is foolishness!'",choice="'But surely that is foolishness!'"},{text="'A most serious matter then!'",choice="'A most serious matter then!'"},"He nodded again.",{text="'But can we win?'",choice="'But can we win?'",node={"'That is what we will endeavour to find out,' he answered."}},{text="'A modest wager, I trust?'",choice="'A modest wager, I trust?'",node={"'Twenty thousand pounds,' he replied, quite flatly."}},{text="I asked nothing further of him then, and after a final, polite cough, he offered nothing more to me. <>",choice="I asked nothing further of him then."}}},{text="'Ah,' I replied, uncertain what I thought.",choice="'Ah.'"},"After that, <>"}},{text="... but I said nothing and <>",choice="... but I said nothing"},"we passed the day in silence.",{divert="END"}}}},lists={},variables={}}
|
||||
35
lua-scripts/stories/initiation.ink
Normal file
35
lua-scripts/stories/initiation.ink
Normal file
@@ -0,0 +1,35 @@
|
||||
- The party was hot, girls were sexy the wine, beer and whisky were in <>
|
||||
enormous numbers. It was anniversary since you set sail with your friends.
|
||||
The sea was calm and everything promised you another great night. <>
|
||||
The whole year with your friends on your ship over vast seas of the world in decay was almost over. <>
|
||||
It was so good year full of adventure, romance, indecency and luxury.
|
||||
You did not know what future had for you and you never thought much about it. However <>
|
||||
you had zero insights on what to follow even if you cared enough about it. You thought you could not <>
|
||||
wish for brigher future far away from troubles of this world.
|
||||
One day (a few days ago) your trusted friends decided that you have too much, they have too much of you and you owe them a lot.<>
|
||||
They waited for opportunity to get rid of you. You totally missed the point when people started conspiring against you<>
|
||||
and the following events came at total surprize.
|
||||
They found you drunk in your room and moved to the deck and used ropes to <>
|
||||
restrain you and attach a bucket of stones or something else heavy to your body. <>
|
||||
They left you lying on the deck while were checking your room, looking for papers and something of value.
|
||||
After a few hours passed two strong people pulled you to the side and <>
|
||||
dropped you into the sea. You did not see their faces. Last thing you heard before you hit the water was happy laugher...
|
||||
Suddenly next you found yourself under water out of breath in agony. You did not know nor care about time and place, you just cared for <>
|
||||
last air leaving your body. All you wished for was quick end.
|
||||
Suddenly the suffering stopped and your mind cleared. Some impactful presence <>
|
||||
subdued everything in your mind leaving you with question. The only thing you could see was a strange altar for some ancient deity nearby.
|
||||
You had very bad feeling about this.
|
||||
'You have just a few seconds, so be quick' - the voice sounded directly somewhere inside your head.
|
||||
'WILL you serve me?' Something told me my life depends on the answer.
|
||||
* Sure
|
||||
Of course you will.
|
||||
* Do I have a choice?
|
||||
You have or you have not. That depends on what you expect.
|
||||
* I will
|
||||
I know.
|
||||
- An abrupt silence.
|
||||
Then I feel my restraints removed and I started rising to the surface. 'We'll talk again soon enough' - the voice in my head told me.
|
||||
'Your service will be rewarded. Go enjoy your new life.'
|
||||
* [Ascend]
|
||||
~ mc_is_free()
|
||||
- ->END
|
||||
@@ -22,6 +22,7 @@ FileSystem=skybox
|
||||
FileSystem=resources/buildings
|
||||
FileSystem=resources/vehicles
|
||||
FileSystem=resources/debug
|
||||
FileSystem=resources/fonts
|
||||
# PBR media must come before the scripts that reference it
|
||||
#FileSystem=./Media/PBR
|
||||
#FileSystem=./Media/PBR/filament
|
||||
|
||||
35
src/gamedata/BoatModule.cpp
Normal file
35
src/gamedata/BoatModule.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <iostream>
|
||||
#include <OgreMeshManager.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "BoatModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
BoatModule::BoatModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<BoatModule>();
|
||||
ecs.component<BoatBase>();
|
||||
ecs.component<BoatBody>();
|
||||
ecs.component<BoatType>().on_set([](flecs::entity e, BoatType &type) {
|
||||
int i;
|
||||
if (e.has<BoatBody>() || e.has<BoatBase>())
|
||||
return;
|
||||
BoatBase &boat = e.ensure<BoatBase>();
|
||||
boat.mEnt = ECS::get<EngineData>().mScnMgr->createEntity(
|
||||
type.resourceName);
|
||||
boat.mNode = ECS::get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode(type.position,
|
||||
type.orientation);
|
||||
boat.mNode->attachObject(boat.mEnt);
|
||||
|
||||
BoatBody &body = e.ensure<BoatBody>();
|
||||
body.body = ECS::get<EngineData>().mWorld->addRigidBody(
|
||||
0, boat.mEnt, Ogre::Bullet::CT_HULL, nullptr, 2,
|
||||
0x7fffffff);
|
||||
e.modified<BoatBody>();
|
||||
});
|
||||
}
|
||||
}
|
||||
28
src/gamedata/BoatModule.h
Normal file
28
src/gamedata/BoatModule.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef BOAT_MODULE_H_
|
||||
#define BOAT_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
namespace Ogre
|
||||
{
|
||||
class Entity;
|
||||
class SceneNode;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct BoatType {
|
||||
Ogre::String resourceName;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
};
|
||||
struct BoatBase {
|
||||
Ogre::Entity *mEnt;
|
||||
Ogre::SceneNode *mNode;
|
||||
};
|
||||
struct BoatBody {
|
||||
btRigidBody *body;
|
||||
};
|
||||
struct BoatModule {
|
||||
BoatModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
project(gamedata)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp)
|
||||
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp
|
||||
BoatModule.cpp EventTriggerModule.cpp)
|
||||
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static lua)
|
||||
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@@ -4,28 +4,22 @@
|
||||
#include "GameData.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "WaterModule.h"
|
||||
#include "TerrainModule.h"
|
||||
#include "Components.h"
|
||||
namespace ECS
|
||||
{
|
||||
CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterModule>();
|
||||
player = ecs.entity("player");
|
||||
player.set<AnimationControl>({ AnimationControl::ANIM_NONE,
|
||||
AnimationControl::ANIM_NONE, false,
|
||||
false });
|
||||
player.set<CharacterBase>(
|
||||
{ "normal-male.glb", 0.0f, nullptr, nullptr, nullptr });
|
||||
player.set<CharacterBody>(
|
||||
{ nullptr, nullptr, nullptr, { 0, 0, 0 }, false, false });
|
||||
player.add<Character>();
|
||||
player.add<Player>();
|
||||
ecs.component<Character>();
|
||||
ecs.component<Player>();
|
||||
ecs.component<CharacterBase>();
|
||||
ecs.component<CharacterVelocity>();
|
||||
ecs.component<CharacterBody>();
|
||||
ecs.system<EngineData, CharacterBase>("UpdateTimer")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](EngineData &eng, CharacterBase &ch) {
|
||||
ch.mTimer += eng.delta;
|
||||
if (eng.startupDelay >= 0.0f)
|
||||
eng.startupDelay -= eng.delta;
|
||||
});
|
||||
ecs.system<Input, Camera>("HandleInput")
|
||||
.kind(flecs::OnUpdate)
|
||||
@@ -38,6 +32,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
uint32_t active = input.control;
|
||||
float zaxis = input.motion.z;
|
||||
zaxis *= 0.9f;
|
||||
if (!camera.mCameraPivot || !camera.mCameraGoal)
|
||||
return;
|
||||
if (pressed & 1)
|
||||
std::cout << "W pressed\n";
|
||||
if (released & 1)
|
||||
@@ -92,7 +88,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
Ogre::ANIMBLEND_CUMULATIVE);
|
||||
Ogre::String
|
||||
animNames[AnimationControl::NUM_ANIMS] = {
|
||||
"idle", "walking", "running"
|
||||
"idle", "walking", "running",
|
||||
"treading_water", "swimming"
|
||||
};
|
||||
for (i = 0; i < AnimationControl::NUM_ANIMS;
|
||||
i++) {
|
||||
@@ -160,11 +157,11 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
if (anim.currentAnim != anim.nextAnim)
|
||||
setAnimation(anim);
|
||||
});
|
||||
ecs.system<EngineData, CharacterBase, AnimationControl>(
|
||||
"HandleAnimations1")
|
||||
ecs.system<const EngineData, const Input, CharacterBase,
|
||||
AnimationControl>("HandleAnimations1")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](EngineData &eng, CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
.each([this](const EngineData &eng, const Input &input,
|
||||
CharacterBase &ch, AnimationControl &anim) {
|
||||
float delta = eng.delta;
|
||||
Ogre::Real animSpeed = 1;
|
||||
if (anim.currentAnim != AnimationControl::ANIM_NONE) {
|
||||
@@ -172,6 +169,11 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
AnimationControl::ANIM_WALK)
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * 1.0f);
|
||||
else if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING &&
|
||||
input.fast)
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * 20.0f);
|
||||
else
|
||||
anim.mAnims[anim.currentAnim]->addTime(
|
||||
delta * animSpeed);
|
||||
@@ -181,26 +183,38 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
return;
|
||||
ch.mBoneMotion = ch.mRootBone->getPosition();
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody,
|
||||
AnimationControl>("HandleRootMotion")
|
||||
ecs.system<CharacterBase>()
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, CharacterBody &body,
|
||||
AnimationControl &anim) {
|
||||
float delta = e.world().delta_time();
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, CharacterBase &ch) {
|
||||
float full_subm = 2.0f;
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
Ogre::Vector3 boneMotion = ch.mBoneMotion;
|
||||
Ogre::Vector3 velocity = rot * boneMotion / delta;
|
||||
OgreAssert(delta > 0.0f, "Zero delta");
|
||||
int maxPen = 0;
|
||||
Ogre::Vector3 colNormal;
|
||||
bool is_on_floor = false;
|
||||
bool penetration = false;
|
||||
float current_subm = -Ogre::Math::Clamp(
|
||||
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
|
||||
130.0f) *
|
||||
0.07f,
|
||||
-full_subm, 0.0f);
|
||||
if (current_subm > 0.9f)
|
||||
ch.is_submerged = true;
|
||||
else if (current_subm < 0.8f)
|
||||
ch.is_submerged = false;
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleGravityBouyanceWater")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
Ogre::Vector3 gravity(0, -9.8f, 0);
|
||||
if (e.has<InWater>()) {
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
Ogre::Vector3 v(0, 0, 0);
|
||||
if (e.has<CharacterGravity>())
|
||||
v += gravity;
|
||||
if (e.has<CharacterBuoyancy>()) {
|
||||
float volume = 2.0f * 0.5f * 0.5f;
|
||||
float density = 900.0f;
|
||||
float full_subm = 2.0f;
|
||||
@@ -216,63 +230,183 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
Ogre::Vector3 b = -gravity * density * volume *
|
||||
multiplier * current_subm /
|
||||
full_subm / mass;
|
||||
body.gvelocity += (gravity + b) * delta;
|
||||
body.gvelocity.y = Ogre::Math::Clamp(
|
||||
body.gvelocity.y, -2.5f, 2.5f);
|
||||
} else
|
||||
body.gvelocity += gravity * delta;
|
||||
if (eng.startupDelay < 0.0f) {
|
||||
body.gvelocity *= 0.99;
|
||||
velocity += body.gvelocity;
|
||||
Ogre::Vector3 rotMotion = velocity * delta;
|
||||
btVector3 currentPosition =
|
||||
body.mGhostObject->getWorldTransform()
|
||||
.getOrigin();
|
||||
is_on_floor = body.mController->isOnFloor();
|
||||
penetration = body.mController->isPenetrating();
|
||||
if (is_on_floor)
|
||||
body.gvelocity =
|
||||
Ogre::Vector3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
btTransform from(
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getOrientation()),
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getPosition()));
|
||||
ch.mBodyNode->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
rotMotion);
|
||||
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
|
||||
v += b;
|
||||
}
|
||||
gr.gvelocity += v * eng.delta;
|
||||
gr.gvelocity.y =
|
||||
Ogre::Math::Clamp(gr.gvelocity.y, -2.5f, 1.5f);
|
||||
gr.gvelocity *= (1.0 - eng.delta);
|
||||
gr.velocity.y *= (1.0 - eng.delta);
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleGravityNoWater")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.without<InWater>()
|
||||
.with<CharacterGravity>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
Ogre::Vector3 gravity(0, -9.8f, 0);
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
gr.gvelocity += gravity * eng.delta;
|
||||
if (pos.y < -1.2) {
|
||||
gr.gvelocity.y = 0.0f;
|
||||
}
|
||||
gr.gvelocity *= (1.0 - eng.delta);
|
||||
gr.velocity.y *= (1.0 - eng.delta);
|
||||
});
|
||||
ecs.system<const EngineData, const AnimationControl,
|
||||
const CharacterBase, CharacterVelocity>("HandleSwimming")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<InWater>()
|
||||
.with<CharacterBuoyancy>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const AnimationControl &anim,
|
||||
const CharacterBase &ch, CharacterVelocity &gr) {
|
||||
if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING) {
|
||||
float h = Ogre::Math::Clamp(
|
||||
0.0f - ch.mBodyNode->getPosition().y,
|
||||
0.0f, 2000.0f);
|
||||
if (h > 0.05 && h < 2.0f)
|
||||
gr.gvelocity.y += 0.1f * (h + 1.0f) *
|
||||
h * eng.delta;
|
||||
}
|
||||
});
|
||||
ecs.system<const Input, AnimationControl>("HandlePlayerAnimations")
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
|
||||
"HandleRootMotionVelocity")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterVelocity &v) {
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
Ogre::Vector3 boneMotion = ch.mBoneMotion;
|
||||
v.velocity = rot * boneMotion / eng.delta;
|
||||
if (eng.startupDelay <= 0.0f)
|
||||
v.velocity += v.gvelocity;
|
||||
v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f,
|
||||
1000000.0f);
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody,
|
||||
AnimationControl, CharacterVelocity>("HandleRootMotion")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const EngineData &eng,
|
||||
CharacterBase &ch, CharacterBody &body,
|
||||
AnimationControl &anim, CharacterVelocity &v) {
|
||||
if (!ch.mBodyNode)
|
||||
return;
|
||||
if (eng.delta < 0.0000001f)
|
||||
return;
|
||||
OgreAssert(eng.delta > 0.0f, "Zero delta");
|
||||
int maxPen = 0;
|
||||
Ogre::Vector3 colNormal;
|
||||
bool is_on_floor = false;
|
||||
bool penetration = false;
|
||||
if (eng.startupDelay < 0.0f) {
|
||||
if (body.mController) {
|
||||
Ogre::Vector3 rotMotion =
|
||||
v.velocity * eng.delta;
|
||||
btVector3 currentPosition =
|
||||
body.mGhostObject
|
||||
->getWorldTransform()
|
||||
.getOrigin();
|
||||
is_on_floor =
|
||||
body.mController->isOnFloor();
|
||||
penetration = body.mController
|
||||
->isPenetrating();
|
||||
if (is_on_floor)
|
||||
v.gvelocity = Ogre::Vector3(
|
||||
0.0f, 0.0f, 0.0f);
|
||||
|
||||
btTransform from(
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getOrientation()),
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getPosition()));
|
||||
ch.mBodyNode->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
rotMotion);
|
||||
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl>(
|
||||
"HandlePlayerAnimations")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.each([](const Input &input, AnimationControl &anim) {
|
||||
.each([](flecs::entity e, const Input &input,
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
bool controls_idle = input.motion.zeroLength();
|
||||
bool anim_is_idle = anim.currentAnim ==
|
||||
AnimationControl::ANIM_IDLE;
|
||||
bool anim_is_idle =
|
||||
anim.currentAnim ==
|
||||
AnimationControl::ANIM_IDLE ||
|
||||
anim.currentAnim ==
|
||||
AnimationControl::ANIM_TREADING_WATER;
|
||||
bool anim_is_walking = anim.currentAnim ==
|
||||
AnimationControl::ANIM_WALK;
|
||||
bool anim_is_running = anim.currentAnim ==
|
||||
AnimationControl::ANIM_RUN;
|
||||
bool anim_is_swimming = anim.currentAnim ==
|
||||
AnimationControl::ANIM_SWIMMING;
|
||||
bool anim_is_motion = anim_is_walking ||
|
||||
anim_is_running;
|
||||
anim_is_running ||
|
||||
anim_is_swimming;
|
||||
if (controls_idle) {
|
||||
if (anim.currentAnim ==
|
||||
AnimationControl::ANIM_IDLE &&
|
||||
ch.is_submerged)
|
||||
anim.nextAnim = AnimationControl::
|
||||
ANIM_TREADING_WATER;
|
||||
else if (anim.currentAnim ==
|
||||
AnimationControl::
|
||||
ANIM_TREADING_WATER &&
|
||||
!ch.is_submerged)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_IDLE;
|
||||
}
|
||||
if (!controls_idle && anim_is_idle) {
|
||||
anim.reset = true;
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_WALK;
|
||||
if (ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
} else {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_WALK;
|
||||
}
|
||||
} else
|
||||
anim.reset = false;
|
||||
if (controls_idle && anim_is_motion)
|
||||
anim.nextAnim = AnimationControl::ANIM_IDLE;
|
||||
if (ch.is_submerged)
|
||||
anim.nextAnim = AnimationControl::
|
||||
ANIM_TREADING_WATER;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_IDLE;
|
||||
else if (!controls_idle && anim_is_motion) {
|
||||
if (input.fast && anim_is_walking)
|
||||
anim.nextAnim =
|
||||
@@ -280,123 +414,26 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
else if (!input.fast && anim_is_running)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_WALK;
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody>(
|
||||
"UpdateCharacterBase")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.each([](const EngineData &eng, CharacterBase &ch,
|
||||
CharacterBody &body) {
|
||||
if (!ch.mBodyNode) {
|
||||
ch.mBodyEnt = eng.mScnMgr->createEntity(
|
||||
"normal-male.glb");
|
||||
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
ch.mBodyNode->attachObject(ch.mBodyEnt);
|
||||
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
|
||||
body.mGhostObject =
|
||||
new btPairCachingGhostObject();
|
||||
body.mCollisionShape = new btCompoundShape;
|
||||
body.mGhostObject->setCollisionShape(
|
||||
body.mCollisionShape);
|
||||
{
|
||||
btVector3 inertia(0, 0, 0);
|
||||
// mCollisionShape = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
btCapsuleShape *shape =
|
||||
new btCapsuleShape(
|
||||
radius,
|
||||
2 * height -
|
||||
2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(
|
||||
body.mCollisionShape)
|
||||
->addChildShape(transform,
|
||||
shape);
|
||||
btScalar masses[1] = { 0 };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(
|
||||
body.mCollisionShape)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal,
|
||||
inertia);
|
||||
if ((anim_is_walking || anim_is_running) &&
|
||||
ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_SWIMMING;
|
||||
} else if ((anim_is_swimming) &&
|
||||
!ch.is_submerged) {
|
||||
if (input.fast)
|
||||
anim.nextAnim =
|
||||
AnimationControl::ANIM_RUN;
|
||||
else
|
||||
anim.nextAnim =
|
||||
AnimationControl::
|
||||
ANIM_WALK;
|
||||
}
|
||||
body.mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT /*|
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
|
||||
body.mGhostObject->setActivationState(
|
||||
DISABLE_DEACTIVATION);
|
||||
eng.mWorld->attachCollisionObject(
|
||||
body.mGhostObject, ch.mBodyEnt, 1,
|
||||
0x7FFFFFFF);
|
||||
body.mController =
|
||||
new Ogre::Bullet::KinematicMotionSimple(
|
||||
body.mGhostObject,
|
||||
ch.mBodyNode);
|
||||
OgreAssert(body.mGhostObject,
|
||||
"Need GhostObject");
|
||||
OgreAssert(body.mController, "Need controller");
|
||||
eng.mWorld->getBtWorld()->addAction(
|
||||
body.mController);
|
||||
|
||||
OgreAssert(body.mCollisionShape,
|
||||
"No collision shape");
|
||||
OgreAssert(ch.mSkeleton->hasBone("Root"),
|
||||
"No root bone");
|
||||
ch.mRootBone = ch.mSkeleton->getBone("Root");
|
||||
OgreAssert(ch.mRootBone, "No root bone");
|
||||
}
|
||||
});
|
||||
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
|
||||
ecs.system<const EngineData, Camera, const CharacterBase>(
|
||||
"UpdateCamera")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Player>()
|
||||
.each([](const EngineData &eng, Camera &camera,
|
||||
const CharacterBase &ch) {
|
||||
float delta = eng.delta;
|
||||
if (!camera.configured) {
|
||||
// create a pivot at roughly the character's shoulder
|
||||
camera.mCameraPivot =
|
||||
eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
camera.mCameraGoal =
|
||||
camera.mCameraPivot
|
||||
->createChildSceneNode(
|
||||
Ogre::Vector3(0, 2, 3));
|
||||
camera.mCameraNode->setPosition(
|
||||
camera.mCameraPivot->getPosition() +
|
||||
camera.mCameraGoal->getPosition());
|
||||
camera.mCameraPivot->setFixedYawAxis(true);
|
||||
camera.mCameraGoal->setFixedYawAxis(true);
|
||||
camera.mCameraNode->setFixedYawAxis(true);
|
||||
// our model is quite small, so reduce the clipping planes
|
||||
camera.mCamera->setNearClipDistance(0.1f);
|
||||
camera.mCamera->setFarClipDistance(700);
|
||||
|
||||
camera.mPivotPitch = 0;
|
||||
camera.configured = true;
|
||||
} else {
|
||||
// place the camera pivot roughly at the character's shoulder
|
||||
camera.mCameraPivot->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
|
||||
// move the camera smoothly to the goal
|
||||
Ogre::Vector3 goalOffset =
|
||||
camera.mCameraGoal
|
||||
->_getDerivedPosition() -
|
||||
camera.mCameraNode->getPosition();
|
||||
camera.mCameraNode->translate(goalOffset *
|
||||
delta * 9.0f);
|
||||
// always look at the pivot
|
||||
camera.mCameraNode->lookAt(
|
||||
camera.mCameraPivot
|
||||
->_getDerivedPosition(),
|
||||
Ogre::Node::TS_PARENT);
|
||||
}
|
||||
});
|
||||
#define TURN_SPEED 500.0f // character turning in degrees per second
|
||||
@@ -450,6 +487,161 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ch.mBodyNode->yaw(Ogre::Degree(yawToGoal));
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, CharacterLocation, CharacterBase,
|
||||
CharacterBody>("UpdateCharacterBase")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<CharacterBody>()
|
||||
.with<CharacterBase>()
|
||||
.each([](const EngineData &eng, CharacterLocation &loc,
|
||||
CharacterBase &ch, CharacterBody &body) {
|
||||
if (!ch.mBodyNode) {
|
||||
} else {
|
||||
loc.orientation =
|
||||
ch.mBodyNode->_getDerivedOrientation();
|
||||
loc.position =
|
||||
ch.mBodyNode->_getDerivedPosition();
|
||||
}
|
||||
});
|
||||
ecs.system<const EngineData, const CharacterLocation,
|
||||
const CharacterConf>("SetupCharacter")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<CharacterBase>()
|
||||
.without<CharacterBody>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterLocation &loc,
|
||||
const CharacterConf &conf) {
|
||||
CharacterBase &ch = e.ensure<CharacterBase>();
|
||||
CharacterBody &body = e.ensure<CharacterBody>();
|
||||
AnimationControl &anim = e.ensure<AnimationControl>();
|
||||
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
|
||||
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
ch.mBodyNode->setOrientation(loc.orientation);
|
||||
ch.mBodyNode->setPosition(loc.position);
|
||||
ch.mBodyNode->attachObject(ch.mBodyEnt);
|
||||
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
|
||||
OgreAssert(ch.mSkeleton->hasBone("Root"),
|
||||
"No root bone");
|
||||
ch.mRootBone = ch.mSkeleton->getBone("Root");
|
||||
OgreAssert(ch.mRootBone, "No root bone");
|
||||
body.mController = nullptr;
|
||||
e.set<CharacterVelocity>(
|
||||
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
|
||||
body.checkGround = false;
|
||||
body.checkGroundResult = false;
|
||||
body.mCollisionShape = nullptr;
|
||||
body.mGhostObject = nullptr;
|
||||
body.mController = nullptr;
|
||||
body.mGhostObject = new btPairCachingGhostObject();
|
||||
body.mCollisionShape = new btCompoundShape(false);
|
||||
body.mGhostObject->setCollisionShape(
|
||||
body.mCollisionShape);
|
||||
{
|
||||
btVector3 inertia(0, 0, 0);
|
||||
// mCollisionShape = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
btCapsuleShape *shape = new btCapsuleShape(
|
||||
radius, 2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(
|
||||
body.mCollisionShape)
|
||||
->addChildShape(transform, shape);
|
||||
btScalar masses[1] = { 0 };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(
|
||||
body.mCollisionShape)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal, inertia);
|
||||
}
|
||||
body.mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT /*|
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
|
||||
body.mGhostObject->setActivationState(
|
||||
DISABLE_DEACTIVATION);
|
||||
eng.mWorld->attachCollisionObject(
|
||||
body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF);
|
||||
OgreAssert(body.mGhostObject, "Need GhostObject");
|
||||
OgreAssert(body.mCollisionShape, "No collision shape");
|
||||
e.add<CharacterGravity>();
|
||||
e.add<CharacterBuoyancy>();
|
||||
anim.currentAnim = AnimationControl::ANIM_NONE;
|
||||
anim.nextAnim = AnimationControl::ANIM_NONE;
|
||||
anim.reset = false;
|
||||
anim.configured = false;
|
||||
});
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody>(
|
||||
"UpdateCharacterPhysics")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([](const EngineData &eng, CharacterBase &ch,
|
||||
CharacterBody &body) {
|
||||
if (ch.mBodyNode && !body.mController &&
|
||||
eng.startupDelay < 0.0f) {
|
||||
body.mController =
|
||||
new Ogre::Bullet::KinematicMotionSimple(
|
||||
body.mGhostObject,
|
||||
ch.mBodyNode);
|
||||
eng.mWorld->getBtWorld()->addAction(
|
||||
body.mController);
|
||||
OgreAssert(body.mController, "Need controller");
|
||||
}
|
||||
});
|
||||
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
|
||||
ecs.system<const EngineData, Camera, const CharacterBase>(
|
||||
"UpdateCamera")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Player>()
|
||||
.with<GroundCheckReady>()
|
||||
.each([](const EngineData &eng, Camera &camera,
|
||||
const CharacterBase &ch) {
|
||||
float delta = eng.delta;
|
||||
if (!camera.configured) {
|
||||
// create a pivot at roughly the character's shoulder
|
||||
camera.mCameraPivot =
|
||||
eng.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
camera.mCameraGoal =
|
||||
camera.mCameraPivot
|
||||
->createChildSceneNode(
|
||||
Ogre::Vector3(0, 2, 3));
|
||||
camera.mCameraNode->setPosition(
|
||||
camera.mCameraPivot->getPosition() +
|
||||
camera.mCameraGoal->getPosition());
|
||||
camera.mCameraPivot->setFixedYawAxis(true);
|
||||
camera.mCameraGoal->setFixedYawAxis(true);
|
||||
camera.mCameraNode->setFixedYawAxis(true);
|
||||
// our model is quite small, so reduce the clipping planes
|
||||
camera.mCamera->setNearClipDistance(0.1f);
|
||||
camera.mCamera->setFarClipDistance(700);
|
||||
|
||||
camera.mPivotPitch = 0;
|
||||
camera.configured = true;
|
||||
} else {
|
||||
// place the camera pivot roughly at the character's shoulder
|
||||
camera.mCameraPivot->setPosition(
|
||||
ch.mBodyNode->getPosition() +
|
||||
Ogre::Vector3::UNIT_Y * CAM_HEIGHT);
|
||||
// move the camera smoothly to the goal
|
||||
Ogre::Vector3 goalOffset =
|
||||
camera.mCameraGoal
|
||||
->_getDerivedPosition() -
|
||||
camera.mCameraNode->getPosition();
|
||||
camera.mCameraNode->translate(goalOffset *
|
||||
delta * 9.0f);
|
||||
// always look at the pivot
|
||||
camera.mCameraNode->lookAt(
|
||||
camera.mCameraPivot
|
||||
->_getDerivedPosition(),
|
||||
Ogre::Node::TS_PARENT);
|
||||
}
|
||||
});
|
||||
class ClosestNotMeRayResultCallback
|
||||
: public btCollisionWorld::ClosestRayResultCallback {
|
||||
btCollisionObject *mMe;
|
||||
@@ -475,8 +667,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
ecs.system<const EngineData, CharacterBody>("CheckGround")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.without<GroundCheckReady>()
|
||||
.each([](const EngineData &eng, CharacterBody &body) {
|
||||
if (body.checkGround) {
|
||||
if (body.mGhostObject) {
|
||||
btVector3 from =
|
||||
body.mGhostObject->getWorldTransform()
|
||||
.getOrigin() +
|
||||
@@ -488,7 +682,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
resultCallback);
|
||||
body.checkGroundResult =
|
||||
resultCallback.hasHit();
|
||||
body.checkGround = false;
|
||||
if (resultCallback.hasHit())
|
||||
ECS::get().add<GroundCheckReady>();
|
||||
}
|
||||
});
|
||||
ecs.system<const WaterBody, const CharacterBase, CharacterBody>(
|
||||
@@ -498,8 +693,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.without<InWater>()
|
||||
.each([](flecs::entity e, const WaterBody &waterb,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
if (waterb.mInWater.find(body.mGhostObject) !=
|
||||
waterb.mInWater.end() &&
|
||||
if (waterb.isInWater(body.mGhostObject) &&
|
||||
ch.mBodyNode->_getDerivedPosition().y < -0.05f) {
|
||||
e.add<InWater>();
|
||||
std::cout << "Big Splash\n";
|
||||
@@ -518,8 +712,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
.with<InWater>()
|
||||
.each([](flecs::entity e, const WaterBody &waterb,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
if (waterb.mInWater.find(body.mGhostObject) ==
|
||||
waterb.mInWater.end() &&
|
||||
if (waterb.isInWater(body.mGhostObject) &&
|
||||
ch.mBodyNode->_getDerivedPosition().y > 0.05f)
|
||||
e.remove<InWater>();
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@ struct Camera;
|
||||
/* character */
|
||||
struct Character {}; /* tag */
|
||||
struct Player {}; /* tag */
|
||||
struct CharacterGravity {};
|
||||
struct CharacterBuoyancy {};
|
||||
struct CharacterBase {
|
||||
Ogre::String type;
|
||||
float mTimer;
|
||||
@@ -16,20 +18,33 @@ struct CharacterBase {
|
||||
Ogre::Node *mRootBone;
|
||||
Ogre::Vector3 mBoneMotion;
|
||||
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
|
||||
bool is_submerged;
|
||||
};
|
||||
struct CharacterLocation {
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::Vector3 position;
|
||||
};
|
||||
struct CharacterConf {
|
||||
Ogre::String type;
|
||||
};
|
||||
struct CharacterBody {
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
btCompoundShape *mCollisionShape;
|
||||
Ogre::Bullet::KinematicMotionSimple *mController;
|
||||
Ogre::Vector3 gvelocity;
|
||||
bool checkGround;
|
||||
bool checkGroundResult;
|
||||
};
|
||||
struct CharacterVelocity {
|
||||
Ogre::Vector3 gvelocity;
|
||||
Ogre::Vector3 velocity;
|
||||
};
|
||||
struct AnimationControl {
|
||||
enum AnimID {
|
||||
ANIM_IDLE = 0,
|
||||
ANIM_WALK,
|
||||
ANIM_RUN,
|
||||
ANIM_TREADING_WATER,
|
||||
ANIM_SWIMMING,
|
||||
NUM_ANIMS,
|
||||
ANIM_NONE = NUM_ANIMS
|
||||
};
|
||||
@@ -45,7 +60,6 @@ struct AnimationControl {
|
||||
Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS];
|
||||
};
|
||||
struct CharacterModule {
|
||||
flecs::entity player;
|
||||
CharacterModule(flecs::world &ecs);
|
||||
void setAnimation(AnimationControl &anim);
|
||||
void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime);
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
#define COMPONENTS_H_
|
||||
#include <Ogre.h>
|
||||
#include <OgreBullet.h>
|
||||
namespace Ogre
|
||||
{
|
||||
class ImGuiOverlay;
|
||||
}
|
||||
namespace OgreBites
|
||||
{
|
||||
class ApplicationContextSDL;
|
||||
@@ -18,6 +22,8 @@ struct EngineData {
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
float delta;
|
||||
float startupDelay;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
struct Vector3 {
|
||||
float x;
|
||||
@@ -58,10 +64,13 @@ struct RenderWindow {
|
||||
float dpi;
|
||||
};
|
||||
struct App {
|
||||
OgreBites::ApplicationContextSDL *app;
|
||||
Ogre::ImGuiOverlay *mGuiOverlay;
|
||||
OgreBites::InputListenerChain *mInput;
|
||||
std::vector<OgreBites::InputListener *> listeners;
|
||||
};
|
||||
struct InWater {};
|
||||
struct TerrainReady {};
|
||||
struct WaterReady {};
|
||||
struct GroundCheckReady {};
|
||||
}
|
||||
#endif
|
||||
255
src/gamedata/EventTriggerModule.cpp
Normal file
255
src/gamedata/EventTriggerModule.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
#include <iostream>
|
||||
#include <OgreBullet.h>
|
||||
#include <OgreMeshManager.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "LuaData.h"
|
||||
#include "EventTriggerModule.h"
|
||||
|
||||
struct TriggerBody {
|
||||
btPairCachingGhostObject *mBody;
|
||||
btCylinderShape *shape;
|
||||
Ogre::SceneNode *mSceneNode;
|
||||
std::set<const btCollisionObject *> contactBodies;
|
||||
};
|
||||
struct DeepPenetrationContactResultCallback : public btManifoldResult {
|
||||
DeepPenetrationContactResultCallback(
|
||||
const btCollisionObjectWrapper *body0Wrap,
|
||||
const btCollisionObjectWrapper *body1Wrap)
|
||||
: btManifoldResult(body0Wrap, body1Wrap)
|
||||
, mPenetrationDistance(0)
|
||||
, mOtherIndex(0)
|
||||
{
|
||||
}
|
||||
float mPenetrationDistance;
|
||||
int mOtherIndex;
|
||||
btVector3 mNormal, mPoint;
|
||||
void reset()
|
||||
{
|
||||
mPenetrationDistance = 0.0f;
|
||||
}
|
||||
bool hasHit()
|
||||
{
|
||||
return mPenetrationDistance < 0.0f;
|
||||
}
|
||||
virtual void addContactPoint(const btVector3 &normalOnBInWorld,
|
||||
const btVector3 &pointInWorldOnB,
|
||||
btScalar depth)
|
||||
{
|
||||
#ifdef VDEBUG
|
||||
std::cout
|
||||
<< "contact: " << Ogre::Bullet::convert(pointInWorldOnB)
|
||||
<< " " << Ogre::Bullet::convert(normalOnBInWorld)
|
||||
<< "\n";
|
||||
#endif
|
||||
if (mPenetrationDistance > depth) { // Has penetration?
|
||||
|
||||
const bool isSwapped =
|
||||
m_manifoldPtr->getBody0() !=
|
||||
m_body0Wrap->getCollisionObject();
|
||||
mPenetrationDistance = depth;
|
||||
mOtherIndex = isSwapped ? m_index0 : m_index1;
|
||||
mPoint = isSwapped ? (pointInWorldOnB +
|
||||
(normalOnBInWorld * depth)) :
|
||||
pointInWorldOnB;
|
||||
|
||||
mNormal = isSwapped ? normalOnBInWorld * -1 :
|
||||
normalOnBInWorld;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<EventTriggerModule>();
|
||||
ecs.component<EventTriggerExit>();
|
||||
ecs.component<TriggerBody>().on_add([](flecs::entity e,
|
||||
TriggerBody &body) {
|
||||
bool kinematic = false;
|
||||
if (e.get<EventTrigger>().parent) {
|
||||
body.mSceneNode =
|
||||
e.get<EventTrigger>()
|
||||
.parent->createChildSceneNode(
|
||||
e.get<EventTrigger>().position,
|
||||
Ogre::Quaternion(0, 0, 0, 1));
|
||||
kinematic = true;
|
||||
} else {
|
||||
body.mSceneNode =
|
||||
ECS::get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode()
|
||||
->createChildSceneNode(
|
||||
e.get<EventTrigger>().position,
|
||||
Ogre::Quaternion(0, 0, 0, 1));
|
||||
}
|
||||
Ogre::MeshPtr mesh =
|
||||
Ogre::MeshManager::getSingleton().createManual(
|
||||
"trigger", "General");
|
||||
Ogre::Entity *ent =
|
||||
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
|
||||
body.mSceneNode->attachObject(ent);
|
||||
body.mBody = new btPairCachingGhostObject();
|
||||
body.mBody->getWorldTransform().setOrigin(Ogre::Bullet::convert(
|
||||
body.mSceneNode->_getDerivedPosition()));
|
||||
float h = e.get<EventTrigger>().halfheight;
|
||||
float r = e.get<EventTrigger>().radius;
|
||||
Ogre::Vector3 position = e.get<EventTrigger>().position;
|
||||
body.shape = new btCylinderShape(btVector3(r, h, r));
|
||||
body.mBody->setCollisionShape(body.shape);
|
||||
int flags = body.mBody->getCollisionFlags();
|
||||
flags |= btCollisionObject::CF_NO_CONTACT_RESPONSE;
|
||||
if (kinematic)
|
||||
flags |= btCollisionObject::CF_STATIC_OBJECT;
|
||||
body.mBody->setCollisionFlags(flags);
|
||||
ECS::get<EngineData>().mWorld->attachCollisionObject(
|
||||
body.mBody, ent, 16, 0x1);
|
||||
});
|
||||
ecs.component<EventTrigger>().on_set(
|
||||
[](flecs::entity e, EventTrigger &ev) {
|
||||
e.add<TriggerBody>();
|
||||
});
|
||||
ecs.system<const EngineData, const EventTrigger, TriggerBody>(
|
||||
"CheckCollisions")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const EventTrigger &evt, TriggerBody &body) {
|
||||
btDispatcher *dispatch =
|
||||
eng.mWorld->getBtWorld()->getDispatcher();
|
||||
btHashedOverlappingPairCache *cache =
|
||||
body.mBody->getOverlappingPairCache();
|
||||
int i;
|
||||
int count = cache->getNumOverlappingPairs();
|
||||
std::set<const btCollisionObject *> currentContactBodies;
|
||||
if (count > 0) {
|
||||
btManifoldArray contacts;
|
||||
btBroadphasePairArray &collisionPairs =
|
||||
cache->getOverlappingPairArray();
|
||||
for (i = 0; i < count; i++) {
|
||||
contacts.resize(0);
|
||||
if (collisionPairs[i].m_algorithm) {
|
||||
collisionPairs[i]
|
||||
.m_algorithm
|
||||
->getAllContactManifolds(
|
||||
contacts);
|
||||
OgreAssert(false,
|
||||
"Not implemented");
|
||||
} else {
|
||||
const btBroadphasePair *collisionPairPtr =
|
||||
eng.mWorld->getBtWorld()
|
||||
->getBroadphase()
|
||||
->getOverlappingPairCache()
|
||||
->findPair(
|
||||
collisionPairs[i]
|
||||
.m_pProxy0,
|
||||
collisionPairs[i]
|
||||
.m_pProxy1);
|
||||
if (collisionPairPtr) {
|
||||
const btBroadphasePair
|
||||
&collisionPair =
|
||||
*collisionPairPtr;
|
||||
const btCollisionObject *objA = static_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
collisionPair
|
||||
.m_pProxy0
|
||||
->m_clientObject);
|
||||
const btCollisionObject *objB = static_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
collisionPair
|
||||
.m_pProxy1
|
||||
->m_clientObject);
|
||||
const btCollisionObject
|
||||
*me,
|
||||
*other;
|
||||
if (objA ==
|
||||
static_cast<
|
||||
btCollisionObject
|
||||
*>(
|
||||
body.mBody)) {
|
||||
me = objA;
|
||||
other = objB;
|
||||
} else {
|
||||
me = objB;
|
||||
other = objA;
|
||||
}
|
||||
const btCollisionShape *my_shape =
|
||||
me->getCollisionShape();
|
||||
const btCollisionShape *other_shape =
|
||||
other->getCollisionShape();
|
||||
btCollisionObjectWrapper obA(
|
||||
NULL, my_shape,
|
||||
body.mBody,
|
||||
body.mBody
|
||||
->getWorldTransform(),
|
||||
-1, i);
|
||||
btCollisionObjectWrapper obB(
|
||||
NULL,
|
||||
other_shape,
|
||||
other,
|
||||
other->getWorldTransform(),
|
||||
-1, 0);
|
||||
btCollisionAlgorithm *algorithm =
|
||||
dispatch->findAlgorithm(
|
||||
&obA,
|
||||
&obB,
|
||||
NULL,
|
||||
BT_CONTACT_POINT_ALGORITHMS);
|
||||
DeepPenetrationContactResultCallback
|
||||
contactPointResult(
|
||||
&obA,
|
||||
&obB);
|
||||
|
||||
algorithm->processCollision(
|
||||
&obA, &obB,
|
||||
eng.mWorld
|
||||
->getBtWorld()
|
||||
->getDispatchInfo(),
|
||||
&contactPointResult);
|
||||
algorithm
|
||||
->~btCollisionAlgorithm();
|
||||
dispatch->freeCollisionAlgorithm(
|
||||
algorithm);
|
||||
if (contactPointResult
|
||||
.hasHit()) {
|
||||
currentContactBodies
|
||||
.insert(other);
|
||||
if (body.contactBodies
|
||||
.find(other) ==
|
||||
body.contactBodies
|
||||
.end()) {
|
||||
body.contactBodies
|
||||
.insert(other);
|
||||
ECS::get<
|
||||
LuaBase>()
|
||||
.mLua
|
||||
->call_handler(
|
||||
evt.event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::set<const btCollisionObject *>::iterator it =
|
||||
body.contactBodies.begin();
|
||||
while (it != body.contactBodies.end()) {
|
||||
if (currentContactBodies.find(*it) ==
|
||||
currentContactBodies.end()) {
|
||||
if (e.has<EventTriggerExit>()) {
|
||||
const Ogre::String &exit_event =
|
||||
ECS::get<
|
||||
EventTriggerExit>()
|
||||
.event;
|
||||
ECS::get<LuaBase>()
|
||||
.mLua->call_handler(
|
||||
exit_event);
|
||||
} else {
|
||||
std::cout << "body exited"
|
||||
<< std::endl;
|
||||
}
|
||||
body.contactBodies.erase(*it);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
});
|
||||
}
|
||||
21
src/gamedata/EventTriggerModule.h
Normal file
21
src/gamedata/EventTriggerModule.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef EVENT_TRIGGER_MODULE_H_
|
||||
#define EVENT_TRIGGER_MODULE_H_
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct EventTrigger {
|
||||
Ogre::SceneNode *parent;
|
||||
Ogre::Vector3 position;
|
||||
float halfheight;
|
||||
float radius;
|
||||
Ogre::String event;
|
||||
};
|
||||
struct EventTriggerExit {
|
||||
Ogre::String event;
|
||||
};
|
||||
struct EventTriggerModule {
|
||||
EventTriggerModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -7,8 +7,10 @@
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreApplicationContext.h>
|
||||
#include <OgreImGuiInputListener.h>
|
||||
#include <OgreFontManager.h>
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "LuaData.h"
|
||||
#include "GUIModule.h"
|
||||
namespace ECS
|
||||
{
|
||||
@@ -20,6 +22,50 @@ struct GUIData {
|
||||
};
|
||||
struct GUIListener : public Ogre::RenderTargetListener {
|
||||
float panel_width;
|
||||
bool enableEditor;
|
||||
bool enableMapEditor;
|
||||
ImFont *smallFont, *midFont, *bigFont;
|
||||
Ogre::FontPtr _smallFont, _midFont, _bigFont;
|
||||
GUIListener()
|
||||
: Ogre::RenderTargetListener()
|
||||
{
|
||||
_midFont = createFont("midFont", "General",
|
||||
"Jupiteroid-Regular.ttf", 18.0f);
|
||||
_smallFont = createFont("smallFont", "General",
|
||||
"Jupiteroid-Regular.ttf", 13.0f);
|
||||
_bigFont = createFont("bigFont", "General", "Kenney Bold.ttf",
|
||||
32.0f);
|
||||
smallFont = ECS::get<GUIData>().mGuiOverlay->addFont(
|
||||
"smallFont", "General");
|
||||
OgreAssert(smallFont, "Could not load font");
|
||||
midFont = ECS::get<GUIData>().mGuiOverlay->addFont("midFont",
|
||||
"General");
|
||||
OgreAssert(midFont, "Could not load font");
|
||||
bigFont = ECS::get<GUIData>().mGuiOverlay->addFont("bigFont",
|
||||
"General");
|
||||
OgreAssert(bigFont, "Could not load font");
|
||||
#if 0
|
||||
Ogre::FontPtr _midFont = createFont("midFont", "General",
|
||||
"Kenney Bold.ttf", 28.0f);
|
||||
#endif
|
||||
#if 0
|
||||
ImGui::GetIO().Fonts->Build();
|
||||
#endif
|
||||
}
|
||||
Ogre::FontPtr createFont(const Ogre::String &name,
|
||||
const Ogre::String &group,
|
||||
const Ogre::String &ttfname, float fontSize)
|
||||
{
|
||||
Ogre::FontPtr ret =
|
||||
Ogre::FontManager::getSingleton().create(name, group);
|
||||
ret->setType(Ogre::FontType::FT_TRUETYPE);
|
||||
ret->setSource(ttfname);
|
||||
ret->setTrueTypeSize(fontSize);
|
||||
ret->setTrueTypeResolution(75);
|
||||
ret->addCodePointRange(Ogre::Font::CodePointRange(30, 128));
|
||||
ret->load();
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override
|
||||
{
|
||||
@@ -36,14 +82,24 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Dumb and Stupid");
|
||||
ImGui::Begin("Control");
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
if (ImGui::Button("Shitty Quit button"))
|
||||
if (ImGui::Button("Quit"))
|
||||
Ogre::Root::getSingleton().queueEndRendering();
|
||||
if (ImGui::Button("Chick-chick"))
|
||||
if (ImGui::Button("Return"))
|
||||
ECS::get().get<GUI>().finish();
|
||||
ImGui::Text("We do stoopid...");
|
||||
if (ImGui::Button("Enable/Disable item editor")) {
|
||||
enableEditor ^= true;
|
||||
if (enableEditor)
|
||||
enableMapEditor = false;
|
||||
}
|
||||
if (ImGui::Button("Enable/Disable map editor")) {
|
||||
enableMapEditor ^= true;
|
||||
if (enableMapEditor)
|
||||
enableEditor = false;
|
||||
}
|
||||
ImGui::Text("Text message...");
|
||||
ImGui::End();
|
||||
}
|
||||
void create_entity_node(const Ogre::String &name, int key)
|
||||
@@ -140,127 +196,286 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGui::Text("Name: %s", pname.c_str());
|
||||
}
|
||||
}
|
||||
void map_editor()
|
||||
{
|
||||
}
|
||||
void preview(const Ogre::RenderTargetViewportEvent &evt)
|
||||
{
|
||||
int i;
|
||||
Ogre::ImGuiOverlay::NewFrame();
|
||||
if (ECS::get().get<GUI>().enabled) {
|
||||
buttons_panel();
|
||||
buildings_editor();
|
||||
if (ECS::get().get<EngineData>().startupDelay > 0.0f) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5f - 20;
|
||||
ImGui::SetNextWindowPos(ImVec2(size.x - window_width,
|
||||
size.y * 0.5f + 20),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width,
|
||||
window_height),
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
|
||||
ImGuiCond_Always);
|
||||
// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
|
||||
ImGui::Begin("Panel...");
|
||||
std::deque<Ogre::SceneNode *> tree_input_queue,
|
||||
tree_output_queue;
|
||||
std::vector<Ogre::SceneNode *> tree_list;
|
||||
tree_input_queue.push_back(
|
||||
ECS::get()
|
||||
.get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode());
|
||||
tree_input_queue.push_back(nullptr);
|
||||
std::set<Ogre::SceneNode *> visited;
|
||||
while (true) {
|
||||
int new_nodes_count = 0;
|
||||
while (!tree_input_queue.empty()) {
|
||||
int child;
|
||||
Ogre::SceneNode *item =
|
||||
tree_input_queue.front();
|
||||
tree_input_queue.pop_front();
|
||||
if (item &&
|
||||
visited.find(item) ==
|
||||
visited.end()) { // new node
|
||||
new_nodes_count++;
|
||||
tree_output_queue.push_back(
|
||||
item);
|
||||
visited.insert(item);
|
||||
const Ogre::Node::ChildNodeMap
|
||||
&children =
|
||||
item->getChildren();
|
||||
for (child = 0;
|
||||
child < children.size();
|
||||
child++) {
|
||||
tree_output_queue.push_back(
|
||||
static_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
children[child]));
|
||||
ImGui::Begin(
|
||||
"StartupScreen", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
ImGuiWindowFlags_NoInputs);
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
ImGui::PushFont(bigFont);
|
||||
ImGui::TextWrapped(
|
||||
"%s",
|
||||
"This game does not autosave. Please use save function to keep your state");
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().enabled) {
|
||||
if (ECS::get().get<GUI>().narrationBox) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0,
|
||||
size.y * 0.75f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x,
|
||||
size.y * 0.25f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Narration...", NULL,
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushFont(midFont);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::TextWrapped(
|
||||
"%s", ECS::get()
|
||||
.get<GUI>()
|
||||
.narrationText.c_str());
|
||||
if (ECS::get().get<GUI>().choices.size() == 0) {
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
if (ImGui::InvisibleButton(
|
||||
"Background",
|
||||
ImGui::GetWindowSize()))
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"narration_progress");
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < ECS::get()
|
||||
.get<GUI>()
|
||||
.choices.size();
|
||||
i++) {
|
||||
if (ImGui::Button(
|
||||
ECS::get()
|
||||
.get<GUI>()
|
||||
.choices[i]
|
||||
.c_str())) {
|
||||
ECS::get()
|
||||
.get_mut<GUI>()
|
||||
.narration_answer =
|
||||
i + 1;
|
||||
std::cout << "answer: "
|
||||
<< i + 1
|
||||
<< std::endl;
|
||||
ECS::modified<GUI>();
|
||||
ECS::get<LuaBase>()
|
||||
.mLua
|
||||
->call_handler(
|
||||
"narration_answered");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().mainMenu) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin(
|
||||
"MainMenu", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
0);
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
ImGui::PushFont(bigFont);
|
||||
ImGui::TextWrapped("%s", "Booo!!!!");
|
||||
bool pressed = false;
|
||||
bool new_game = false, cont = false,
|
||||
load_game = false, opts = false,
|
||||
quit = false;
|
||||
ImGui::SetCursorPosY(size.y / 2.0f - 300.0f);
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
new_game = ImGui::Button("New Game");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
cont = ImGui::Button("Continue");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
load_game = ImGui::Button("Load Game");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
opts = ImGui::Button("Options");
|
||||
ImGui::SetCursorPosX(size.x / 2.0f - 300.0f);
|
||||
quit = ImGui::Button("Quit###quit");
|
||||
pressed = new_game || cont || load_game ||
|
||||
opts || quit;
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
if (quit)
|
||||
Ogre::Root::getSingleton()
|
||||
.queueEndRendering();
|
||||
if (pressed)
|
||||
ECS::get().get<GUI>().finish();
|
||||
if (new_game) {
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"new_game");
|
||||
}
|
||||
} else {
|
||||
buttons_panel();
|
||||
if (enableEditor)
|
||||
buildings_editor();
|
||||
if (enableMapEditor)
|
||||
map_editor();
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
float window_width = size.x * 0.2f;
|
||||
if (window_width > panel_width)
|
||||
window_width = panel_width;
|
||||
float window_height = size.y * 0.5f - 20;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(size.x - window_width,
|
||||
size.y * 0.5f + 20),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width,
|
||||
window_height),
|
||||
ImGuiCond_Always);
|
||||
// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
|
||||
ImGui::Begin("Panel...");
|
||||
std::deque<Ogre::SceneNode *> tree_input_queue,
|
||||
tree_output_queue;
|
||||
std::vector<Ogre::SceneNode *> tree_list;
|
||||
tree_input_queue.push_back(
|
||||
ECS::get()
|
||||
.get<EngineData>()
|
||||
.mScnMgr->getRootSceneNode());
|
||||
tree_input_queue.push_back(nullptr);
|
||||
std::set<Ogre::SceneNode *> visited;
|
||||
while (true) {
|
||||
int new_nodes_count = 0;
|
||||
while (!tree_input_queue.empty()) {
|
||||
int child;
|
||||
Ogre::SceneNode *item =
|
||||
tree_input_queue.front();
|
||||
tree_input_queue.pop_front();
|
||||
if (item &&
|
||||
visited.find(item) ==
|
||||
visited.end()) { // new node
|
||||
new_nodes_count++;
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
nullptr);
|
||||
}
|
||||
} else
|
||||
tree_output_queue.push_back(
|
||||
item);
|
||||
item);
|
||||
visited.insert(item);
|
||||
const Ogre::Node::ChildNodeMap
|
||||
&children =
|
||||
item->getChildren();
|
||||
for (child = 0;
|
||||
child <
|
||||
children.size();
|
||||
child++) {
|
||||
tree_output_queue
|
||||
.push_back(static_cast<
|
||||
Ogre::SceneNode
|
||||
*>(
|
||||
children[child]));
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
nullptr);
|
||||
}
|
||||
} else
|
||||
tree_output_queue
|
||||
.push_back(
|
||||
item);
|
||||
}
|
||||
if (new_nodes_count == 0)
|
||||
break;
|
||||
tree_input_queue = tree_output_queue;
|
||||
tree_output_queue.clear();
|
||||
}
|
||||
if (new_nodes_count == 0)
|
||||
break;
|
||||
tree_input_queue = tree_output_queue;
|
||||
tree_output_queue.clear();
|
||||
}
|
||||
tree_list.insert(tree_list.begin(),
|
||||
tree_output_queue.begin(),
|
||||
tree_output_queue.end());
|
||||
int count = 0;
|
||||
int depth = 0;
|
||||
std::vector<int> check_depth;
|
||||
int max_depth = 0;
|
||||
check_depth.push_back(0);
|
||||
for (count = 0; count < tree_list.size(); count++) {
|
||||
int t;
|
||||
tree_list.insert(tree_list.begin(),
|
||||
tree_output_queue.begin(),
|
||||
tree_output_queue.end());
|
||||
int count = 0;
|
||||
int depth = 0;
|
||||
std::vector<int> check_depth;
|
||||
int max_depth = 0;
|
||||
check_depth.push_back(0);
|
||||
for (count = 0; count < tree_list.size();
|
||||
count++) {
|
||||
int t;
|
||||
|
||||
Ogre::SceneNode *node = tree_list[count];
|
||||
if (node && max_depth >= depth) {
|
||||
Ogre::String name = node->getName();
|
||||
if (name.length() == 0) {
|
||||
name = "Node #" +
|
||||
Ogre::StringConverter::
|
||||
toString(count);
|
||||
Ogre::SceneNode *node =
|
||||
tree_list[count];
|
||||
if (node && max_depth >= depth) {
|
||||
Ogre::String name =
|
||||
node->getName();
|
||||
if (name.length() == 0) {
|
||||
name = "Node #" +
|
||||
Ogre::StringConverter::
|
||||
toString(
|
||||
count);
|
||||
}
|
||||
if (ImGui::TreeNode(
|
||||
name.c_str())) {
|
||||
check_depth.push_back(
|
||||
max_depth);
|
||||
max_depth++;
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
(name +
|
||||
"##caption")
|
||||
.c_str());
|
||||
position_editor(node);
|
||||
ImGui::Separator();
|
||||
orientation_editor(
|
||||
node);
|
||||
ImGui::Separator();
|
||||
ImGui::Text(
|
||||
"Attachments");
|
||||
attachments_editor(
|
||||
node);
|
||||
}
|
||||
} else if (!node &&
|
||||
max_depth >= depth) {
|
||||
max_depth = check_depth.back();
|
||||
check_depth.pop_back();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode(name.c_str())) {
|
||||
check_depth.push_back(
|
||||
max_depth);
|
||||
max_depth++;
|
||||
ImGui::Text("%s",
|
||||
(name + "##caption")
|
||||
.c_str());
|
||||
position_editor(node);
|
||||
ImGui::Separator();
|
||||
orientation_editor(node);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Attachments");
|
||||
attachments_editor(node);
|
||||
}
|
||||
} else if (!node && max_depth >= depth) {
|
||||
max_depth = check_depth.back();
|
||||
check_depth.pop_back();
|
||||
ImGui::TreePop();
|
||||
if (tree_list[count])
|
||||
depth++;
|
||||
else
|
||||
depth--;
|
||||
}
|
||||
if (tree_list[count])
|
||||
depth++;
|
||||
else
|
||||
depth--;
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GUIModule::GUIModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.component<GUI>().add(flecs::Singleton);
|
||||
ecs.component<GUIData>().add(flecs::Singleton);
|
||||
ecs.set<GUI>({ false, true, false, nullptr });
|
||||
ecs.component<GUI>()
|
||||
.on_add([](GUI &gui) {
|
||||
gui.enabled = false;
|
||||
gui.grab = false;
|
||||
gui.grabChanged = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.component<GUIData>()
|
||||
.on_add([](GUIData &priv) {
|
||||
priv.glb_names.clear();
|
||||
priv.mGUIListener = nullptr;
|
||||
priv.mGuiOverlay = nullptr;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.set<GUI>({ false, true, false, false, false, "", {}, -1 });
|
||||
ecs.set<GUIData>({ nullptr, {}, nullptr });
|
||||
ui_wait =
|
||||
ecs.system<const RenderWindow, App, GUIData>("SetupGUI")
|
||||
@@ -275,11 +490,14 @@ GUIModule::GUIModule(flecs::world &ecs)
|
||||
Ogre::OverlayManager::getSingleton()
|
||||
.setPixelRatio(vpScale);
|
||||
std::cout << "GUI configure\n";
|
||||
gui.mGuiOverlay =
|
||||
app.app->initialiseImGui();
|
||||
OgreAssert(app.mGuiOverlay,
|
||||
"No ImGUI overlay");
|
||||
gui.mGuiOverlay = app.mGuiOverlay;
|
||||
gui.mGUIListener = new GUIListener();
|
||||
gui.mGuiOverlay->setZOrder(300);
|
||||
gui.mGuiOverlay->show();
|
||||
gui.mGUIListener = new GUIListener();
|
||||
gui.mGUIListener->panel_width = 300.0f;
|
||||
gui.mGUIListener->enableEditor = false;
|
||||
window.window->addListener(
|
||||
gui.mGUIListener);
|
||||
int i;
|
||||
@@ -299,10 +517,6 @@ GUIModule::GUIModule(flecs::world &ecs)
|
||||
names.begin(),
|
||||
names.end());
|
||||
}
|
||||
ECS::get_mut<ECS::GUI>()
|
||||
.mGuiInpitListener =
|
||||
new OgreBites::
|
||||
ImGuiInputListener();
|
||||
ECS::modified<ECS::GUI>();
|
||||
std::cout << "GUI configure finished\n";
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <flecs.h>
|
||||
namespace OgreBites
|
||||
{
|
||||
class InputListener;
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
@@ -11,7 +10,11 @@ struct GUI {
|
||||
bool enabled;
|
||||
bool grab;
|
||||
bool grabChanged;
|
||||
OgreBites::InputListener *mGuiInpitListener;
|
||||
bool narrationBox;
|
||||
bool mainMenu;
|
||||
Ogre::String narrationText;
|
||||
std::vector<Ogre::String> choices;
|
||||
int narration_answer;
|
||||
static void setWindowGrab(bool g = true)
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
@@ -25,6 +28,8 @@ struct GUI {
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
|
||||
gui.enabled = false;
|
||||
gui.mainMenu = false;
|
||||
gui.narrationBox = false;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
setWindowGrab(true);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
#include "TerrainModule.h"
|
||||
#include "SunModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "WorldMapModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
static flecs::world ecs;
|
||||
flecs::entity player;
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera)
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
|
||||
Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup GameData\n";
|
||||
ecs.component<EngineData>().add(flecs::Singleton);
|
||||
@@ -20,23 +26,66 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
ecs.component<Input>().add(flecs::Singleton);
|
||||
ecs.component<Camera>().add(flecs::Singleton);
|
||||
ecs.component<InWater>();
|
||||
ecs.component<App>().add(flecs::Singleton);
|
||||
ecs.component<WaterReady>().add(flecs::Singleton);
|
||||
ecs.component<GroundCheckReady>().add(flecs::Singleton);
|
||||
ecs.component<App>()
|
||||
.on_add([](App &app) {
|
||||
app.mInput = nullptr;
|
||||
app.mGuiOverlay = nullptr;
|
||||
app.listeners.clear();
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
/* lots of things depend on it */
|
||||
ecs.component<TerrainReady>().add(flecs::Singleton);
|
||||
ecs.import <WaterModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <TerrainModule>();
|
||||
ecs.import <SunModule>();
|
||||
ecs.import <GUIModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <WorldMapModule>();
|
||||
ecs.import <LuaModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <EventTriggerModule>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](EngineData &eng) {
|
||||
eng.delta = ECS::get().delta_time();
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, world, 0.0f, 0.0f });
|
||||
ecs.system<EngineData>("UpdateDelay")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<GroundCheckReady>()
|
||||
.each([](EngineData &eng) {
|
||||
if (eng.startupDelay >= 0.0f)
|
||||
eng.startupDelay -= eng.delta;
|
||||
#ifdef VDEBUG
|
||||
if (ECS::get().has<GroundCheckReady>())
|
||||
std::cout << "ground check ready\n";
|
||||
#endif
|
||||
});
|
||||
ecs.system<EngineData>("CheckStatus")
|
||||
.kind(flecs::OnUpdate)
|
||||
.run([](flecs::iter &it) {
|
||||
#ifdef VDEBUG
|
||||
if (ECS::get().has<WaterReady>())
|
||||
std::cout << "water ready\n";
|
||||
if (ECS::get().has<TerrainReady>())
|
||||
std::cout << "terrain ready\n";
|
||||
if (ECS::get().has<GroundCheckReady>())
|
||||
std::cout << "ground check ready\n";
|
||||
#endif
|
||||
});
|
||||
ecs.set<EngineData>({ scnMgr, world, 0.0f, 5.0f,
|
||||
(int)window->getWidth(),
|
||||
(int)window->getHeight() });
|
||||
ecs.set<Camera>({ cameraNode, camera, false });
|
||||
ecs.add<GameData>();
|
||||
ecs.add<Input>();
|
||||
ecs.set<WaterSurface>({ nullptr, nullptr, nullptr, nullptr, nullptr });
|
||||
ecs.set<WaterBody>({ nullptr });
|
||||
ecs.add<WaterSurface>();
|
||||
ecs.set<Sun>({ nullptr, nullptr, nullptr, nullptr });
|
||||
ecs.set<Terrain>({ nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
@@ -46,6 +95,14 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
false,
|
||||
{ 0, 0, 0 } });
|
||||
std::cout << "Setup GameData done\n";
|
||||
|
||||
/* Create player */
|
||||
player = ecs.entity("player");
|
||||
Ogre::Vector3 playerPos(0, 0, 4);
|
||||
player.set<CharacterLocation>({ { 0, 0, 0, 1 }, playerPos });
|
||||
player.set<CharacterConf>({ "normal-male.glb" });
|
||||
player.add<Character>();
|
||||
player.add<Player>();
|
||||
}
|
||||
void update(float delta)
|
||||
{
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
extern flecs::entity player;
|
||||
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera);
|
||||
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
|
||||
Ogre::RenderWindow *window);
|
||||
void update(float delta);
|
||||
flecs::world get();
|
||||
template <class T> const T &get()
|
||||
|
||||
418
src/gamedata/LuaData.cpp
Normal file
418
src/gamedata/LuaData.cpp
Normal file
@@ -0,0 +1,418 @@
|
||||
#include <OgreFileSystemLayer.h>
|
||||
#include "GameData.h"
|
||||
#include "Components.h"
|
||||
#include "GUIModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "BoatModule.h"
|
||||
#include "EventTriggerModule.h"
|
||||
#include "LuaData.h"
|
||||
|
||||
extern "C" {
|
||||
int luaopen_lpeg(lua_State *L);
|
||||
}
|
||||
namespace ECS
|
||||
{
|
||||
struct idmap {
|
||||
std::unordered_map<int, flecs::entity> id2entity;
|
||||
int next_id;
|
||||
idmap()
|
||||
: id2entity({})
|
||||
, next_id(0)
|
||||
{
|
||||
}
|
||||
int get_next_id()
|
||||
{
|
||||
next_id++;
|
||||
return next_id;
|
||||
}
|
||||
int add_entity(flecs::entity e)
|
||||
{
|
||||
int id = get_next_id();
|
||||
id2entity[id] = e;
|
||||
return id;
|
||||
}
|
||||
flecs::entity get_entity(int id)
|
||||
{
|
||||
OgreAssert(id2entity[id].is_valid(), "Invalid entity");
|
||||
return id2entity[id];
|
||||
}
|
||||
};
|
||||
struct idmap idmap;
|
||||
|
||||
int LuaData::setup_handler()
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_pushvalue(L, 1);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
setup_handlers.push_back(ref);
|
||||
return 0;
|
||||
}
|
||||
int LuaData::call_handler(const Ogre::String &event)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < setup_handlers.size(); i++) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, setup_handlers[i]);
|
||||
lua_pushstring(L, event.c_str());
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luaLibraryLoader(lua_State *L)
|
||||
{
|
||||
int i;
|
||||
if (!lua_isstring(L, 1)) {
|
||||
luaL_error(
|
||||
L,
|
||||
"luaLibraryLoader: Expected string for first parameter");
|
||||
}
|
||||
|
||||
std::string libraryFile = lua_tostring(L, 1);
|
||||
std::cout << libraryFile << std::endl;
|
||||
// In order to be compatible with the normal Lua file loader,
|
||||
// translate '.' to the file system seperator character.
|
||||
// In this case (An ogre resource) '/'
|
||||
while (libraryFile.find('.') != std::string::npos)
|
||||
libraryFile.replace(libraryFile.find('.'), 1, "/");
|
||||
|
||||
libraryFile += ".lua";
|
||||
std::cout << libraryFile << std::endl;
|
||||
Ogre::StringVectorPtr scripts =
|
||||
Ogre::ResourceGroupManager::getSingleton().listResourceNames(
|
||||
"LuaScripts", false);
|
||||
std::vector<Ogre::String> &strings = *scripts;
|
||||
for (i = 0; i < strings.size(); i++)
|
||||
std::cout << strings[i] << std::endl;
|
||||
|
||||
if (0 && !Ogre::ResourceGroupManager::getSingleton()
|
||||
.resourceExistsInAnyGroup(libraryFile)) {
|
||||
// Could not find the file.
|
||||
std::string errMessage = "\n no file '" + libraryFile +
|
||||
"' found in Ogre resource archives.";
|
||||
lua_pushstring(L, errMessage.c_str());
|
||||
} else {
|
||||
Ogre::DataStreamList streams =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResources(
|
||||
"*.lua", "LuaScripts");
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
libraryFile, "LuaScripts");
|
||||
Ogre::String script = stream->getAsString();
|
||||
if (luaL_loadbuffer(L, script.c_str(), script.length(),
|
||||
libraryFile.c_str())) {
|
||||
luaL_error(
|
||||
L,
|
||||
"Error loading library '%s' from resource archive.\n%s",
|
||||
libraryFile.c_str(), lua_tostring(L, -1));
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void installLibraryLoader(lua_State *L)
|
||||
{
|
||||
// Insert the c++ func 'luaLibraryLoader' into package.loaders.
|
||||
// Inserted at the start of the table in order to take precedence.
|
||||
lua_getglobal(L, "table");
|
||||
lua_getfield(L, -1, "insert");
|
||||
lua_remove(L, -2); // table
|
||||
lua_getglobal(L, "package");
|
||||
lua_getfield(L, -1, "searchers");
|
||||
lua_remove(L, -2); // package
|
||||
lua_pushnumber(L, 1); // index where to insert into loaders table
|
||||
lua_pushcfunction(L, luaLibraryLoader);
|
||||
if (lua_pcall(L, 3, 0, 0))
|
||||
Ogre::LogManager::getSingleton().stream() << lua_tostring(L, 1);
|
||||
}
|
||||
|
||||
LuaData::LuaData()
|
||||
: L(luaL_newstate())
|
||||
{
|
||||
luaopen_base(L);
|
||||
luaopen_table(L);
|
||||
luaopen_package(L);
|
||||
luaL_requiref(L, "table", luaopen_table, 1);
|
||||
lua_pop(L, 1);
|
||||
luaL_requiref(L, "math", luaopen_math, 1);
|
||||
lua_pop(L, 1);
|
||||
luaL_requiref(L, "package", luaopen_package, 1);
|
||||
lua_pop(L, 1);
|
||||
luaL_requiref(L, "string", luaopen_string, 1);
|
||||
lua_pop(L, 1);
|
||||
luaL_requiref(L, "io", luaopen_io, 1);
|
||||
lua_pop(L, 1);
|
||||
luaL_requiref(L, "lpeg", luaopen_lpeg, 1);
|
||||
lua_pop(L, 1);
|
||||
#if 0
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
lua_pushcfunction(L, luaopen_lpeg);
|
||||
lua_setfield(L, -2, "lpeg");
|
||||
lua_pop(L, 1); // remove PRELOAD table
|
||||
#endif
|
||||
installLibraryLoader(L);
|
||||
lua_pop(L, 1);
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(false, "Crash function called");
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "crash");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
ECS::get<LuaBase>().mLua->setup_handler();
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
int args = lua_gettop(L);
|
||||
if (args < 1)
|
||||
return 0;
|
||||
ECS::get_mut<GUI>().choices.clear();
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
if (args > 1) {
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
lua_pushnil(L); /* first key */
|
||||
while (lua_next(L, 2) != 0) {
|
||||
Ogre::String s = lua_tostring(L, -1);
|
||||
ECS::get_mut<GUI>().choices.push_back(s);
|
||||
lua_pop(L, 1); /* remove value but keep key */
|
||||
}
|
||||
}
|
||||
size_t len;
|
||||
Ogre::String message(luaL_tolstring(L, 1, &len));
|
||||
std::cout << "narrator message: " << message
|
||||
<< " length: " << message.length() << std::endl;
|
||||
if (message.length() == 0 && ECS::get_mut<GUI>().narrationBox) {
|
||||
ECS::get_mut<GUI>().enabled = false;
|
||||
ECS::get_mut<GUI>().grab = true;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
ECS::get_mut<GUI>().narrationText = message;
|
||||
ECS::get_mut<GUI>().narrationBox = false;
|
||||
ECS::modified<GUI>();
|
||||
std::cout << "narration ended\n";
|
||||
} else if (message.length() > 0) {
|
||||
ECS::get_mut<GUI>().enabled = true;
|
||||
ECS::get_mut<GUI>().grab = false;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
ECS::get_mut<GUI>().narrationText = message;
|
||||
ECS::get_mut<GUI>().narrationBox = true;
|
||||
ECS::modified<GUI>();
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "narrate");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
// ECS::get_mut<GUI>().mainMenu = true;
|
||||
lua_pushinteger(L, ECS::get<GUI>().narration_answer);
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "narration_get_answer");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
// ECS::get_mut<GUI>().mainMenu = true;
|
||||
ECS::get_mut<GUI>().enabled = true;
|
||||
ECS::get_mut<GUI>().mainMenu = true;
|
||||
ECS::get_mut<GUI>().grab = false;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
ECS::modified<GUI>();
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "main_menu");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 3, "Invalid parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TSTRING);
|
||||
luaL_checktype(L, 3, LUA_TBOOLEAN);
|
||||
bool enable = lua_toboolean(L, 3);
|
||||
flecs::entity e = ECS::get().lookup(lua_tostring(L, 1));
|
||||
Ogre::String what = lua_tostring(L, 2);
|
||||
OgreAssert(e.is_valid(), "Invalid character");
|
||||
OgreAssert(e.has<Character>(), "Not a character");
|
||||
if (what == "gravity") {
|
||||
/* clear momentum */
|
||||
e.get_mut<CharacterVelocity>().gvelocity.y = 0.0f;
|
||||
e.get_mut<CharacterVelocity>().velocity.y = 0.0f;
|
||||
e.modified<CharacterVelocity>();
|
||||
if (enable)
|
||||
e.add<CharacterGravity>();
|
||||
else
|
||||
e.remove<CharacterGravity>();
|
||||
} else if (what == "buoyancy") {
|
||||
if (enable)
|
||||
e.add<CharacterBuoyancy>();
|
||||
else
|
||||
e.remove<CharacterBuoyancy>();
|
||||
} else
|
||||
OgreAssert(false, "Bad parameter " + what);
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "ecs_character_params_set");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TNUMBER);
|
||||
luaL_checktype(L, 3, LUA_TNUMBER);
|
||||
luaL_checktype(L, 4, LUA_TNUMBER);
|
||||
luaL_checktype(L, 5, LUA_TNUMBER);
|
||||
Ogre::String what = lua_tostring(L, 1);
|
||||
if (what == "boat") {
|
||||
float yaw = lua_tonumber(L, 5);
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
flecs::entity e = ECS::get().entity();
|
||||
Ogre::Quaternion orientation(Ogre::Radian(yaw),
|
||||
Ogre::Vector3(0, 1, 0));
|
||||
Ogre::Vector3 position(x, y, z);
|
||||
e.set<BoatType>({ "boat.glb", position, orientation });
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
return 1;
|
||||
}
|
||||
lua_pushinteger(L, -1);
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_vehicle_set");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 6, "bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING); // event
|
||||
luaL_checktype(L, 2, LUA_TNUMBER); // x
|
||||
luaL_checktype(L, 3, LUA_TNUMBER); // y
|
||||
luaL_checktype(L, 4, LUA_TNUMBER); // z
|
||||
luaL_checktype(L, 5, LUA_TNUMBER); // halfh
|
||||
luaL_checktype(L, 6, LUA_TNUMBER); // radius
|
||||
flecs::entity e = ECS::get().entity();
|
||||
Ogre::String event = lua_tostring(L, 1);
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
float h = lua_tonumber(L, 5);
|
||||
float r = lua_tonumber(L, 6);
|
||||
Ogre::Vector3 position(x, y, z);
|
||||
e.set<EventTrigger>({ nullptr, position, h, r, event });
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_character_trigger");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 7, "bad parameters");
|
||||
luaL_checktype(L, 1, LUA_TNUMBER); // parent
|
||||
luaL_checktype(L, 2, LUA_TSTRING); // event
|
||||
luaL_checktype(L, 3, LUA_TNUMBER); // x
|
||||
luaL_checktype(L, 4, LUA_TNUMBER); // y
|
||||
luaL_checktype(L, 5, LUA_TNUMBER); // z
|
||||
luaL_checktype(L, 6, LUA_TNUMBER); // halfh
|
||||
luaL_checktype(L, 7, LUA_TNUMBER); // radius
|
||||
int parent = lua_tointeger(L, 1);
|
||||
flecs::entity parent_e = idmap.get_entity(parent);
|
||||
Ogre::SceneNode *parentNode = nullptr;
|
||||
if (parent_e.has<CharacterBase>())
|
||||
parentNode = parent_e.get<CharacterBase>().mBodyNode;
|
||||
|
||||
else if (parent_e.has<BoatBase>())
|
||||
parentNode = parent_e.get<BoatBase>().mNode;
|
||||
flecs::entity e = ECS::get().entity().child_of(parent_e);
|
||||
Ogre::String event = lua_tostring(L, 2);
|
||||
float x = lua_tonumber(L, 3);
|
||||
float y = lua_tonumber(L, 4);
|
||||
float z = lua_tonumber(L, 5);
|
||||
float h = lua_tonumber(L, 6);
|
||||
float r = lua_tonumber(L, 7);
|
||||
OgreAssert(parentNode, "bad parent");
|
||||
Ogre::Vector3 position(x, y, z);
|
||||
e.set<EventTrigger>({ parentNode, position, h, r, event });
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_child_character_trigger");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
OgreAssert(lua_gettop(L) == 5, "Invalid parameters");
|
||||
luaL_checktype(L, 1, LUA_TSTRING); // type
|
||||
luaL_checktype(L, 2, LUA_TNUMBER);
|
||||
luaL_checktype(L, 3, LUA_TNUMBER);
|
||||
luaL_checktype(L, 4, LUA_TNUMBER);
|
||||
luaL_checktype(L, 5, LUA_TNUMBER);
|
||||
Ogre::String type = lua_tostring(L, 1);
|
||||
float yaw = lua_tonumber(L, 5);
|
||||
float x = lua_tonumber(L, 2);
|
||||
float y = lua_tonumber(L, 3);
|
||||
float z = lua_tonumber(L, 4);
|
||||
flecs::entity e = ECS::get().entity();
|
||||
Ogre::Quaternion orientation(Ogre::Radian(yaw),
|
||||
Ogre::Vector3(0, 1, 0));
|
||||
Ogre::Vector3 npcPos(x, y, z);
|
||||
e.set<CharacterLocation>({ orientation, npcPos });
|
||||
e.set<CharacterConf>({ type });
|
||||
e.add<Character>();
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
return 1;
|
||||
});
|
||||
lua_setglobal(L, "ecs_npc_set");
|
||||
}
|
||||
|
||||
LuaData::~LuaData()
|
||||
{
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
void LuaData::lateSetup()
|
||||
{
|
||||
#if 0
|
||||
Ogre::DataStreamList streams =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResources(
|
||||
"*.lua", "LuaScripts");
|
||||
while (!streams.empty()) {
|
||||
Ogre::DataStreamPtr s = streams.front();
|
||||
std::cout << "stream: " << s->getAsString() << "\n";
|
||||
streams.pop_front();
|
||||
if (luaL_dostring(L, s->getAsString().c_str()) != LUA_OK) {
|
||||
std::cout << "error: " << lua_tostring(L, -1) << "\n";
|
||||
OgreAssert(false, "Script failure");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Ogre::DataStreamPtr stream =
|
||||
Ogre::ResourceGroupManager::getSingleton().openResource(
|
||||
"data.lua", "LuaScripts");
|
||||
std::cout << "stream: " << stream->getAsString() << "\n";
|
||||
if (luaL_dostring(L, stream->getAsString().c_str()) != LUA_OK) {
|
||||
std::cout << "error: " << lua_tostring(L, -1) << "\n";
|
||||
OgreAssert(false, "Script failure");
|
||||
}
|
||||
|
||||
const char *lua_code = "\n\
|
||||
function stuff()\n\
|
||||
return 4\n\
|
||||
end\n\
|
||||
x = stuff()\n\
|
||||
";
|
||||
luaL_dostring(L, lua_code);
|
||||
lua_getglobal(L, "x");
|
||||
int x = lua_tonumber(L, 1);
|
||||
std::cout << "lua: " << x << "\n";
|
||||
}
|
||||
|
||||
LuaModule::LuaModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.component<LuaBase>()
|
||||
.on_add([](LuaBase &lua) {
|
||||
lua.mLua = new LuaData;
|
||||
lua.setup_called = false;
|
||||
lua.startup_called = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.add<LuaBase>();
|
||||
ecs.system<const EngineData, LuaBase>("LuaUpdate")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](const EngineData &eng, LuaBase &lua) {
|
||||
if (!lua.setup_called) {
|
||||
lua.mLua->lateSetup();
|
||||
lua.mLua->call_handler("setup");
|
||||
lua.setup_called = true;
|
||||
}
|
||||
if (!lua.startup_called) {
|
||||
if (eng.startupDelay <= 0.0f) {
|
||||
lua.mLua->call_handler("startup");
|
||||
lua.startup_called = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
src/gamedata/LuaData.h
Normal file
29
src/gamedata/LuaData.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef LUA_DATA_H
|
||||
#define LUA_DATA_H
|
||||
#include "lua.hpp"
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include <OgreSerializer.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct LuaData {
|
||||
lua_State *L;
|
||||
std::vector<int> setup_handlers;
|
||||
int setup_handler();
|
||||
int call_handler(const Ogre::String &event);
|
||||
|
||||
LuaData();
|
||||
virtual ~LuaData();
|
||||
void lateSetup();
|
||||
};
|
||||
struct LuaBase {
|
||||
LuaData *mLua;
|
||||
bool setup_called;
|
||||
bool startup_called;
|
||||
};
|
||||
struct LuaModule {
|
||||
LuaModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -7,7 +7,6 @@ namespace ECS
|
||||
SunModule::SunModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.component<Sun>().add(flecs::Singleton);
|
||||
ecs.set<Sun>({ nullptr, nullptr, nullptr, nullptr });
|
||||
ecs.system<const EngineData, Sun>("UpdateSetupSun")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](const EngineData &eng, Sun &sun) {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "TerrainModule.h"
|
||||
|
||||
#define TERRAIN_SIZE 129
|
||||
#define TERRAIN_WORLD_SIZE 4000.0f
|
||||
#define TERRAIN_WORLD_SIZE 1000.0f
|
||||
#define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain")
|
||||
#define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat")
|
||||
|
||||
@@ -31,40 +31,78 @@
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
class FlatTerrainDefiner
|
||||
: public Ogre::TerrainPagedWorldSection::TerrainDefiner,
|
||||
public Ogre::FrameListener {
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
struct gen_collider {
|
||||
Ogre::TerrainGroup *group;
|
||||
long x;
|
||||
long y;
|
||||
};
|
||||
std::deque<struct gen_collider> collider_queue;
|
||||
Ogre::Image img, img_noise, img_brushes;
|
||||
|
||||
public:
|
||||
FlatTerrainDefiner(Ogre::SceneManager *scm,
|
||||
Ogre::Bullet::DynamicsWorld *world)
|
||||
: Ogre::TerrainPagedWorldSection::TerrainDefiner()
|
||||
, Ogre::FrameListener()
|
||||
, mScnMgr(scm)
|
||||
, mWorld(world)
|
||||
#define BRUSH_SIZE 64
|
||||
struct HeightData {
|
||||
Ogre::Image img;
|
||||
Ogre::Image img_brushes;
|
||||
Ogre::Image img_noise;
|
||||
static HeightData *singleton;
|
||||
HeightData()
|
||||
{
|
||||
Ogre::Root::getSingleton().addFrameListener(this);
|
||||
img.load(
|
||||
"world_map.png",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
img_noise.load(
|
||||
"terrain.png",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
img_brushes.load(
|
||||
"brushes.png",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
img_noise.load(
|
||||
"terrain.png",
|
||||
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
}
|
||||
static HeightData *get_singleton()
|
||||
{
|
||||
if (!singleton)
|
||||
singleton = new HeightData();
|
||||
return singleton;
|
||||
}
|
||||
float get_brush_height(int id, int x, int y)
|
||||
{
|
||||
int m = 0;
|
||||
switch (id) {
|
||||
case 0:
|
||||
m = 0;
|
||||
break;
|
||||
case 1:
|
||||
m = BRUSH_SIZE;
|
||||
break;
|
||||
default:
|
||||
OgreAssert(false, "bad brush id");
|
||||
break;
|
||||
}
|
||||
return img_brushes.getColourAt(x, y + m, 0).r;
|
||||
}
|
||||
float get_base_height(const Ogre::Vector2 &worldOffset, int x, int y)
|
||||
{
|
||||
float height = 0.0f;
|
||||
int world_x = worldOffset.x + x;
|
||||
int world_y = worldOffset.y + y;
|
||||
int world_img_x =
|
||||
world_x + (int)img.getWidth() * BRUSH_SIZE / 2;
|
||||
int world_img_y =
|
||||
world_y + (int)img.getHeight() * BRUSH_SIZE / 2;
|
||||
Ogre::ColourValue color, colorb1, colorb2;
|
||||
// float d;
|
||||
int div = 1;
|
||||
int map_img_x = world_img_x / (BRUSH_SIZE) / div;
|
||||
int map_img_y = world_img_y / (BRUSH_SIZE) / div;
|
||||
int brush_img_x = (world_img_x / div) % BRUSH_SIZE;
|
||||
int brush_img_y = (world_img_y / div) % BRUSH_SIZE;
|
||||
if (world_img_x < 0 ||
|
||||
world_img_x >= img.getWidth() * BRUSH_SIZE ||
|
||||
world_img_y < 0 ||
|
||||
world_img_y >= img.getWidth() * BRUSH_SIZE) {
|
||||
height = -1.0f;
|
||||
goto out;
|
||||
}
|
||||
color = img.getColourAt(map_img_x, map_img_y, 0);
|
||||
colorb1 = img_brushes.getColourAt(brush_img_x,
|
||||
brush_img_y + BRUSH_SIZE, 0);
|
||||
colorb2 = img_brushes.getColourAt(brush_img_x, brush_img_y, 0);
|
||||
// d = Ogre::Math::saturate(color.r - 0.05f);
|
||||
height = color.r;
|
||||
out:
|
||||
return height;
|
||||
}
|
||||
|
||||
private:
|
||||
float get_noise_height(const Ogre::Vector2 &worldOffset, int x, int y)
|
||||
{
|
||||
int h;
|
||||
@@ -101,55 +139,60 @@ private:
|
||||
}
|
||||
return noise_values[0] + noise_values[1];
|
||||
}
|
||||
#define BRUSH_SIZE 64
|
||||
float get_brush_height(int id, int x, int y)
|
||||
float get_height(Ogre::TerrainGroup *terrainGroup, long x, long y,
|
||||
int j, int i)
|
||||
{
|
||||
int m = 0;
|
||||
switch (id) {
|
||||
case 0:
|
||||
m = 0;
|
||||
break;
|
||||
case 1:
|
||||
m = BRUSH_SIZE;
|
||||
break;
|
||||
default:
|
||||
OgreAssert(false, "bad brush id");
|
||||
break;
|
||||
}
|
||||
return img_brushes.getColourAt(x, y + m, 0).r;
|
||||
}
|
||||
float get_base_height(const Ogre::Vector2 &worldOffset, int x, int y)
|
||||
{
|
||||
float height = 0.0f;
|
||||
int world_x = worldOffset.x + x;
|
||||
int world_y = worldOffset.y + y;
|
||||
int world_img_x =
|
||||
world_x + (int)img.getWidth() * BRUSH_SIZE / 2;
|
||||
int world_img_y =
|
||||
world_y + (int)img.getHeight() * BRUSH_SIZE / 2;
|
||||
Ogre::ColourValue color, colorb1, colorb2;
|
||||
// float d;
|
||||
int map_img_x = world_img_x / (BRUSH_SIZE);
|
||||
int map_img_y = world_img_y / (BRUSH_SIZE);
|
||||
int brush_img_x = world_img_x % BRUSH_SIZE;
|
||||
int brush_img_y = world_img_y % BRUSH_SIZE;
|
||||
if (world_img_x < 0 ||
|
||||
world_img_x >= img.getWidth() * BRUSH_SIZE ||
|
||||
world_img_y < 0 ||
|
||||
world_img_y >= img.getWidth() * BRUSH_SIZE) {
|
||||
height = -1.0f;
|
||||
goto out;
|
||||
}
|
||||
color = img.getColourAt(map_img_x, map_img_y, 0);
|
||||
colorb1 = img_brushes.getColourAt(brush_img_x,
|
||||
brush_img_y + BRUSH_SIZE, 0);
|
||||
colorb2 = img_brushes.getColourAt(brush_img_x, brush_img_y, 0);
|
||||
// d = Ogre::Math::saturate(color.r - 0.05f);
|
||||
height = color.r;
|
||||
out:
|
||||
uint16_t terrainSize = terrainGroup->getTerrainSize();
|
||||
Ogre::Vector2 worldOffset(Ogre::Real(x * (terrainSize - 1)),
|
||||
Ogre::Real(y * (terrainSize - 1)));
|
||||
float brush_height0 =
|
||||
HeightData::get_singleton()->get_brush_height(
|
||||
0, j % BRUSH_SIZE, i % BRUSH_SIZE) -
|
||||
0.55f;
|
||||
float brush_height1 =
|
||||
HeightData::get_singleton()->get_brush_height(
|
||||
1, j % BRUSH_SIZE, i % BRUSH_SIZE) -
|
||||
0.55f;
|
||||
float mheight =
|
||||
Ogre::Math::lerp(
|
||||
brush_height1, brush_height0,
|
||||
HeightData::get_singleton()->get_base_height(
|
||||
worldOffset, j, i)) *
|
||||
120.0f;
|
||||
float height = mheight;
|
||||
if (mheight > 0.5f)
|
||||
height += 2.0f + get_noise_height(worldOffset, j, i);
|
||||
else if (mheight < -0.5f)
|
||||
height -= 2.0f + get_noise_height(worldOffset, j, i);
|
||||
return height;
|
||||
}
|
||||
};
|
||||
HeightData *HeightData::singleton = nullptr;
|
||||
|
||||
class FlatTerrainDefiner
|
||||
: public Ogre::TerrainPagedWorldSection::TerrainDefiner,
|
||||
public Ogre::FrameListener {
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
Ogre::Bullet::DynamicsWorld *mWorld;
|
||||
struct gen_collider {
|
||||
Ogre::TerrainGroup *group;
|
||||
long x;
|
||||
long y;
|
||||
};
|
||||
std::deque<struct gen_collider> collider_queue;
|
||||
|
||||
public:
|
||||
FlatTerrainDefiner(Ogre::SceneManager *scm,
|
||||
Ogre::Bullet::DynamicsWorld *world)
|
||||
: Ogre::TerrainPagedWorldSection::TerrainDefiner()
|
||||
, Ogre::FrameListener()
|
||||
, mScnMgr(scm)
|
||||
, mWorld(world)
|
||||
{
|
||||
Ogre::Root::getSingleton().addFrameListener(this);
|
||||
}
|
||||
|
||||
private:
|
||||
public:
|
||||
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
|
||||
{
|
||||
@@ -158,37 +201,15 @@ public:
|
||||
MEMCATEGORY_GEOMETRY);
|
||||
// float *heightMapCollider = OGRE_ALLOC_T(
|
||||
// float, terrainSize *terrainSize, MEMCATEGORY_GEOMETRY);
|
||||
Ogre::Vector2 worldOffset(Ogre::Real(x * (terrainSize - 1)),
|
||||
Ogre::Real(y * (terrainSize - 1)));
|
||||
Ogre::Vector2 worldOrigin =
|
||||
Ogre::Vector2(img.getWidth(), img.getHeight()) * 0.5f;
|
||||
// Ogre::Vector2 worldOrigin =
|
||||
// Ogre::Vector2(img.getWidth(), img.getHeight()) * 0.5f;
|
||||
float chunk = 128.0f;
|
||||
Ogre::Vector2 revisedValuePoint;
|
||||
for (int i = 0; i < terrainSize; i++)
|
||||
for (int j = 0; j < terrainSize; j++) {
|
||||
float brush_height0 =
|
||||
get_brush_height(0, j % BRUSH_SIZE,
|
||||
i % BRUSH_SIZE) -
|
||||
0.55f;
|
||||
float brush_height1 =
|
||||
get_brush_height(1, j % BRUSH_SIZE,
|
||||
i % BRUSH_SIZE) -
|
||||
0.55f;
|
||||
float mheight =
|
||||
Ogre::Math::lerp(
|
||||
brush_height1, brush_height0,
|
||||
get_base_height(worldOffset, j,
|
||||
i)) *
|
||||
120.0f;
|
||||
float height = mheight;
|
||||
if (mheight > 0.5f)
|
||||
height += 2.0f +
|
||||
get_noise_height(worldOffset,
|
||||
j, i);
|
||||
else if (mheight < -0.5f)
|
||||
height -= 2.0f +
|
||||
get_noise_height(worldOffset,
|
||||
j, i);
|
||||
float height =
|
||||
HeightData::get_singleton()->get_height(
|
||||
terrainGroup, x, y, j, i);
|
||||
|
||||
// height = -2.0f;
|
||||
heightMap[i * terrainSize + j] = height;
|
||||
@@ -237,11 +258,14 @@ public:
|
||||
float maxH = terrain->getMaxHeight();
|
||||
int size = terrain->getSize();
|
||||
float worldSize = terrain->getWorldSize();
|
||||
if (!created || true) {
|
||||
if (true) {
|
||||
btRigidBody *body =
|
||||
mWorld->addTerrainRigidBody(
|
||||
group, x, y, 2,
|
||||
0x7ffffffd & (~16));
|
||||
OgreAssert(
|
||||
body,
|
||||
"Could not create RigidBody");
|
||||
Ogre::LogManager::getSingleton().logError(
|
||||
"created rigid body " +
|
||||
Ogre::StringConverter::toString(
|
||||
@@ -274,10 +298,40 @@ public:
|
||||
created = true;
|
||||
}
|
||||
collider_queue.pop_front();
|
||||
// FIXME: create entities and components instead
|
||||
Ogre::SceneNode *items =
|
||||
terrain->_getRootSceneNode()
|
||||
->createChildSceneNode();
|
||||
for (int i = 0; i < ECS::get<PlacementObjects>()
|
||||
.altar_items.size();
|
||||
i++) {
|
||||
const struct PlacementObjects::item &item =
|
||||
ECS::get<PlacementObjects>()
|
||||
.altar_items[i];
|
||||
Ogre::Entity *ent =
|
||||
group->getSceneManager()
|
||||
->createEntity(
|
||||
item.entity);
|
||||
Ogre::SceneNode *what =
|
||||
items->createChildSceneNode();
|
||||
what->attachObject(ent);
|
||||
what->setOrientation(item.rotation);
|
||||
what->setPosition(item.position);
|
||||
ECS::get<EngineData>()
|
||||
.mWorld->addRigidBody(
|
||||
0, ent,
|
||||
Ogre::Bullet::CT_TRIMESH,
|
||||
nullptr, 2, 0x7fffffff);
|
||||
}
|
||||
} else {
|
||||
output.push_back(collider_queue.front());
|
||||
collider_queue.pop_front();
|
||||
}
|
||||
if (collider_queue.empty() &&
|
||||
!ECS::get<Terrain>().mTerrainReady) {
|
||||
ECS::get_mut<Terrain>().mTerrainReady = true;
|
||||
ECS::modified<Terrain>();
|
||||
}
|
||||
}
|
||||
collider_queue = output;
|
||||
}
|
||||
@@ -319,6 +373,8 @@ struct TerrainPrivate {
|
||||
|
||||
TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
{
|
||||
struct CanSetPlayerPosition {};
|
||||
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
|
||||
ecs.component<Terrain>().add(flecs::Singleton);
|
||||
ecs.component<TerrainPrivate>().add(flecs::Singleton);
|
||||
ecs.set<TerrainPrivate>({ nullptr, {} });
|
||||
@@ -328,7 +384,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
.each([](const EngineData &eng, const Camera &camera,
|
||||
const Sun &sun, Terrain &terrain,
|
||||
TerrainPrivate &priv) {
|
||||
if (!terrain.mTerrainGroup) {
|
||||
if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) {
|
||||
std::cout << "Terrain setup\n";
|
||||
if (!priv.mDummyPageProvider)
|
||||
priv.mDummyPageProvider =
|
||||
@@ -338,6 +394,9 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
terrain.mTerrainGlobals =
|
||||
OGRE_NEW Ogre::TerrainGlobalOptions();
|
||||
|
||||
OgreAssert(terrain.mTerrainGlobals,
|
||||
"Failed to allocate global options");
|
||||
|
||||
Ogre::LogManager::getSingleton().setMinLogLevel(
|
||||
Ogre::LML_TRIVIAL);
|
||||
|
||||
@@ -422,48 +481,16 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
|
||||
terrain.mTerrainGroup->freeTemporaryResources();
|
||||
std::cout << "Terrain setup done\n";
|
||||
ECS::get().set<PlacementObjects>({});
|
||||
}
|
||||
bool playerCheck = false;
|
||||
ECS::get()
|
||||
.query_builder<CharacterBase, CharacterBody>()
|
||||
.with<Character>()
|
||||
.with<Player>()
|
||||
.each([&playerCheck,
|
||||
terrain](flecs::entity e,
|
||||
CharacterBase &ch,
|
||||
CharacterBody &body) {
|
||||
if (!body.checkGround)
|
||||
body.checkGround = true;
|
||||
if (ch.mBodyNode &&
|
||||
body.checkGroundResult) {
|
||||
long x, y;
|
||||
Ogre::Vector3 pos =
|
||||
ch.mBodyNode
|
||||
->getPosition();
|
||||
terrain.mTerrainGroup
|
||||
->convertWorldPositionToTerrainSlot(
|
||||
pos, &x, &y);
|
||||
if (terrain.mTerrainGroup
|
||||
->getTerrain(x,
|
||||
y) &&
|
||||
terrain.mTerrainGroup
|
||||
->getTerrain(x, y)
|
||||
->isLoaded())
|
||||
playerCheck = true;
|
||||
}
|
||||
});
|
||||
if (playerCheck)
|
||||
terrain.mTerrainReady = true;
|
||||
if (priv.mSunUpdate.getMilliseconds() > 1000) {
|
||||
Ogre::TerrainGlobalOptions::getSingleton()
|
||||
.setCompositeMapAmbient(
|
||||
eng.mScnMgr->getAmbientLight());
|
||||
Ogre::TerrainGlobalOptions::getSingleton()
|
||||
.setCompositeMapDiffuse(
|
||||
sun.mSun->getDiffuseColour());
|
||||
Ogre::TerrainGlobalOptions::getSingleton()
|
||||
.setLightMapDirection(
|
||||
sun.mSun->getDerivedDirection());
|
||||
if (sun.mSun &&
|
||||
priv.mSunUpdate.getMilliseconds() > 1000) {
|
||||
terrain.mTerrainGlobals->setCompositeMapAmbient(
|
||||
eng.mScnMgr->getAmbientLight());
|
||||
terrain.mTerrainGlobals->setCompositeMapDiffuse(
|
||||
sun.mSun->getDiffuseColour());
|
||||
terrain.mTerrainGlobals->setLightMapDirection(
|
||||
sun.mSun->getDerivedDirection());
|
||||
std::cout << "sun pitch: "
|
||||
<< sun.mSunNode->getOrientation()
|
||||
.getPitch()
|
||||
@@ -471,5 +498,154 @@ TerrainModule::TerrainModule(flecs::world &ecs)
|
||||
priv.mSunUpdate.reset();
|
||||
}
|
||||
});
|
||||
ecs.system<const CharacterBase, const Terrain>("UpdateTerrainStatus")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<TerrainReady>()
|
||||
.each([](const CharacterBase &ch, const Terrain &terrain) {
|
||||
std::cout << "mTerrainReady: " << terrain.mTerrainReady
|
||||
<< "\n";
|
||||
std::cout << "mBodyNode: " << ch.mBodyNode << "\n";
|
||||
if (ch.mBodyNode && terrain.mTerrainReady) {
|
||||
long x, y;
|
||||
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
|
||||
terrain.mTerrainGroup
|
||||
->convertWorldPositionToTerrainSlot(
|
||||
pos, &x, &y);
|
||||
if (terrain.mTerrainGroup->getTerrain(x, y) &&
|
||||
terrain.mTerrainGroup->getTerrain(x, y)
|
||||
->isLoaded())
|
||||
ECS::get().add<TerrainReady>();
|
||||
}
|
||||
});
|
||||
ecs.system<const Terrain, PlacementObjects>("UpdatePlacementObjects")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](const Terrain &terrain, PlacementObjects &placement) {
|
||||
if (placement.altar_items.size() == 0) {
|
||||
struct PlacementObjects::item item;
|
||||
int i, j;
|
||||
int worldSize = terrain.mTerrainGroup
|
||||
->getTerrainWorldSize();
|
||||
uint16_t terrainSize =
|
||||
terrain.mTerrainGroup->getTerrainSize();
|
||||
item.entity = "altar.glb";
|
||||
item.rotation = Ogre::Quaternion(0, 0, 0, 1);
|
||||
item.position = Ogre::Vector3(0, 0, 0);
|
||||
float height =
|
||||
HeightData::get_singleton()->get_height(
|
||||
terrain.mTerrainGroup, 0, 0, 0,
|
||||
0);
|
||||
item.position.y = height;
|
||||
placement.altar_items.push_back(item);
|
||||
for (i = -64000; i < 64000; i += 2000)
|
||||
for (j = -64000; j < 64000; j += 2000) {
|
||||
if (i == 0 && j == 0)
|
||||
continue;
|
||||
Ogre::Vector3 position(i, 0, j);
|
||||
long xslot, yslot;
|
||||
terrain.mTerrainGroup
|
||||
->convertWorldPositionToTerrainSlot(
|
||||
position,
|
||||
&xslot, &yslot);
|
||||
Ogre::Vector3 slotpos;
|
||||
terrain.mTerrainGroup
|
||||
->convertTerrainSlotToWorldPosition(
|
||||
xslot, yslot,
|
||||
&slotpos);
|
||||
Ogre::Vector3 offset =
|
||||
(position - slotpos) *
|
||||
terrainSize / worldSize;
|
||||
height =
|
||||
HeightData::get_singleton()
|
||||
->get_height(
|
||||
terrain.mTerrainGroup,
|
||||
xslot,
|
||||
yslot,
|
||||
(int)offset.x +
|
||||
(terrainSize -
|
||||
1) / 2,
|
||||
(int)offset.z +
|
||||
(terrainSize -
|
||||
1) / 2);
|
||||
#if 0
|
||||
height =
|
||||
terrain.mTerrainGroup
|
||||
->getHeightAtWorldPosition(
|
||||
position);
|
||||
#endif
|
||||
if (height > -9.0f)
|
||||
continue;
|
||||
#ifdef VDEBUG
|
||||
std::cout << "worldSize: "
|
||||
<< worldSize - 1
|
||||
<< std::endl;
|
||||
std::cout << "height: " << i
|
||||
<< " " << j << " "
|
||||
<< height
|
||||
<< std::endl;
|
||||
#endif
|
||||
item.entity = "altar.glb";
|
||||
item.rotation =
|
||||
Ogre::Quaternion(0, 0,
|
||||
0, 1);
|
||||
position.y = height;
|
||||
item.position = position;
|
||||
placement.altar_items.push_back(
|
||||
item);
|
||||
}
|
||||
#ifdef VDEBUG
|
||||
for (i = 0; i < placement.altar_items.size();
|
||||
i++) {
|
||||
std::cout << "placement: " << i << " "
|
||||
<< placement.altar_items[i]
|
||||
.position
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
ECS::get().add<CanSetPlayerPosition>();
|
||||
}
|
||||
});
|
||||
ecs.system<const Terrain>("SetPlayerPosition")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<CanSetPlayerPosition>()
|
||||
.each([this](const Terrain &terrain) {
|
||||
flecs::entity player = ECS::player;
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
if (!player.has<CharacterLocation>())
|
||||
return;
|
||||
if (!player.has<CharacterBase>())
|
||||
return;
|
||||
if (!player.has<Player>())
|
||||
return;
|
||||
CharacterLocation &loc =
|
||||
player.get_mut<CharacterLocation>();
|
||||
const CharacterBase &ch = player.get<CharacterBase>();
|
||||
if (!ch.mBodyNode) {
|
||||
std::cout << "no player yet";
|
||||
return;
|
||||
}
|
||||
float height =
|
||||
get_height(terrain.mTerrainGroup, loc.position);
|
||||
loc.position.y = height + 0.0f;
|
||||
ch.mBodyNode->setPosition(loc.position);
|
||||
ch.mBodyNode->setOrientation(Ogre::Quaternion());
|
||||
player.modified<CharacterLocation>();
|
||||
ECS::get().remove<CanSetPlayerPosition>();
|
||||
});
|
||||
}
|
||||
float TerrainModule::get_height(Ogre::TerrainGroup *group,
|
||||
const Ogre::Vector3 &position)
|
||||
{
|
||||
int worldSize = group->getTerrainWorldSize();
|
||||
uint16_t terrainSize = group->getTerrainSize();
|
||||
long xslot, yslot;
|
||||
group->convertWorldPositionToTerrainSlot(position, &xslot, &yslot);
|
||||
Ogre::Vector3 slotpos;
|
||||
group->convertTerrainSlotToWorldPosition(xslot, yslot, &slotpos);
|
||||
Ogre::Vector3 offset = (position - slotpos) * terrainSize / worldSize;
|
||||
float height = HeightData::get_singleton()->get_height(
|
||||
group, xslot, yslot, (int)offset.x + (terrainSize - 1) / 2,
|
||||
(int)offset.z + (terrainSize - 1) / 2);
|
||||
return height;
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,18 @@ struct Terrain {
|
||||
|
||||
Ogre::Vector3 mTerrainPos;
|
||||
};
|
||||
struct PlacementObjects {
|
||||
struct item {
|
||||
Ogre::String entity;
|
||||
Ogre::Quaternion rotation;
|
||||
Ogre::Vector3 position;
|
||||
};
|
||||
std::vector<struct item> altar_items;
|
||||
};
|
||||
struct TerrainModule {
|
||||
TerrainModule(flecs::world &ecs);
|
||||
static float get_height(Ogre::TerrainGroup *group,
|
||||
const Ogre::Vector3 &position);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,14 +38,19 @@ struct WaterSurface {
|
||||
Ogre::Viewport *mViewports[4];
|
||||
};
|
||||
struct WaterBody {
|
||||
std::vector<btCollisionShape *> mChildShapes;
|
||||
btCollisionShape *mWaterShape;
|
||||
btPairCachingGhostObject *mWaterBody;
|
||||
std::set<btCollisionObject *> mInWater;
|
||||
std::unordered_map<btCollisionObject *, float> mSurface;
|
||||
btManifoldArray mManifoldArray;
|
||||
btVector3 mShapeAabbMin, mShapeAabbMax;
|
||||
int count;
|
||||
btActionInterface *action;
|
||||
bool isInWater(const btCollisionObject *body) const;
|
||||
};
|
||||
struct WaterModule {
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
WaterModule(flecs::world &ecs);
|
||||
void createWaterShape(WaterBody *water);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
6
src/gamedata/WorldMapModule.cpp
Normal file
6
src/gamedata/WorldMapModule.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "WorldMapModule.h"
|
||||
|
||||
ECS::WorldMapModule::WorldMapModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.component<WorldMap>();
|
||||
}
|
||||
11
src/gamedata/WorldMapModule.h
Normal file
11
src/gamedata/WorldMapModule.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef WORLD_MAP_MODULE_H
|
||||
#define WORLD_MAP_MODULE_H
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct WorldMap {};
|
||||
struct WorldMapModule {
|
||||
WorldMapModule(flecs::world &ecs);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -14,17 +14,28 @@ set(LUA_OBJ
|
||||
lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o
|
||||
lutf8lib.o linit.o)
|
||||
set(LUA_SRC)
|
||||
set(LPEG_OBJ
|
||||
lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o
|
||||
)
|
||||
set(LPEG_SRC)
|
||||
foreach(LUA_FILE ${LUA_OBJ})
|
||||
string(REPLACE ".o" ".c" LUA_SRC_ITEM ${LUA_FILE})
|
||||
list(APPEND LUA_SRC lua-5.4.8/src/${LUA_SRC_ITEM})
|
||||
endforeach()
|
||||
add_library(lua ${LUA_SRC})
|
||||
target_include_directories(lua PRIVATE lua-5.4.8/src)
|
||||
foreach(LPEG_FILE ${LPEG_OBJ})
|
||||
string(REPLACE ".o" ".c" LPEG_SRC_ITEM ${LPEG_FILE})
|
||||
list(APPEND LPEG_SRC lpeg-1.1.0/${LPEG_SRC_ITEM})
|
||||
endforeach()
|
||||
add_library(lua ${LUA_SRC} ${LPEG_SRC})
|
||||
target_include_directories(lua PUBLIC lua-5.4.8/src lpeg-1.1.0)
|
||||
add_executable(luavm lua-5.4.8/src/lua.c)
|
||||
target_link_libraries(luavm lua m)
|
||||
target_include_directories(luavm PRIVATE lua-5.4.8/src)
|
||||
add_executable(luac lua-5.4.8/src/luac.c ${LUA_SRC})
|
||||
add_executable(luac lua-5.4.8/src/luac.c ${LUA_SRC} ${LPEG_SRC})
|
||||
target_link_libraries(luac m)
|
||||
target_include_directories(luac PRIVATE lua-5.4.8/src)
|
||||
target_include_directories(luac PRIVATE lua-5.4.8/src lpeg-1.1.0)
|
||||
add_executable(lualpegvm lua.c)
|
||||
target_link_libraries(lualpegvm lua m)
|
||||
target_include_directories(lualpegvm PRIVATE lua-5.4.8/src)
|
||||
|
||||
|
||||
|
||||
109
src/lua/lpeg-1.1.0/HISTORY
Normal file
109
src/lua/lpeg-1.1.0/HISTORY
Normal file
@@ -0,0 +1,109 @@
|
||||
HISTORY for LPeg 1.1.0
|
||||
|
||||
* Changes from version 1.0.2 to 1.1.0
|
||||
---------------------------------
|
||||
+ accumulator capture
|
||||
+ UTF-8 ranges
|
||||
+ Larger limit for number of rules in a grammar
|
||||
+ Larger limit for number of captures in a match
|
||||
+ bug fixes
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 1.0.1 to 1.0.2
|
||||
---------------------------------
|
||||
+ some bugs fixed
|
||||
|
||||
* Changes from version 0.12 to 1.0.1
|
||||
---------------------------------
|
||||
+ group "names" can be any Lua value
|
||||
+ some bugs fixed
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 0.11 to 0.12
|
||||
---------------------------------
|
||||
+ no "unsigned short" limit for pattern sizes
|
||||
+ mathtime captures considered nullable
|
||||
+ some bugs fixed
|
||||
|
||||
* Changes from version 0.10 to 0.11
|
||||
-------------------------------
|
||||
+ complete reimplementation of the code generator
|
||||
+ new syntax for table captures
|
||||
+ new functions in module 're'
|
||||
+ other small improvements
|
||||
|
||||
* Changes from version 0.9 to 0.10
|
||||
-------------------------------
|
||||
+ backtrack stack has configurable size
|
||||
+ better error messages
|
||||
+ Notation for non-terminals in 're' back to A instead o <A>
|
||||
+ experimental look-behind pattern
|
||||
+ support for external extensions
|
||||
+ works with Lua 5.2
|
||||
+ consumes less C stack
|
||||
|
||||
- "and" predicates do not keep captures
|
||||
|
||||
* Changes from version 0.8 to 0.9
|
||||
-------------------------------
|
||||
+ The accumulator capture was replaced by a fold capture;
|
||||
programs that used the old 'lpeg.Ca' will need small changes.
|
||||
+ Some support for character classes from old C locales.
|
||||
+ A new named-group capture.
|
||||
|
||||
* Changes from version 0.7 to 0.8
|
||||
-------------------------------
|
||||
+ New "match-time" capture.
|
||||
+ New "argument capture" that allows passing arguments into the pattern.
|
||||
+ Better documentation for 're'.
|
||||
+ Several small improvements for 're'.
|
||||
+ The 're' module has an incompatibility with previous versions:
|
||||
now, any use of a non-terminal must be enclosed in angle brackets
|
||||
(like <B>).
|
||||
|
||||
* Changes from version 0.6 to 0.7
|
||||
-------------------------------
|
||||
+ Several improvements in module 're':
|
||||
- better documentation;
|
||||
- support for most captures (all but accumulator);
|
||||
- limited repetitions p{n,m}.
|
||||
+ Small improvements in efficiency.
|
||||
+ Several small bugs corrected (special thanks to Hans Hagen
|
||||
and Taco Hoekwater).
|
||||
|
||||
* Changes from version 0.5 to 0.6
|
||||
-------------------------------
|
||||
+ Support for non-numeric indices in grammars.
|
||||
+ Some bug fixes (thanks to the luatex team).
|
||||
+ Some new optimizations; (thanks to Mike Pall).
|
||||
+ A new page layout (thanks to Andre Carregal).
|
||||
+ Minimal documentation for module 're'.
|
||||
|
||||
* Changes from version 0.4 to 0.5
|
||||
-------------------------------
|
||||
+ Several optimizations.
|
||||
+ lpeg.P now accepts booleans.
|
||||
+ Some new examples.
|
||||
+ A proper license.
|
||||
+ Several small improvements.
|
||||
|
||||
* Changes from version 0.3 to 0.4
|
||||
-------------------------------
|
||||
+ Static check for loops in repetitions and grammars.
|
||||
+ Removed label option in captures.
|
||||
+ The implementation of captures uses less memory.
|
||||
|
||||
* Changes from version 0.2 to 0.3
|
||||
-------------------------------
|
||||
+ User-defined patterns in Lua.
|
||||
+ Several new captures.
|
||||
|
||||
* Changes from version 0.1 to 0.2
|
||||
-------------------------------
|
||||
+ Several small corrections.
|
||||
+ Handles embedded zeros like any other character.
|
||||
+ Capture "name" can be any Lua value.
|
||||
+ Unlimited number of captures.
|
||||
+ Match gets an optional initial position.
|
||||
|
||||
(end of HISTORY)
|
||||
4
src/lua/lpeg-1.1.0/README.md
Normal file
4
src/lua/lpeg-1.1.0/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# LPeg - Parsing Expression Grammars For Lua
|
||||
|
||||
For more information,
|
||||
see [Lpeg](//www.inf.puc-rio.br/~roberto/lpeg/).
|
||||
612
src/lua/lpeg-1.1.0/lpcap.c
Normal file
612
src/lua/lpeg-1.1.0/lpcap.c
Normal file
@@ -0,0 +1,612 @@
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lpprint.h"
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
#define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
|
||||
|
||||
#define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
|
||||
|
||||
|
||||
|
||||
#define skipclose(cs,head) \
|
||||
if (isopencap(head)) { assert(isclosecap(cs->cap)); cs->cap++; }
|
||||
|
||||
|
||||
/*
|
||||
** Return the size of capture 'cap'. If it is an open capture, 'close'
|
||||
** must be its corresponding close.
|
||||
*/
|
||||
static Index_t capsize (Capture *cap, Capture *close) {
|
||||
if (isopencap(cap)) {
|
||||
assert(isclosecap(close));
|
||||
return close->index - cap->index;
|
||||
}
|
||||
else
|
||||
return cap->siz - 1;
|
||||
}
|
||||
|
||||
|
||||
static Index_t closesize (CapState *cs, Capture *head) {
|
||||
return capsize(head, cs->cap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Put at the cache for Lua values the value indexed by 'v' in ktable
|
||||
** of the running pattern (if it is not there yet); returns its index.
|
||||
*/
|
||||
static int updatecache (CapState *cs, int v) {
|
||||
int idx = cs->ptop + 1; /* stack index of cache for Lua values */
|
||||
if (v != cs->valuecached) { /* not there? */
|
||||
getfromktable(cs, v); /* get value from 'ktable' */
|
||||
lua_replace(cs->L, idx); /* put it at reserved stack position */
|
||||
cs->valuecached = v; /* keep track of what is there */
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
static int pushcapture (CapState *cs);
|
||||
|
||||
|
||||
/*
|
||||
** Goes back in a list of captures looking for an open capture
|
||||
** corresponding to a close one.
|
||||
*/
|
||||
static Capture *findopen (Capture *cap) {
|
||||
int n = 0; /* number of closes waiting an open */
|
||||
for (;;) {
|
||||
cap--;
|
||||
if (isclosecap(cap)) n++; /* one more open to skip */
|
||||
else if (isopencap(cap))
|
||||
if (n-- == 0) return cap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Go to the next capture at the same level.
|
||||
*/
|
||||
static void nextcap (CapState *cs) {
|
||||
Capture *cap = cs->cap;
|
||||
if (isopencap(cap)) { /* must look for a close? */
|
||||
int n = 0; /* number of opens waiting a close */
|
||||
for (;;) { /* look for corresponding close */
|
||||
cap++;
|
||||
if (isopencap(cap)) n++;
|
||||
else if (isclosecap(cap))
|
||||
if (n-- == 0) break;
|
||||
}
|
||||
cs->cap = cap + 1; /* + 1 to skip last close */
|
||||
}
|
||||
else {
|
||||
Capture *next;
|
||||
for (next = cap + 1; capinside(cap, next); next++)
|
||||
; /* skip captures inside current one */
|
||||
cs->cap = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push on the Lua stack all values generated by nested captures inside
|
||||
** the current capture. Returns number of values pushed. 'addextra'
|
||||
** makes it push the entire match after all captured values. The
|
||||
** entire match is pushed also if there are no other nested values,
|
||||
** so the function never returns zero.
|
||||
*/
|
||||
static int pushnestedvalues (CapState *cs, int addextra) {
|
||||
Capture *head = cs->cap++; /* original capture */
|
||||
int n = 0; /* number of pushed subvalues */
|
||||
/* repeat for all nested patterns */
|
||||
while (capinside(head, cs->cap))
|
||||
n += pushcapture(cs);
|
||||
if (addextra || n == 0) { /* need extra? */
|
||||
lua_pushlstring(cs->L, cs->s + head->index, closesize(cs, head));
|
||||
n++;
|
||||
}
|
||||
skipclose(cs, head);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push only the first value generated by nested captures
|
||||
*/
|
||||
static void pushonenestedvalue (CapState *cs) {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n > 1)
|
||||
lua_pop(cs->L, n - 1); /* pop extra values */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Checks whether group 'grp' is visible to 'ref', that is, 'grp' is
|
||||
** not nested inside a full capture that does not contain 'ref'. (We
|
||||
** only need to care for full captures because the search at 'findback'
|
||||
** skips open-end blocks; so, if 'grp' is nested in a non-full capture,
|
||||
** 'ref' is also inside it.) To check this, we search backward for the
|
||||
** inner full capture enclosing 'grp'. A full capture cannot contain
|
||||
** non-full captures, so a close capture means we cannot be inside a
|
||||
** full capture anymore.
|
||||
*/
|
||||
static int capvisible (CapState *cs, Capture *grp, Capture *ref) {
|
||||
Capture *cap = grp;
|
||||
int i = MAXLOP; /* maximum distance for an 'open' */
|
||||
while (i-- > 0 && cap-- > cs->ocap) {
|
||||
if (isclosecap(cap))
|
||||
return 1; /* can stop the search */
|
||||
else if (grp->index - cap->index >= UCHAR_MAX)
|
||||
return 1; /* can stop the search */
|
||||
else if (capinside(cap, grp)) /* is 'grp' inside cap? */
|
||||
return capinside(cap, ref); /* ok iff cap also contains 'ref' */
|
||||
}
|
||||
return 1; /* 'grp' is not inside any capture */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a named group capture with the name given at the top of
|
||||
** the stack; goes backward from 'ref'.
|
||||
*/
|
||||
static Capture *findback (CapState *cs, Capture *ref) {
|
||||
lua_State *L = cs->L;
|
||||
Capture *cap = ref;
|
||||
while (cap-- > cs->ocap) { /* repeat until end of list */
|
||||
if (isclosecap(cap))
|
||||
cap = findopen(cap); /* skip nested captures */
|
||||
else if (capinside(cap, ref))
|
||||
continue; /* enclosing captures are not visible to 'ref' */
|
||||
if (captype(cap) == Cgroup && capvisible(cs, cap, ref)) {
|
||||
getfromktable(cs, cap->idx); /* get group name */
|
||||
if (lp_equal(L, -2, -1)) { /* right group? */
|
||||
lua_pop(L, 2); /* remove reference name and group name */
|
||||
return cap;
|
||||
}
|
||||
else lua_pop(L, 1); /* remove group name */
|
||||
}
|
||||
}
|
||||
luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
|
||||
return NULL; /* to avoid warnings */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Back-reference capture. Return number of values pushed.
|
||||
*/
|
||||
static int backrefcap (CapState *cs) {
|
||||
int n;
|
||||
Capture *curr = cs->cap;
|
||||
pushluaval(cs); /* reference name */
|
||||
cs->cap = findback(cs, curr); /* find corresponding group */
|
||||
n = pushnestedvalues(cs, 0); /* push group's values */
|
||||
cs->cap = curr + 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table capture: creates a new table and populates it with nested
|
||||
** captures.
|
||||
*/
|
||||
static int tablecap (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
Capture *head = cs->cap++;
|
||||
int n = 0;
|
||||
lua_newtable(L);
|
||||
while (capinside(head, cs->cap)) {
|
||||
if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
|
||||
pushluaval(cs); /* push group name */
|
||||
pushonenestedvalue(cs);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else { /* not a named group */
|
||||
int i;
|
||||
int k = pushcapture(cs);
|
||||
for (i = k; i > 0; i--) /* store all values into table */
|
||||
lua_rawseti(L, -(i + 1), n + i);
|
||||
n += k;
|
||||
}
|
||||
}
|
||||
skipclose(cs, head);
|
||||
return 1; /* number of values pushed (only the table) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Table-query capture
|
||||
*/
|
||||
static int querycap (CapState *cs) {
|
||||
int idx = cs->cap->idx;
|
||||
pushonenestedvalue(cs); /* get nested capture */
|
||||
lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
|
||||
if (!lua_isnil(cs->L, -1))
|
||||
return 1;
|
||||
else { /* no value */
|
||||
lua_pop(cs->L, 1); /* remove nil */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fold capture
|
||||
*/
|
||||
static int foldcap (CapState *cs) {
|
||||
int n;
|
||||
lua_State *L = cs->L;
|
||||
Capture *head = cs->cap++;
|
||||
int idx = head->idx;
|
||||
if (isclosecap(cs->cap) || /* no nested captures (large subject)? */
|
||||
(n = pushcapture(cs)) == 0) /* nested captures with no values? */
|
||||
return luaL_error(L, "no initial value for fold capture");
|
||||
if (n > 1)
|
||||
lua_pop(L, n - 1); /* leave only one result for accumulator */
|
||||
while (capinside(head, cs->cap)) {
|
||||
lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
|
||||
lua_insert(L, -2); /* put it before accumulator */
|
||||
n = pushcapture(cs); /* get next capture's values */
|
||||
lua_call(L, n + 1, 1); /* call folding function */
|
||||
}
|
||||
skipclose(cs, head);
|
||||
return 1; /* only accumulator left on the stack */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function capture
|
||||
*/
|
||||
static int functioncap (CapState *cs) {
|
||||
int n;
|
||||
int top = lua_gettop(cs->L);
|
||||
pushluaval(cs); /* push function */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(cs->L, n, LUA_MULTRET); /* call function */
|
||||
return lua_gettop(cs->L) - top; /* return function's results */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Accumulator capture
|
||||
*/
|
||||
static int accumulatorcap (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
int n;
|
||||
if (lua_gettop(L) < cs->firstcap)
|
||||
luaL_error(L, "no previous value for accumulator capture");
|
||||
pushluaval(cs); /* push function */
|
||||
lua_insert(L, -2); /* previous value becomes first argument */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(L, n + 1, 1); /* call function */
|
||||
return 0; /* did not add any extra value */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Select capture
|
||||
*/
|
||||
static int numcap (CapState *cs) {
|
||||
int idx = cs->cap->idx; /* value to select */
|
||||
if (idx == 0) { /* no values? */
|
||||
nextcap(cs); /* skip entire capture */
|
||||
return 0; /* no value produced */
|
||||
}
|
||||
else {
|
||||
int n = pushnestedvalues(cs, 0);
|
||||
if (n < idx) /* invalid index? */
|
||||
return luaL_error(cs->L, "no capture '%d'", idx);
|
||||
else {
|
||||
lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
|
||||
lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
|
||||
lua_pop(cs->L, n - 1); /* remove other captures */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the stack index of the first runtime capture in the given
|
||||
** list of captures (or zero if no runtime captures)
|
||||
*/
|
||||
int finddyncap (Capture *cap, Capture *last) {
|
||||
for (; cap < last; cap++) {
|
||||
if (cap->kind == Cruntime)
|
||||
return cap->idx; /* stack position of first capture */
|
||||
}
|
||||
return 0; /* no dynamic captures in this segment */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls a runtime capture. Returns number of captures "removed" by the
|
||||
** call, that is, those inside the group capture. Captures to be added
|
||||
** are on the Lua stack.
|
||||
*/
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
|
||||
int n, id;
|
||||
lua_State *L = cs->L;
|
||||
int otop = lua_gettop(L);
|
||||
Capture *open = findopen(close); /* get open group capture */
|
||||
assert(captype(open) == Cgroup);
|
||||
id = finddyncap(open, close); /* get first dynamic capture argument */
|
||||
close->kind = Cclose; /* closes the group */
|
||||
close->index = s - cs->s;
|
||||
cs->cap = open; cs->valuecached = 0; /* prepare capture state */
|
||||
luaL_checkstack(L, 4, "too many runtime captures");
|
||||
pushluaval(cs); /* push function to be called */
|
||||
lua_pushvalue(L, SUBJIDX); /* push original subject */
|
||||
lua_pushinteger(L, s - cs->s + 1); /* push current position */
|
||||
n = pushnestedvalues(cs, 0); /* push nested captures */
|
||||
lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
|
||||
if (id > 0) { /* are there old dynamic captures to be removed? */
|
||||
int i;
|
||||
for (i = id; i <= otop; i++)
|
||||
lua_remove(L, id); /* remove old dynamic captures */
|
||||
*rem = otop - id + 1; /* total number of dynamic captures removed */
|
||||
}
|
||||
else
|
||||
*rem = 0; /* no dynamic captures removed */
|
||||
return close - open - 1; /* number of captures to be removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary structure for substitution and string captures: keep
|
||||
** information about nested captures for future use, avoiding to push
|
||||
** string results into Lua
|
||||
*/
|
||||
typedef struct StrAux {
|
||||
int isstring; /* whether capture is a string */
|
||||
union {
|
||||
Capture *cp; /* if not a string, respective capture */
|
||||
struct { /* if it is a string... */
|
||||
Index_t idx; /* starts here */
|
||||
Index_t siz; /* with this size */
|
||||
} s;
|
||||
} u;
|
||||
} StrAux;
|
||||
|
||||
#define MAXSTRCAPS 10
|
||||
|
||||
/*
|
||||
** Collect values from current capture into array 'cps'. Current
|
||||
** capture must be Cstring (first call) or Csimple (recursive calls).
|
||||
** (In first call, fills %0 with whole match for Cstring.)
|
||||
** Returns number of elements in the array that were filled.
|
||||
*/
|
||||
static int getstrcaps (CapState *cs, StrAux *cps, int n) {
|
||||
int k = n++;
|
||||
Capture *head = cs->cap++;
|
||||
cps[k].isstring = 1; /* get string value */
|
||||
cps[k].u.s.idx = head->index; /* starts here */
|
||||
while (capinside(head, cs->cap)) {
|
||||
if (n >= MAXSTRCAPS) /* too many captures? */
|
||||
nextcap(cs); /* skip extra captures (will not need them) */
|
||||
else if (captype(cs->cap) == Csimple) /* string? */
|
||||
n = getstrcaps(cs, cps, n); /* put info. into array */
|
||||
else {
|
||||
cps[n].isstring = 0; /* not a string */
|
||||
cps[n].u.cp = cs->cap; /* keep original capture */
|
||||
nextcap(cs);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
cps[k].u.s.siz = closesize(cs, head);
|
||||
skipclose(cs, head);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** add next capture value (which should be a string) to buffer 'b'
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
|
||||
|
||||
|
||||
/*
|
||||
** String capture: add result to buffer 'b' (instead of pushing
|
||||
** it into the stack)
|
||||
*/
|
||||
static void stringcap (luaL_Buffer *b, CapState *cs) {
|
||||
StrAux cps[MAXSTRCAPS];
|
||||
int n;
|
||||
size_t len, i;
|
||||
const char *fmt; /* format string */
|
||||
fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
|
||||
n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
|
||||
for (i = 0; i < len; i++) { /* traverse format string */
|
||||
if (fmt[i] != '%') /* not an escape? */
|
||||
luaL_addchar(b, fmt[i]); /* add it to buffer */
|
||||
else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
|
||||
luaL_addchar(b, fmt[i]); /* add to buffer */
|
||||
else {
|
||||
int l = fmt[i] - '0'; /* capture index */
|
||||
if (l > n)
|
||||
luaL_error(cs->L, "invalid capture index (%d)", l);
|
||||
else if (cps[l].isstring)
|
||||
luaL_addlstring(b, cs->s + cps[l].u.s.idx, cps[l].u.s.siz);
|
||||
else {
|
||||
Capture *curr = cs->cap;
|
||||
cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
|
||||
if (!addonestring(b, cs, "capture"))
|
||||
luaL_error(cs->L, "no values in capture index %d", l);
|
||||
cs->cap = curr; /* continue from where it stopped */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Substitution capture: add result to buffer 'b'
|
||||
*/
|
||||
static void substcap (luaL_Buffer *b, CapState *cs) {
|
||||
const char *curr = cs->s + cs->cap->index;
|
||||
Capture *head = cs->cap++;
|
||||
while (capinside(head, cs->cap)) {
|
||||
Capture *cap = cs->cap;
|
||||
const char *caps = cs->s + cap->index;
|
||||
luaL_addlstring(b, curr, caps - curr); /* add text up to capture */
|
||||
if (addonestring(b, cs, "replacement"))
|
||||
curr = caps + capsize(cap, cs->cap - 1); /* continue after match */
|
||||
else /* no capture value */
|
||||
curr = caps; /* keep original text in final result */
|
||||
}
|
||||
/* add last piece of text */
|
||||
luaL_addlstring(b, curr, cs->s + head->index + closesize(cs, head) - curr);
|
||||
skipclose(cs, head);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Evaluates a capture and adds its first value to buffer 'b'; returns
|
||||
** whether there was a value
|
||||
*/
|
||||
static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
|
||||
switch (captype(cs->cap)) {
|
||||
case Cstring:
|
||||
stringcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
case Csubst:
|
||||
substcap(b, cs); /* add capture directly to buffer */
|
||||
return 1;
|
||||
case Cacc: /* accumulator capture? */
|
||||
return luaL_error(cs->L, "invalid context for an accumulator capture");
|
||||
default: {
|
||||
lua_State *L = cs->L;
|
||||
int n = pushcapture(cs);
|
||||
if (n > 0) {
|
||||
if (n > 1) lua_pop(L, n - 1); /* only one result */
|
||||
if (!lua_isstring(L, -1))
|
||||
return luaL_error(L, "invalid %s value (a %s)",
|
||||
what, luaL_typename(L, -1));
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(MAXRECLEVEL)
|
||||
#define MAXRECLEVEL 200
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Push all values of the current capture into the stack; returns
|
||||
** number of values pushed
|
||||
*/
|
||||
static int pushcapture (CapState *cs) {
|
||||
lua_State *L = cs->L;
|
||||
int res;
|
||||
luaL_checkstack(L, 4, "too many captures");
|
||||
if (cs->reclevel++ > MAXRECLEVEL)
|
||||
return luaL_error(L, "subcapture nesting too deep");
|
||||
switch (captype(cs->cap)) {
|
||||
case Cposition: {
|
||||
lua_pushinteger(L, cs->cap->index + 1);
|
||||
cs->cap++;
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Cconst: {
|
||||
pushluaval(cs);
|
||||
cs->cap++;
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Carg: {
|
||||
int arg = (cs->cap++)->idx;
|
||||
if (arg + FIXEDARGS > cs->ptop)
|
||||
return luaL_error(L, "reference to absent extra argument #%d", arg);
|
||||
lua_pushvalue(L, arg + FIXEDARGS);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Csimple: {
|
||||
int k = pushnestedvalues(cs, 1);
|
||||
lua_insert(L, -k); /* make whole match be first result */
|
||||
res = k;
|
||||
break;
|
||||
}
|
||||
case Cruntime: {
|
||||
lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Cstring: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
stringcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Csubst: {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
substcap(&b, cs);
|
||||
luaL_pushresult(&b);
|
||||
res = 1;
|
||||
break;
|
||||
}
|
||||
case Cgroup: {
|
||||
if (cs->cap->idx == 0) /* anonymous group? */
|
||||
res = pushnestedvalues(cs, 0); /* add all nested values */
|
||||
else { /* named group: add no values */
|
||||
nextcap(cs); /* skip capture */
|
||||
res = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Cbackref: res = backrefcap(cs); break;
|
||||
case Ctable: res = tablecap(cs); break;
|
||||
case Cfunction: res = functioncap(cs); break;
|
||||
case Cacc: res = accumulatorcap(cs); break;
|
||||
case Cnum: res = numcap(cs); break;
|
||||
case Cquery: res = querycap(cs); break;
|
||||
case Cfold: res = foldcap(cs); break;
|
||||
default: assert(0); res = 0;
|
||||
}
|
||||
cs->reclevel--;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a CapState structure and traverse the entire list of
|
||||
** captures in the stack pushing its results. 's' is the subject
|
||||
** string, 'r' is the final position of the match, and 'ptop'
|
||||
** the index in the stack where some useful values were pushed.
|
||||
** Returns the number of results pushed. (If the list produces no
|
||||
** results, push the final position of the match.)
|
||||
*/
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
|
||||
Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
|
||||
int n = 0;
|
||||
/* printcaplist(capture); */
|
||||
if (!isclosecap(capture)) { /* is there any capture? */
|
||||
CapState cs;
|
||||
cs.ocap = cs.cap = capture; cs.L = L; cs.reclevel = 0;
|
||||
cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
|
||||
cs.firstcap = lua_gettop(L) + 1; /* where first value (if any) will go */
|
||||
do { /* collect their values */
|
||||
n += pushcapture(&cs);
|
||||
} while (!isclosecap(cs.cap));
|
||||
assert(lua_gettop(L) - cs.firstcap == n - 1);
|
||||
}
|
||||
if (n == 0) { /* no capture values? */
|
||||
lua_pushinteger(L, r - s + 1); /* return only end position */
|
||||
n = 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
86
src/lua/lpeg-1.1.0/lpcap.h
Normal file
86
src/lua/lpeg-1.1.0/lpcap.h
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
#if !defined(lpcap_h)
|
||||
#define lpcap_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/* kinds of captures */
|
||||
typedef enum CapKind {
|
||||
Cclose, /* not used in trees */
|
||||
Cposition,
|
||||
Cconst, /* ktable[key] is Lua constant */
|
||||
Cbackref, /* ktable[key] is "name" of group to get capture */
|
||||
Carg, /* 'key' is arg's number */
|
||||
Csimple, /* next node is pattern */
|
||||
Ctable, /* next node is pattern */
|
||||
Cfunction, /* ktable[key] is function; next node is pattern */
|
||||
Cacc, /* ktable[key] is function; next node is pattern */
|
||||
Cquery, /* ktable[key] is table; next node is pattern */
|
||||
Cstring, /* ktable[key] is string; next node is pattern */
|
||||
Cnum, /* numbered capture; 'key' is number of value to return */
|
||||
Csubst, /* substitution capture; next node is pattern */
|
||||
Cfold, /* ktable[key] is function; next node is pattern */
|
||||
Cruntime, /* not used in trees (is uses another type for tree) */
|
||||
Cgroup /* ktable[key] is group's "name" */
|
||||
} CapKind;
|
||||
|
||||
|
||||
/*
|
||||
** An unsigned integer large enough to index any subject entirely.
|
||||
** It can be size_t, but that will double the size of the array
|
||||
** of captures in a 64-bit machine.
|
||||
*/
|
||||
#if !defined(Index_t)
|
||||
typedef uint Index_t;
|
||||
#endif
|
||||
|
||||
#define MAXINDT (~(Index_t)0)
|
||||
|
||||
|
||||
typedef struct Capture {
|
||||
Index_t index; /* subject position */
|
||||
unsigned short idx; /* extra info (group name, arg index, etc.) */
|
||||
byte kind; /* kind of capture */
|
||||
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
||||
} Capture;
|
||||
|
||||
|
||||
typedef struct CapState {
|
||||
Capture *cap; /* current capture */
|
||||
Capture *ocap; /* (original) capture list */
|
||||
lua_State *L;
|
||||
int ptop; /* stack index of last argument to 'match' */
|
||||
int firstcap; /* stack index of first capture pushed in the stack */
|
||||
const char *s; /* original string */
|
||||
int valuecached; /* value stored in cache slot */
|
||||
int reclevel; /* recursion level */
|
||||
} CapState;
|
||||
|
||||
|
||||
#define captype(cap) ((cap)->kind)
|
||||
|
||||
#define isclosecap(cap) (captype(cap) == Cclose)
|
||||
#define isopencap(cap) ((cap)->siz == 0)
|
||||
|
||||
/* true if c2 is (any number of levels) inside c1 */
|
||||
#define capinside(c1,c2) \
|
||||
(isopencap(c1) ? !isclosecap(c2) \
|
||||
: (c2)->index < (c1)->index + (c1)->siz - 1)
|
||||
|
||||
|
||||
/**
|
||||
** Maximum number of captures to visit when looking for an 'open'.
|
||||
*/
|
||||
#define MAXLOP 20
|
||||
|
||||
|
||||
|
||||
int runtimecap (CapState *cs, Capture *close, const char *s, int *rem);
|
||||
int getcaptures (lua_State *L, const char *s, const char *r, int ptop);
|
||||
int finddyncap (Capture *cap, Capture *last);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1051
src/lua/lpeg-1.1.0/lpcode.c
Normal file
1051
src/lua/lpeg-1.1.0/lpcode.c
Normal file
File diff suppressed because it is too large
Load Diff
36
src/lua/lpeg-1.1.0/lpcode.h
Normal file
36
src/lua/lpeg-1.1.0/lpcode.h
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
#if !defined(lpcode_h)
|
||||
#define lpcode_h
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
int checkaux (TTree *tree, int pred);
|
||||
int fixedlen (TTree *tree);
|
||||
int hascaptures (TTree *tree);
|
||||
int lp_gc (lua_State *L);
|
||||
Instruction *compile (lua_State *L, Pattern *p, uint size);
|
||||
void freecode (lua_State *L, Pattern *p);
|
||||
int sizei (const Instruction *i);
|
||||
|
||||
|
||||
#define PEnullable 0
|
||||
#define PEnofail 1
|
||||
|
||||
/*
|
||||
** nofail(t) implies that 't' cannot fail with any input
|
||||
*/
|
||||
#define nofail(t) checkaux(t, PEnofail)
|
||||
|
||||
/*
|
||||
** (not nullable(t)) implies 't' cannot match without consuming
|
||||
** something
|
||||
*/
|
||||
#define nullable(t) checkaux(t, PEnullable)
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
110
src/lua/lpeg-1.1.0/lpcset.c
Normal file
110
src/lua/lpeg-1.1.0/lpcset.c
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpcset.h"
|
||||
|
||||
|
||||
/*
|
||||
** Add to 'c' the index of the (only) bit set in byte 'b'
|
||||
*/
|
||||
static int onlybit (int c, int b) {
|
||||
if ((b & 0xF0) != 0) { c += 4; b >>= 4; }
|
||||
if ((b & 0x0C) != 0) { c += 2; b >>= 2; }
|
||||
if ((b & 0x02) != 0) { c += 1; }
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether a charset is empty (returns IFail), singleton (IChar),
|
||||
** full (IAny), or none of those (ISet). When singleton, 'info.offset'
|
||||
** returns which character it is. When generic set, 'info' returns
|
||||
** information about its range.
|
||||
*/
|
||||
Opcode charsettype (const byte *cs, charsetinfo *info) {
|
||||
int low0, low1, high0, high1;
|
||||
for (low1 = 0; low1 < CHARSETSIZE && cs[low1] == 0; low1++)
|
||||
/* find lowest byte with a 1-bit */;
|
||||
if (low1 == CHARSETSIZE)
|
||||
return IFail; /* no characters in set */
|
||||
for (high1 = CHARSETSIZE - 1; cs[high1] == 0; high1--)
|
||||
/* find highest byte with a 1-bit; low1 is a sentinel */;
|
||||
if (low1 == high1) { /* only one byte with 1-bits? */
|
||||
int b = cs[low1];
|
||||
if ((b & (b - 1)) == 0) { /* does byte has only one 1-bit? */
|
||||
info->offset = onlybit(low1 * BITSPERCHAR, b); /* get that bit */
|
||||
return IChar; /* single character */
|
||||
}
|
||||
}
|
||||
for (low0 = 0; low0 < CHARSETSIZE && cs[low0] == 0xFF; low0++)
|
||||
/* find lowest byte with a 0-bit */;
|
||||
if (low0 == CHARSETSIZE)
|
||||
return IAny; /* set has all bits set */
|
||||
for (high0 = CHARSETSIZE - 1; cs[high0] == 0xFF; high0--)
|
||||
/* find highest byte with a 0-bit; low0 is a sentinel */;
|
||||
if (high1 - low1 <= high0 - low0) { /* range of 1s smaller than of 0s? */
|
||||
info->offset = low1;
|
||||
info->size = high1 - low1 + 1;
|
||||
info->deflt = 0; /* all discharged bits were 0 */
|
||||
}
|
||||
else {
|
||||
info->offset = low0;
|
||||
info->size = high0 - low0 + 1;
|
||||
info->deflt = 0xFF; /* all discharged bits were 1 */
|
||||
}
|
||||
info->cs = cs + info->offset;
|
||||
return ISet;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get a byte from a compact charset. If index is inside the charset
|
||||
** range, get the byte from the supporting charset (correcting it
|
||||
** by the offset). Otherwise, return the default for the set.
|
||||
*/
|
||||
byte getbytefromcharset (const charsetinfo *info, int index) {
|
||||
if (index < info->size)
|
||||
return info->cs[index];
|
||||
else return info->deflt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If 'tree' is a 'char' pattern (TSet, TChar, TAny, TFalse), convert it
|
||||
** into a charset and return 1; else return 0.
|
||||
*/
|
||||
int tocharset (TTree *tree, Charset *cs) {
|
||||
switch (tree->tag) {
|
||||
case TChar: { /* only one char */
|
||||
assert(0 <= tree->u.n && tree->u.n <= UCHAR_MAX);
|
||||
clearset(cs->cs); /* erase all chars */
|
||||
setchar(cs->cs, tree->u.n); /* add that one */
|
||||
return 1;
|
||||
}
|
||||
case TAny: {
|
||||
fillset(cs->cs, 0xFF); /* add all characters to the set */
|
||||
return 1;
|
||||
}
|
||||
case TFalse: {
|
||||
clearset(cs->cs); /* empty set */
|
||||
return 1;
|
||||
}
|
||||
case TSet: { /* fill set */
|
||||
int i;
|
||||
fillset(cs->cs, tree->u.set.deflt);
|
||||
for (i = 0; i < tree->u.set.size; i++)
|
||||
cs->cs[tree->u.set.offset + i] = treebuffer(tree)[i];
|
||||
return 1;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tree2cset (TTree *tree, charsetinfo *info) {
|
||||
assert(tree->tag == TSet);
|
||||
info->offset = tree->u.set.offset;
|
||||
info->size = tree->u.set.size;
|
||||
info->deflt = tree->u.set.deflt;
|
||||
info->cs = treebuffer(tree);
|
||||
}
|
||||
|
||||
30
src/lua/lpeg-1.1.0/lpcset.h
Normal file
30
src/lua/lpeg-1.1.0/lpcset.h
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
#if !defined(lpset_h)
|
||||
#define lpset_h
|
||||
|
||||
#include "lpcset.h"
|
||||
#include "lpcode.h"
|
||||
#include "lptree.h"
|
||||
|
||||
|
||||
/*
|
||||
** Extra information for the result of 'charsettype'. When result is
|
||||
** IChar, 'offset' is the character. When result is ISet, 'cs' is the
|
||||
** supporting bit array (with offset included), 'offset' is the offset
|
||||
** (in bytes), 'size' is the size (in bytes), and 'delt' is the default
|
||||
** value for bytes outside the set.
|
||||
*/
|
||||
typedef struct {
|
||||
const byte *cs;
|
||||
int offset;
|
||||
int size;
|
||||
int deflt;
|
||||
} charsetinfo;
|
||||
|
||||
|
||||
int tocharset (TTree *tree, Charset *cs);
|
||||
Opcode charsettype (const byte *cs, charsetinfo *info);
|
||||
byte getbytefromcharset (const charsetinfo *info, int index);
|
||||
void tree2cset (TTree *tree, charsetinfo *info);
|
||||
|
||||
#endif
|
||||
BIN
src/lua/lpeg-1.1.0/lpeg-128.gif
Normal file
BIN
src/lua/lpeg-1.1.0/lpeg-128.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
1430
src/lua/lpeg-1.1.0/lpeg.html
Normal file
1430
src/lua/lpeg-1.1.0/lpeg.html
Normal file
File diff suppressed because it is too large
Load Diff
298
src/lua/lpeg-1.1.0/lpprint.c
Normal file
298
src/lua/lpeg-1.1.0/lpprint.c
Normal file
@@ -0,0 +1,298 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
#include "lpprint.h"
|
||||
#include "lpcode.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing patterns (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
void printcharset (const byte *st) {
|
||||
int i;
|
||||
printf("[");
|
||||
for (i = 0; i <= UCHAR_MAX; i++) {
|
||||
int first = i;
|
||||
while (i <= UCHAR_MAX && testchar(st, i)) i++;
|
||||
if (i - 1 == first) /* unary range? */
|
||||
printf("(%02x)", first);
|
||||
else if (i - 1 > first) /* non-empty range? */
|
||||
printf("(%02x-%02x)", first, i - 1);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
|
||||
|
||||
static void printIcharset (const Instruction *inst, const byte *buff) {
|
||||
byte cs[CHARSETSIZE];
|
||||
int i;
|
||||
printf("(%02x-%d) ", inst->i.aux2.set.offset, inst->i.aux2.set.size);
|
||||
clearset(cs);
|
||||
for (i = 0; i < CHARSETSIZE * 8; i++) {
|
||||
if (charinset(inst, buff, i))
|
||||
setchar(cs, i);
|
||||
}
|
||||
printcharset(cs);
|
||||
}
|
||||
|
||||
|
||||
static void printTcharset (TTree *tree) {
|
||||
byte cs[CHARSETSIZE];
|
||||
int i;
|
||||
printf("(%02x-%d) ", tree->u.set.offset, tree->u.set.size);
|
||||
fillset(cs, tree->u.set.deflt);
|
||||
for (i = 0; i < tree->u.set.size; i++)
|
||||
cs[tree->u.set.offset + i] = treebuffer(tree)[i];
|
||||
printcharset(cs);
|
||||
}
|
||||
|
||||
|
||||
static const char *capkind (int kind) {
|
||||
const char *const modes[] = {
|
||||
"close", "position", "constant", "backref",
|
||||
"argument", "simple", "table", "function", "accumulator",
|
||||
"query", "string", "num", "substitution", "fold",
|
||||
"runtime", "group"};
|
||||
return modes[kind];
|
||||
}
|
||||
|
||||
|
||||
static void printjmp (const Instruction *op, const Instruction *p) {
|
||||
printf("-> %d", (int)(p + (p + 1)->offset - op));
|
||||
}
|
||||
|
||||
|
||||
void printinst (const Instruction *op, const Instruction *p) {
|
||||
const char *const names[] = {
|
||||
"any", "char", "set",
|
||||
"testany", "testchar", "testset",
|
||||
"span", "utf-range", "behind",
|
||||
"ret", "end",
|
||||
"choice", "jmp", "call", "open_call",
|
||||
"commit", "partial_commit", "back_commit", "failtwice", "fail", "giveup",
|
||||
"fullcapture", "opencapture", "closecapture", "closeruntime",
|
||||
"--"
|
||||
};
|
||||
printf("%02ld: %s ", (long)(p - op), names[p->i.code]);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IChar: {
|
||||
printf("'%c' (%02x)", p->i.aux1, p->i.aux1);
|
||||
break;
|
||||
}
|
||||
case ITestChar: {
|
||||
printf("'%c' (%02x)", p->i.aux1, p->i.aux1); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case IUTFR: {
|
||||
printf("%d - %d", p[1].offset, utf_to(p));
|
||||
break;
|
||||
}
|
||||
case IFullCapture: {
|
||||
printf("%s (size = %d) (idx = %d)",
|
||||
capkind(getkind(p)), getoff(p), p->i.aux2.key);
|
||||
break;
|
||||
}
|
||||
case IOpenCapture: {
|
||||
printf("%s (idx = %d)", capkind(getkind(p)), p->i.aux2.key);
|
||||
break;
|
||||
}
|
||||
case ISet: {
|
||||
printIcharset(p, (p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case ITestSet: {
|
||||
printIcharset(p, (p+2)->buff); printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
case ISpan: {
|
||||
printIcharset(p, (p+1)->buff);
|
||||
break;
|
||||
}
|
||||
case IOpenCall: {
|
||||
printf("-> %d", (p + 1)->offset);
|
||||
break;
|
||||
}
|
||||
case IBehind: {
|
||||
printf("%d", p->i.aux1);
|
||||
break;
|
||||
}
|
||||
case IJmp: case ICall: case ICommit: case IChoice:
|
||||
case IPartialCommit: case IBackCommit: case ITestAny: {
|
||||
printjmp(op, p);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
void printpatt (Instruction *p) {
|
||||
Instruction *op = p;
|
||||
uint n = op[-1].codesize - 1;
|
||||
while (p < op + n) {
|
||||
printinst(op, p);
|
||||
p += sizei(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void printcap (Capture *cap, int ident) {
|
||||
while (ident--) printf(" ");
|
||||
printf("%s (idx: %d - size: %d) -> %lu (%p)\n",
|
||||
capkind(cap->kind), cap->idx, cap->siz, (long)cap->index, (void*)cap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Print a capture and its nested captures
|
||||
*/
|
||||
static Capture *printcap2close (Capture *cap, int ident) {
|
||||
Capture *head = cap++;
|
||||
printcap(head, ident); /* print head capture */
|
||||
while (capinside(head, cap))
|
||||
cap = printcap2close(cap, ident + 2); /* print nested captures */
|
||||
if (isopencap(head)) {
|
||||
assert(isclosecap(cap));
|
||||
printcap(cap++, ident); /* print and skip close capture */
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
|
||||
void printcaplist (Capture *cap) {
|
||||
{ /* for debugging, print first a raw list of captures */
|
||||
Capture *c = cap;
|
||||
while (c->index != MAXINDT) { printcap(c, 0); c++; }
|
||||
}
|
||||
printf(">======\n");
|
||||
while (!isclosecap(cap))
|
||||
cap = printcap2close(cap, 0);
|
||||
printf("=======\n");
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Printing trees (for debugging)
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static const char *tagnames[] = {
|
||||
"char", "set", "any",
|
||||
"true", "false", "utf8.range",
|
||||
"rep",
|
||||
"seq", "choice",
|
||||
"not", "and",
|
||||
"call", "opencall", "rule", "xinfo", "grammar",
|
||||
"behind",
|
||||
"capture", "run-time"
|
||||
};
|
||||
|
||||
|
||||
void printtree (TTree *tree, int ident) {
|
||||
int i;
|
||||
int sibs = numsiblings[tree->tag];
|
||||
for (i = 0; i < ident; i++) printf(" ");
|
||||
printf("%s", tagnames[tree->tag]);
|
||||
switch (tree->tag) {
|
||||
case TChar: {
|
||||
int c = tree->u.n;
|
||||
if (isprint(c))
|
||||
printf(" '%c'\n", c);
|
||||
else
|
||||
printf(" (%02X)\n", c);
|
||||
break;
|
||||
}
|
||||
case TSet: {
|
||||
printTcharset(tree);
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
case TUTFR: {
|
||||
assert(sib1(tree)->tag == TXInfo);
|
||||
printf(" %d (%02x %d) - %d (%02x %d) \n",
|
||||
tree->u.n, tree->key, tree->cap,
|
||||
sib1(tree)->u.n, sib1(tree)->key, sib1(tree)->cap);
|
||||
break;
|
||||
}
|
||||
case TOpenCall: case TCall: {
|
||||
assert(sib1(sib2(tree))->tag == TXInfo);
|
||||
printf(" key: %d (rule: %d)\n", tree->key, sib1(sib2(tree))->u.n);
|
||||
break;
|
||||
}
|
||||
case TBehind: {
|
||||
printf(" %d\n", tree->u.n);
|
||||
break;
|
||||
}
|
||||
case TCapture: {
|
||||
printf(" kind: '%s' key: %d\n", capkind(tree->cap), tree->key);
|
||||
break;
|
||||
}
|
||||
case TRule: {
|
||||
printf(" key: %d\n", tree->key);
|
||||
sibs = 1; /* do not print 'sib2' (next rule) as a sibling */
|
||||
break;
|
||||
}
|
||||
case TXInfo: {
|
||||
printf(" n: %d\n", tree->u.n);
|
||||
break;
|
||||
}
|
||||
case TGrammar: {
|
||||
TTree *rule = sib1(tree);
|
||||
printf(" %d\n", tree->u.n); /* number of rules */
|
||||
for (i = 0; i < tree->u.n; i++) {
|
||||
printtree(rule, ident + 2);
|
||||
rule = sib2(rule);
|
||||
}
|
||||
assert(rule->tag == TTrue); /* sentinel */
|
||||
sibs = 0; /* siblings already handled */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
if (sibs >= 1) {
|
||||
printtree(sib1(tree), ident + 2);
|
||||
if (sibs >= 2)
|
||||
printtree(sib2(tree), ident + 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printktable (lua_State *L, int idx) {
|
||||
int n, i;
|
||||
lua_getuservalue(L, idx);
|
||||
if (lua_isnil(L, -1)) /* no ktable? */
|
||||
return;
|
||||
n = lua_rawlen(L, -1);
|
||||
printf("[");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%d = ", i);
|
||||
lua_rawgeti(L, -1, i);
|
||||
if (lua_isstring(L, -1))
|
||||
printf("%s ", lua_tostring(L, -1));
|
||||
else
|
||||
printf("%s ", lua_typename(L, lua_type(L, -1)));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
printf("]\n");
|
||||
/* leave ktable at the stack */
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
#endif
|
||||
32
src/lua/lpeg-1.1.0/lpprint.h
Normal file
32
src/lua/lpeg-1.1.0/lpprint.h
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
#if !defined(lpprint_h)
|
||||
#define lpprint_h
|
||||
|
||||
|
||||
#include "lptree.h"
|
||||
#include "lpvm.h"
|
||||
|
||||
|
||||
#if defined(LPEG_DEBUG)
|
||||
|
||||
void printpatt (Instruction *p);
|
||||
void printtree (TTree *tree, int ident);
|
||||
void printktable (lua_State *L, int idx);
|
||||
void printcharset (const byte *st);
|
||||
void printcaplist (Capture *cap);
|
||||
void printinst (const Instruction *op, const Instruction *p);
|
||||
|
||||
#else
|
||||
|
||||
#define printktable(L,idx) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printtree(tree,i) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
#define printpatt(p) \
|
||||
luaL_error(L, "function only implemented in debug mode")
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1399
src/lua/lpeg-1.1.0/lptree.c
Normal file
1399
src/lua/lpeg-1.1.0/lptree.c
Normal file
File diff suppressed because it is too large
Load Diff
92
src/lua/lpeg-1.1.0/lptree.h
Normal file
92
src/lua/lpeg-1.1.0/lptree.h
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
#if !defined(lptree_h)
|
||||
#define lptree_h
|
||||
|
||||
|
||||
#include "lptypes.h"
|
||||
|
||||
|
||||
/*
|
||||
** types of trees
|
||||
*/
|
||||
typedef enum TTag {
|
||||
TChar = 0, /* 'n' = char */
|
||||
TSet, /* the set is encoded in 'u.set' and the next 'u.set.size' bytes */
|
||||
TAny,
|
||||
TTrue,
|
||||
TFalse,
|
||||
TUTFR, /* range of UTF-8 codepoints; 'n' has initial codepoint;
|
||||
'cap' has length; 'key' has first byte;
|
||||
extra info is similar for end codepoint */
|
||||
TRep, /* 'sib1'* */
|
||||
TSeq, /* 'sib1' 'sib2' */
|
||||
TChoice, /* 'sib1' / 'sib2' */
|
||||
TNot, /* !'sib1' */
|
||||
TAnd, /* &'sib1' */
|
||||
TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */
|
||||
TOpenCall, /* ktable[key] is rule's key */
|
||||
TRule, /* ktable[key] is rule's key (but key == 0 for unused rules);
|
||||
'sib1' is rule's pattern pre-rule; 'sib2' is next rule;
|
||||
extra info 'n' is rule's sequential number */
|
||||
TXInfo, /* extra info */
|
||||
TGrammar, /* 'sib1' is initial (and first) rule */
|
||||
TBehind, /* 'sib1' is pattern, 'n' is how much to go back */
|
||||
TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind');
|
||||
ktable[key] is Lua value associated with capture;
|
||||
'sib1' is capture body */
|
||||
TRunTime /* run-time capture: 'key' is Lua function;
|
||||
'sib1' is capture body */
|
||||
} TTag;
|
||||
|
||||
|
||||
/*
|
||||
** Tree trees
|
||||
** The first child of a tree (if there is one) is immediately after
|
||||
** the tree. A reference to a second child (ps) is its position
|
||||
** relative to the position of the tree itself.
|
||||
*/
|
||||
typedef struct TTree {
|
||||
byte tag;
|
||||
byte cap; /* kind of capture (if it is a capture) */
|
||||
unsigned short key; /* key in ktable for Lua data (0 if no key) */
|
||||
union {
|
||||
int ps; /* occasional second child */
|
||||
int n; /* occasional counter */
|
||||
struct {
|
||||
byte offset; /* compact set offset (in bytes) */
|
||||
byte size; /* compact set size (in bytes) */
|
||||
byte deflt; /* default value */
|
||||
byte bitmap[1]; /* bitmap (open array) */
|
||||
} set; /* for compact sets */
|
||||
} u;
|
||||
} TTree;
|
||||
|
||||
|
||||
/* access to charset */
|
||||
#define treebuffer(t) ((t)->u.set.bitmap)
|
||||
|
||||
|
||||
/*
|
||||
** A complete pattern has its tree plus, if already compiled,
|
||||
** its corresponding code
|
||||
*/
|
||||
typedef struct Pattern {
|
||||
union Instruction *code;
|
||||
TTree tree[1];
|
||||
} Pattern;
|
||||
|
||||
|
||||
/* number of children for each tree */
|
||||
extern const byte numsiblings[];
|
||||
|
||||
/* access to children */
|
||||
#define sib1(t) ((t) + 1)
|
||||
#define sib2(t) ((t) + (t)->u.ps)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
150
src/lua/lpeg-1.1.0/lptypes.h
Normal file
150
src/lua/lpeg-1.1.0/lptypes.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
** LPeg - PEG pattern matching for Lua
|
||||
** Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
** written by Roberto Ierusalimschy
|
||||
*/
|
||||
|
||||
#if !defined(lptypes_h)
|
||||
#define lptypes_h
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
#define VERSION "1.1.0"
|
||||
|
||||
|
||||
#define PATTERN_T "lpeg-pattern"
|
||||
#define MAXSTACKIDX "lpeg-maxstack"
|
||||
|
||||
|
||||
/*
|
||||
** compatibility with Lua 5.1
|
||||
*/
|
||||
#if (LUA_VERSION_NUM == 501)
|
||||
|
||||
#define lp_equal lua_equal
|
||||
|
||||
#define lua_getuservalue lua_getfenv
|
||||
#define lua_setuservalue lua_setfenv
|
||||
|
||||
#define lua_rawlen lua_objlen
|
||||
|
||||
#define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f)
|
||||
#define luaL_newlib(L,f) luaL_register(L,"lpeg",f)
|
||||
|
||||
typedef size_t lua_Unsigned;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(lp_equal)
|
||||
#define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
|
||||
#endif
|
||||
|
||||
|
||||
/* default maximum size for call/backtrack stack */
|
||||
#if !defined(MAXBACK)
|
||||
#define MAXBACK 400
|
||||
#endif
|
||||
|
||||
|
||||
/* maximum number of rules in a grammar (limited by 'unsigned short') */
|
||||
#if !defined(MAXRULES)
|
||||
#define MAXRULES 1000
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* initial size for capture's list */
|
||||
#define INITCAPSIZE 32
|
||||
|
||||
|
||||
/* index, on Lua stack, for subject */
|
||||
#define SUBJIDX 2
|
||||
|
||||
/* number of fixed arguments to 'match' (before capture arguments) */
|
||||
#define FIXEDARGS 3
|
||||
|
||||
/* index, on Lua stack, for capture list */
|
||||
#define caplistidx(ptop) ((ptop) + 2)
|
||||
|
||||
/* index, on Lua stack, for pattern's ktable */
|
||||
#define ktableidx(ptop) ((ptop) + 3)
|
||||
|
||||
/* index, on Lua stack, for backtracking stack */
|
||||
#define stackidx(ptop) ((ptop) + 4)
|
||||
|
||||
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
|
||||
#define BITSPERCHAR 8
|
||||
|
||||
#define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1)
|
||||
|
||||
|
||||
|
||||
typedef struct Charset {
|
||||
byte cs[CHARSETSIZE];
|
||||
} Charset;
|
||||
|
||||
|
||||
|
||||
#define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} }
|
||||
|
||||
#define fillset(s,c) memset(s,c,CHARSETSIZE)
|
||||
#define clearset(s) fillset(s,0)
|
||||
|
||||
/* number of slots needed for 'n' bytes */
|
||||
#define bytes2slots(n) (((n) - 1u) / (uint)sizeof(TTree) + 1u)
|
||||
|
||||
/* set 'b' bit in charset 'cs' */
|
||||
#define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7)))
|
||||
|
||||
|
||||
/*
|
||||
** in capture instructions, 'kind' of capture and its offset are
|
||||
** packed in field 'aux', 4 bits for each
|
||||
*/
|
||||
#define getkind(op) ((op)->i.aux1 & 0xF)
|
||||
#define getoff(op) (((op)->i.aux1 >> 4) & 0xF)
|
||||
#define joinkindoff(k,o) ((k) | ((o) << 4))
|
||||
|
||||
#define MAXOFF 0xF
|
||||
#define MAXAUX 0xFF
|
||||
|
||||
|
||||
/* maximum number of bytes to look behind */
|
||||
#define MAXBEHIND MAXAUX
|
||||
|
||||
|
||||
/* maximum size (in elements) for a pattern */
|
||||
#define MAXPATTSIZE (SHRT_MAX - 10)
|
||||
|
||||
|
||||
/* size (in instructions) for l bytes (l > 0) */
|
||||
#define instsize(l) ((int)(((l) + (uint)sizeof(Instruction) - 1u) \
|
||||
/ (uint)sizeof(Instruction)))
|
||||
|
||||
|
||||
/* size (in elements) for a ISet instruction */
|
||||
#define CHARSETINSTSIZE (1 + instsize(CHARSETSIZE))
|
||||
|
||||
/* size (in elements) for a IFunc instruction */
|
||||
#define funcinstsize(p) ((p)->i.aux + 2)
|
||||
|
||||
|
||||
|
||||
#define testchar(st,c) ((((uint)(st)[((c) >> 3)]) >> ((c) & 7)) & 1)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
455
src/lua/lpeg-1.1.0/lpvm.c
Normal file
455
src/lua/lpeg-1.1.0/lpvm.c
Normal file
@@ -0,0 +1,455 @@
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lpcap.h"
|
||||
#include "lptypes.h"
|
||||
#include "lpvm.h"
|
||||
#include "lpprint.h"
|
||||
|
||||
|
||||
/* initial size for call/backtrack stack */
|
||||
#if !defined(INITBACK)
|
||||
#define INITBACK MAXBACK
|
||||
#endif
|
||||
|
||||
|
||||
#define getoffset(p) (((p) + 1)->offset)
|
||||
|
||||
static const Instruction giveup = {{IGiveup, 0, {0}}};
|
||||
|
||||
|
||||
int charinset (const Instruction *i, const byte *buff, uint c) {
|
||||
c -= i->i.aux2.set.offset;
|
||||
if (c >= ((uint)i->i.aux2.set.size /* size in instructions... */
|
||||
* (uint)sizeof(Instruction) /* in bytes... */
|
||||
* 8u)) /* in bits */
|
||||
return i->i.aux1; /* out of range; return default value */
|
||||
return testchar(buff, c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
|
||||
*/
|
||||
static const char *utf8_decode (const char *o, int *val) {
|
||||
static const uint limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFFu};
|
||||
const unsigned char *s = (const unsigned char *)o;
|
||||
uint c = s[0]; /* first byte */
|
||||
uint res = 0; /* final result */
|
||||
if (c < 0x80) /* ascii? */
|
||||
res = c;
|
||||
else {
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
while (c & 0x40) { /* still have continuation bytes? */
|
||||
int cc = s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
c <<= 1; /* to test next bit */
|
||||
}
|
||||
res |= (c & 0x7F) << (count * 5); /* add first byte */
|
||||
if (count > 3 || res > 0x10FFFFu || res <= limits[count])
|
||||
return NULL; /* invalid byte sequence */
|
||||
s += count; /* skip continuation bytes read */
|
||||
}
|
||||
*val = res;
|
||||
return (const char *)s + 1; /* +1 to include first byte */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Virtual Machine
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
typedef struct Stack {
|
||||
const char *s; /* saved position (or NULL for calls) */
|
||||
const Instruction *p; /* next instruction */
|
||||
int caplevel;
|
||||
} Stack;
|
||||
|
||||
|
||||
#define getstackbase(L, ptop) ((Stack *)lua_touserdata(L, stackidx(ptop)))
|
||||
|
||||
|
||||
/*
|
||||
** Ensures the size of array 'capture' (with size '*capsize' and
|
||||
** 'captop' elements being used) is enough to accomodate 'n' extra
|
||||
** elements plus one. (Because several opcodes add stuff to the capture
|
||||
** array, it is simpler to ensure the array always has at least one free
|
||||
** slot upfront and check its size later.)
|
||||
*/
|
||||
|
||||
/* new size in number of elements cannot overflow integers, and new
|
||||
size in bytes cannot overflow size_t. */
|
||||
#define MAXNEWSIZE \
|
||||
(((size_t)INT_MAX) <= (~(size_t)0 / sizeof(Capture)) ? \
|
||||
((size_t)INT_MAX) : (~(size_t)0 / sizeof(Capture)))
|
||||
|
||||
static Capture *growcap (lua_State *L, Capture *capture, int *capsize,
|
||||
int captop, int n, int ptop) {
|
||||
if (*capsize - captop > n)
|
||||
return capture; /* no need to grow array */
|
||||
else { /* must grow */
|
||||
Capture *newc;
|
||||
uint newsize = captop + n + 1; /* minimum size needed */
|
||||
if (newsize < (MAXNEWSIZE / 3) * 2)
|
||||
newsize += newsize / 2; /* 1.5 that size, if not too big */
|
||||
else if (newsize < (MAXNEWSIZE / 9) * 8)
|
||||
newsize += newsize / 8; /* else, try 9/8 that size */
|
||||
else
|
||||
luaL_error(L, "too many captures");
|
||||
newc = (Capture *)lua_newuserdata(L, newsize * sizeof(Capture));
|
||||
memcpy(newc, capture, captop * sizeof(Capture));
|
||||
*capsize = newsize;
|
||||
lua_replace(L, caplistidx(ptop));
|
||||
return newc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Double the size of the stack
|
||||
*/
|
||||
static Stack *doublestack (lua_State *L, Stack **stacklimit, int ptop) {
|
||||
Stack *stack = getstackbase(L, ptop);
|
||||
Stack *newstack;
|
||||
int n = *stacklimit - stack; /* current stack size */
|
||||
int max, newn;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, MAXSTACKIDX);
|
||||
max = lua_tointeger(L, -1); /* maximum allowed size */
|
||||
lua_pop(L, 1);
|
||||
if (n >= max) /* already at maximum size? */
|
||||
luaL_error(L, "backtrack stack overflow (current limit is %d)", max);
|
||||
newn = 2 * n; /* new size */
|
||||
if (newn > max) newn = max;
|
||||
newstack = (Stack *)lua_newuserdata(L, newn * sizeof(Stack));
|
||||
memcpy(newstack, stack, n * sizeof(Stack));
|
||||
lua_replace(L, stackidx(ptop));
|
||||
*stacklimit = newstack + newn;
|
||||
return newstack + n; /* return next position */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interpret the result of a dynamic capture: false -> fail;
|
||||
** true -> keep current position; number -> next position.
|
||||
** Return new subject position. 'fr' is stack index where
|
||||
** is the result; 'curr' is current subject position; 'limit'
|
||||
** is subject's size.
|
||||
*/
|
||||
static int resdyncaptures (lua_State *L, int fr, int curr, int limit) {
|
||||
lua_Integer res;
|
||||
if (!lua_toboolean(L, fr)) { /* false value? */
|
||||
lua_settop(L, fr - 1); /* remove results */
|
||||
return -1; /* and fail */
|
||||
}
|
||||
else if (lua_isboolean(L, fr)) /* true? */
|
||||
res = curr; /* keep current position */
|
||||
else {
|
||||
res = lua_tointeger(L, fr) - 1; /* new position */
|
||||
if (res < curr || res > limit)
|
||||
luaL_error(L, "invalid position returned by match-time capture");
|
||||
}
|
||||
lua_remove(L, fr); /* remove first result (offset) */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add capture values returned by a dynamic capture to the list
|
||||
** 'capture', nested inside a group. 'fd' indexes the first capture
|
||||
** value, 'n' is the number of values (at least 1). The open group
|
||||
** capture is already in 'capture', before the place for the new entries.
|
||||
*/
|
||||
static void adddyncaptures (Index_t index, Capture *capture, int n, int fd) {
|
||||
int i;
|
||||
assert(capture[-1].kind == Cgroup && capture[-1].siz == 0);
|
||||
capture[-1].idx = 0; /* make group capture an anonymous group */
|
||||
for (i = 0; i < n; i++) { /* add runtime captures */
|
||||
capture[i].kind = Cruntime;
|
||||
capture[i].siz = 1; /* mark it as closed */
|
||||
capture[i].idx = fd + i; /* stack index of capture value */
|
||||
capture[i].index = index;
|
||||
}
|
||||
capture[n].kind = Cclose; /* close group */
|
||||
capture[n].siz = 1;
|
||||
capture[n].index = index;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove dynamic captures from the Lua stack (called in case of failure)
|
||||
*/
|
||||
static int removedyncap (lua_State *L, Capture *capture,
|
||||
int level, int last) {
|
||||
int id = finddyncap(capture + level, capture + last); /* index of 1st cap. */
|
||||
int top = lua_gettop(L);
|
||||
if (id == 0) return 0; /* no dynamic captures? */
|
||||
lua_settop(L, id - 1); /* remove captures */
|
||||
return top - id + 1; /* number of values removed */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find the corresponding 'open' capture before 'cap', when that capture
|
||||
** can become a full capture. If a full capture c1 is followed by an
|
||||
** empty capture c2, there is no way to know whether c2 is inside
|
||||
** c1. So, full captures can enclose only captures that start *before*
|
||||
** its end.
|
||||
*/
|
||||
static Capture *findopen (Capture *cap, Index_t currindex) {
|
||||
int i;
|
||||
cap--; /* check last capture */
|
||||
/* Must it be inside current one, but starts where current one ends? */
|
||||
if (!isopencap(cap) && cap->index == currindex)
|
||||
return NULL; /* current one cannot be a full capture */
|
||||
/* else, look for an 'open' capture */
|
||||
for (i = 0; i < MAXLOP; i++, cap--) {
|
||||
if (currindex - cap->index >= UCHAR_MAX)
|
||||
return NULL; /* capture too long for a full capture */
|
||||
else if (isopencap(cap)) /* open capture? */
|
||||
return cap; /* that's the one to be closed */
|
||||
else if (cap->kind == Cclose)
|
||||
return NULL; /* a full capture should not nest a non-full one */
|
||||
}
|
||||
return NULL; /* not found within allowed search limit */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Opcode interpreter
|
||||
*/
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop) {
|
||||
Stack stackbase[INITBACK];
|
||||
Stack *stacklimit = stackbase + INITBACK;
|
||||
Stack *stack = stackbase; /* point to first empty slot in stack */
|
||||
int capsize = INITCAPSIZE;
|
||||
int captop = 0; /* point to first empty slot in captures */
|
||||
int ndyncap = 0; /* number of dynamic captures (in Lua stack) */
|
||||
const Instruction *p = op; /* current instruction */
|
||||
stack->p = &giveup; stack->s = s; stack->caplevel = 0; stack++;
|
||||
lua_pushlightuserdata(L, stackbase);
|
||||
for (;;) {
|
||||
#if defined(DEBUG)
|
||||
printf("-------------------------------------\n");
|
||||
printcaplist(capture, capture + captop);
|
||||
printf("s: |%s| stck:%d, dyncaps:%d, caps:%d ",
|
||||
s, (int)(stack - getstackbase(L, ptop)), ndyncap, captop);
|
||||
printinst(op, p);
|
||||
#endif
|
||||
assert(stackidx(ptop) + ndyncap == lua_gettop(L) && ndyncap <= captop);
|
||||
switch ((Opcode)p->i.code) {
|
||||
case IEnd: {
|
||||
assert(stack == getstackbase(L, ptop) + 1);
|
||||
capture[captop].kind = Cclose;
|
||||
capture[captop].index = MAXINDT;
|
||||
return s;
|
||||
}
|
||||
case IGiveup: {
|
||||
assert(stack == getstackbase(L, ptop));
|
||||
return NULL;
|
||||
}
|
||||
case IRet: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s == NULL);
|
||||
p = (--stack)->p;
|
||||
continue;
|
||||
}
|
||||
case IAny: {
|
||||
if (s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case IUTFR: {
|
||||
int codepoint;
|
||||
if (s >= e)
|
||||
goto fail;
|
||||
s = utf8_decode (s, &codepoint);
|
||||
if (s && p[1].offset <= codepoint && codepoint <= utf_to(p))
|
||||
p += 2;
|
||||
else
|
||||
goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestAny: {
|
||||
if (s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChar: {
|
||||
if ((byte)*s == p->i.aux1 && s < e) { p++; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestChar: {
|
||||
if ((byte)*s == p->i.aux1 && s < e) p += 2;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ISet: {
|
||||
uint c = (byte)*s;
|
||||
if (charinset(p, (p+1)->buff, c) && s < e)
|
||||
{ p += 1 + p->i.aux2.set.size; s++; }
|
||||
else goto fail;
|
||||
continue;
|
||||
}
|
||||
case ITestSet: {
|
||||
uint c = (byte)*s;
|
||||
if (charinset(p, (p + 2)->buff, c) && s < e)
|
||||
p += 2 + p->i.aux2.set.size;
|
||||
else p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBehind: {
|
||||
int n = p->i.aux1;
|
||||
if (n > s - o) goto fail;
|
||||
s -= n; p++;
|
||||
continue;
|
||||
}
|
||||
case ISpan: {
|
||||
for (; s < e; s++) {
|
||||
uint c = (byte)*s;
|
||||
if (!charinset(p, (p+1)->buff, c)) break;
|
||||
}
|
||||
p += 1 + p->i.aux2.set.size;
|
||||
continue;
|
||||
}
|
||||
case IJmp: {
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IChoice: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->p = p + getoffset(p);
|
||||
stack->s = s;
|
||||
stack->caplevel = captop;
|
||||
stack++;
|
||||
p += 2;
|
||||
continue;
|
||||
}
|
||||
case ICall: {
|
||||
if (stack == stacklimit)
|
||||
stack = doublestack(L, &stacklimit, ptop);
|
||||
stack->s = NULL;
|
||||
stack->p = p + 2; /* save return address */
|
||||
stack++;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case ICommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
stack--;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IPartialCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
(stack - 1)->s = s;
|
||||
(stack - 1)->caplevel = captop;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IBackCommit: {
|
||||
assert(stack > getstackbase(L, ptop) && (stack - 1)->s != NULL);
|
||||
s = (--stack)->s;
|
||||
if (ndyncap > 0) /* are there matchtime captures? */
|
||||
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
|
||||
captop = stack->caplevel;
|
||||
p += getoffset(p);
|
||||
continue;
|
||||
}
|
||||
case IFailTwice:
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
stack--;
|
||||
/* FALLTHROUGH */
|
||||
case IFail:
|
||||
fail: { /* pattern failed: try to backtrack */
|
||||
do { /* remove pending calls */
|
||||
assert(stack > getstackbase(L, ptop));
|
||||
s = (--stack)->s;
|
||||
} while (s == NULL);
|
||||
if (ndyncap > 0) /* is there matchtime captures? */
|
||||
ndyncap -= removedyncap(L, capture, stack->caplevel, captop);
|
||||
captop = stack->caplevel;
|
||||
p = stack->p;
|
||||
#if defined(DEBUG)
|
||||
printf("**FAIL**\n");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
case ICloseRunTime: {
|
||||
CapState cs;
|
||||
int rem, res, n;
|
||||
int fr = lua_gettop(L) + 1; /* stack index of first result */
|
||||
cs.reclevel = 0; cs.L = L;
|
||||
cs.s = o; cs.ocap = capture; cs.ptop = ptop;
|
||||
n = runtimecap(&cs, capture + captop, s, &rem); /* call function */
|
||||
captop -= n; /* remove nested captures */
|
||||
ndyncap -= rem; /* update number of dynamic captures */
|
||||
fr -= rem; /* 'rem' items were popped from Lua stack */
|
||||
res = resdyncaptures(L, fr, s - o, e - o); /* get result */
|
||||
if (res == -1) /* fail? */
|
||||
goto fail;
|
||||
s = o + res; /* else update current position */
|
||||
n = lua_gettop(L) - fr + 1; /* number of new captures */
|
||||
ndyncap += n; /* update number of dynamic captures */
|
||||
if (n == 0) /* no new captures? */
|
||||
captop--; /* remove open group */
|
||||
else { /* new captures; keep original open group */
|
||||
if (fr + n >= SHRT_MAX)
|
||||
luaL_error(L, "too many results in match-time capture");
|
||||
/* add new captures + close group to 'capture' list */
|
||||
capture = growcap(L, capture, &capsize, captop, n + 1, ptop);
|
||||
adddyncaptures(s - o, capture + captop, n, fr);
|
||||
captop += n + 1; /* new captures + close group */
|
||||
}
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
case ICloseCapture: {
|
||||
Capture *open = findopen(capture + captop, s - o);
|
||||
assert(captop > 0);
|
||||
if (open) { /* if possible, turn capture into a full capture */
|
||||
open->siz = (s - o) - open->index + 1;
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
else { /* must create a close capture */
|
||||
capture[captop].siz = 1; /* mark entry as closed */
|
||||
capture[captop].index = s - o;
|
||||
goto pushcapture;
|
||||
}
|
||||
}
|
||||
case IOpenCapture:
|
||||
capture[captop].siz = 0; /* mark entry as open */
|
||||
capture[captop].index = s - o;
|
||||
goto pushcapture;
|
||||
case IFullCapture:
|
||||
capture[captop].siz = getoff(p) + 1; /* save capture size */
|
||||
capture[captop].index = s - o - getoff(p);
|
||||
/* goto pushcapture; */
|
||||
pushcapture: {
|
||||
capture[captop].idx = p->i.aux2.key;
|
||||
capture[captop].kind = getkind(p);
|
||||
captop++;
|
||||
capture = growcap(L, capture, &capsize, captop, 0, ptop);
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
default: assert(0); return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
79
src/lua/lpeg-1.1.0/lpvm.h
Normal file
79
src/lua/lpeg-1.1.0/lpvm.h
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
#if !defined(lpvm_h)
|
||||
#define lpvm_h
|
||||
|
||||
#include "lpcap.h"
|
||||
|
||||
|
||||
/*
|
||||
** About Character sets in instructions: a set is a bit map with an
|
||||
** initial offset, in bits, and a size, in number of instructions.
|
||||
** aux1 has the default value for the bits outsize that range.
|
||||
*/
|
||||
|
||||
|
||||
/* Virtual Machine's instructions */
|
||||
typedef enum Opcode {
|
||||
IAny, /* if no char, fail */
|
||||
IChar, /* if char != aux1, fail */
|
||||
ISet, /* if char not in set, fail */
|
||||
ITestAny, /* in no char, jump to 'offset' */
|
||||
ITestChar, /* if char != aux1, jump to 'offset' */
|
||||
ITestSet, /* if char not in set, jump to 'offset' */
|
||||
ISpan, /* read a span of chars in set */
|
||||
IUTFR, /* if codepoint not in range [offset, utf_to], fail */
|
||||
IBehind, /* walk back 'aux1' characters (fail if not possible) */
|
||||
IRet, /* return from a rule */
|
||||
IEnd, /* end of pattern */
|
||||
IChoice, /* stack a choice; next fail will jump to 'offset' */
|
||||
IJmp, /* jump to 'offset' */
|
||||
ICall, /* call rule at 'offset' */
|
||||
IOpenCall, /* call rule number 'key' (must be closed to a ICall) */
|
||||
ICommit, /* pop choice and jump to 'offset' */
|
||||
IPartialCommit, /* update top choice to current position and jump */
|
||||
IBackCommit, /* backtrack like "fail" but jump to its own 'offset' */
|
||||
IFailTwice, /* pop one choice and then fail */
|
||||
IFail, /* go back to saved state on choice and jump to saved offset */
|
||||
IGiveup, /* internal use */
|
||||
IFullCapture, /* complete capture of last 'off' chars */
|
||||
IOpenCapture, /* start a capture */
|
||||
ICloseCapture,
|
||||
ICloseRunTime,
|
||||
IEmpty /* to fill empty slots left by optimizations */
|
||||
} Opcode;
|
||||
|
||||
|
||||
/*
|
||||
** All array of instructions has a 'codesize' as its first element
|
||||
** and is referred by a pointer to its second element, which is the
|
||||
** first actual opcode.
|
||||
*/
|
||||
typedef union Instruction {
|
||||
struct Inst {
|
||||
byte code;
|
||||
byte aux1;
|
||||
union {
|
||||
short key;
|
||||
struct {
|
||||
byte offset;
|
||||
byte size;
|
||||
} set;
|
||||
} aux2;
|
||||
} i;
|
||||
int offset;
|
||||
uint codesize;
|
||||
byte buff[1];
|
||||
} Instruction;
|
||||
|
||||
|
||||
/* extract 24-bit value from an instruction */
|
||||
#define utf_to(inst) (((inst)->i.aux2.key << 8) | (inst)->i.aux1)
|
||||
|
||||
|
||||
int charinset (const Instruction *i, const byte *buff, uint c);
|
||||
const char *match (lua_State *L, const char *o, const char *s, const char *e,
|
||||
Instruction *op, Capture *capture, int ptop);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
59
src/lua/lpeg-1.1.0/makefile
Normal file
59
src/lua/lpeg-1.1.0/makefile
Normal file
@@ -0,0 +1,59 @@
|
||||
LIBNAME = lpeg
|
||||
LUADIR = ./lua/
|
||||
|
||||
COPT = -O2 -DNDEBUG
|
||||
# COPT = -O0 -DLPEG_DEBUG -g
|
||||
|
||||
CWARNS = -Wall -Wextra -pedantic \
|
||||
-Waggregate-return \
|
||||
-Wcast-align \
|
||||
-Wcast-qual \
|
||||
-Wdisabled-optimization \
|
||||
-Wpointer-arith \
|
||||
-Wshadow \
|
||||
-Wredundant-decls \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wbad-function-cast \
|
||||
-Wdeclaration-after-statement \
|
||||
-Wmissing-prototypes \
|
||||
-Wmissing-declarations \
|
||||
-Wnested-externs \
|
||||
-Wstrict-prototypes \
|
||||
-Wc++-compat \
|
||||
# -Wunreachable-code \
|
||||
|
||||
|
||||
CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
|
||||
CC = gcc
|
||||
|
||||
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o
|
||||
|
||||
# For Linux
|
||||
linux:
|
||||
$(MAKE) lpeg.so "DLLFLAGS = -shared -fPIC"
|
||||
|
||||
# For Mac OS
|
||||
macosx:
|
||||
$(MAKE) lpeg.so "DLLFLAGS = -bundle -undefined dynamic_lookup"
|
||||
|
||||
lpeg.so: $(FILES)
|
||||
env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
|
||||
|
||||
$(FILES): makefile
|
||||
|
||||
test: test.lua re.lua lpeg.so
|
||||
./test.lua
|
||||
|
||||
clean:
|
||||
rm -f $(FILES) lpeg.so
|
||||
|
||||
|
||||
lpcap.o: lpcap.c lpcap.h lptypes.h
|
||||
lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h lpcset.h
|
||||
lpcset.o: lpcset.c lptypes.h lpcset.h lpcode.h lptree.h lpvm.h lpcap.h
|
||||
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h lpcode.h
|
||||
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h \
|
||||
lpcset.h
|
||||
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
|
||||
472
src/lua/lpeg-1.1.0/re.html
Normal file
472
src/lua/lpeg-1.1.0/re.html
Normal file
@@ -0,0 +1,472 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"//www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>LPeg.re - Regex syntax for LPEG</title>
|
||||
<link rel="stylesheet"
|
||||
href="//www.inf.puc-rio.br/~roberto/lpeg/doc.css"
|
||||
type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="//www.inf.puc-rio.br/~roberto/lpeg/">
|
||||
<img alt="LPeg logo" src="lpeg-128.gif"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LPeg.re</strong></big></div>
|
||||
<div id="product_description">
|
||||
Regex syntax for LPEG
|
||||
</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>re</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="#basic">Basic Constructions</a></li>
|
||||
<li><a href="#func">Functions</a></li>
|
||||
<li><a href="#ex">Some Examples</a></li>
|
||||
<li><a href="#license">License</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="basic"></a>The <code>re</code> Module</h2>
|
||||
|
||||
<p>
|
||||
The <code>re</code> module
|
||||
(provided by file <code>re.lua</code> in the distribution)
|
||||
supports a somewhat conventional regex syntax
|
||||
for pattern usage within <a href="lpeg.html">LPeg</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The next table summarizes <code>re</code>'s syntax.
|
||||
A <code>p</code> represents an arbitrary pattern;
|
||||
<code>num</code> represents a number (<code>[0-9]+</code>);
|
||||
<code>name</code> represents an identifier
|
||||
(<code>[a-zA-Z][a-zA-Z0-9_]*</code>).
|
||||
Constructions are listed in order of decreasing precedence.
|
||||
<table border="1">
|
||||
<tbody><tr><td><b>Syntax</b></td><td><b>Description</b></td></tr>
|
||||
<tr><td><code>( p )</code></td> <td>grouping</td></tr>
|
||||
<tr><td><code>& p</code></td> <td>and predicate</td></tr>
|
||||
<tr><td><code>! p</code></td> <td>not predicate</td></tr>
|
||||
<tr><td><code>p1 p2</code></td> <td>concatenation</td></tr>
|
||||
<tr><td><code>p1 / p2</code></td> <td>ordered choice</td></tr>
|
||||
<tr><td><code>p ?</code></td> <td>optional match</td></tr>
|
||||
<tr><td><code>p *</code></td> <td>zero or more repetitions</td></tr>
|
||||
<tr><td><code>p +</code></td> <td>one or more repetitions</td></tr>
|
||||
<tr><td><code>p^num</code></td>
|
||||
<td>exactly <code>num</code> repetitions</td></tr>
|
||||
<tr><td><code>p^+num</code></td>
|
||||
<td>at least <code>num</code> repetitions</td></tr>
|
||||
<tr><td><code>p^-num</code></td>
|
||||
<td>at most <code>num</code> repetitions</td></tr>
|
||||
<tr><td>(<code>name <- p</code>)<sup>+</sup></td> <td>grammar</td></tr>
|
||||
<tr><td><code>'string'</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>"string"</code></td> <td>literal string</td></tr>
|
||||
<tr><td><code>[class]</code></td> <td>character class</td></tr>
|
||||
<tr><td><code>.</code></td> <td>any character</td></tr>
|
||||
<tr><td><code>%name</code></td>
|
||||
<td>pattern <code>defs[name]</code> or a pre-defined pattern</td></tr>
|
||||
<tr><td><code>name</code></td><td>non terminal</td></tr>
|
||||
<tr><td><code><name></code></td><td>non terminal</td></tr>
|
||||
|
||||
<tr><td><code>{}</code></td> <td>position capture</td></tr>
|
||||
<tr><td><code>{ p }</code></td> <td>simple capture</td></tr>
|
||||
<tr><td><code>{: p :}</code></td> <td>anonymous group capture</td></tr>
|
||||
<tr><td><code>{:name: p :}</code></td> <td>named group capture</td></tr>
|
||||
<tr><td><code>{~ p ~}</code></td> <td>substitution capture</td></tr>
|
||||
<tr><td><code>{| p |}</code></td> <td>table capture</td></tr>
|
||||
<tr><td><code>=name</code></td> <td>back reference</td></tr>
|
||||
|
||||
<tr><td><code>p -> 'string'</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> "string"</code></td> <td>string capture</td></tr>
|
||||
<tr><td><code>p -> num</code></td> <td>numbered capture</td></tr>
|
||||
<tr><td><code>p -> name</code></td> <td>function/query/string capture
|
||||
equivalent to <code>p / defs[name]</code></td></tr>
|
||||
<tr><td><code>p => name</code></td> <td>match-time capture
|
||||
equivalent to <code>lpeg.Cmt(p, defs[name])</code></td></tr>
|
||||
<tr><td><code>p ~> name</code></td> <td>fold capture
|
||||
(deprecated)</td></tr>
|
||||
<tr><td><code>p >> name</code></td> <td>accumulator capture
|
||||
equivalent to <code>(p % defs[name])</code></td></tr>
|
||||
</tbody></table>
|
||||
<p>
|
||||
Any space appearing in a syntax description can be
|
||||
replaced by zero or more space characters and Lua-style short comments
|
||||
(<code>--</code> until end of line).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Character classes define sets of characters.
|
||||
An initial <code>^</code> complements the resulting set.
|
||||
A range <em>x</em><code>-</code><em>y</em> includes in the set
|
||||
all characters with codes between the codes of <em>x</em> and <em>y</em>.
|
||||
A pre-defined class <code>%</code><em>name</em> includes all
|
||||
characters of that class.
|
||||
A simple character includes itself in the set.
|
||||
The only special characters inside a class are <code>^</code>
|
||||
(special only if it is the first character);
|
||||
<code>]</code>
|
||||
(can be included in the set as the first character,
|
||||
after the optional <code>^</code>);
|
||||
<code>%</code> (special only if followed by a letter);
|
||||
and <code>-</code>
|
||||
(can be included in the set as the first or the last character).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Currently the pre-defined classes are similar to those from the
|
||||
Lua's string library
|
||||
(<code>%a</code> for letters,
|
||||
<code>%A</code> for non letters, etc.).
|
||||
There is also a class <code>%nl</code>
|
||||
containing only the newline character,
|
||||
which is particularly handy for grammars written inside long strings,
|
||||
as long strings do not interpret escape sequences like <code>\n</code>.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="func">Functions</a></h2>
|
||||
|
||||
<h3><code>re.compile (string, [, defs])</code></h3>
|
||||
<p>
|
||||
Compiles the given string and
|
||||
returns an equivalent LPeg pattern.
|
||||
The given string may define either an expression or a grammar.
|
||||
The optional <code>defs</code> table provides extra Lua values
|
||||
to be used by the pattern.
|
||||
</p>
|
||||
|
||||
<h3><code>re.find (subject, pattern [, init])</code></h3>
|
||||
<p>
|
||||
Searches the given pattern in the given subject.
|
||||
If it finds a match,
|
||||
returns the index where this occurrence starts and
|
||||
the index where it ends.
|
||||
Otherwise, returns nil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An optional numeric argument <code>init</code> makes the search
|
||||
starts at that position in the subject string.
|
||||
As usual in Lua libraries,
|
||||
a negative value counts from the end.
|
||||
</p>
|
||||
|
||||
<h3><code>re.gsub (subject, pattern, replacement)</code></h3>
|
||||
<p>
|
||||
Does a <em>global substitution</em>,
|
||||
replacing all occurrences of <code>pattern</code>
|
||||
in the given <code>subject</code> by <code>replacement</code>.
|
||||
|
||||
<h3><code>re.match (subject, pattern)</code></h3>
|
||||
<p>
|
||||
Matches the given pattern against the given subject,
|
||||
returning all captures.
|
||||
</p>
|
||||
|
||||
<h3><code>re.updatelocale ()</code></h3>
|
||||
<p>
|
||||
Updates the pre-defined character classes to the current locale.
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="ex">Some Examples</a></h2>
|
||||
|
||||
<h3>A complete simple program</h3>
|
||||
<p>
|
||||
The next code shows a simple complete Lua program using
|
||||
the <code>re</code> module:
|
||||
</p>
|
||||
<pre class="example">
|
||||
local re = require"re"
|
||||
|
||||
-- find the position of the first numeral in a string
|
||||
print(re.find("the number 423 is odd", "[0-9]+")) --> 12 14
|
||||
|
||||
-- returns all words in a string
|
||||
print(re.match("the number 423 is odd", "({%a+} / .)*"))
|
||||
--> the number is odd
|
||||
|
||||
-- returns the first numeral in a string
|
||||
print(re.match("the number 423 is odd", "s <- {%d+} / . s"))
|
||||
--> 423
|
||||
|
||||
-- substitutes a dot for each vowel in a string
|
||||
print(re.gsub("hello World", "[aeiou]", "."))
|
||||
--> h.ll. W.rld
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Balanced parentheses</h3>
|
||||
<p>
|
||||
The following call will produce the same pattern produced by the
|
||||
Lua expression in the
|
||||
<a href="lpeg.html#balanced">balanced parentheses</a> example:
|
||||
</p>
|
||||
<pre class="example">
|
||||
b = re.compile[[ balanced <- "(" ([^()] / balanced)* ")" ]]
|
||||
</pre>
|
||||
|
||||
<h3>String reversal</h3>
|
||||
<p>
|
||||
The next example reverses a string:
|
||||
</p>
|
||||
<pre class="example">
|
||||
rev = re.compile[[ R <- (!.) -> '' / ({.} R) -> '%2%1']]
|
||||
print(rev:match"0123456789") --> 9876543210
|
||||
</pre>
|
||||
|
||||
<h3>CSV decoder</h3>
|
||||
<p>
|
||||
The next example replicates the <a href="lpeg.html#CSV">CSV decoder</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
record = re.compile[[
|
||||
record <- {| field (',' field)* |} (%nl / !.)
|
||||
field <- escaped / nonescaped
|
||||
nonescaped <- { [^,"%nl]* }
|
||||
escaped <- '"' {~ ([^"] / '""' -> '"')* ~} '"'
|
||||
]]
|
||||
</pre>
|
||||
|
||||
<h3>Lua's long strings</h3>
|
||||
<p>
|
||||
The next example matches Lua long strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile([[
|
||||
longstring <- ('[' {:eq: '='* :} '[' close)
|
||||
close <- ']' =eq ']' / . close
|
||||
]])
|
||||
|
||||
print(c:match'[==[]]===]]]]==]===[]') --> 17
|
||||
</pre>
|
||||
|
||||
<h3>Abstract Syntax Trees</h3>
|
||||
<p>
|
||||
This example shows a simple way to build an
|
||||
abstract syntax tree (AST) for a given grammar.
|
||||
To keep our example simple,
|
||||
let us consider the following grammar
|
||||
for lists of names:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
listname <- (name s)*
|
||||
name <- [a-z][a-z]*
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, we will add captures to build a corresponding AST.
|
||||
As a first step, the pattern will build a table to
|
||||
represent each non terminal;
|
||||
terminals will be represented by their corresponding strings:
|
||||
</p>
|
||||
<pre class="example">
|
||||
c = re.compile[[
|
||||
listname <- {| (name s)* |}
|
||||
name <- {| {[a-z][a-z]*} |}
|
||||
s <- %s*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
Now, a match against <code>"hi hello bye"</code>
|
||||
results in the table
|
||||
<code>{{"hi"}, {"hello"}, {"bye"}}</code>.
|
||||
</p>
|
||||
<p>
|
||||
For such a simple grammar,
|
||||
this AST is more than enough;
|
||||
actually, the tables around each single name
|
||||
are already overkilling.
|
||||
More complex grammars,
|
||||
however, may need some more structure.
|
||||
Specifically,
|
||||
it would be useful if each table had
|
||||
a <code>tag</code> field telling what non terminal
|
||||
that table represents.
|
||||
We can add such a tag using
|
||||
<a href="lpeg.html#cap-g">named group captures</a>:
|
||||
</p>
|
||||
<pre class="example">
|
||||
x = re.compile[[
|
||||
listname <- {| {:tag: '' -> 'list':} (name s)* |}
|
||||
name <- {| {:tag: '' -> 'id':} {[a-z][a-z]*} |}
|
||||
s <- ' '*
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
With these group captures,
|
||||
a match against <code>"hi hello bye"</code>
|
||||
results in the following table:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{tag="list",
|
||||
{tag="id", "hi"},
|
||||
{tag="id", "hello"},
|
||||
{tag="id", "bye"}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Indented blocks</h3>
|
||||
<p>
|
||||
This example breaks indented blocks into tables,
|
||||
respecting the indentation:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
block <- {| {:ident:' '*:} line
|
||||
((=ident !' ' line) / &(=ident ' ') block)* |}
|
||||
line <- {[^%nl]*} %nl
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
As an example,
|
||||
consider the following text:
|
||||
</p>
|
||||
<pre class="example">
|
||||
t = p:match[[
|
||||
first line
|
||||
subline 1
|
||||
subline 2
|
||||
second line
|
||||
third line
|
||||
subline 3.1
|
||||
subline 3.1.1
|
||||
subline 3.2
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
The resulting table <code>t</code> will be like this:
|
||||
</p>
|
||||
<pre class="example">
|
||||
{'first line'; {'subline 1'; 'subline 2'; ident = ' '};
|
||||
'second line';
|
||||
'third line'; { 'subline 3.1'; {'subline 3.1.1'; ident = ' '};
|
||||
'subline 3.2'; ident = ' '};
|
||||
ident = ''}
|
||||
</pre>
|
||||
|
||||
<h3>Macro expander</h3>
|
||||
<p>
|
||||
This example implements a simple macro expander.
|
||||
Macros must be defined as part of the pattern,
|
||||
following some simple rules:
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = re.compile[[
|
||||
text <- {~ item* ~}
|
||||
item <- macro / [^()] / '(' item* ')'
|
||||
arg <- ' '* {~ (!',' item)* ~}
|
||||
args <- '(' arg (',' arg)* ')'
|
||||
-- now we define some macros
|
||||
macro <- ('apply' args) -> '%1(%2)'
|
||||
/ ('add' args) -> '%1 + %2'
|
||||
/ ('mul' args) -> '%1 * %2'
|
||||
]]
|
||||
|
||||
print(p:match"add(mul(a,b), apply(f,x))") --> a * b + f(x)
|
||||
</pre>
|
||||
<p>
|
||||
A <code>text</code> is a sequence of items,
|
||||
wherein we apply a substitution capture to expand any macros.
|
||||
An <code>item</code> is either a macro,
|
||||
any character different from parentheses,
|
||||
or a parenthesized expression.
|
||||
A macro argument (<code>arg</code>) is a sequence
|
||||
of items different from a comma.
|
||||
(Note that a comma may appear inside an item,
|
||||
e.g., inside a parenthesized expression.)
|
||||
Again we do a substitution capture to expand any macro
|
||||
in the argument before expanding the outer macro.
|
||||
<code>args</code> is a list of arguments separated by commas.
|
||||
Finally we define the macros.
|
||||
Each macro is a string substitution;
|
||||
it replaces the macro name and its arguments by its corresponding string,
|
||||
with each <code>%</code><em>n</em> replaced by the <em>n</em>-th argument.
|
||||
</p>
|
||||
|
||||
<h3>Patterns</h3>
|
||||
<p>
|
||||
This example shows the complete syntax
|
||||
of patterns accepted by <code>re</code>.
|
||||
</p>
|
||||
<pre class="example">
|
||||
p = [=[
|
||||
|
||||
pattern <- exp !.
|
||||
exp <- S (grammar / alternative)
|
||||
|
||||
alternative <- seq ('/' S seq)*
|
||||
seq <- prefix*
|
||||
prefix <- '&' S prefix / '!' S prefix / suffix
|
||||
suffix <- primary S (([+*?]
|
||||
/ '^' [+-]? num
|
||||
/ '->' S (string / '{}' / name)
|
||||
/ '>>' S name
|
||||
/ '=>' S name) S)*
|
||||
|
||||
primary <- '(' exp ')' / string / class / defined
|
||||
/ '{:' (name ':')? exp ':}'
|
||||
/ '=' name
|
||||
/ '{}'
|
||||
/ '{~' exp '~}'
|
||||
/ '{|' exp '|}'
|
||||
/ '{' exp '}'
|
||||
/ '.'
|
||||
/ name S !arrow
|
||||
/ '<' name '>' -- old-style non terminals
|
||||
|
||||
grammar <- definition+
|
||||
definition <- name S arrow exp
|
||||
|
||||
class <- '[' '^'? item (!']' item)* ']'
|
||||
item <- defined / range / .
|
||||
range <- . '-' [^]]
|
||||
|
||||
S <- (%s / '--' [^%nl]*)* -- spaces and comments
|
||||
name <- [A-Za-z_][A-Za-z0-9_]*
|
||||
arrow <- '<-'
|
||||
num <- [0-9]+
|
||||
string <- '"' [^"]* '"' / "'" [^']* "'"
|
||||
defined <- '%' name
|
||||
|
||||
]=]
|
||||
|
||||
print(re.match(p, p)) -- a self description must match itself
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
<h2><a name="license">License</a></h2>
|
||||
|
||||
<p>
|
||||
This module is part of the <a href="lpeg.html">LPeg</a> package and shares
|
||||
its <a href="lpeg.html#license">license</a>.
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
270
src/lua/lpeg-1.1.0/re.lua
Normal file
270
src/lua/lpeg-1.1.0/re.lua
Normal file
@@ -0,0 +1,270 @@
|
||||
--
|
||||
-- Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license)
|
||||
-- written by Roberto Ierusalimschy
|
||||
--
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, type, print, error = tonumber, type, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeg"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- patterns' metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
|
||||
|
||||
local version = _VERSION
|
||||
|
||||
-- No more global accesses after this point
|
||||
_ENV = nil -- does no harm in Lua 5.1
|
||||
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
-- match a name and return a group of its corresponding definition
|
||||
-- and 'f' (to be folded in 'Suffix')
|
||||
local function defwithfunc (f)
|
||||
return m.Cg(Def / getdef * m.Cc(f))
|
||||
end
|
||||
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = (defined + Range + m.C(any)) / m.P
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* (item * ((item % mt.__add) - "]")^0) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.V"Seq" * ("/" * S * m.V"Seq" % mt.__add)^0 );
|
||||
Seq = (m.Cc(m.P"") * (m.V"Prefix" % mt.__mul)^0)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ defwithfunc(mt.__div)
|
||||
)
|
||||
+ "=>" * S * defwithfunc(mm.Cmt)
|
||||
+ ">>" * S * defwithfunc(mt.__mod)
|
||||
+ "~>" * S * defwithfunc(mm.Cf)
|
||||
) % function (a,b,f) return f(a,b) end * S
|
||||
)^0;
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * -arrow + "<" * name * ">") * m.Cb("G") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
((m.V"Definition" / firstdef) * (m.V"Definition" % adddef)^0) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
if version == "Lua 5.1" then _G.re = re end
|
||||
|
||||
return re
|
||||
1667
src/lua/lpeg-1.1.0/test.lua
Executable file
1667
src/lua/lpeg-1.1.0/test.lua
Executable file
File diff suppressed because it is too large
Load Diff
694
src/lua/lua.c
Normal file
694
src/lua/lua.c
Normal file
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
** $Id: lua.c $
|
||||
** Lua stand-alone interpreter
|
||||
** See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lua_c
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
|
||||
#if !defined(LUA_PROGNAME)
|
||||
#define LUA_PROGNAME "lua"
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_INIT_VAR)
|
||||
#define LUA_INIT_VAR "LUA_INIT"
|
||||
#endif
|
||||
|
||||
#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
|
||||
|
||||
|
||||
static lua_State *globalL = NULL;
|
||||
|
||||
static const char *progname = LUA_PROGNAME;
|
||||
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
/*
|
||||
** Use 'sigaction' when available.
|
||||
*/
|
||||
static void setsignal (int sig, void (*handler)(int)) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handler;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask); /* do not mask any signal */
|
||||
sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define setsignal signal
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Hook set by signal function to stop the interpreter.
|
||||
*/
|
||||
static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
(void)ar; /* unused arg. */
|
||||
lua_sethook(L, NULL, 0, 0); /* reset hook */
|
||||
luaL_error(L, "interrupted!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function to be called at a C signal. Because a C signal cannot
|
||||
** just change a Lua state (as there is no proper synchronization),
|
||||
** this function only sets a hook that, when called, will stop the
|
||||
** interpreter.
|
||||
*/
|
||||
static void laction (int i) {
|
||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, flag, 1);
|
||||
}
|
||||
|
||||
|
||||
static void print_usage (const char *badoption) {
|
||||
lua_writestringerror("%s: ", progname);
|
||||
if (badoption[1] == 'e' || badoption[1] == 'l')
|
||||
lua_writestringerror("'%s' needs argument\n", badoption);
|
||||
else
|
||||
lua_writestringerror("unrecognized option '%s'\n", badoption);
|
||||
lua_writestringerror(
|
||||
"usage: %s [options] [script [args]]\n"
|
||||
"Available options are:\n"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -l mod require library 'mod' into global 'mod'\n"
|
||||
" -l g=mod require library 'mod' into global 'g'\n"
|
||||
" -v show version information\n"
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - stop handling options and execute stdin\n"
|
||||
,
|
||||
progname);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints an error message, adding the program name in front of it
|
||||
** (if present)
|
||||
*/
|
||||
static void l_message (const char *pname, const char *msg) {
|
||||
if (pname) lua_writestringerror("%s: ", pname);
|
||||
lua_writestringerror("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' is not OK and, if so, prints the error
|
||||
** message on the top of the stack.
|
||||
*/
|
||||
static int report (lua_State *L, int status) {
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL)
|
||||
msg = "(error message not a string)";
|
||||
l_message(progname, msg);
|
||||
lua_pop(L, 1); /* remove message */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Message handler used to run all chunks
|
||||
*/
|
||||
static int msghandler (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, 1);
|
||||
if (msg == NULL) { /* is error object not a string? */
|
||||
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
||||
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
||||
return 1; /* that is the message */
|
||||
else
|
||||
msg = lua_pushfstring(L, "(error object is a %s value)",
|
||||
luaL_typename(L, 1));
|
||||
}
|
||||
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
|
||||
return 1; /* return the traceback */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interface to 'lua_pcall', which sets appropriate message function
|
||||
** and C-signal handler. Used to run all chunks.
|
||||
*/
|
||||
static int docall (lua_State *L, int narg, int nres) {
|
||||
int status;
|
||||
int base = lua_gettop(L) - narg; /* function index */
|
||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||
lua_insert(L, base); /* put it under function and args */
|
||||
globalL = L; /* to be available to 'laction' */
|
||||
setsignal(SIGINT, laction); /* set C-signal handler */
|
||||
status = lua_pcall(L, narg, nres, base);
|
||||
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||
lua_remove(L, base); /* remove message handler from the stack */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void print_version (void) {
|
||||
lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
|
||||
lua_writeline();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create the 'arg' table, which stores all arguments from the
|
||||
** command line ('argv'). It should be aligned so that, at index 0,
|
||||
** it has 'argv[script]', which is the script name. The arguments
|
||||
** to the script (everything after 'script') go to positive indices;
|
||||
** other arguments (before the script name) go to negative indices.
|
||||
** If there is no script name, assume interpreter's name as base.
|
||||
** (If there is no interpreter's name either, 'script' is -1, so
|
||||
** table sizes are zero.)
|
||||
*/
|
||||
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||
int i, narg;
|
||||
narg = argc - (script + 1); /* number of positive indices */
|
||||
lua_createtable(L, narg, script + 1);
|
||||
for (i = 0; i < argc; i++) {
|
||||
lua_pushstring(L, argv[i]);
|
||||
lua_rawseti(L, -2, i - script);
|
||||
}
|
||||
lua_setglobal(L, "arg");
|
||||
}
|
||||
|
||||
|
||||
static int dochunk (lua_State *L, int status) {
|
||||
if (status == LUA_OK) status = docall(L, 0, 0);
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
static int dofile (lua_State *L, const char *name) {
|
||||
return dochunk(L, luaL_loadfile(L, name));
|
||||
}
|
||||
|
||||
|
||||
static int dostring (lua_State *L, const char *s, const char *name) {
|
||||
return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
|
||||
** If there is no explicit modname and globname contains a '-', cut
|
||||
** the suffix after '-' (the "version") to make the global name.
|
||||
*/
|
||||
static int dolibrary (lua_State *L, char *globname) {
|
||||
int status;
|
||||
char *suffix = NULL;
|
||||
char *modname = strchr(globname, '=');
|
||||
if (modname == NULL) { /* no explicit name? */
|
||||
modname = globname; /* module name is equal to global name */
|
||||
suffix = strchr(modname, *LUA_IGMARK); /* look for a suffix mark */
|
||||
}
|
||||
else {
|
||||
*modname = '\0'; /* global name ends here */
|
||||
modname++; /* module name starts after the '=' */
|
||||
}
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, modname);
|
||||
status = docall(L, 1, 1); /* call 'require(modname)' */
|
||||
if (status == LUA_OK) {
|
||||
if (suffix != NULL) /* is there a suffix mark? */
|
||||
*suffix = '\0'; /* remove suffix from global name */
|
||||
lua_setglobal(L, globname); /* globname = require(modname) */
|
||||
}
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Push on the stack the contents of table 'arg' from 1 to #arg
|
||||
*/
|
||||
static int pushargs (lua_State *L) {
|
||||
int i, n;
|
||||
if (lua_getglobal(L, "arg") != LUA_TTABLE)
|
||||
luaL_error(L, "'arg' is not a table");
|
||||
n = (int)luaL_len(L, -1);
|
||||
luaL_checkstack(L, n + 3, "too many arguments to script");
|
||||
for (i = 1; i <= n; i++)
|
||||
lua_rawgeti(L, -i, i);
|
||||
lua_remove(L, -i); /* remove table from the stack */
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static int handle_script (lua_State *L, char **argv) {
|
||||
int status;
|
||||
const char *fname = argv[0];
|
||||
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
|
||||
fname = NULL; /* stdin */
|
||||
status = luaL_loadfile(L, fname);
|
||||
if (status == LUA_OK) {
|
||||
int n = pushargs(L); /* push arguments to script */
|
||||
status = docall(L, n, LUA_MULTRET);
|
||||
}
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
|
||||
/* bits of various argument indicators in 'args' */
|
||||
#define has_error 1 /* bad option */
|
||||
#define has_i 2 /* -i */
|
||||
#define has_v 4 /* -v */
|
||||
#define has_e 8 /* -e */
|
||||
#define has_E 16 /* -E */
|
||||
|
||||
|
||||
/*
|
||||
** Traverses all arguments from 'argv', returning a mask with those
|
||||
** needed before running any Lua code or an error code if it finds any
|
||||
** invalid argument. In case of error, 'first' is the index of the bad
|
||||
** argument. Otherwise, 'first' is -1 if there is no program name,
|
||||
** 0 if there is no script name, or the index of the script name.
|
||||
*/
|
||||
static int collectargs (char **argv, int *first) {
|
||||
int args = 0;
|
||||
int i;
|
||||
if (argv[0] != NULL) { /* is there a program name? */
|
||||
if (argv[0][0]) /* not empty? */
|
||||
progname = argv[0]; /* save it */
|
||||
}
|
||||
else { /* no program name */
|
||||
*first = -1;
|
||||
return 0;
|
||||
}
|
||||
for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
|
||||
*first = i;
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return args; /* stop handling options */
|
||||
switch (argv[i][1]) { /* else check option */
|
||||
case '-': /* '--' */
|
||||
if (argv[i][2] != '\0') /* extra characters after '--'? */
|
||||
return has_error; /* invalid option */
|
||||
*first = i + 1;
|
||||
return args;
|
||||
case '\0': /* '-' */
|
||||
return args; /* script "name" is '-' */
|
||||
case 'E':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_E;
|
||||
break;
|
||||
case 'W':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
break;
|
||||
case 'i':
|
||||
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
|
||||
case 'v':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_v;
|
||||
break;
|
||||
case 'e':
|
||||
args |= has_e; /* FALLTHROUGH */
|
||||
case 'l': /* both options need an argument */
|
||||
if (argv[i][2] == '\0') { /* no concatenated argument? */
|
||||
i++; /* try next 'argv' */
|
||||
if (argv[i] == NULL || argv[i][0] == '-')
|
||||
return has_error; /* no next argument or it is another option */
|
||||
}
|
||||
break;
|
||||
default: /* invalid option */
|
||||
return has_error;
|
||||
}
|
||||
}
|
||||
*first = 0; /* no script name */
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Processes options 'e' and 'l', which involve running Lua code, and
|
||||
** 'W', which also affects the state.
|
||||
** Returns 0 if some code raises an error.
|
||||
*/
|
||||
static int runargs (lua_State *L, char **argv, int n) {
|
||||
int i;
|
||||
for (i = 1; i < n; i++) {
|
||||
int option = argv[i][1];
|
||||
lua_assert(argv[i][0] == '-'); /* already checked */
|
||||
switch (option) {
|
||||
case 'e': case 'l': {
|
||||
int status;
|
||||
char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
? dostring(L, extra, "=(command line)")
|
||||
: dolibrary(L, extra);
|
||||
if (status != LUA_OK) return 0;
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
lua_warning(L, "@on", 0); /* warnings on */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int handle_luainit (lua_State *L) {
|
||||
const char *name = "=" LUA_INITVARVERSION;
|
||||
const char *init = getenv(name + 1);
|
||||
if (init == NULL) {
|
||||
name = "=" LUA_INIT_VAR;
|
||||
init = getenv(name + 1); /* try alternative name */
|
||||
}
|
||||
if (init == NULL) return LUA_OK;
|
||||
else if (init[0] == '@')
|
||||
return dofile(L, init+1);
|
||||
else
|
||||
return dostring(L, init, name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Read-Eval-Print Loop (REPL)
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(LUA_PROMPT)
|
||||
#define LUA_PROMPT "> "
|
||||
#define LUA_PROMPT2 ">> "
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_MAXINPUT)
|
||||
#define LUA_MAXINPUT 512
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
|
||||
** is, whether we're running lua interactively).
|
||||
*/
|
||||
#if !defined(lua_stdin_is_tty) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
#include <unistd.h>
|
||||
#define lua_stdin_is_tty() isatty(0)
|
||||
|
||||
#elif defined(LUA_USE_WINDOWS) /* }{ */
|
||||
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define lua_stdin_is_tty() _isatty(_fileno(stdin))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definition */
|
||||
#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** lua_readline defines how to show a prompt and then read a line from
|
||||
** the standard input.
|
||||
** lua_saveline defines how to "save" a read line in a "history".
|
||||
** lua_freeline defines how to free a line read by lua_readline.
|
||||
*/
|
||||
#if !defined(lua_readline) /* { */
|
||||
|
||||
#if defined(LUA_USE_READLINE) /* { */
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
|
||||
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
|
||||
#define lua_saveline(L,line) ((void)L, add_history(line))
|
||||
#define lua_freeline(L,b) ((void)L, free(b))
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define lua_initreadline(L) ((void)L)
|
||||
#define lua_readline(L,b,p) \
|
||||
((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
|
||||
fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
|
||||
#define lua_saveline(L,line) { (void)L; (void)line; }
|
||||
#define lua_freeline(L,b) { (void)L; (void)b; }
|
||||
|
||||
#endif /* } */
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Return the string to be used as a prompt by the interpreter. Leave
|
||||
** the string (or nil, if using the default value) on the stack, to keep
|
||||
** it anchored.
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||
else { /* apply 'tostring' over the value */
|
||||
const char *p = luaL_tolstring(L, -1, NULL);
|
||||
lua_remove(L, -2); /* remove original value */
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
#define EOFMARK "<eof>"
|
||||
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' signals a syntax error and the error
|
||||
** message at the top of the stack ends with the above mark for
|
||||
** incomplete statements.
|
||||
*/
|
||||
static int incomplete (lua_State *L, int status) {
|
||||
if (status == LUA_ERRSYNTAX) {
|
||||
size_t lmsg;
|
||||
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0; /* else... */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prompt the user, read a line, and push it into the Lua stack.
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
char buffer[LUA_MAXINPUT];
|
||||
char *b = buffer;
|
||||
size_t l;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
int readstatus = lua_readline(L, b, prmt);
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
if (readstatus == 0)
|
||||
return 0; /* no input */
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||
else
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to compile line on the stack as 'return <line>;'; on return, stack
|
||||
** has either compiled chunk or original line (if compilation failed).
|
||||
*/
|
||||
static int addreturn (lua_State *L) {
|
||||
const char *line = lua_tostring(L, -1); /* original line */
|
||||
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||
if (status == LUA_OK) {
|
||||
lua_remove(L, -2); /* remove modified line */
|
||||
if (line[0] != '\0') /* non empty? */
|
||||
lua_saveline(L, line); /* keep history */
|
||||
}
|
||||
else
|
||||
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read multiple lines until a complete Lua statement
|
||||
*/
|
||||
static int multiline (lua_State *L) {
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
lua_saveline(L, line); /* keep history */
|
||||
return status; /* should not or cannot try to add continuation line */
|
||||
}
|
||||
lua_remove(L, -2); /* remove error message (from incomplete line) */
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a line and try to load (compile) it first as an expression (by
|
||||
** adding "return " in front of it) and second as a statement. Return
|
||||
** the final status of load/call with the resulting function (if any)
|
||||
** in the top of the stack.
|
||||
*/
|
||||
static int loadline (lua_State *L) {
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints (calling the Lua 'print' function) any values on the stack
|
||||
*/
|
||||
static void l_print (lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
if (n > 0) { /* any result to be printed? */
|
||||
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
|
||||
lua_getglobal(L, "print");
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, n, 0, 0) != LUA_OK)
|
||||
l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
|
||||
lua_tostring(L, -1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
|
||||
** print any results.
|
||||
*/
|
||||
static void doREPL (lua_State *L) {
|
||||
int status;
|
||||
const char *oldprogname = progname;
|
||||
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||
lua_initreadline(L);
|
||||
while ((status = loadline(L)) != -1) {
|
||||
if (status == LUA_OK)
|
||||
status = docall(L, 0, LUA_MULTRET);
|
||||
if (status == LUA_OK) l_print(L);
|
||||
else report(L, status);
|
||||
}
|
||||
lua_settop(L, 0); /* clear stack */
|
||||
lua_writeline();
|
||||
progname = oldprogname;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
/*
|
||||
** Main body of stand-alone interpreter (to be called in protected mode).
|
||||
** Reads the options and handles them all.
|
||||
*/
|
||||
extern int luaopen_lpeg(lua_State *L);
|
||||
static int pmain (lua_State *L) {
|
||||
int argc = (int)lua_tointeger(L, 1);
|
||||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int args = collectargs(argv, &script);
|
||||
int optlim = (script > 0) ? script : argc; /* first argv not an option */
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
}
|
||||
if (args & has_v) /* option '-v'? */
|
||||
print_version();
|
||||
if (args & has_E) { /* option '-E'? */
|
||||
lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||
}
|
||||
luaL_openlibs(L); /* open standard libraries */
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
lua_pushcfunction(L, luaopen_lpeg);
|
||||
lua_setfield(L, -2, "lpeg");
|
||||
lua_pop(L, 1); // remove PRELOAD table
|
||||
|
||||
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||
lua_gc(L, LUA_GCRESTART); /* start GC... */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
|
||||
if (!(args & has_E)) { /* no option '-E'? */
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
}
|
||||
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script > 0) { /* execute main script (if there is one) */
|
||||
if (handle_script(L, argv + script) != LUA_OK)
|
||||
return 0; /* interrupt in case of error */
|
||||
}
|
||||
if (args & has_i) /* -i option? */
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
|
||||
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||
print_version();
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
}
|
||||
else dofile(L, NULL); /* executes stdin as a file */
|
||||
}
|
||||
lua_pushboolean(L, 1); /* signal no errors */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
int status, result;
|
||||
lua_State *L = luaL_newstate(); /* create state */
|
||||
if (L == NULL) {
|
||||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
lua_pushinteger(L, argc); /* 1st argument */
|
||||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
||||
result = lua_toboolean(L, -1); /* get result */
|
||||
report(L, status);
|
||||
lua_close(L);
|
||||
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
830
terrain.cpp
830
terrain.cpp
File diff suppressed because it is too large
Load Diff
8
tests/CMakeLists.txt
Normal file
8
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
project(tests)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bullet CONFIG)
|
||||
find_package(Bullet REQUIRED)
|
||||
add_executable(compound_shapes compound_shapes.cpp)
|
||||
target_link_libraries(compound_shapes OgreBullet ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY})
|
||||
include_directories(${BULLET_INCLUDE_DIRS})
|
||||
|
||||
add_custom_target(tests ALL DEPENDS compound_shapes)
|
||||
290
tests/compound_shapes.cpp
Normal file
290
tests/compound_shapes.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include <iostream>
|
||||
#include <OgreBullet.h>
|
||||
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
|
||||
#include <btBulletCollisionCommon.h>
|
||||
#include <btBulletDynamicsCommon.h>
|
||||
|
||||
struct objects {
|
||||
btDynamicsWorld *world;
|
||||
btRigidBody *groundBody;
|
||||
btPairCachingGhostObject *characterBody;
|
||||
btPairCachingGhostObject *waterBody;
|
||||
};
|
||||
void setupGround(btDynamicsWorld *world, struct objects *objects)
|
||||
{
|
||||
// Static ground
|
||||
btCollisionShape *groundShape =
|
||||
new btBoxShape(btVector3(1000, 1, 1000));
|
||||
btDefaultMotionState *groundMotionState = new btDefaultMotionState(
|
||||
btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, -10, 0)));
|
||||
btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(
|
||||
0, groundMotionState, groundShape, btVector3(0, 0, 0));
|
||||
btRigidBody *groundRigidBody = new btRigidBody(groundRigidBodyCI);
|
||||
world->addRigidBody(groundRigidBody, 2, 0x7fffffff & ~16);
|
||||
objects->groundBody = groundRigidBody;
|
||||
}
|
||||
void setupCharacter(btDynamicsWorld *world, struct objects *objects)
|
||||
{
|
||||
btCompoundShape *shape = new btCompoundShape();
|
||||
btCapsuleShape *subshape = new btCapsuleShape(0.2f, 1.5f);
|
||||
btTransform shapePos;
|
||||
shapePos.setIdentity();
|
||||
shapePos.setOrigin(btVector3(0, 0.75f, 0));
|
||||
shape->addChildShape(shapePos, subshape);
|
||||
btPairCachingGhostObject *characterBody =
|
||||
new btPairCachingGhostObject();
|
||||
characterBody->setCollisionFlags(
|
||||
characterBody->getCollisionFlags() |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||
btTransform bodyPos;
|
||||
bodyPos.setIdentity();
|
||||
bodyPos.setOrigin(btVector3(0, 0.4f, 0));
|
||||
characterBody->setWorldTransform(bodyPos);
|
||||
characterBody->setCollisionShape(shape);
|
||||
world->addCollisionObject(characterBody, 1, 0x7fffffff);
|
||||
objects->characterBody = characterBody;
|
||||
}
|
||||
void setupWater(btDynamicsWorld *world, struct objects *objects)
|
||||
{
|
||||
btCompoundShape *shape = new btCompoundShape();
|
||||
btBoxShape *subshape =
|
||||
new btBoxShape(btVector3(1000.0f, 100.0f, 1000.0f));
|
||||
btTransform shapePos;
|
||||
shapePos.setIdentity();
|
||||
shapePos.setOrigin(btVector3(0, -100, 0));
|
||||
shape->addChildShape(shapePos, subshape);
|
||||
btPairCachingGhostObject *waterBody = new btPairCachingGhostObject();
|
||||
waterBody->setCollisionFlags(waterBody->getCollisionFlags() |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT);
|
||||
btTransform bodyPos;
|
||||
bodyPos.setIdentity();
|
||||
waterBody->setWorldTransform(bodyPos);
|
||||
waterBody->setCollisionShape(shape);
|
||||
world->addCollisionObject(waterBody, 16, 0x7fffffff & ~2);
|
||||
objects->waterBody = waterBody;
|
||||
}
|
||||
struct DeepPenetrationContactResultCallback : public btManifoldResult {
|
||||
DeepPenetrationContactResultCallback(
|
||||
const btCollisionObjectWrapper *body0Wrap,
|
||||
const btCollisionObjectWrapper *body1Wrap)
|
||||
: btManifoldResult(body0Wrap, body1Wrap)
|
||||
, mPenetrationDistance(0)
|
||||
, mOtherIndex(0)
|
||||
{
|
||||
}
|
||||
float mPenetrationDistance;
|
||||
int mOtherIndex;
|
||||
btVector3 mNormal, mPoint;
|
||||
void reset()
|
||||
{
|
||||
mPenetrationDistance = 0.0f;
|
||||
}
|
||||
bool hasHit()
|
||||
{
|
||||
return mPenetrationDistance < 0.0f;
|
||||
}
|
||||
virtual void addContactPoint(const btVector3 &normalOnBInWorld,
|
||||
const btVector3 &pointInWorldOnB,
|
||||
btScalar depth)
|
||||
{
|
||||
std::cout
|
||||
<< "contact: " << Ogre::Bullet::convert(pointInWorldOnB)
|
||||
<< " " << Ogre::Bullet::convert(normalOnBInWorld)
|
||||
<< "\n";
|
||||
if (mPenetrationDistance > depth) { // Has penetration?
|
||||
|
||||
const bool isSwapped =
|
||||
m_manifoldPtr->getBody0() !=
|
||||
m_body0Wrap->getCollisionObject();
|
||||
mPenetrationDistance = depth;
|
||||
mOtherIndex = isSwapped ? m_index0 : m_index1;
|
||||
mPoint = isSwapped ? (pointInWorldOnB +
|
||||
(normalOnBInWorld * depth)) :
|
||||
pointInWorldOnB;
|
||||
|
||||
mNormal = isSwapped ? normalOnBInWorld * -1 :
|
||||
normalOnBInWorld;
|
||||
}
|
||||
}
|
||||
};
|
||||
void dumpCompoundShape(const char *what, const btCollisionObject *body)
|
||||
{
|
||||
if (body->getCollisionShape()->isCompound()) {
|
||||
const btCompoundShape *shape =
|
||||
static_cast<const btCompoundShape *>(
|
||||
body->getCollisionShape());
|
||||
int i;
|
||||
for (i = 0; i < shape->getNumChildShapes(); i++) {
|
||||
btTransform transform = body->getWorldTransform() *
|
||||
shape->getChildTransform(i);
|
||||
std::cout << what << ": " << " shape: " << i << ": "
|
||||
<< transform.getOrigin().getX() << ", ";
|
||||
std::cout << transform.getOrigin().getY() << ", ";
|
||||
std::cout << transform.getOrigin().getZ();
|
||||
std::cout << " convex: "
|
||||
<< shape->getChildShape(i)->isConvex();
|
||||
std::cout << " shape name: "
|
||||
<< shape->getChildShape(i)->getName();
|
||||
switch (shape->getChildShape(i)->getShapeType()) {
|
||||
case 0: {
|
||||
const btBoxShape *box =
|
||||
static_cast<const btBoxShape *>(
|
||||
shape->getChildShape(i));
|
||||
btVector3 hextents =
|
||||
box->getHalfExtentsWithoutMargin();
|
||||
std::cout << " box: " << hextents.getX() << ", "
|
||||
<< hextents.getY() << ", "
|
||||
<< hextents.getZ();
|
||||
} break;
|
||||
case 10: {
|
||||
const btCapsuleShape *capsule =
|
||||
static_cast<const btCapsuleShape *>(
|
||||
shape->getChildShape(i));
|
||||
float hh = capsule->getHalfHeight();
|
||||
float r = capsule->getRadius();
|
||||
std::cout << " capsule: " << hh << ", " << r;
|
||||
} break;
|
||||
default:
|
||||
std::cout << " shape type: "
|
||||
<< shape->getChildShape(i)
|
||||
->getShapeType();
|
||||
break;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
void showContacts(btCollisionWorld *world, struct objects *objects)
|
||||
{
|
||||
int numManifolds = world->getDispatcher()->getNumManifolds();
|
||||
if (numManifolds == 0)
|
||||
std::cout << "No contacts in world\n";
|
||||
for (int i = 0; i < numManifolds; i++) {
|
||||
btPersistentManifold *contactManifold =
|
||||
world->getDispatcher()->getManifoldByIndexInternal(i);
|
||||
const btCollisionObject *obA = contactManifold->getBody0();
|
||||
const btCollisionObject *obB = contactManifold->getBody1();
|
||||
|
||||
int numContacts = contactManifold->getNumContacts();
|
||||
for (int j = 0; j < numContacts; j++) {
|
||||
btManifoldPoint &pt =
|
||||
contactManifold->getContactPoint(j);
|
||||
if (pt.getDistance() < 0.f) {
|
||||
const btVector3 &ptA = pt.getPositionWorldOnA();
|
||||
const btVector3 &ptB = pt.getPositionWorldOnB();
|
||||
const btVector3 &normalOnB =
|
||||
pt.m_normalWorldOnB;
|
||||
std::cout << "contact: " << i << " " << j << " "
|
||||
<< ptA.getX() << ", " << ptA.getY()
|
||||
<< ", " << ptA.getZ() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int main()
|
||||
{
|
||||
struct objects objects;
|
||||
btDefaultCollisionConfiguration collisionConfig;
|
||||
btCollisionDispatcher dispatcher(&collisionConfig);
|
||||
btCollisionDispatcher *dispatch = &dispatcher;
|
||||
btDbvtBroadphase broadphase;
|
||||
btSequentialImpulseConstraintSolver solver;
|
||||
btDiscreteDynamicsWorld world(&dispatcher, &broadphase, &solver,
|
||||
&collisionConfig);
|
||||
world.setGravity(btVector3(0, -9.81, 0));
|
||||
setupGround(&world, &objects);
|
||||
setupCharacter(&world, &objects);
|
||||
setupWater(&world, &objects);
|
||||
|
||||
#if 0
|
||||
btCompoundShape *compoundShape = new btCompoundShape();
|
||||
|
||||
// Add shapes to the compound shape
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
btBoxShape *box = new btBoxShape(btVector3(1, 1, 1));
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1 - i, 0));
|
||||
compoundShape->addChildShape(transform, box);
|
||||
}
|
||||
|
||||
btDefaultMotionState *compoundMotionState = new btDefaultMotionState(
|
||||
btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 10, 0)));
|
||||
btScalar mass = 5.0;
|
||||
btVector3 inertia(0, 0, 0);
|
||||
compoundShape->calculateLocalInertia(mass, inertia);
|
||||
btRigidBody::btRigidBodyConstructionInfo compoundRigidBodyCI(
|
||||
mass, compoundMotionState, compoundShape, inertia);
|
||||
btRigidBody *compoundRigidBody = new btRigidBody(compoundRigidBodyCI);
|
||||
world.addRigidBody(compoundRigidBody);
|
||||
#endif
|
||||
|
||||
// Simulation
|
||||
btVector3 velocity(0, 0, 0);
|
||||
for (int i = 0; i < 300; i++) {
|
||||
world.stepSimulation(1.f / 60.f, 10);
|
||||
showContacts(&world, &objects);
|
||||
|
||||
#if 0
|
||||
btTransform transform;
|
||||
compoundRigidBody->getMotionState()->getWorldTransform(
|
||||
transform);
|
||||
std::cout << "Step " << i
|
||||
<< ": Position = " << transform.getOrigin().getX()
|
||||
<< ", " << transform.getOrigin().getY() << ", "
|
||||
<< transform.getOrigin().getZ() << std::endl;
|
||||
std::cout << "water overlaps:"
|
||||
<< objects.waterBody->getOverlappingPairCache()
|
||||
->getNumOverlappingPairs()
|
||||
<< "\n";
|
||||
#endif
|
||||
btTransform transform;
|
||||
transform = objects.characterBody->getWorldTransform();
|
||||
std::cout << "Step " << i << " ";
|
||||
std::cout << "Character: "
|
||||
<< "Position = " << transform.getOrigin().getX()
|
||||
<< ", " << transform.getOrigin().getY() << ", "
|
||||
<< transform.getOrigin().getZ() << std::endl;
|
||||
transform = objects.waterBody->getWorldTransform();
|
||||
std::cout << "Water: "
|
||||
<< "Position = " << transform.getOrigin().getX()
|
||||
<< ", " << transform.getOrigin().getY() << ", "
|
||||
<< transform.getOrigin().getZ() << std::endl;
|
||||
std::cout << "water overlaps:"
|
||||
<< objects.waterBody->getOverlappingPairCache()
|
||||
->getNumOverlappingPairs()
|
||||
<< "\n";
|
||||
dumpCompoundShape("Character", objects.characterBody);
|
||||
dumpCompoundShape("Water", objects.waterBody);
|
||||
btCollisionObjectWrapper obA(
|
||||
NULL, objects.waterBody->getCollisionShape(),
|
||||
objects.waterBody,
|
||||
objects.waterBody->getWorldTransform(), -1, 0);
|
||||
btCollisionObjectWrapper obB(
|
||||
NULL, objects.characterBody->getCollisionShape(),
|
||||
objects.characterBody,
|
||||
objects.characterBody->getWorldTransform(), -1, 0);
|
||||
std::cout << __func__ << ": call findAlgorithm: ";
|
||||
btCollisionAlgorithm *algorithm = dispatch->findAlgorithm(
|
||||
&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS);
|
||||
std::cout << "found algorithm: " << algorithm << "\n";
|
||||
if (!algorithm)
|
||||
continue;
|
||||
std::cout << "algorithm: " << algorithm << "\n";
|
||||
DeepPenetrationContactResultCallback contactPointResult(&obA,
|
||||
&obB);
|
||||
std::cout << "process collision\n";
|
||||
algorithm->processCollision(&obA, &obB, world.getDispatchInfo(),
|
||||
&contactPointResult);
|
||||
algorithm->~btCollisionAlgorithm();
|
||||
dispatch->freeCollisionAlgorithm(algorithm);
|
||||
if (contactPointResult.hasHit()) {
|
||||
std::cout << "InWater!!1\n";
|
||||
}
|
||||
velocity += btVector3(0, -9.8, 0) * 1.0f / 16.0f;
|
||||
objects.characterBody->getWorldTransform().getOrigin() += velocity * 1.0f / 16.0f;
|
||||
std::cout << "process collision done\n";
|
||||
}
|
||||
}
|
||||
BIN
world_map.kra
LFS
BIN
world_map.kra
LFS
Binary file not shown.
Reference in New Issue
Block a user