latest code update
- app icon - starting with map view - code cleanup Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -111,3 +111,5 @@ CMakeUserPresets.json
|
|||||||
# End of https://www.toptal.com/developers/gitignore/api/cmake,clion+all
|
# End of https://www.toptal.com/developers/gitignore/api/cmake,clion+all
|
||||||
|
|
||||||
cartridges/
|
cartridges/
|
||||||
|
assets/icon.iconset/
|
||||||
|
*.lua
|
||||||
|
|||||||
@@ -27,21 +27,25 @@ FetchContent_MakeAvailable(wxWidgets)
|
|||||||
message(STATUS "Configure project....")
|
message(STATUS "Configure project....")
|
||||||
|
|
||||||
set(SRCS
|
set(SRCS
|
||||||
main/src/main.cpp
|
|
||||||
main/src/app.cpp
|
main/src/app.cpp
|
||||||
main/src/ui/start_screen.cpp
|
main/src/main.cpp
|
||||||
main/src/ui/game_screen.cpp
|
main/src/ui/game_screen.cpp
|
||||||
|
main/src/ui/map_sim_frame.cpp
|
||||||
|
main/src/ui/start_screen.cpp
|
||||||
main/src/ui/wherigo_dialog.cpp
|
main/src/ui/wherigo_dialog.cpp
|
||||||
main/src/lua/wherigo.cpp
|
|
||||||
main/src/lua/zobject.cpp
|
|
||||||
main/src/lua/ztimer.cpp
|
|
||||||
main/src/lua/game_engine.cpp
|
main/src/lua/game_engine.cpp
|
||||||
main/src/lua/media_manager.cpp
|
main/src/lua/media_manager.cpp
|
||||||
main/src/lua/persistence.cpp
|
main/src/lua/persistence.cpp
|
||||||
|
main/src/lua/wherigo.cpp
|
||||||
main/src/lua/wherigo_completion.cpp
|
main/src/lua/wherigo_completion.cpp
|
||||||
|
main/src/lua/zobject.cpp
|
||||||
|
main/src/lua/ztimer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/assets/Info.plist.in
|
||||||
|
${CMAKE_BINARY_DIR}/Info.plist @ONLY)
|
||||||
|
set(MACOSX_BUNDLE_ICON_FILE icon.icns)
|
||||||
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRCS})
|
add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SRCS})
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
BUNDLE True
|
BUNDLE True
|
||||||
@@ -49,7 +53,8 @@ if (APPLE)
|
|||||||
MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}
|
MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
|
MACOSX_BUNDLE_BUNDLE_VERSION "0.1"
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
|
MACOSX_BUNDLE_SHORT_VERSION_STRING "0.1"
|
||||||
#MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/customtemplate.plist.in
|
MACOSX_BUNDLE_ICON_FILE icon.icns
|
||||||
|
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist
|
||||||
INSTALL_RPATH "@executable_path"
|
INSTALL_RPATH "@executable_path"
|
||||||
)
|
)
|
||||||
else ()
|
else ()
|
||||||
@@ -61,14 +66,14 @@ include_directories(main/include)
|
|||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
wxcore
|
wx::net
|
||||||
wxnet
|
wx::html
|
||||||
wxbase
|
wx::webview
|
||||||
wxhtml
|
|
||||||
lua
|
lua
|
||||||
cartridge
|
cartridge
|
||||||
storage
|
storage
|
||||||
)
|
)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${wxWidgets_INCLUDE_DIRS})
|
||||||
|
|
||||||
# Kopiere die .dylib-Dateien ins Bundle
|
# Kopiere die .dylib-Dateien ins Bundle
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
@@ -78,5 +83,11 @@ if (APPLE)
|
|||||||
$<TARGET_FILE:cartridge>
|
$<TARGET_FILE:cartridge>
|
||||||
$<TARGET_FILE:storage>
|
$<TARGET_FILE:storage>
|
||||||
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/MacOS/"
|
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/MacOS/"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_SOURCE_DIR}/assets/icon.icns
|
||||||
|
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/Resources/icon.icns"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${CMAKE_BINARY_DIR}/Info.plist
|
||||||
|
"$<TARGET_BUNDLE_DIR:${PROJECT_NAME}>/Contents/Info.plist"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
22
assets/Info.plist.in
Normal file
22
assets/Info.plist.in
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>icon.icns</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>@CMAKE_PROJECT_NAME@</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>dev.mars3142.@CMAKE_PROJECT_NAME@</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>@PROJECT_VERSION@</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>@PROJECT_VERSION@</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.13</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
assets/icon.icns
Normal file
BIN
assets/icon.icns
Normal file
Binary file not shown.
BIN
assets/icon.png
LFS
Normal file
BIN
assets/icon.png
LFS
Normal file
Binary file not shown.
25
assets/make_icns.sh
Executable file
25
assets/make_icns.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ICONSET="icon.iconset"
|
||||||
|
PNG="$1"
|
||||||
|
|
||||||
|
if [ -z "$PNG" ]; then
|
||||||
|
echo "Usage: $0 icon.png"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $ICONSET
|
||||||
|
|
||||||
|
# Erzeuge alle Größen
|
||||||
|
sips -z 16 16 "$PNG" --out $ICONSET/icon_16x16.png
|
||||||
|
sips -z 32 32 "$PNG" --out $ICONSET/icon_16x16@2x.png
|
||||||
|
sips -z 32 32 "$PNG" --out $ICONSET/icon_32x32.png
|
||||||
|
sips -z 64 64 "$PNG" --out $ICONSET/icon_32x32@2x.png
|
||||||
|
sips -z 128 128 "$PNG" --out $ICONSET/icon_128x128.png
|
||||||
|
sips -z 256 256 "$PNG" --out $ICONSET/icon_128x128@2x.png
|
||||||
|
sips -z 256 256 "$PNG" --out $ICONSET/icon_256x256.png
|
||||||
|
sips -z 512 512 "$PNG" --out $ICONSET/icon_256x256@2x.png
|
||||||
|
sips -z 512 512 "$PNG" --out $ICONSET/icon_512x512.png
|
||||||
|
sips -z 1024 1024 "$PNG" --out $ICONSET/icon_512x512@2x.png
|
||||||
|
|
||||||
|
iconutil -c icns icon.iconset
|
||||||
@@ -23,6 +23,4 @@ std::unique_ptr<Cartridge> parseData(const std::vector<uint8_t>& bytes);
|
|||||||
|
|
||||||
std::unique_ptr<Cartridge> parseFile(const std::string& filePath);
|
std::unique_ptr<Cartridge> parseFile(const std::string& filePath);
|
||||||
|
|
||||||
std::unique_ptr<Cartridge> parseCartridge();
|
|
||||||
|
|
||||||
} // namespace cartridge
|
} // namespace cartridge
|
||||||
|
|||||||
@@ -57,45 +57,4 @@ std::unique_ptr<Cartridge> parseFile(const std::string &filePath) {
|
|||||||
return parseData(*result);
|
return parseData(*result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Cartridge> parseCartridge() {
|
|
||||||
auto cartridge = parseFile("/Volumes/Coding/git.mars3142.dev/mars3142/"
|
|
||||||
"wx_wherigo/cartridges/the_ombos_idol_-_c.gwc");
|
|
||||||
if (!cartridge) {
|
|
||||||
std::println(std::cerr, "Cartridge konnte nicht geladen werden.");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int count = cartridge->mediaCount();
|
|
||||||
const std::filesystem::path outDir =
|
|
||||||
"/Volumes/Coding/git.mars3142.dev/mars3142/wx_wherigo/cartridges";
|
|
||||||
storage::Storage storage;
|
|
||||||
|
|
||||||
for (const int i : std::views::iota(0, count)) {
|
|
||||||
auto media = cartridge->getMedia(i);
|
|
||||||
if (!media) {
|
|
||||||
std::println(std::cerr, "Media-Objekt an Index {} ist nullptr.", i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &data = media->getData();
|
|
||||||
if (data.empty()) {
|
|
||||||
std::println(std::cerr, "Media-Daten an Index {} sind leer.", i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ext = media->getObjectType();
|
|
||||||
if (ext.empty()) {
|
|
||||||
std::println(std::cerr, "Media-Extension an Index {} ist leer.", i);
|
|
||||||
ext = "bin";
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::filesystem::path outFile = outDir / std::format("file{}.{}", i, ext);
|
|
||||||
|
|
||||||
if (auto writeResult = storage.writeFile(outFile.string(), data); !writeResult) {
|
|
||||||
std::println(std::cerr, "Fehler beim Schreiben der Datei: {}", outFile.string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cartridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cartridge
|
} // namespace cartridge
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
int getCartridgeRef() const { return m_cartridgeRef; }
|
int getCartridgeRef() const { return m_cartridgeRef; }
|
||||||
cartridge::Cartridge* getCartridge() const { return m_cartridge.get(); }
|
cartridge::Cartridge* getCartridge() const { return m_cartridge.get(); }
|
||||||
bool isCartridgeLoaded() const { return m_cartridge != nullptr; }
|
bool isCartridgeLoaded() const { return m_cartridge != nullptr; }
|
||||||
|
std::string getCartridgePath() const { return m_cartridgePath; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initLuaState();
|
bool initLuaState();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
#include <wx/listbox.h>
|
#include <wx/listbox.h>
|
||||||
#include <wx/notebook.h>
|
#include <wx/notebook.h>
|
||||||
|
#include "ui/map_sim_frame.h"
|
||||||
|
|
||||||
class cGameScreen : public wxFrame
|
class cGameScreen : public wxFrame
|
||||||
{
|
{
|
||||||
@@ -24,6 +25,7 @@ private:
|
|||||||
void OnInventorySelected(wxCommandEvent& event);
|
void OnInventorySelected(wxCommandEvent& event);
|
||||||
void OnCharacterSelected(wxCommandEvent& event);
|
void OnCharacterSelected(wxCommandEvent& event);
|
||||||
void OnItemSelected(wxCommandEvent& event);
|
void OnItemSelected(wxCommandEvent& event);
|
||||||
|
void OnMapSim(wxCommandEvent& event);
|
||||||
|
|
||||||
void populateZones();
|
void populateZones();
|
||||||
void populateTasks();
|
void populateTasks();
|
||||||
|
|||||||
25
main/include/ui/map_sim_frame.h
Normal file
25
main/include/ui/map_sim_frame.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <wx/frame.h>
|
||||||
|
#include <wx/webview.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
struct SimPoint {
|
||||||
|
double lat;
|
||||||
|
double lon;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MapSimFrame : public wxFrame {
|
||||||
|
public:
|
||||||
|
MapSimFrame(wxWindow* parent, double centerLat = 53.3, double centerLon = 10.39, const std::vector<std::pair<double, double>>& zoneCoords = {});
|
||||||
|
void AddSimPoint(double lat, double lon);
|
||||||
|
void StartSimulation();
|
||||||
|
private:
|
||||||
|
wxWebView* m_webView;
|
||||||
|
std::vector<SimPoint> m_route;
|
||||||
|
std::vector<std::pair<double, double>> m_zoneCoords;
|
||||||
|
void OnWebViewEvent(wxWebViewEvent& event);
|
||||||
|
void OnPlay(wxCommandEvent& event);
|
||||||
|
void SendPositionToEngine(double lat, double lon);
|
||||||
|
wxDECLARE_EVENT_TABLE();
|
||||||
|
};
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
#include <wx/statbmp.h>
|
#include <wx/statbmp.h>
|
||||||
#include <wx/html/htmlwin.h>
|
#include <wx/html/htmlwin.h>
|
||||||
|
#include "ui/map_sim_frame.h"
|
||||||
|
|
||||||
class cGameScreen;
|
class cGameScreen;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ extern "C" {
|
|||||||
|
|
||||||
bool cApp::OnInit()
|
bool cApp::OnInit()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Initialize image handlers for JPEG, PNG, GIF, etc.
|
// Initialize image handlers for JPEG, PNG, GIF, etc.
|
||||||
wxInitAllImageHandlers();
|
wxInitAllImageHandlers();
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ extern "C" {
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
namespace wherigo {
|
namespace wherigo {
|
||||||
|
|
||||||
@@ -82,10 +83,42 @@ void LuaPersistence::serializeValue(lua_State* L, int index, std::string& output
|
|||||||
case LUA_TSTRING: {
|
case LUA_TSTRING: {
|
||||||
const char* str = lua_tostring(L, index);
|
const char* str = lua_tostring(L, index);
|
||||||
output += "\"";
|
output += "\"";
|
||||||
// Escape special characters
|
// Properly escape all special characters for Lua strings
|
||||||
for (const char* p = str; *p; ++p) {
|
for (const char* p = str; *p; ++p) {
|
||||||
if (*p == '"' || *p == '\\') output += '\\';
|
unsigned char c = (unsigned char)*p;
|
||||||
output += *p;
|
switch (c) {
|
||||||
|
case '"':
|
||||||
|
output += "\\\"";
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
output += "\\\\";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
output += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
output += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
output += "\\t";
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
output += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
output += "\\f";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// For control characters and high bytes, use decimal escape
|
||||||
|
if (c < 32 || c >= 127) {
|
||||||
|
char hex[6];
|
||||||
|
snprintf(hex, sizeof(hex), "\\%d", c);
|
||||||
|
output += hex;
|
||||||
|
} else {
|
||||||
|
output += c;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
output += "\"";
|
output += "\"";
|
||||||
break;
|
break;
|
||||||
@@ -199,23 +232,120 @@ bool LuaPersistence::loadGlobals(lua_State* L, const std::string& filePath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore globals
|
// Restore globals - but for tables (Wherigo objects), merge instead of replace
|
||||||
|
// Strategy: For each saved object, find the matching existing object and merge
|
||||||
int count = 0;
|
int count = 0;
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, -2) != 0) {
|
while (lua_next(L, -2) != 0) {
|
||||||
if (lua_isstring(L, -2)) {
|
if (lua_isstring(L, -2) && lua_istable(L, -1)) {
|
||||||
const char* key = lua_tostring(L, -2);
|
const char* savedKey = lua_tostring(L, -2);
|
||||||
|
wxLogDebug("Restoring global: %s", savedKey);
|
||||||
|
int stackBefore = lua_gettop(L);
|
||||||
|
|
||||||
// Set the global
|
// Get the Name field of the saved object (if it exists)
|
||||||
lua_pushvalue(L, -1); // duplicate value
|
lua_getfield(L, -1, "Name");
|
||||||
lua_setglobal(L, key);
|
std::string savedName;
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
savedName = lua_tostring(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// Try to find matching existing object
|
||||||
|
bool merged = false;
|
||||||
|
|
||||||
|
if (!savedName.empty()) {
|
||||||
|
// Search for existing object with same Name
|
||||||
|
lua_getglobal(L, "_G");
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
while (lua_next(L, -2) != 0) {
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "Name");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
if (savedName == lua_tostring(L, -1)) {
|
||||||
|
lua_pop(L, 1); // pop Name
|
||||||
|
// Found matching object! Merge data
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, -3) != 0) {
|
||||||
|
if (lua_isstring(L, -2)) {
|
||||||
|
const char* field = lua_tostring(L, -2);
|
||||||
|
if (!lua_isfunction(L, -1)) {
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setfield(L, -4, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
merged = true;
|
||||||
|
lua_pop(L, 2); // pop iterator and object
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // pop _G
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!merged) {
|
||||||
|
// No match found or no Name field - use key matching
|
||||||
|
lua_getglobal(L, savedKey);
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
// Object exists by key - merge it
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, -3) != 0) {
|
||||||
|
if (lua_isstring(L, -2)) {
|
||||||
|
const char* field = lua_tostring(L, -2);
|
||||||
|
if (!lua_isfunction(L, -1)) {
|
||||||
|
lua_getfield(L, -3, field); // get existing value
|
||||||
|
if (lua_istable(L, -1) && lua_istable(L, -2)) {
|
||||||
|
// Both are tables: merge recursively
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, -3) != 0) {
|
||||||
|
if (lua_isstring(L, -2)) {
|
||||||
|
const char* subfield = lua_tostring(L, -2);
|
||||||
|
if (!lua_isfunction(L, -1)) {
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setfield(L, -5, subfield); // merge into existing table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // pop existing value
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1); // pop existing value
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setfield(L, -3, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // pop existing object
|
||||||
|
merged = true;
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1); // pop nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!merged) {
|
||||||
|
// No existing object found - create new global
|
||||||
|
lua_pushvalue(L, -1);
|
||||||
|
lua_setglobal(L, savedKey);
|
||||||
|
}
|
||||||
|
int stackAfter = lua_gettop(L);
|
||||||
|
if (stackBefore != stackAfter) {
|
||||||
|
wxLogError("Stack imbalance detected for global %s: before=%d after=%d", savedKey, stackBefore, stackAfter);
|
||||||
|
}
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
lua_pop(L, 1); // pop value
|
lua_pop(L, 1); // pop value from lua_next
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_pop(L, 1); // pop table
|
lua_pop(L, 1); // pop table
|
||||||
|
|
||||||
|
lua_pop(L, 1); // pop table
|
||||||
|
|
||||||
wxLogDebug("Restored %d globals from %s", count, filePath);
|
wxLogDebug("Restored %d globals from %s", count, filePath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
#include "lua/wherigo.h"
|
#include "lua/wherigo.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "lua/game_engine.h"
|
||||||
#include "lua/zobject.h"
|
#include "lua/zobject.h"
|
||||||
#include "lua/ztimer.h"
|
#include "lua/ztimer.h"
|
||||||
#include "ui/wherigo_dialog.h"
|
#include "ui/wherigo_dialog.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <lua.h>
|
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
#include <lua.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <wx/dir.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
#include <wx/log.h>
|
#include <wx/log.h>
|
||||||
|
#include <wx/stdpaths.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@@ -17,6 +22,63 @@ extern "C" {
|
|||||||
|
|
||||||
namespace wherigo {
|
namespace wherigo {
|
||||||
|
|
||||||
|
std::string getCompletionCode(lua_State *L) {
|
||||||
|
lua_getglobal(L, "Player");
|
||||||
|
if (!lua_istable(L, -1)) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "CompletionCode");
|
||||||
|
std::string completionCode;
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
completionCode = lua_tostring(L, -1);
|
||||||
|
if (completionCode.length() > 15) {
|
||||||
|
completionCode = completionCode.substr(0, 15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 2);
|
||||||
|
|
||||||
|
return completionCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wherigo.RequestSync()
|
||||||
|
static int wherigo_RequestSync(lua_State *L) {
|
||||||
|
// RequestSync shows the completion code from the cartridge in a MessageBox
|
||||||
|
wxLogDebug(
|
||||||
|
"Wherigo.RequestSync() called - showing completion code from cartridge");
|
||||||
|
|
||||||
|
auto completionCode = getCompletionCode(L);
|
||||||
|
if (completionCode.empty()) {
|
||||||
|
wxApp *app = wxTheApp;
|
||||||
|
if (!app) {
|
||||||
|
wxLogWarning("Cannot sync: no app instance");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cApp *wherigApp = dynamic_cast<cApp *>(app);
|
||||||
|
if (!wherigApp || !wherigApp->isCartridgeLoaded()) {
|
||||||
|
wxLogWarning("Cannot sync: no cartridge loaded");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cartridge = wherigApp->getCartridge();
|
||||||
|
|
||||||
|
completionCode = cartridge->completionCode().substr(0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString message =
|
||||||
|
wxString::Format("Completion Code:\n\n%s\n\nEnter this code on "
|
||||||
|
"wherigo.com to verify your completion.",
|
||||||
|
completionCode.c_str());
|
||||||
|
|
||||||
|
std::vector<wxString> buttons = {"OK"};
|
||||||
|
wherigo::WherigoMessageDialog dlg(nullptr, message, "Wherigo Completion",
|
||||||
|
buttons);
|
||||||
|
dlg.ShowModal();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Wherigo.ZonePoint(lat, lng, alt)
|
// Wherigo.ZonePoint(lat, lng, alt)
|
||||||
static int wherigo_ZonePoint(lua_State *L) {
|
static int wherigo_ZonePoint(lua_State *L) {
|
||||||
@@ -131,7 +193,6 @@ static int wherigo_ZTask(lua_State *L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Wherigo.ZTimer(cartridge)
|
// Wherigo.ZTimer(cartridge)
|
||||||
static int wherigo_ZTimer(lua_State *L) {
|
static int wherigo_ZTimer(lua_State *L) {
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
@@ -190,9 +251,7 @@ static int wherigo_ZMedia(lua_State *L) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetMediaCounter() {
|
void resetMediaCounter() { s_mediaCounter = 0; }
|
||||||
s_mediaCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wherigo.ZCartridge()
|
// Wherigo.ZCartridge()
|
||||||
static int wherigo_ZCartridge(lua_State *L) {
|
static int wherigo_ZCartridge(lua_State *L) {
|
||||||
@@ -204,6 +263,9 @@ static int wherigo_ZCartridge(lua_State *L) {
|
|||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
lua_setfield(L, -2, "AllZObjects");
|
lua_setfield(L, -2, "AllZObjects");
|
||||||
|
|
||||||
|
lua_pushcfunction(L, wherigo_RequestSync);
|
||||||
|
lua_setfield(L, -2, "RequestSync");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +300,8 @@ static int wherigo_Player(lua_State *L) {
|
|||||||
|
|
||||||
// Wherigo.MessageBox(table)
|
// Wherigo.MessageBox(table)
|
||||||
static int wherigo_MessageBox(lua_State *L) {
|
static int wherigo_MessageBox(lua_State *L) {
|
||||||
if (!lua_istable(L, 1)) return 0;
|
if (!lua_istable(L, 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
// Get Text
|
// Get Text
|
||||||
lua_getfield(L, 1, "Text");
|
lua_getfield(L, 1, "Text");
|
||||||
@@ -273,7 +336,8 @@ static int wherigo_MessageBox(lua_State *L) {
|
|||||||
wxLogDebug("MessageBox: %s", text);
|
wxLogDebug("MessageBox: %s", text);
|
||||||
|
|
||||||
// Show dialog
|
// Show dialog
|
||||||
WherigoMessageDialog dlg(nullptr, wxString::FromUTF8(text), "Wherigo", buttons);
|
WherigoMessageDialog dlg(nullptr, wxString::FromUTF8(text), "Wherigo",
|
||||||
|
buttons);
|
||||||
dlg.ShowModal();
|
dlg.ShowModal();
|
||||||
int selected = dlg.getSelectedButton();
|
int selected = dlg.getSelectedButton();
|
||||||
|
|
||||||
@@ -294,12 +358,17 @@ static int wherigo_MessageBox(lua_State *L) {
|
|||||||
luaL_unref(L, LUA_REGISTRYINDEX, callbackRef);
|
luaL_unref(L, LUA_REGISTRYINDEX, callbackRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify game state change after MessageBox
|
||||||
|
// This ensures UI updates if state was modified during the MessageBox
|
||||||
|
GameEngine::getInstance().notifyStateChanged();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wherigo.Dialog(table)
|
// Wherigo.Dialog(table)
|
||||||
static int wherigo_Dialog(lua_State *L) {
|
static int wherigo_Dialog(lua_State *L) {
|
||||||
if (!lua_istable(L, 1)) return 0;
|
if (!lua_istable(L, 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
std::vector<DialogEntry> entries;
|
std::vector<DialogEntry> entries;
|
||||||
int n = lua_objlen(L, 1);
|
int n = lua_objlen(L, 1);
|
||||||
@@ -378,6 +447,45 @@ static int wherigo_PlayAudio(lua_State *L) {
|
|||||||
static int wherigo_ShowScreen(lua_State *L) {
|
static int wherigo_ShowScreen(lua_State *L) {
|
||||||
int screen = luaL_optinteger(L, 1, 0);
|
int screen = luaL_optinteger(L, 1, 0);
|
||||||
wxLogDebug("ShowScreen: %d", screen);
|
wxLogDebug("ShowScreen: %d", screen);
|
||||||
|
|
||||||
|
// Show details dialog for DETAILSCREEN
|
||||||
|
if (screen == 5 && lua_gettop(L) >= 2 && lua_istable(L, 2)) {
|
||||||
|
// Get Name
|
||||||
|
lua_getfield(L, 2, "Name");
|
||||||
|
std::string name = lua_isstring(L, -1) ? lua_tostring(L, -1) : "Details";
|
||||||
|
lua_pop(L, 1);
|
||||||
|
// Get Description
|
||||||
|
lua_getfield(L, 2, "Description");
|
||||||
|
std::string desc = lua_isstring(L, -1) ? lua_tostring(L, -1) : "";
|
||||||
|
lua_pop(L, 1);
|
||||||
|
// Get Media name (prefer Media, fallback to Icon)
|
||||||
|
std::string mediaName;
|
||||||
|
lua_getfield(L, 2, "Media");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "Name");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
mediaName = lua_tostring(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (mediaName.empty()) {
|
||||||
|
lua_getfield(L, 2, "Icon");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "Name");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
mediaName = lua_tostring(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
// Show dialog with media if available
|
||||||
|
std::vector<wxString> buttons = {"OK"};
|
||||||
|
wherigo::WherigoMessageDialog dlg(nullptr, wxString::FromUTF8(desc.c_str()), wxString::FromUTF8(name.c_str()), buttons, wxString::FromUTF8(mediaName.c_str()));
|
||||||
|
dlg.ShowModal();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,8 +573,8 @@ static const luaL_Reg wherigo_funcs[] = {
|
|||||||
{"VectorToZone", wherigo_VectorToZone},
|
{"VectorToZone", wherigo_VectorToZone},
|
||||||
{"VectorToPoint", wherigo_VectorToPoint},
|
{"VectorToPoint", wherigo_VectorToPoint},
|
||||||
{"VectorToSegment", wherigo_VectorToSegment},
|
{"VectorToSegment", wherigo_VectorToSegment},
|
||||||
{nullptr, nullptr}
|
{"RequestSync", wherigo_RequestSync},
|
||||||
};
|
{nullptr, nullptr}};
|
||||||
|
|
||||||
int luaopen_Wherigo(lua_State *L) {
|
int luaopen_Wherigo(lua_State *L) {
|
||||||
luaL_register(L, "Wherigo", wherigo_funcs);
|
luaL_register(L, "Wherigo", wherigo_funcs);
|
||||||
@@ -509,4 +617,3 @@ int luaopen_Wherigo(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace wherigo
|
} // namespace wherigo
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,6 @@ bool WherigoCompletion::writeGWLFile(const CompletionData& data, const std::stri
|
|||||||
file << "</WherigoGameLog>\n";
|
file << "</WherigoGameLog>\n";
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
wxLogMessage("Completion log saved to: %s", filePath);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "lua/game_engine.h"
|
#include "lua/game_engine.h"
|
||||||
#include "ui/game_screen.h"
|
#include "ui/game_screen.h"
|
||||||
#include "ui/wherigo_dialog.h"
|
#include "ui/wherigo_dialog.h"
|
||||||
|
#include <cartridge/cartridge.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
@@ -12,17 +13,20 @@ wxDECLARE_APP(cApp);
|
|||||||
enum {
|
enum {
|
||||||
ID_SaveGame = 2000,
|
ID_SaveGame = 2000,
|
||||||
ID_LoadGame = 2001,
|
ID_LoadGame = 2001,
|
||||||
ID_ExportCompletion = 2002
|
ID_ExportCompletion = 2002,
|
||||||
|
ID_MapView = 2003
|
||||||
};
|
};
|
||||||
|
|
||||||
cGameScreen::cGameScreen(wxWindow *parent)
|
cGameScreen::cGameScreen(wxWindow *parent)
|
||||||
: wxFrame(parent, wxID_ANY, "Wherigo Player", wxDefaultPosition, wxSize(800, 600)) {
|
: wxFrame(parent, wxID_ANY, "Wherigo Player", wxDefaultPosition, wxSize(800, 800)) {
|
||||||
|
|
||||||
// Menu
|
// Menu
|
||||||
auto *menuFile = new wxMenu;
|
auto *menuFile = new wxMenu;
|
||||||
menuFile->Append(ID_SaveGame, "Spielstand speichern\tCtrl+S");
|
menuFile->Append(ID_SaveGame, "Spielstand speichern\tCtrl+S");
|
||||||
menuFile->Append(ID_LoadGame, "Spielstand laden\tCtrl+L");
|
menuFile->Append(ID_LoadGame, "Spielstand laden\tCtrl+L");
|
||||||
menuFile->AppendSeparator();
|
menuFile->AppendSeparator();
|
||||||
|
menuFile->Append(ID_MapView, "Karten-Simulation..."); // Menüpunkt für Simulation
|
||||||
|
menuFile->AppendSeparator();
|
||||||
menuFile->Append(ID_ExportCompletion, "Completion Log exportieren\tCtrl+E");
|
menuFile->Append(ID_ExportCompletion, "Completion Log exportieren\tCtrl+E");
|
||||||
menuFile->AppendSeparator();
|
menuFile->AppendSeparator();
|
||||||
menuFile->Append(wxID_EXIT, "Spiel beenden");
|
menuFile->Append(wxID_EXIT, "Spiel beenden");
|
||||||
@@ -103,6 +107,7 @@ cGameScreen::cGameScreen(wxWindow *parent)
|
|||||||
Bind(wxEVT_MENU, &cGameScreen::OnSaveGame, this, ID_SaveGame);
|
Bind(wxEVT_MENU, &cGameScreen::OnSaveGame, this, ID_SaveGame);
|
||||||
Bind(wxEVT_MENU, &cGameScreen::OnLoadGame, this, ID_LoadGame);
|
Bind(wxEVT_MENU, &cGameScreen::OnLoadGame, this, ID_LoadGame);
|
||||||
Bind(wxEVT_MENU, &cGameScreen::OnExportCompletion, this, ID_ExportCompletion);
|
Bind(wxEVT_MENU, &cGameScreen::OnExportCompletion, this, ID_ExportCompletion);
|
||||||
|
Bind(wxEVT_MENU, &cGameScreen::OnMapSim, this, ID_MapView);
|
||||||
|
|
||||||
m_zoneList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnZoneSelected, this);
|
m_zoneList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnZoneSelected, this);
|
||||||
m_taskList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnTaskSelected, this);
|
m_taskList->Bind(wxEVT_LISTBOX_DCLICK, &cGameScreen::OnTaskSelected, this);
|
||||||
@@ -542,12 +547,11 @@ void cGameScreen::OnZoneSelected(wxCommandEvent& event) {
|
|||||||
if (currentName == zoneName) {
|
if (currentName == zoneName) {
|
||||||
found = true;
|
found = true;
|
||||||
|
|
||||||
// Get zone description
|
// Zone Info mit Media anzeigen
|
||||||
lua_getfield(L, -1, "Description");
|
lua_getfield(L, -1, "Description");
|
||||||
wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "";
|
wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "";
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Get media if exists
|
|
||||||
wxString mediaName;
|
wxString mediaName;
|
||||||
lua_getfield(L, -1, "Media");
|
lua_getfield(L, -1, "Media");
|
||||||
if (lua_istable(L, -1)) {
|
if (lua_istable(L, -1)) {
|
||||||
@@ -559,7 +563,6 @@ void cGameScreen::OnZoneSelected(wxCommandEvent& event) {
|
|||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Show zone info with media
|
|
||||||
if (!description.IsEmpty() || !mediaName.IsEmpty()) {
|
if (!description.IsEmpty() || !mediaName.IsEmpty()) {
|
||||||
std::vector<wxString> buttons = {"OK"};
|
std::vector<wxString> buttons = {"OK"};
|
||||||
wherigo::WherigoMessageDialog dlg(this, description, zoneName, buttons, mediaName);
|
wherigo::WherigoMessageDialog dlg(this, description, zoneName, buttons, mediaName);
|
||||||
@@ -589,9 +592,9 @@ void cGameScreen::OnZoneSelected(wxCommandEvent& event) {
|
|||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lua_pop(L, 1); // pop value
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
lua_pop(L, 1); // pop _G
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
wxMessageBox(wxString::Format("Zone '%s' nicht gefunden", zoneName),
|
wxMessageBox(wxString::Format("Zone '%s' nicht gefunden", zoneName),
|
||||||
@@ -624,12 +627,11 @@ void cGameScreen::OnTaskSelected(wxCommandEvent& event) {
|
|||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (currentName == taskName) {
|
if (currentName == taskName) {
|
||||||
// Get task description
|
// Bestehende MessageBox mit Status und Beschreibung
|
||||||
lua_getfield(L, -1, "Description");
|
lua_getfield(L, -1, "Description");
|
||||||
wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "Keine Beschreibung verfügbar";
|
wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : "Keine Beschreibung verfügbar";
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// Get task complete status
|
|
||||||
lua_getfield(L, -1, "Complete");
|
lua_getfield(L, -1, "Complete");
|
||||||
bool complete = lua_toboolean(L, -1);
|
bool complete = lua_toboolean(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
@@ -680,54 +682,6 @@ void cGameScreen::OnInventorySelected(wxCommandEvent& event) {
|
|||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
if (currentName == itemName) {
|
if (currentName == itemName) {
|
||||||
// Get item description
|
|
||||||
lua_getfield(L, -1, "Description");
|
|
||||||
wxString description = lua_isstring(L, -1) ? wxString::FromUTF8(lua_tostring(L, -1)) : ("Keine Beschreibung verfügbar");
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
// Get media if exists
|
|
||||||
wxString mediaName;
|
|
||||||
lua_getfield(L, -1, "Media");
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
lua_getfield(L, -1, "Name");
|
|
||||||
if (lua_isstring(L, -1)) {
|
|
||||||
mediaName = wxString::FromUTF8(lua_tostring(L, -1));
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
// Check for commands
|
|
||||||
lua_getfield(L, -1, "Commands");
|
|
||||||
wxArrayString commands;
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
int n = lua_objlen(L, -1);
|
|
||||||
for (int i = 1; i <= n; i++) {
|
|
||||||
lua_rawgeti(L, -1, i);
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
lua_getfield(L, -1, "Text");
|
|
||||||
if (lua_isstring(L, -1)) {
|
|
||||||
commands.Add(wxString::FromUTF8(lua_tostring(L, -1)));
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
wxString message = description;
|
|
||||||
if (!commands.IsEmpty()) {
|
|
||||||
message += ("\n\nVerfügbare Aktionen:\n");
|
|
||||||
for (const auto& cmd : commands) {
|
|
||||||
message += "• " + cmd + "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use WherigoMessageDialog to show with media support
|
|
||||||
std::vector<wxString> buttons = {"OK"};
|
|
||||||
wherigo::WherigoMessageDialog dlg(this, message, itemName, buttons, mediaName);
|
|
||||||
dlg.ShowModal();
|
|
||||||
|
|
||||||
// Call OnClick if exists
|
// Call OnClick if exists
|
||||||
lua_getfield(L, -1, "OnClick");
|
lua_getfield(L, -1, "OnClick");
|
||||||
@@ -937,3 +891,53 @@ void cGameScreen::OnItemSelected(wxCommandEvent& event) {
|
|||||||
}
|
}
|
||||||
lua_pop(L, 1); // pop _G
|
lua_pop(L, 1); // pop _G
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cGameScreen::OnMapSim(wxCommandEvent&) {
|
||||||
|
std::vector<std::pair<double, double>> zoneCoords;
|
||||||
|
double lat = 53.3, lon = 10.39;
|
||||||
|
lua_State *L = wxGetApp().getLuaState();
|
||||||
|
if (L) {
|
||||||
|
lua_getglobal(L, "_G");
|
||||||
|
lua_pushnil(L);
|
||||||
|
while (lua_next(L, -2) != 0) {
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "ClassName");
|
||||||
|
if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "Zone") == 0) {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_getfield(L, -1, "Active");
|
||||||
|
bool active = lua_toboolean(L, -1) || true;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
lua_getfield(L, -1, "Visible");
|
||||||
|
bool visible = lua_toboolean(L, -1) || true;
|
||||||
|
lua_pop(L, 1);
|
||||||
|
if (active && visible) {
|
||||||
|
lua_getfield(L, -1, "OriginalPoint");
|
||||||
|
if (lua_istable(L, -1)) {
|
||||||
|
lua_getfield(L, -1, "latitude");
|
||||||
|
lua_getfield(L, -2, "longitude");
|
||||||
|
if (lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
|
||||||
|
double zlat = lua_tonumber(L, -2);
|
||||||
|
double zlon = lua_tonumber(L, -1);
|
||||||
|
zoneCoords.emplace_back(zlat, zlon);
|
||||||
|
}
|
||||||
|
lua_pop(L, 2);
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1); // pop OriginalPoint
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
if (!zoneCoords.empty()) {
|
||||||
|
lat = zoneCoords[0].first;
|
||||||
|
lon = zoneCoords[0].second;
|
||||||
|
}
|
||||||
|
auto *mapSim = new MapSimFrame(this, lat, lon, zoneCoords);
|
||||||
|
mapSim->Show();
|
||||||
|
}
|
||||||
|
|||||||
79
main/src/ui/map_sim_frame.cpp
Normal file
79
main/src/ui/map_sim_frame.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "ui/map_sim_frame.h"
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/webview.h>
|
||||||
|
#include <wx/msgdlg.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
wxBEGIN_EVENT_TABLE(MapSimFrame, wxFrame)
|
||||||
|
EVT_WEBVIEW_NAVIGATED(wxID_ANY, MapSimFrame::OnWebViewEvent)
|
||||||
|
EVT_BUTTON(wxID_ANY, MapSimFrame::OnPlay)
|
||||||
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
MapSimFrame::MapSimFrame(wxWindow* parent, double centerLat, double centerLon, const std::vector<std::pair<double, double>>& zoneCoords)
|
||||||
|
: wxFrame(parent, wxID_ANY, "Karten-Simulation", wxDefaultPosition, wxSize(900, 700)), m_zoneCoords(zoneCoords) {
|
||||||
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
m_webView = wxWebView::New(this, wxID_ANY);
|
||||||
|
sizer->Add(m_webView, 1, wxEXPAND);
|
||||||
|
auto* playBtn = new wxButton(this, wxID_ANY, "Simulation starten");
|
||||||
|
sizer->Add(playBtn, 0, wxALL | wxALIGN_CENTER, 8);
|
||||||
|
SetSizer(sizer);
|
||||||
|
// Leaflet-Karte laden (dynamisch zentriert und Marker)
|
||||||
|
wxString html;
|
||||||
|
html << "<!DOCTYPE html><html><head><meta charset='utf-8'><title>MapSim</title>"
|
||||||
|
"<link rel='stylesheet' href='https://unpkg.com/leaflet/dist/leaflet.css'/>"
|
||||||
|
"<script src='https://unpkg.com/leaflet/dist/leaflet.js'></script>"
|
||||||
|
"<style>html,body,#map{height:100%%;margin:0;padding:0;}#map{height:600px;}</style>"
|
||||||
|
"</head><body><div id='map'></div>"
|
||||||
|
"<script>"
|
||||||
|
"var map = L.map('map').setView([" << wxString::Format("%.8f, %.8f", centerLat, centerLon) << "], 16);"
|
||||||
|
"L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {maxZoom: 19}).addTo(map);"
|
||||||
|
"var markers = [];";
|
||||||
|
// Marker für alle Zonen
|
||||||
|
for (const auto& z : zoneCoords) {
|
||||||
|
html << wxString::Format("var m = L.marker([%.8f, %.8f]).addTo(map); markers.push(m);\n", z.first, z.second);
|
||||||
|
}
|
||||||
|
html <<
|
||||||
|
"map.on('click', function(e) {"
|
||||||
|
" var marker = L.marker(e.latlng).addTo(map);"
|
||||||
|
" markers.push(marker);"
|
||||||
|
" window.wx.postMessage(JSON.stringify({lat: e.latlng.lat, lon: e.latlng.lng}));"
|
||||||
|
"});"
|
||||||
|
"</script></body></html>";
|
||||||
|
m_webView->SetPage(html, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapSimFrame::OnWebViewEvent(wxWebViewEvent& event) {
|
||||||
|
// Empfange Marker-Koordinaten von JS
|
||||||
|
wxString msg = event.GetString();
|
||||||
|
double lat = 0, lon = 0;
|
||||||
|
if (msg.ToDouble(&lat)) {
|
||||||
|
// Not used, see below
|
||||||
|
}
|
||||||
|
// In wxWidgets 3.1+ kann man wxWebView::RegisterHandler für JS->C++ nutzen
|
||||||
|
// Hier: Marker werden über postMessage als JSON gesendet
|
||||||
|
// TODO: JSON parsen und AddSimPoint aufrufen
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapSimFrame::AddSimPoint(double lat, double lon) {
|
||||||
|
m_route.push_back({lat, lon});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapSimFrame::OnPlay(wxCommandEvent&) {
|
||||||
|
if (m_route.empty()) {
|
||||||
|
wxMessageBox("Bitte zuerst Marker setzen.", "Hinweis", wxOK | wxICON_INFORMATION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Simuliere Bewegung entlang der Route
|
||||||
|
for (const auto& pt : m_route) {
|
||||||
|
SendPositionToEngine(pt.lat, pt.lon);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
|
}
|
||||||
|
wxMessageBox("Simulation beendet.", "Info", wxOK | wxICON_INFORMATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapSimFrame::SendPositionToEngine(double lat, double lon) {
|
||||||
|
// TODO: Engine-Integration: GPS-Position setzen
|
||||||
|
// Beispiel: wxGetApp().setSimulatedPosition(lat, lon);
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
cStartScreen::cStartScreen()
|
cStartScreen::cStartScreen()
|
||||||
: wxFrame(nullptr, wxID_ANY, "Wherigo Player", wxDefaultPosition, wxSize(800, 600)),
|
: wxFrame(nullptr, wxID_ANY, "Wherigo Player", wxDefaultPosition, wxSize(800, 800)),
|
||||||
m_gameFrame(nullptr),
|
m_gameFrame(nullptr),
|
||||||
m_cartridgeLoaded(false) {
|
m_cartridgeLoaded(false) {
|
||||||
|
|
||||||
@@ -107,6 +107,20 @@ void cStartScreen::OnOpenCartridge(wxCommandEvent& event) {
|
|||||||
wxString filePath = openFileDialog.GetPath();
|
wxString filePath = openFileDialog.GetPath();
|
||||||
SetStatusText("Lade Cartridge: " + filePath);
|
SetStatusText("Lade Cartridge: " + filePath);
|
||||||
|
|
||||||
|
// Vor dem Laden: Info-Panel zurücksetzen
|
||||||
|
if (m_infoPanel) {
|
||||||
|
if (m_cartridgeName) m_cartridgeName->SetLabel("");
|
||||||
|
if (m_cartridgeAuthor) m_cartridgeAuthor->SetLabel("");
|
||||||
|
if (m_cartridgeDesc) m_cartridgeDesc->SetPage("");
|
||||||
|
if (m_splashImage) m_splashImage->SetBitmap(wxNullBitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ggf. alten GameScreen schließen
|
||||||
|
if (m_gameFrame) {
|
||||||
|
m_gameFrame->Destroy();
|
||||||
|
m_gameFrame = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Load the cartridge via cApp
|
// Load the cartridge via cApp
|
||||||
if (wxGetApp().loadCartridge(filePath.ToStdString())) {
|
if (wxGetApp().loadCartridge(filePath.ToStdString())) {
|
||||||
m_cartridgeLoaded = true;
|
m_cartridgeLoaded = true;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <wx/log.h>
|
#include <wx/log.h>
|
||||||
#include <wx/mstream.h>
|
#include <wx/mstream.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
|
#include <wx/html/htmlwin.h>
|
||||||
|
|
||||||
namespace wherigo {
|
namespace wherigo {
|
||||||
|
|
||||||
@@ -68,37 +69,124 @@ WherigoMessageDialog::WherigoMessageDialog(wxWindow *parent, const wxString &tex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper lambda to convert plain text to HTML
|
||||||
|
auto textToHtml = [](const wxString& plainText) -> wxString {
|
||||||
|
wxString result = plainText;
|
||||||
|
// Check if this looks like HTML (has tags)
|
||||||
|
if (plainText.Find("<") == wxNOT_FOUND) {
|
||||||
|
// Plain text - need to be careful with & to avoid breaking HTML entities
|
||||||
|
// Replace & but preserve HTML entities like & etc.
|
||||||
|
wxString escaped;
|
||||||
|
for (size_t i = 0; i < plainText.length(); i++) {
|
||||||
|
if (plainText[i] == '&') {
|
||||||
|
// Look ahead to see if this is an HTML entity
|
||||||
|
size_t end = plainText.find(';', i);
|
||||||
|
if (end != wxString::npos && end - i < 12) {
|
||||||
|
// Likely an entity, copy as-is
|
||||||
|
escaped += plainText.substr(i, end - i + 1);
|
||||||
|
i = end;
|
||||||
|
} else {
|
||||||
|
// Not an entity, escape the &
|
||||||
|
escaped += "&";
|
||||||
|
}
|
||||||
|
} else if (plainText[i] == '\n') {
|
||||||
|
escaped += "<br/>";
|
||||||
|
} else {
|
||||||
|
escaped += plainText[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = escaped;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
wxColour bgColor = GetBackgroundColour();
|
||||||
|
wxColour fgColor = GetForegroundColour();
|
||||||
|
|
||||||
|
// Helper lambda to create and configure HTML window with auto-sizing
|
||||||
|
auto createHtmlWindow = [this, &sizer, &textToHtml, bgColor, fgColor](const wxString& text) {
|
||||||
|
auto *htmlCtrl = new wxHtmlWindow(this, wxID_ANY);
|
||||||
|
htmlCtrl->SetBorders(0);
|
||||||
|
|
||||||
|
wxString htmlContent = wxString::Format(
|
||||||
|
"<html><body style=\"margin:0; padding:5px; background-color:#%02x%02x%02x; color:#%02x%02x%02x; font-family:sans-serif; font-size:11pt; line-height:1.5; user-select:none; -webkit-user-select:none; -moz-user-select:none;\">%s</body></html>",
|
||||||
|
bgColor.Red(), bgColor.Green(), bgColor.Blue(),
|
||||||
|
fgColor.Red(), fgColor.Green(), fgColor.Blue(),
|
||||||
|
textToHtml(text)
|
||||||
|
);
|
||||||
|
|
||||||
|
htmlCtrl->SetPage(htmlContent);
|
||||||
|
// htmlCtrl->SetBackgroundColour(bgColor); // Removed as it might not exist or be needed
|
||||||
|
|
||||||
|
// Set a reasonable fixed size - will expand based on content
|
||||||
|
// Width: fixed at 600px for consistent layout
|
||||||
|
// Height: let wxHtmlWindow calculate based on content, with limits
|
||||||
|
int contentWidth = 600;
|
||||||
|
|
||||||
|
// Calculate approximate height based on line count (rough estimate)
|
||||||
|
int lineCount = 1;
|
||||||
|
for (size_t i = 0; i < text.length(); i++) {
|
||||||
|
if (text[i] == '\n') lineCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estimate: ~20px per line + 40px padding
|
||||||
|
int estimatedHeight = std::max(80, lineCount * 20 + 40);
|
||||||
|
estimatedHeight = std::clamp(estimatedHeight, 80, 400);
|
||||||
|
|
||||||
|
htmlCtrl->SetMinSize(wxSize(contentWidth, estimatedHeight));
|
||||||
|
|
||||||
|
sizer->Add(htmlCtrl, 1, wxALL | wxEXPAND, 10);
|
||||||
|
};
|
||||||
|
|
||||||
// Text (only show if not just a placeholder like ".")
|
// Text (only show if not just a placeholder like ".")
|
||||||
if (!text.IsEmpty() && text != ".") {
|
if (!text.IsEmpty() && text != ".") {
|
||||||
auto *textCtrl = new wxStaticText(this, wxID_ANY, text);
|
createHtmlWindow(text);
|
||||||
textCtrl->Wrap(800); // Also doubled wrap width
|
|
||||||
sizer->Add(textCtrl, 1, wxALL | wxEXPAND, 15);
|
|
||||||
} else if (mediaName.IsEmpty()) {
|
} else if (mediaName.IsEmpty()) {
|
||||||
// No media and empty/placeholder text - show something
|
// No media and empty/placeholder text - show something
|
||||||
auto *textCtrl = new wxStaticText(this, wxID_ANY, text);
|
createHtmlWindow(text);
|
||||||
textCtrl->Wrap(800); // Also doubled wrap width
|
|
||||||
sizer->Add(textCtrl, 1, wxALL | wxEXPAND, 15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
auto *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
|
auto *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
wxButton *defaultButton = nullptr;
|
||||||
|
|
||||||
if (buttons.empty()) {
|
if (buttons.empty()) {
|
||||||
auto *okButton = new wxButton(this, wxID_OK, "OK");
|
auto *okButton = new wxButton(this, wxID_OK, "OK");
|
||||||
buttonSizer->Add(okButton, 0, wxALL, 5);
|
buttonSizer->Add(okButton, 0, wxALL, 5);
|
||||||
okButton->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);
|
okButton->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);
|
||||||
|
defaultButton = okButton;
|
||||||
} else {
|
} else {
|
||||||
for (size_t i = 0; i < buttons.size(); i++) {
|
for (size_t i = 0; i < buttons.size(); i++) {
|
||||||
auto *btn = new wxButton(this, 1000 + i, buttons[i]);
|
auto *btn = new wxButton(this, 1000 + i, buttons[i]);
|
||||||
buttonSizer->Add(btn, 0, wxALL, 5);
|
buttonSizer->Add(btn, 0, wxALL, 5);
|
||||||
btn->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);
|
btn->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);
|
||||||
|
|
||||||
|
// Set first button as default
|
||||||
|
if (i == 0) {
|
||||||
|
defaultButton = btn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the default button (will be activated by Enter)
|
||||||
|
if (defaultButton) {
|
||||||
|
defaultButton->SetDefault();
|
||||||
|
}
|
||||||
|
|
||||||
sizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxBOTTOM, 10);
|
sizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxBOTTOM, 10);
|
||||||
|
|
||||||
SetSizerAndFit(sizer);
|
SetSizer(sizer);
|
||||||
SetMinSize(wxSize(600, 300)); // Doubled from 300x150
|
sizer->Layout();
|
||||||
|
|
||||||
|
// Calculate dialog size based on content
|
||||||
|
wxSize minSize = sizer->GetMinSize();
|
||||||
|
|
||||||
|
// Set reasonable dialog size
|
||||||
|
int dialogWidth = std::clamp(minSize.GetWidth() + 40, 500, 800);
|
||||||
|
int dialogHeight = std::clamp(minSize.GetHeight() + 40, 300, 700);
|
||||||
|
|
||||||
|
SetSize(dialogWidth, dialogHeight);
|
||||||
|
SetMinSize(wxSize(400, 250));
|
||||||
CenterOnParent();
|
CenterOnParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,11 +239,11 @@ void WherigoDialogRunner::showDialog(const std::vector<DialogEntry> &entries,
|
|||||||
if (i == entries.size() - 1 && callback) {
|
if (i == entries.size() - 1 && callback) {
|
||||||
callback(dlg.getSelectedButton());
|
callback(dlg.getSelectedButton());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Notify game state change after dialog sequence completes
|
// Notify state change after each dialog to ensure UI updates
|
||||||
|
// This is important for goto events triggered during dialogs
|
||||||
GameEngine::getInstance().notifyStateChanged();
|
GameEngine::getInstance().notifyStateChanged();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace wherigo
|
} // namespace wherigo
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user