From bf7d3f22c67ac855c9cf46f60648ea5db30e9f28 Mon Sep 17 00:00:00 2001 From: pelya Date: Wed, 13 Oct 2010 17:30:44 +0300 Subject: [PATCH] Added Enigma game --- ChangeAppSettings.sh | 32 +- project/AndroidManifest.xml | 6 +- project/jni/Android.mk | 10 +- project/jni/application/Android.mk | 2 + .../jni/application/enigma/ACKNOWLEDGEMENTS | 190 + project/jni/application/enigma/AUTHORS | 1 + .../application/enigma/AndroidAppSettings.cfg | 27 + project/jni/application/enigma/CHANGES | 379 ++ project/jni/application/enigma/COPYING | 340 ++ project/jni/application/enigma/INSTALL | 182 + project/jni/application/enigma/README | 102 + project/jni/application/enigma/icon.png | Bin 0 -> 3294 bytes .../application/enigma/lib-src/enet/README | 14 + .../enigma/lib-src/enet/callbacks.c | 53 + .../application/enigma/lib-src/enet/host.c | 391 ++ .../lib-src/enet/include/enet/callbacks.h | 28 + .../enigma/lib-src/enet/include/enet/enet.h | 432 ++ .../enigma/lib-src/enet/include/enet/list.h | 42 + .../lib-src/enet/include/enet/protocol.h | 167 + .../enigma/lib-src/enet/include/enet/time.h | 18 + .../enigma/lib-src/enet/include/enet/types.h | 13 + .../enigma/lib-src/enet/include/enet/unix.h | 36 + .../lib-src/enet/include/enet/utility.h | 12 + .../enigma/lib-src/enet/include/enet/win32.h | 51 + .../application/enigma/lib-src/enet/list.c | 57 + .../application/enigma/lib-src/enet/packet.c | 74 + .../application/enigma/lib-src/enet/peer.c | 657 +++ .../enigma/lib-src/enet/protocol.c | 1349 ++++++ .../application/enigma/lib-src/enet/unix.c | 407 ++ .../application/enigma/lib-src/enet/win32.c | 309 ++ .../enigma/lib-src/enigma-core/IMG_SavePNG.c | 151 + .../enigma/lib-src/enigma-core/IMG_SavePNG.h | 28 + .../enigma/lib-src/enigma-core/README | 47 + .../lib-src/enigma-core/SDL_gfxPrimitives.c | 3387 +++++++++++++++ .../lib-src/enigma-core/SDL_gfxPrimitives.h | 143 + .../enigma-core/SDL_gfxPrimitives_font.h | 3082 ++++++++++++++ .../enigma/lib-src/enigma-core/SDL_rotozoom.c | 1223 ++++++ .../enigma/lib-src/enigma-core/SDL_rotozoom.h | 88 + .../enigma/lib-src/enigma-core/ecl.hh | 32 + .../enigma/lib-src/enigma-core/ecl_alist.hh | 70 + .../enigma/lib-src/enigma-core/ecl_argp.cpp | 167 + .../enigma/lib-src/enigma-core/ecl_argp.hh | 120 + .../enigma/lib-src/enigma-core/ecl_array2.hh | 172 + .../enigma/lib-src/enigma-core/ecl_buffer.cpp | 392 ++ .../enigma/lib-src/enigma-core/ecl_buffer.hh | 109 + .../enigma/lib-src/enigma-core/ecl_cache.hh | 128 + .../lib-src/enigma-core/ecl_callback.hh | 65 + .../enigma/lib-src/enigma-core/ecl_dict.cpp | 17 + .../enigma/lib-src/enigma-core/ecl_dict.hh | 274 ++ .../enigma/lib-src/enigma-core/ecl_error.hh | 80 + .../enigma/lib-src/enigma-core/ecl_font.cpp | 303 ++ .../enigma/lib-src/enigma-core/ecl_font.hh | 60 + .../enigma/lib-src/enigma-core/ecl_fwd.hh | 48 + .../enigma/lib-src/enigma-core/ecl_geom.cpp | 101 + .../enigma/lib-src/enigma-core/ecl_geom.hh | 160 + .../enigma/lib-src/enigma-core/ecl_math.cpp | 32 + .../enigma/lib-src/enigma-core/ecl_math.hh | 286 ++ .../enigma/lib-src/enigma-core/ecl_sdl.cpp | 40 + .../enigma/lib-src/enigma-core/ecl_sdl.hh | 75 + .../enigma-core/ecl_sys_localename.cpp | 1164 ++++++ .../enigma/lib-src/enigma-core/ecl_system.hh | 52 + .../lib-src/enigma-core/ecl_system_unix.cpp | 178 + .../enigma/lib-src/enigma-core/ecl_utf.cpp | 199 + .../enigma/lib-src/enigma-core/ecl_utf.hh | 86 + .../enigma/lib-src/enigma-core/ecl_util.cpp | 94 + .../enigma/lib-src/enigma-core/ecl_util.hh | 271 ++ .../enigma/lib-src/enigma-core/ecl_video.cpp | 694 ++++ .../enigma/lib-src/enigma-core/ecl_video.hh | 366 ++ .../enigma/lib-src/oxydlib/Bitmap.cpp | 1256 ++++++ .../enigma/lib-src/oxydlib/Bitmap.h | 97 + .../enigma/lib-src/oxydlib/COPYING | 340 ++ .../enigma/lib-src/oxydlib/DatFile.cpp | 612 +++ .../enigma/lib-src/oxydlib/DatFile.h | 84 + .../enigma/lib-src/oxydlib/FileUtils.cpp | 81 + .../enigma/lib-src/oxydlib/FileUtils.h | 34 + .../enigma/lib-src/oxydlib/Level.cpp | 2978 +++++++++++++ .../enigma/lib-src/oxydlib/Level.h | 482 +++ .../enigma/lib-src/oxydlib/OxydVersion.h | 41 + .../application/enigma/lib-src/oxydlib/README | 27 + .../enigma/lib-src/oxydlib/README.enigma | 4 + .../enigma/lib-src/oxydlib/VecUtils.cpp | 79 + .../enigma/lib-src/oxydlib/VecUtils.h | 42 + .../enigma/lib-src/zipios++/AUTHORS | 7 + .../enigma/lib-src/zipios++/COPYING | 515 +++ .../enigma/lib-src/zipios++/ChangeLog | 1101 +++++ .../enigma/lib-src/zipios++/INSTALL | 182 + .../application/enigma/lib-src/zipios++/NEWS | 64 + .../enigma/lib-src/zipios++/README | 81 + .../enigma/lib-src/zipios++/src/backbuffer.h | 117 + .../lib-src/zipios++/src/basicentry.cpp | 163 + .../enigma/lib-src/zipios++/src/collcoll.cpp | 162 + .../zipios++/src/deflateoutputstreambuf.cpp | 224 + .../enigma/lib-src/zipios++/src/dircoll.cpp | 170 + .../enigma/lib-src/zipios++/src/directory.cpp | 393 ++ .../enigma/lib-src/zipios++/src/directory.h | 351 ++ .../enigma/lib-src/zipios++/src/fcoll.cpp | 90 + .../lib-src/zipios++/src/fcollexceptions.cpp | 144 + .../enigma/lib-src/zipios++/src/fileentry.cpp | 39 + .../enigma/lib-src/zipios++/src/filepath.cpp | 72 + .../zipios++/src/filterinputstreambuf.cpp | 47 + .../zipios++/src/filteroutputstreambuf.cpp | 47 + .../zipios++/src/inflateinputstreambuf.cpp | 179 + .../lib-src/zipios++/src/outputstringstream.h | 69 + .../enigma/lib-src/zipios++/src/zipfile.cpp | 208 + .../enigma/lib-src/zipios++/src/ziphead.cpp | 291 ++ .../enigma/lib-src/zipios++/src/zipheadio.cpp | 195 + .../lib-src/zipios++/src/zipinputstream.cpp | 80 + .../zipios++/src/zipinputstreambuf.cpp | 140 + .../lib-src/zipios++/src/zipios_common.h | 52 + .../lib-src/zipios++/src/zipoutputstream.cpp | 101 + .../zipios++/src/zipoutputstreambuf.cpp | 193 + .../lib-src/zipios++/zipios++/basicentry.h | 87 + .../lib-src/zipios++/zipios++/collcoll.h | 174 + .../zipios++/deflateoutputstreambuf.h | 109 + .../lib-src/zipios++/zipios++/dircoll.h | 93 + .../enigma/lib-src/zipios++/zipios++/fcoll.h | 307 ++ .../zipios++/zipios++/fcollexceptions.h | 100 + .../lib-src/zipios++/zipios++/fileentry.h | 240 ++ .../lib-src/zipios++/zipios++/filepath.h | 202 + .../zipios++/zipios++/filterinputstreambuf.h | 66 + .../zipios++/zipios++/filteroutputstreambuf.h | 58 + .../zipios++/zipios++/inflateinputstreambuf.h | 89 + .../zipios++/zipios++/meta-iostreams.h | 17 + .../zipios++/zipios++/simplesmartptr.h | 173 + .../lib-src/zipios++/zipios++/virtualseeker.h | 105 + .../lib-src/zipios++/zipios++/zipfile.h | 105 + .../lib-src/zipios++/zipios++/ziphead.h | 271 ++ .../lib-src/zipios++/zipios++/zipheadio.h | 192 + .../zipios++/zipios++/zipinputstream.h | 91 + .../zipios++/zipios++/zipinputstreambuf.h | 89 + .../lib-src/zipios++/zipios++/zipios-config.h | 80 + .../zipios++/zipios++/zipios-config.h.in | 79 + .../lib-src/zipios++/zipios++/zipios_defs.h | 34 + .../zipios++/zipios++/zipoutputstream.h | 101 + .../zipios++/zipios++/zipoutputstreambuf.h | 117 + .../enigma/src/DOMErrorReporter.cpp | 103 + .../enigma/src/DOMErrorReporter.hh | 114 + .../enigma/src/DOMSchemaResolver.cpp | 127 + .../enigma/src/DOMSchemaResolver.hh | 91 + .../jni/application/enigma/src/Inventory.cpp | 169 + .../jni/application/enigma/src/Inventory.hh | 67 + .../jni/application/enigma/src/ItemHolder.hh | 29 + .../jni/application/enigma/src/LocalToXML.cpp | 47 + .../jni/application/enigma/src/LocalToXML.hh | 72 + .../enigma/src/PreferenceManager.cpp | 197 + .../enigma/src/PreferenceManager.hh | 55 + .../enigma/src/PropertyManager.cpp | 186 + .../application/enigma/src/PropertyManager.hh | 81 + .../application/enigma/src/StateManager.cpp | 408 ++ .../application/enigma/src/StateManager.hh | 83 + .../jni/application/enigma/src/Utf8ToXML.cpp | 80 + .../jni/application/enigma/src/Utf8ToXML.hh | 76 + .../jni/application/enigma/src/XMLtoLocal.cpp | 40 + .../jni/application/enigma/src/XMLtoLocal.hh | 73 + .../jni/application/enigma/src/XMLtoUtf8.cpp | 63 + .../jni/application/enigma/src/XMLtoUtf8.hh | 61 + project/jni/application/enigma/src/actors.cpp | 1182 ++++++ project/jni/application/enigma/src/actors.hh | 208 + .../application/enigma/src/actors_internal.hh | 216 + project/jni/application/enigma/src/client.cpp | 970 +++++ project/jni/application/enigma/src/client.hh | 71 + .../application/enigma/src/client_internal.hh | 172 + project/jni/application/enigma/src/config.h | 323 ++ .../jni/application/enigma/src/config.h.in | 317 ++ .../jni/application/enigma/src/d_engine.hh | 480 +++ .../jni/application/enigma/src/d_models.cpp | 623 +++ .../jni/application/enigma/src/d_models.hh | 241 ++ .../jni/application/enigma/src/display.cpp | 2132 ++++++++++ project/jni/application/enigma/src/display.hh | 246 ++ .../enigma/src/display_internal.hh | 96 + project/jni/application/enigma/src/editor.cpp | 469 +++ project/jni/application/enigma/src/editor.hh | 37 + .../jni/application/enigma/src/editor_impl.hh | 335 ++ project/jni/application/enigma/src/enigma.cpp | 519 +++ .../jni/application/enigma/src/enigma.doxygen | 946 +++++ project/jni/application/enigma/src/enigma.hh | 323 ++ project/jni/application/enigma/src/enigma.ico | Bin 0 -> 100022 bytes project/jni/application/enigma/src/enigma.rc | 1 + project/jni/application/enigma/src/errors.hh | 56 + project/jni/application/enigma/src/file.cpp | 372 ++ project/jni/application/enigma/src/file.hh | 202 + .../jni/application/enigma/src/file_zip.cpp | 81 + project/jni/application/enigma/src/floors.cpp | 1021 +++++ project/jni/application/enigma/src/floors.hh | 144 + project/jni/application/enigma/src/fwd.hh | 55 + project/jni/application/enigma/src/game.cpp | 126 + project/jni/application/enigma/src/game.hh | 45 + .../application/enigma/src/gui/ErrorMenu.cpp | 114 + .../application/enigma/src/gui/ErrorMenu.hh | 50 + .../application/enigma/src/gui/GameMenu.cpp | 177 + .../application/enigma/src/gui/GameMenu.hh | 40 + .../application/enigma/src/gui/HelpMenu.cpp | 86 + .../application/enigma/src/gui/HelpMenu.hh | 60 + .../application/enigma/src/gui/InfoMenu.cpp | 84 + .../application/enigma/src/gui/InfoMenu.hh | 42 + .../enigma/src/gui/LPGroupConfig.cpp | 231 + .../enigma/src/gui/LPGroupConfig.hh | 56 + .../enigma/src/gui/LevelInspector.cpp | 712 ++++ .../enigma/src/gui/LevelInspector.hh | 68 + .../application/enigma/src/gui/LevelMenu.cpp | 499 +++ .../application/enigma/src/gui/LevelMenu.hh | 99 + .../enigma/src/gui/LevelPackComposer.cpp | 391 ++ .../enigma/src/gui/LevelPackComposer.hh | 61 + .../enigma/src/gui/LevelPackConfig.cpp | 540 +++ .../enigma/src/gui/LevelPackConfig.hh | 112 + .../enigma/src/gui/LevelPackMenu.cpp | 506 +++ .../enigma/src/gui/LevelPackMenu.hh | 87 + .../enigma/src/gui/LevelPreviewCache.cpp | 178 + .../enigma/src/gui/LevelPreviewCache.hh | 91 + .../enigma/src/gui/LevelWidget.cpp | 489 +++ .../application/enigma/src/gui/LevelWidget.hh | 111 + .../application/enigma/src/gui/MainMenu.cpp | 392 ++ .../application/enigma/src/gui/MainMenu.hh | 83 + .../jni/application/enigma/src/gui/Menu.cpp | 213 + .../jni/application/enigma/src/gui/Menu.hh | 111 + .../enigma/src/gui/MonospacedLabel.cpp | 85 + .../enigma/src/gui/MonospacedLabel.hh | 57 + .../enigma/src/gui/OptionsMenu.cpp | 477 +++ .../application/enigma/src/gui/OptionsMenu.hh | 104 + .../enigma/src/gui/ScreenshotViewer.cpp | 109 + .../enigma/src/gui/ScreenshotViewer.hh | 49 + .../application/enigma/src/gui/SearchMenu.cpp | 91 + .../application/enigma/src/gui/SearchMenu.hh | 43 + .../application/enigma/src/gui/TextField.cpp | 281 ++ .../application/enigma/src/gui/TextField.hh | 96 + .../application/enigma/src/gui/widgets.cpp | 943 +++++ .../jni/application/enigma/src/gui/widgets.hh | 465 +++ project/jni/application/enigma/src/items.cpp | 3701 +++++++++++++++++ project/jni/application/enigma/src/items.hh | 259 ++ project/jni/application/enigma/src/laser.cpp | 797 ++++ project/jni/application/enigma/src/laser.hh | 130 + .../jni/application/enigma/src/lev/Index.cpp | 720 ++++ .../jni/application/enigma/src/lev/Index.hh | 179 + .../enigma/src/lev/PersistentIndex.cpp | 1102 +++++ .../enigma/src/lev/PersistentIndex.hh | 134 + .../jni/application/enigma/src/lev/Proxy.cpp | 1172 ++++++ .../jni/application/enigma/src/lev/Proxy.hh | 195 + .../enigma/src/lev/RatingManager.cpp | 698 ++++ .../enigma/src/lev/RatingManager.hh | 175 + .../enigma/src/lev/ScoreManager.cpp | 1152 +++++ .../enigma/src/lev/ScoreManager.hh | 136 + .../enigma/src/lev/VolatileIndex.cpp | 53 + .../enigma/src/lev/VolatileIndex.hh | 46 + .../application/enigma/src/lua-display.cpp | 580 +++ .../jni/application/enigma/src/lua-display.hh | 8 + .../jni/application/enigma/src/lua-ecl.cpp | 1645 ++++++++ project/jni/application/enigma/src/lua-ecl.hh | 8 + .../jni/application/enigma/src/lua-editor.cpp | 212 + .../jni/application/enigma/src/lua-editor.hh | 8 + .../jni/application/enigma/src/lua-enigma.cpp | 1310 ++++++ .../jni/application/enigma/src/lua-enigma.hh | 8 + .../jni/application/enigma/src/lua-global.cpp | 82 + .../jni/application/enigma/src/lua-global.hh | 8 + project/jni/application/enigma/src/lua.cpp | 928 +++++ project/jni/application/enigma/src/lua.hh | 122 + project/jni/application/enigma/src/main.cpp | 850 ++++ project/jni/application/enigma/src/main.hh | 243 ++ .../jni/application/enigma/src/netgame.cpp | 315 ++ project/jni/application/enigma/src/netgame.hh | 32 + .../jni/application/enigma/src/network.cpp | 146 + project/jni/application/enigma/src/network.hh | 76 + project/jni/application/enigma/src/nls.cpp | 43 + project/jni/application/enigma/src/nls.hh | 22 + .../jni/application/enigma/src/objects.cpp | 234 ++ project/jni/application/enigma/src/objects.hh | 37 + .../application/enigma/src/objects_decl.hh | 175 + .../jni/application/enigma/src/options.cpp | 290 ++ project/jni/application/enigma/src/options.hh | 108 + .../jni/application/enigma/src/ox_extra.cpp | 385 ++ .../jni/application/enigma/src/ox_magnum.cpp | 404 ++ .../jni/application/enigma/src/ox_oxyd1.cpp | 412 ++ .../jni/application/enigma/src/ox_peroxyd.cpp | 513 +++ project/jni/application/enigma/src/oxyd.cpp | 1092 +++++ project/jni/application/enigma/src/oxyd.hh | 35 + .../application/enigma/src/oxyd_internal.hh | 324 ++ project/jni/application/enigma/src/player.cpp | 655 +++ project/jni/application/enigma/src/player.hh | 101 + project/jni/application/enigma/src/server.cpp | 623 +++ project/jni/application/enigma/src/server.hh | 174 + .../application/enigma/src/server_internal.hh | 35 + project/jni/application/enigma/src/sound.cpp | 1036 +++++ project/jni/application/enigma/src/sound.hh | 237 ++ .../application/enigma/src/sound_internal.hh | 240 ++ .../application/enigma/src/st_switches.cpp | 702 ++++ project/jni/application/enigma/src/stones.cpp | 843 ++++ project/jni/application/enigma/src/stones.hh | 169 + .../application/enigma/src/stones_complex.cpp | 3559 ++++++++++++++++ .../application/enigma/src/stones_internal.hh | 119 + .../application/enigma/src/stones_simple.cpp | 2325 +++++++++++ project/jni/application/enigma/src/util.cpp | 170 + project/jni/application/enigma/src/util.hh | 45 + .../jni/application/enigma/src/utilXML.cpp | 56 + project/jni/application/enigma/src/utilXML.hh | 33 + project/jni/application/enigma/src/video.cpp | 827 ++++ project/jni/application/enigma/src/video.hh | 150 + project/jni/application/enigma/src/world.cpp | 2114 ++++++++++ project/jni/application/enigma/src/world.hh | 417 ++ .../application/enigma/src/world_internal.hh | 361 ++ project/jni/application/src | 2 +- project/res/values/strings.xml | 2 +- project/src/Accelerometer.java | 2 +- project/src/AssetExtract.java | 2 +- project/src/Audio.java | 2 +- project/src/GLSurfaceView_SDL.java | 2 +- project/src/Globals.java | 20 +- project/src/MainActivity.java | 2 +- project/src/Settings.java | 2 +- project/src/Video.java | 2 +- 308 files changed, 92986 insertions(+), 39 deletions(-) create mode 100644 project/jni/application/enigma/ACKNOWLEDGEMENTS create mode 100644 project/jni/application/enigma/AUTHORS create mode 100644 project/jni/application/enigma/AndroidAppSettings.cfg create mode 100644 project/jni/application/enigma/CHANGES create mode 100644 project/jni/application/enigma/COPYING create mode 100644 project/jni/application/enigma/INSTALL create mode 100644 project/jni/application/enigma/README create mode 100644 project/jni/application/enigma/icon.png create mode 100644 project/jni/application/enigma/lib-src/enet/README create mode 100644 project/jni/application/enigma/lib-src/enet/callbacks.c create mode 100644 project/jni/application/enigma/lib-src/enet/host.c create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/callbacks.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/enet.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/list.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/protocol.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/time.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/types.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/unix.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/utility.h create mode 100644 project/jni/application/enigma/lib-src/enet/include/enet/win32.h create mode 100644 project/jni/application/enigma/lib-src/enet/list.c create mode 100644 project/jni/application/enigma/lib-src/enet/packet.c create mode 100644 project/jni/application/enigma/lib-src/enet/peer.c create mode 100644 project/jni/application/enigma/lib-src/enet/protocol.c create mode 100644 project/jni/application/enigma/lib-src/enet/unix.c create mode 100644 project/jni/application/enigma/lib-src/enet/win32.c create mode 100644 project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.c create mode 100644 project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.h create mode 100644 project/jni/application/enigma/lib-src/enigma-core/README create mode 100644 project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.c create mode 100644 project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.h create mode 100644 project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives_font.h create mode 100644 project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.c create mode 100644 project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.h create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_alist.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_argp.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_argp.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_array2.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_buffer.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_buffer.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_cache.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_callback.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_dict.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_dict.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_error.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_font.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_font.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_fwd.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_geom.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_geom.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_math.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_math.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_sdl.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_sdl.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_sys_localename.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_system.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_system_unix.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_utf.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_utf.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_util.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_util.hh create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_video.cpp create mode 100644 project/jni/application/enigma/lib-src/enigma-core/ecl_video.hh create mode 100644 project/jni/application/enigma/lib-src/oxydlib/Bitmap.cpp create mode 100644 project/jni/application/enigma/lib-src/oxydlib/Bitmap.h create mode 100644 project/jni/application/enigma/lib-src/oxydlib/COPYING create mode 100644 project/jni/application/enigma/lib-src/oxydlib/DatFile.cpp create mode 100644 project/jni/application/enigma/lib-src/oxydlib/DatFile.h create mode 100644 project/jni/application/enigma/lib-src/oxydlib/FileUtils.cpp create mode 100644 project/jni/application/enigma/lib-src/oxydlib/FileUtils.h create mode 100644 project/jni/application/enigma/lib-src/oxydlib/Level.cpp create mode 100644 project/jni/application/enigma/lib-src/oxydlib/Level.h create mode 100644 project/jni/application/enigma/lib-src/oxydlib/OxydVersion.h create mode 100644 project/jni/application/enigma/lib-src/oxydlib/README create mode 100644 project/jni/application/enigma/lib-src/oxydlib/README.enigma create mode 100644 project/jni/application/enigma/lib-src/oxydlib/VecUtils.cpp create mode 100644 project/jni/application/enigma/lib-src/oxydlib/VecUtils.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/AUTHORS create mode 100644 project/jni/application/enigma/lib-src/zipios++/COPYING create mode 100644 project/jni/application/enigma/lib-src/zipios++/ChangeLog create mode 100644 project/jni/application/enigma/lib-src/zipios++/INSTALL create mode 100644 project/jni/application/enigma/lib-src/zipios++/NEWS create mode 100644 project/jni/application/enigma/lib-src/zipios++/README create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/backbuffer.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/basicentry.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/collcoll.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/deflateoutputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/dircoll.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/directory.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/directory.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/fcoll.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/fcollexceptions.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/fileentry.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/filepath.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/filterinputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/filteroutputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/inflateinputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/outputstringstream.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipfile.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/ziphead.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipheadio.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipinputstream.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipinputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipios_common.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipoutputstream.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/src/zipoutputstreambuf.cpp create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/basicentry.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/collcoll.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/deflateoutputstreambuf.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/dircoll.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/fcoll.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/fcollexceptions.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/fileentry.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/filepath.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/filterinputstreambuf.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/filteroutputstreambuf.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/inflateinputstreambuf.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/meta-iostreams.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/simplesmartptr.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/virtualseeker.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipfile.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/ziphead.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipheadio.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstream.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstreambuf.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h.in create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipios_defs.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstream.h create mode 100644 project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstreambuf.h create mode 100644 project/jni/application/enigma/src/DOMErrorReporter.cpp create mode 100644 project/jni/application/enigma/src/DOMErrorReporter.hh create mode 100644 project/jni/application/enigma/src/DOMSchemaResolver.cpp create mode 100644 project/jni/application/enigma/src/DOMSchemaResolver.hh create mode 100644 project/jni/application/enigma/src/Inventory.cpp create mode 100644 project/jni/application/enigma/src/Inventory.hh create mode 100644 project/jni/application/enigma/src/ItemHolder.hh create mode 100644 project/jni/application/enigma/src/LocalToXML.cpp create mode 100644 project/jni/application/enigma/src/LocalToXML.hh create mode 100644 project/jni/application/enigma/src/PreferenceManager.cpp create mode 100644 project/jni/application/enigma/src/PreferenceManager.hh create mode 100644 project/jni/application/enigma/src/PropertyManager.cpp create mode 100644 project/jni/application/enigma/src/PropertyManager.hh create mode 100644 project/jni/application/enigma/src/StateManager.cpp create mode 100644 project/jni/application/enigma/src/StateManager.hh create mode 100644 project/jni/application/enigma/src/Utf8ToXML.cpp create mode 100644 project/jni/application/enigma/src/Utf8ToXML.hh create mode 100644 project/jni/application/enigma/src/XMLtoLocal.cpp create mode 100644 project/jni/application/enigma/src/XMLtoLocal.hh create mode 100644 project/jni/application/enigma/src/XMLtoUtf8.cpp create mode 100644 project/jni/application/enigma/src/XMLtoUtf8.hh create mode 100644 project/jni/application/enigma/src/actors.cpp create mode 100644 project/jni/application/enigma/src/actors.hh create mode 100644 project/jni/application/enigma/src/actors_internal.hh create mode 100644 project/jni/application/enigma/src/client.cpp create mode 100644 project/jni/application/enigma/src/client.hh create mode 100644 project/jni/application/enigma/src/client_internal.hh create mode 100644 project/jni/application/enigma/src/config.h create mode 100644 project/jni/application/enigma/src/config.h.in create mode 100644 project/jni/application/enigma/src/d_engine.hh create mode 100644 project/jni/application/enigma/src/d_models.cpp create mode 100644 project/jni/application/enigma/src/d_models.hh create mode 100644 project/jni/application/enigma/src/display.cpp create mode 100644 project/jni/application/enigma/src/display.hh create mode 100644 project/jni/application/enigma/src/display_internal.hh create mode 100644 project/jni/application/enigma/src/editor.cpp create mode 100644 project/jni/application/enigma/src/editor.hh create mode 100644 project/jni/application/enigma/src/editor_impl.hh create mode 100644 project/jni/application/enigma/src/enigma.cpp create mode 100644 project/jni/application/enigma/src/enigma.doxygen create mode 100644 project/jni/application/enigma/src/enigma.hh create mode 100644 project/jni/application/enigma/src/enigma.ico create mode 100644 project/jni/application/enigma/src/enigma.rc create mode 100644 project/jni/application/enigma/src/errors.hh create mode 100644 project/jni/application/enigma/src/file.cpp create mode 100644 project/jni/application/enigma/src/file.hh create mode 100644 project/jni/application/enigma/src/file_zip.cpp create mode 100644 project/jni/application/enigma/src/floors.cpp create mode 100644 project/jni/application/enigma/src/floors.hh create mode 100644 project/jni/application/enigma/src/fwd.hh create mode 100644 project/jni/application/enigma/src/game.cpp create mode 100644 project/jni/application/enigma/src/game.hh create mode 100644 project/jni/application/enigma/src/gui/ErrorMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/ErrorMenu.hh create mode 100644 project/jni/application/enigma/src/gui/GameMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/GameMenu.hh create mode 100644 project/jni/application/enigma/src/gui/HelpMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/HelpMenu.hh create mode 100644 project/jni/application/enigma/src/gui/InfoMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/InfoMenu.hh create mode 100644 project/jni/application/enigma/src/gui/LPGroupConfig.cpp create mode 100644 project/jni/application/enigma/src/gui/LPGroupConfig.hh create mode 100644 project/jni/application/enigma/src/gui/LevelInspector.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelInspector.hh create mode 100644 project/jni/application/enigma/src/gui/LevelMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelMenu.hh create mode 100644 project/jni/application/enigma/src/gui/LevelPackComposer.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelPackComposer.hh create mode 100644 project/jni/application/enigma/src/gui/LevelPackConfig.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelPackConfig.hh create mode 100644 project/jni/application/enigma/src/gui/LevelPackMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelPackMenu.hh create mode 100644 project/jni/application/enigma/src/gui/LevelPreviewCache.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelPreviewCache.hh create mode 100644 project/jni/application/enigma/src/gui/LevelWidget.cpp create mode 100644 project/jni/application/enigma/src/gui/LevelWidget.hh create mode 100644 project/jni/application/enigma/src/gui/MainMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/MainMenu.hh create mode 100644 project/jni/application/enigma/src/gui/Menu.cpp create mode 100644 project/jni/application/enigma/src/gui/Menu.hh create mode 100644 project/jni/application/enigma/src/gui/MonospacedLabel.cpp create mode 100644 project/jni/application/enigma/src/gui/MonospacedLabel.hh create mode 100644 project/jni/application/enigma/src/gui/OptionsMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/OptionsMenu.hh create mode 100644 project/jni/application/enigma/src/gui/ScreenshotViewer.cpp create mode 100644 project/jni/application/enigma/src/gui/ScreenshotViewer.hh create mode 100644 project/jni/application/enigma/src/gui/SearchMenu.cpp create mode 100644 project/jni/application/enigma/src/gui/SearchMenu.hh create mode 100644 project/jni/application/enigma/src/gui/TextField.cpp create mode 100644 project/jni/application/enigma/src/gui/TextField.hh create mode 100644 project/jni/application/enigma/src/gui/widgets.cpp create mode 100644 project/jni/application/enigma/src/gui/widgets.hh create mode 100644 project/jni/application/enigma/src/items.cpp create mode 100644 project/jni/application/enigma/src/items.hh create mode 100644 project/jni/application/enigma/src/laser.cpp create mode 100644 project/jni/application/enigma/src/laser.hh create mode 100644 project/jni/application/enigma/src/lev/Index.cpp create mode 100644 project/jni/application/enigma/src/lev/Index.hh create mode 100644 project/jni/application/enigma/src/lev/PersistentIndex.cpp create mode 100644 project/jni/application/enigma/src/lev/PersistentIndex.hh create mode 100644 project/jni/application/enigma/src/lev/Proxy.cpp create mode 100644 project/jni/application/enigma/src/lev/Proxy.hh create mode 100644 project/jni/application/enigma/src/lev/RatingManager.cpp create mode 100644 project/jni/application/enigma/src/lev/RatingManager.hh create mode 100644 project/jni/application/enigma/src/lev/ScoreManager.cpp create mode 100644 project/jni/application/enigma/src/lev/ScoreManager.hh create mode 100644 project/jni/application/enigma/src/lev/VolatileIndex.cpp create mode 100644 project/jni/application/enigma/src/lev/VolatileIndex.hh create mode 100644 project/jni/application/enigma/src/lua-display.cpp create mode 100644 project/jni/application/enigma/src/lua-display.hh create mode 100644 project/jni/application/enigma/src/lua-ecl.cpp create mode 100644 project/jni/application/enigma/src/lua-ecl.hh create mode 100644 project/jni/application/enigma/src/lua-editor.cpp create mode 100644 project/jni/application/enigma/src/lua-editor.hh create mode 100644 project/jni/application/enigma/src/lua-enigma.cpp create mode 100644 project/jni/application/enigma/src/lua-enigma.hh create mode 100644 project/jni/application/enigma/src/lua-global.cpp create mode 100644 project/jni/application/enigma/src/lua-global.hh create mode 100644 project/jni/application/enigma/src/lua.cpp create mode 100644 project/jni/application/enigma/src/lua.hh create mode 100644 project/jni/application/enigma/src/main.cpp create mode 100644 project/jni/application/enigma/src/main.hh create mode 100644 project/jni/application/enigma/src/netgame.cpp create mode 100644 project/jni/application/enigma/src/netgame.hh create mode 100644 project/jni/application/enigma/src/network.cpp create mode 100644 project/jni/application/enigma/src/network.hh create mode 100644 project/jni/application/enigma/src/nls.cpp create mode 100644 project/jni/application/enigma/src/nls.hh create mode 100644 project/jni/application/enigma/src/objects.cpp create mode 100644 project/jni/application/enigma/src/objects.hh create mode 100644 project/jni/application/enigma/src/objects_decl.hh create mode 100644 project/jni/application/enigma/src/options.cpp create mode 100644 project/jni/application/enigma/src/options.hh create mode 100644 project/jni/application/enigma/src/ox_extra.cpp create mode 100644 project/jni/application/enigma/src/ox_magnum.cpp create mode 100644 project/jni/application/enigma/src/ox_oxyd1.cpp create mode 100644 project/jni/application/enigma/src/ox_peroxyd.cpp create mode 100644 project/jni/application/enigma/src/oxyd.cpp create mode 100644 project/jni/application/enigma/src/oxyd.hh create mode 100644 project/jni/application/enigma/src/oxyd_internal.hh create mode 100644 project/jni/application/enigma/src/player.cpp create mode 100644 project/jni/application/enigma/src/player.hh create mode 100644 project/jni/application/enigma/src/server.cpp create mode 100644 project/jni/application/enigma/src/server.hh create mode 100644 project/jni/application/enigma/src/server_internal.hh create mode 100644 project/jni/application/enigma/src/sound.cpp create mode 100644 project/jni/application/enigma/src/sound.hh create mode 100644 project/jni/application/enigma/src/sound_internal.hh create mode 100644 project/jni/application/enigma/src/st_switches.cpp create mode 100644 project/jni/application/enigma/src/stones.cpp create mode 100644 project/jni/application/enigma/src/stones.hh create mode 100644 project/jni/application/enigma/src/stones_complex.cpp create mode 100644 project/jni/application/enigma/src/stones_internal.hh create mode 100644 project/jni/application/enigma/src/stones_simple.cpp create mode 100644 project/jni/application/enigma/src/util.cpp create mode 100644 project/jni/application/enigma/src/util.hh create mode 100644 project/jni/application/enigma/src/utilXML.cpp create mode 100644 project/jni/application/enigma/src/utilXML.hh create mode 100644 project/jni/application/enigma/src/video.cpp create mode 100644 project/jni/application/enigma/src/video.hh create mode 100644 project/jni/application/enigma/src/world.cpp create mode 100644 project/jni/application/enigma/src/world.hh create mode 100644 project/jni/application/enigma/src/world_internal.hh diff --git a/ChangeAppSettings.sh b/ChangeAppSettings.sh index d1ec82abc..a1bae2aa8 100755 --- a/ChangeAppSettings.sh +++ b/ChangeAppSettings.sh @@ -89,7 +89,7 @@ if [ -n "$var" ] ; then AppNeedsArrowKeys="$var" fi -echo -n "\nApplication uses joystick (y) or (n), the accelerometer (2-axis) or orientation sensor (3-axis)\nwill be used as joystick 0 if not used as arrow keys ($AppUsesJoystick): " +echo -n "\nApplication uses joystick (y) or (n), the accelerometer (2-axis) or orientation sensor (3-axis)\nwill be used as joystick 0, also on-screen DPAD will be used as joystick ($AppUsesJoystick): " read var if [ -n "$var" ] ; then AppUsesJoystick="$var" @@ -147,15 +147,6 @@ if [ -n "$var" ] ; then AppVersionName="$var" fi -echo -n "\nOptional shared libraries to compile - removing some of them will save space\nMP3 support by libMAD is encumbered by patents and libMAD is GPL-ed\n" -grep 'Available libraries:' project/jni/Application.mk -grep 'depends on' project/jni/Application.mk -echo -n "Current: $CompiledLibraries\n\n: " -read var -if [ -n "$var" ] ; then - CompiledLibraries="$var" -fi - echo -n "\nApplication uses custom build script AndroidBuild.sh instead of Android.mk (y) or (n) ($CustomBuildScript): " read var if [ -n "$var" ] ; then @@ -168,13 +159,22 @@ if [ -n "$var" ] ; then AppCflags="$var" fi +echo -n "\nOptional shared libraries to compile - removing some of them will save space\nMP3 support by libMAD is encumbered by patents and libMAD is GPL-ed\n" +grep 'Available' project/jni/Application.mk +grep 'depends on' project/jni/Application.mk +echo -n "Current: $CompiledLibraries\n\n: " +read var +if [ -n "$var" ] ; then + CompiledLibraries="$var" +fi + echo -n "\nAditional LDFLAGS for application ($AppLdflags): " read var if [ -n "$var" ] ; then AppLdflags="$var" fi -echo -n "\nBuild only following subdirs (empty will build all dirs) ($AppSubdirsBuild): " +echo -n "\nBuild only following subdirs (empty will build all dirs, ignored with custom script) ($AppSubdirsBuild): " read var if [ -n "$var" ] ; then AppSubdirsBuild="$var" @@ -369,7 +369,7 @@ fi echo Patching project/jni/Application.mk cat project/jni/Application.mk | \ - sed "s/APP_MODULES := .*/APP_MODULES := application sdl-$LibSdlVersion sdl_main stlport tremor png jpeg freetype $CompiledLibraries/" | \ + sed "s/APP_MODULES := .*/APP_MODULES := application sdl-$LibSdlVersion sdl_main stlport tremor png jpeg freetype xerces $CompiledLibraries/" | \ sed "s/APP_ABI := .*/APP_ABI := $MultiABI/" > \ project/jni/Application.mk.1 if [ -n "`diff -w project/jni/Application.mk.1 project/jni/Application.mk`" ] ; then @@ -392,6 +392,14 @@ rm -rf project/$OUT/local/*/libsdl-*.so rm -rf project/$OUT/local/*/objs/sdl-*/src/*/android rm -rf project/$OUT/local/*/objs/sdl-*/src/video/SDL_video.o rm -rf project/$OUT/local/*/objs/sdl-*/SDL_renderer_gles.o +# Do not rebuild several huge libraries that do not depend on SDL version +for LIB in freetype intl jpeg png lua mad stlport tremor xerces xml2; do + for ARCH in armeabi armeabi-v7a; do + if [ -e "project/$OUT/local/$ARCH/objs/$LIB" ] ; then + find project/$OUT/local/$ARCH/objs/$LIB -name "*.o" | xargs touch -c + fi + done +done done echo Done diff --git a/project/AndroidManifest.xml b/project/AndroidManifest.xml index bf765cd4e..34529bd51 100644 --- a/project/AndroidManifest.xml +++ b/project/AndroidManifest.xml @@ -1,8 +1,8 @@ , st-camouflage + - new floors: fl-thief, fl-nomouse, fl-woven_orange + - new items: it-rubberband, it-booze-broken, it-death, it-drop + - removed: fl-ice_001 + - it-pencil -> it-cross, it-crack + - it-brush -> it-cross, it-squashed + - st-scissor actions + - bigbricks unswappable and unpullable + - st-flash/actorimpulse*/spitter : distorted forces + - small whiteballs can jump + - new libraries: andreas_itemfreeze, andreas_ghosts, libpuzzle, libterrain + - icons polish + - sound additions, volume reduction on stone hit + + for details on the subjects read the reference manual, for a complete list + of changes see svn commit messages at berlios.de: + http://svn.berlios.de/wsvn/enigma-game/tags/1.00/?op=log&rev=0&sc=0&isdir=1) + +Changes in Version 0.92 +======================= + +Internal changes +---------------- + + - fl-ice_001 is back, but by all means, use fl-ice if you can + +User-Visible changes +-------------------- + + - Ice is working again + - Cannonballs are working again + - Spanish translation (thanks to Samuel Elías Martínez �?lvarez) + - New application icon on Windows (thanks to Tobias Schmidbauer) + + +Changes in Version 0.91 +======================= + +Internal changes +---------------- + + - removed fl-ice_001 + - new game variable enigma.IceFriction + + +Changes in Version 0.90 +======================= + +User-Visible Changes +-------------------- + + * Support for internationalized text + * Support for 800x600 and 1024x768 video modes + * Many new levels + * Tutorial levels for new players + * Much improved support for loading Oxyd levels + * New game mode: Time hunt + * Can reorder inventory with TAB key + * Support for gamma correction on some systems + * New command line options: --dumpinfo, --lang, --data + * Removed --8bpp command line option + * Removed support for 8bit graphics + + +Internal changes +---------------- + +* New item types + + - it-bag + - it-banana + - it-blackbomb-burning + - it-booze + - it-changefloor + - it-drop (not 100% finished) + - it-easykeepstone + - it-easykillstone + - it-magnet-off + - it-magnet-on + - it-squashed + - it-surprise + +* Other item changes + + - Renamed it-soother to it-ring + +* New stone types + + - st-spitter + - st-flash + - st-surprise + - st-coffee + - st-blackballs + - st-whiteballs + - st-bug + - st-breaking + - st-fakeoxyda + - st-plain_cracked + - st-plain_hole + - st-plain_breaking + - st-plain_break + - st-plain_falling + - st-plain_move + - st-laserbreak + - st-break_gray + - st-yinyang3 + +* New floor types + + fl-ice (fl-ice_001 still available) + +* Other changes + + - Two new scrolling modes: FOLLOW_SCREENSCROLLING (yes, ugly name, + sorry) and FOLLOW_SMOOTH. + + - Elastic bands also have a minimum length, see daniel6.lua. + + - An XML-based level format. Please refer to the reference manual + for details. + + +Changes in Version 0.81 +======================= + +User-Visible Changes +-------------------- + + * Fixed bug which caused all levels to be restarted when the + marble died. + +Internal Changes +---------------- + +* Changed a few object names + + it-seed_vulcano -> it-seed_volcano + st-vulcano -> st-volcano + st-vulcano-growing -> st-volcano-growing + + + +Changes in Version 0.80 +======================= + +User-Visible Changes +-------------------- + + * Time game + * Easy / difficult game mode + * Sokoban mode + * Game manual + * More than 300 new levels (now more than 550 in total) + * (partially working) Oxyd level importer + * Better soft scrolling + * Countless new game elements: turnstiles, volcanos, warp tunnels, + land-mines, jump pads, rotors, ... + * Reduced memory footprint + + +Internal Changes +---------------- + +* New Lua Variables + + enigma.AllowTogglePlayer = TRUE | FALSE + enigma.ShowMoves = TRUE | FALSE + enigma.Brittleness = 0 .. 1 + enigma.SlopeForce + enigma.SlopeForce2 + enigma.FrictionFactor + enigma.ElectricForce + enigma.BumperForce + + +* New Actor Types + + ac-bug + ac-killerball + ac-rotator + ac-top + + +* New Stone Types + + st-actorimpulse_invisible + st-beads + st-block + st-blocker + st-blue-sand + st-bolder-[nesw] + st-brake + st-break_bolder + st-break_invisible + st-chargeminus + st-chargeplus + st-chargezero + st-death_invisible + st-disco-dark + st-disco-light + st-disco-medium + st-door-h + st-door-h-open + st-door-v + st-door-v-open + st-easymode + st-fourswitch + st-glass_hole + st-glass_move + st-knight + st-laserswitch + st-likeoxyd[abcd] + st-mail + st-oneway-[nesw] + st-oneway_black-[nesw] + st-oneway_white-[nesw] + st-plain + st-pull + st-puzzle-[nesw] + st-puzzle-{ne, ns, nw, es, ew, sw} + st-puzzle-{nes, new, nsw, esw, nesw} + st-puzzle2-[nesw] + st-puzzle2-{ne, ns, nw, es, ew, sw} + st-puzzle2-{nes, new, nsw, esw, nesw} + st-rock3_movebreak + st-rotator-left + st-rotator-right + st-rotator_move-left + st-rotator_move-right + st-shogun-{s, m, l, sm, sl, ml, sml} + st-stoneimpulse_movable + st-switch_black + st-switch_white + st-timeswitch + st-turnstile + st-turnstile-[nesw] + st-turnstile-green + st-volcano + st-volcano_active + st-volcano_inactive + st-window + st-wood1 + st-wood2 + st-yellow + +* New Item Types + + it-abyss + it-blocker + it-brake + it-burnable + it-burnable-ash + it-burnable-fireproof + it-burnable-ignited + it-cherry + it-coffee + it-easymode + it-extinguisher + it-extinguisher-empty + it-extinguisher-full + it-flagblack + it-flagwhite + it-glasses + it-glasses-broken + it-hstrip + it-inversesensor + it-landmine + it-odometer + it-pin + it-puller-[nesw] + it-seed + it-seed_nowood + it-seed_volcano + it-seed_wood + it-sensor + it-soother + it-springboard + it-vortex-closed + it-vortex-open + it-vstrip + it-weight + it-whitebomb + it-wrench + +* New Floor Types + + fl-abyss_fake + fl-acwhite + fl-acblack + fl-black + fl-bumps + fl-concrete + fl-gravel + fl-light + fl-mortar + fl-red + fl-rock + fl-rough-blue + fl-rough-red + fl-rough_medium + fl-rough_slow + fl-springboard + fl-stwood + fl-stwood1 + fl-stwood1 + fl-stwood2 + fl-stwood2 + fl-white + diff --git a/project/jni/application/enigma/COPYING b/project/jni/application/enigma/COPYING new file mode 100644 index 000000000..7a8e8abfd --- /dev/null +++ b/project/jni/application/enigma/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/project/jni/application/enigma/INSTALL b/project/jni/application/enigma/INSTALL new file mode 100644 index 000000000..b42a17ac4 --- /dev/null +++ b/project/jni/application/enigma/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/project/jni/application/enigma/README b/project/jni/application/enigma/README new file mode 100644 index 000000000..437801541 --- /dev/null +++ b/project/jni/application/enigma/README @@ -0,0 +1,102 @@ +About Enigma +============ + +Enigma is a unique puzzle game, with influences from almost every game +genre. Your objective is easily explained: you control a small black +marble with your mouse and have to find and uncover all pairs of +identical Oxyd stones in each landscape. Simple? Yes. Easy? It would +be, if it weren't for hidden traps, vast mazes, insurmountable +obstacles and innumerable puzzles blocking your direct way to the Oxyd +stones... If you like puzzle games and have a steady hand, Enigma's +more than 500 levels will probably keep you busy for hours on end. + +Enigma is developed by a small group of volunteers, and help is always +appreciated. If you like Enigma and want to contribute to its future, +you are welcome to join us on our development mailing list at + + http://mail.nongnu.org/mailman/listinfo/enigma-devel + +or simply send email to + + enigma-devel@nongnu.org + +Every kind of contribution is welcome, whether it is programming, +documentation, graphics, sound effects, or simply good advice. + + +The official Enigma homepage can be found at + + http://www.nongnu.org/enigma/ + +If you have any questions, suggestions, or contributions, feel free to +send email to the mailing list. Have fun! + + The Enigma Team + + +Playing Enigma +============== + +Please refer to the user manual for instructions on how to play Enigma, +or simply start with the tutorial levels included with the game. + + +Installation +============ + +Installation on Windows and Mac OS X is straightforward: Simply download +the appropriate .exe or .dmg file and start it with a double click. + +Things get a little more complicated for other operating systems, +please refer to Enigma's download page + + http://www.nongnu.org/enigma/download.html + +for up-to-date information. If you are running some kind of Unix system, +you have always the option of compiling Enigma yourself; this is +explained in the next section. + +There may or may not be binaries for the Linux distribution of your choice, +and they may or may not work on your computer. This is not our fault: +packaging a Linux version that works everywhere is almost impossible, +thanks to countless subtle and not-so-subtle differences between each +and every Linux distribution. Please complain to your vendor if this +bugs you. + + +Compiling Enigma +================ + +This section briefly describes how to compile Enigma on a Unix +machine. If you want to build Enigma on Windows or Mac OS X, please +see the documentation in `doc/README.mingw32' and `doc/README.macosx', +respectively. + +For a list of libraries and programs that must be installed to compile +Enigma, please refer to `doc/REQUIREMENTS' for a complete list. + +Once you have everything installed, building Enigma is as easy as typing + + ./configure && make && make install + +in the enigma directory. + + +Copying +======= + +Enigma is free software. You may copy and modify it under the terms +of the GNU General Public License, Version 2 or, at your option, any +later version. For details, please refer to the accompanying +COPYING file. + + Please refer to the ACKNOWLEDGEMENTS file that comes with Enigma for +copyright information and licensing terms of external dependencies and +contributions to this project. + + To my best of our knowledge the included sound effects are either in the +public domain or freely distributable. We weren't able to pin down the +exact origin of each sound file -- the copyrights of which we are aware +are collected in soundsets/enigma/README. Please refer to this list +before using the samples in your productions. + diff --git a/project/jni/application/enigma/icon.png b/project/jni/application/enigma/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9d9cbd24666fb426da1004d33eb63f0ae352301b GIT binary patch literal 3294 zcmeAS@N?(olHy`uVBq!ia0y~yV9)?z4mJh`hMs>rav2yHSkfJR9T^yIGCN)KU&z3~ zI62eVIl$A|Ss|b(KP@vSm4TsxaqT2~@52rf$L5E4Ij-vd%HK4>F{@o`W2}fvhVhCk ziqo97M$bC3CE)UeBdg>O?0uld_m5FsP`&UC`wG3L=8gyE0$h4QkE$(~G`ouPzx%ZN z`Mm1;clNTkM6Jrb?!-{0bN=M|l%r?YPLilRrf^6lDA@G+IgL|R^S?^l&r>k?TR83L ztLHylyRD`O2qZn}oH<9h=k%QE67MUwxy!X~3=1qxdgWo&y2s~^ikz9{d?UT$eTjE2 z_-xzmsuNjtRjE!r?gDQ(vy;yB@4ub`B(=XiBg z5|e~3Q~Aqz*KTsWbCJm2efwpIfL}*iGn%O5#B;do(I`~UNu@4LmPJZ8;^ytVE! z$DS{f57~aWK4*Tf`*Tx;w5=SkK2HAmlX*YOoV83x9TmzsWcD)}Uq5g{cL5J$t1HLb z_V?%BYuzx{T)j2xW}vOJ_|4*puea`cd}~!2%jy5-702wZvP^jWtufeCd)|zN*HcxF zntIRn?$XSRJ0!lPsE}9Gj_Vl%0|RG)M`SSr1Gg{;GcwGYBf-GH_0ZGBF(iWX?F{dp zR9DI4|DVsPKKHiz_qXC@%V#DAq)8ZXu&6O{atK|S(O`J-$c_UGw0%pSI)xpRh}~f% zB|anLk(aodRI*Qu!bKP6HzsRBMB3bqbN%C>$Edib$Mo~uRl%Xl zU&a4_AG&?%|LOk=+4g*YP*c1{(D$0>iuU&hXKhx!$9o|7(jn%FtN2;(-8pKoWlf zM_8@@`uTb3<+tyz_dfS~edVR-TAeE*u8oguE*FTbzAOLY(o*xUht}7xe|vViiQo4} zPAAth{K$PW|2<2_(_A(CV?X$po?08Kn_c>H&bvP+{_c8zvAX2nvr=92SF73-gC;(& zK6Tk=u}WCBK}hn>g=@W|rM|nrD$%;AakFyKg&%^~ep`L&-hQ*(Oy*3KyL9~0*UL9- zm&*v${%rpGQ0@0u2amtM9=m>t7=WoI)_ zxV!F>$uWjWt}B0hNm*?#fA>?y`L7IhGgta6Ph4&pTI#fV`MH;W_Ljd8J8*Zyo)tgc zd_Ts7YAPiQ1Y{l!58@1IX?zv8&~a9Fa&q|HyUQMZbg0bpoAQd@6yS!YtUUg0NVtA{0+dE?M$xkci{`-9*e&T-R}C7DX0 z32k>~y}NJGzkuQ+2NG<8>P^C={qxGIHgJ4BcbfV6%Qy@7-n&+z)=YLsPanTC?YIr! zsvp`t{vp}MyfWQM&C^Z_&5$zreQVF1OJ&6?1fo|xE6rmS$9AyK7j{ z_$&Tf(FPwzJBQ~pS2MGnYhze0+Pc;BYPkQY*+jVIRy|> zSY&xK&NraY@mYsd)b5Hve*3cZdHXBEK4%{>kmwA{-W9-q^|+jV{-jgu-W={(zvac| zwRgi68-Gkc^2|=s`>@oRsSLdfR?1!~QJDK#%A(u8y=&pdLxFEhz9|-VyK>|id^nd$%N?~#>l&%|F#*w6mlfBV#%>dcnCh7*}1Qy%U*su*!w zqU4Qir^XlgWml}uOwGAkcJj`YB4)3c)owQgcg_$=J@fJYS_U^WZC2}mJ8vi4>anzr zsXUOm`vgN;AM;|@g9l|fgGG)1X1%<8q=8?Y`9|~2>)yXjvU;ALSgF#@{^}ct=KnnH zw|%BPKMx-Kw|Gl{k3!zIwpUw!dF}9<`OAxUK@j_)11Wu5B`u!s4K4^)n;ADvR%?Td z-m;_~r9a%CHn3aVXI^q$AtfT{!9e-Ox8u?DGE~92K(+S!VWQ&U@B);{v;R*^$Z$ z8-J&?U0kqw@7-x-<0WLU-OMg>#P|5- z8jr#cF_R6IFNreeFM7^jvP-bqhe7Q8=HH^{`LE>~&KG9C^RmWoPVeumt>1QTTc26l zy2H(EVQ^%R3iF#S3L4h~K5n{XC^l_Yr$n^E29p;&+f&ZvT>sfodA8-W=Pv0FXXSY= zY&{ypruy>d)YjbzAKnQm_z3NY7fJWpU0TQ{jQvrU4L0NOFFE4Te*qR@5cg% zoO!2MPL@5iojJ(5VME4(($b}}vUMLt{vMBuN@w_7^CK-F^UgC%F~(zGkC@+1G@oyply`b=ocwyZ1)uu9 zm6z?TGoAX)ibczbr)$mhMrS#9>F zS#RB6FtXUQMQ|HjJGOde#5wIxV(e9uI;T%eKCRdgaPiZo54=3zjT^Y{GcVbxFbYn;W+eL_z|dc`Em-Dhq|nVsupEZ|cSN%Q~s>2~r& zU#syjT8J0%ZZGh;|N_MSIcyZz)Gg}k>q&uk^9^DLWO#dO8U(X-!z~Vl`RYH9uXrqY<>7Np1*6@1iVY`(8JM*E z88UKrKQ??8H0$by)c37hc}>hE?NSm?n{8PBrhLzzo@1O(R5o=LBo=O$WX^8O68@~* z@zN$B%U|HkzT*%5s$;GeDC{wwRiSa*RNd(IrUo8uz3YC(dUCNd!p}QM?zkiLEq_9~ z0JDLs&Dx&_V%y7Bp4GEHZgk?nk|paW)b4nYx;?pN^|W(3*-zf(rN1)SrsnhTpyl+d zO3UvXc_)?&WVIjsrXTn0qxTcnBUdu3r5)Ag6sUd6o6eRr-EFdu)8~wh{pXba*s4A} zUV3Qdp6?saNtxdNxOkqmg0yw*l=;y&4BpPzu}$%|3(u6Nx9kp8@$Fn_J|Rqc!O68= z$L^UWF?`<6l(GB3yo8H4IbW{0(Qr2P9^=b5>7BR!9kTL0z;@zF#{F1Fo;g>qrB~mK zxmNgscf#Gc9j{LQyONN!J@dHC)|m$a*Ka(>eAF+aA malloc != NULL || inits -> free != NULL) + { + if (inits -> malloc == NULL || inits -> free == NULL) + return -1; + + callbacks.malloc = inits -> malloc; + callbacks.free = inits -> free; + } + + if (inits -> rand != NULL) + callbacks.rand = inits -> rand; + + return enet_initialize (); +} + +void * +enet_malloc (size_t size) +{ + void * memory = callbacks.malloc (size); + + if (memory == NULL) + abort (); + + return memory; +} + +void +enet_free (void * memory) +{ + callbacks.free (memory); +} + +int +enet_rand (void) +{ + return callbacks.rand (); +} + diff --git a/project/jni/application/enigma/lib-src/enet/host.c b/project/jni/application/enigma/lib-src/enet/host.c new file mode 100644 index 000000000..e5db56e2d --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/host.c @@ -0,0 +1,391 @@ +/** + @file host.c + @brief ENet host management functions +*/ +#define ENET_BUILDING_LIB 1 +#include +#include "enet/enet.h" + +/** @defgroup host ENet host functions + @{ +*/ + +/** Creates a host for communicating to peers. + + @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. + @param peerCount the maximum number of peers that should be allocated for the host. + @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + + @returns the host on success and NULL on failure + + @remarks ENet will strategically drop packets on specific sides of a connection between hosts + to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine + the window size of a connection which limits the amount of reliable packets that may be in transit + at any given time. +*/ +ENetHost * +enet_host_create (const ENetAddress * address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + ENetHost * host = (ENetHost *) enet_malloc (sizeof (ENetHost)); + ENetPeer * currentPeer; + + host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); + memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); + + host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM, address); + if (host -> socket == ENET_SOCKET_NULL) + { + enet_free (host -> peers); + enet_free (host); + + return NULL; + } + + if (address != NULL) + host -> address = * address; + + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> bandwidthThrottleEpoch = 0; + host -> recalculateBandwidthLimits = 0; + host -> mtu = ENET_HOST_DEFAULT_MTU; + host -> peerCount = peerCount; + host -> lastServicedPeer = host -> peers; + host -> commandCount = 0; + host -> bufferCount = 0; + host -> receivedAddress.host = ENET_HOST_ANY; + host -> receivedAddress.port = 0; + host -> receivedDataLength = 0; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + currentPeer -> host = host; + currentPeer -> incomingPeerID = currentPeer - host -> peers; + currentPeer -> data = NULL; + + enet_list_clear (& currentPeer -> acknowledgements); + enet_list_clear (& currentPeer -> sentReliableCommands); + enet_list_clear (& currentPeer -> sentUnreliableCommands); + enet_list_clear (& currentPeer -> outgoingReliableCommands); + enet_list_clear (& currentPeer -> outgoingUnreliableCommands); + + enet_peer_reset (currentPeer); + } + + return host; +} + +/** Destroys the host and all resources associated with it. + @param host pointer to the host to destroy +*/ +void +enet_host_destroy (ENetHost * host) +{ + ENetPeer * currentPeer; + + enet_socket_destroy (host -> socket); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + enet_peer_reset (currentPeer); + } + + enet_free (host -> peers); + enet_free (host); +} + +/** Initiates a connection to a foreign host. + @param host host seeking the connection + @param address destination for the connection + @param channelCount number of channels to allocate + @returns a peer representing the foreign host on success, NULL on failure + @remarks The peer returned will have not completed the connection until enet_host_service() + notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. +*/ +ENetPeer * +enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount) +{ + ENetPeer * currentPeer; + ENetChannel * channel; + ENetProtocol command; + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + else + if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + currentPeer -> state = ENET_PEER_STATE_CONNECTING; + currentPeer -> address = * address; + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + currentPeer -> channelCount = channelCount; + currentPeer -> challenge = (enet_uint32) enet_rand (); + + if (host -> outgoingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (host -> outgoingBandwidth / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + } + + command.header.command = ENET_PROTOCOL_COMMAND_CONNECT; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolConnect); + command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + command.connect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu); + command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); + command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + + enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); + + return currentPeer; +} + +/** Queues a packet to be sent to all peers associated with the host. + @param host host on which to broadcast the packet + @param channelID channel on which to broadcast + @param packet packet to broadcast +*/ +void +enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) +{ + ENetPeer * currentPeer; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + enet_peer_send (currentPeer, channelID, packet); + } + + if (packet -> referenceCount == 0) + enet_packet_destroy (packet); +} + +/** Adjusts the bandwidth limits of a host. + @param host host to adjust + @param incomingBandwidth new incoming bandwidth + @param outgoingBandwidth new outgoing bandwidth + @remarks the incoming and outgoing bandwidth parameters are identical in function to those + specified in enet_host_create(). +*/ +void +enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> recalculateBandwidthLimits = 1; +} + +void +enet_host_bandwidth_throttle (ENetHost * host) +{ + enet_uint32 timeCurrent = enet_time_get (), + elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, + peersTotal = 0, + dataTotal = 0, + peersRemaining, + bandwidth, + throttle = 0, + bandwidthLimit = 0; + int needsAdjustment; + ENetPeer * peer; + ENetProtocol command; + + if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + return; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + ++ peersTotal; + dataTotal += peer -> outgoingDataTotal; + } + + if (peersTotal == 0) + return; + + peersRemaining = peersTotal; + needsAdjustment = 1; + + if (host -> outgoingBandwidth == 0) + bandwidth = ~0; + else + bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; + + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + + if (dataTotal < bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + enet_uint32 peerBandwidth; + + if (peer -> state != ENET_PEER_STATE_CONNECTED || + peer -> incomingBandwidth == 0 || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; + if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) + continue; + + peer -> packetThrottleLimit = (peerBandwidth * + ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; + + if (peer -> packetThrottleLimit == 0) + peer -> packetThrottleLimit = 1; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> outgoingBandwidthThrottleEpoch = timeCurrent; + + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peerBandwidth; + dataTotal -= peerBandwidth; + } + } + + if (peersRemaining > 0) + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peer -> packetThrottleLimit = throttle; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + } + + if (host -> recalculateBandwidthLimits) + { + host -> recalculateBandwidthLimits = 0; + + peersRemaining = peersTotal; + bandwidth = host -> incomingBandwidth; + needsAdjustment = 1; + + if (bandwidth == 0) + bandwidthLimit = 0; + else + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + bandwidthLimit = bandwidth / peersRemaining; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED || + peer -> incomingBandwidthThrottleEpoch == timeCurrent) + continue; + + if (peer -> outgoingBandwidth > 0 && + bandwidthLimit > peer -> outgoingBandwidth) + continue; + + peer -> incomingBandwidthThrottleEpoch = timeCurrent; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peer -> outgoingBandwidth; + } + } + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolBandwidthLimit); + command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + + if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); + else + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + } + } + + host -> bandwidthThrottleEpoch = timeCurrent; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + } +} + +/** @} */ diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/callbacks.h b/project/jni/application/enigma/lib-src/enet/include/enet/callbacks.h new file mode 100644 index 000000000..be29ae057 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/callbacks.h @@ -0,0 +1,28 @@ +/** + @file callbacks.h + @brief ENet callbacks +*/ +#ifndef __ENET_CALLBACKS_H__ +#define __ENET_CALLBACKS_H__ + +#include + +typedef struct +{ + void * (ENET_CALLBACK * malloc) (size_t size); + void (ENET_CALLBACK * free) (void * memory); + int (ENET_CALLBACK * rand) (void); +} ENetCallbacks; + +/** @defgroup callbacks ENet internal callbacks + @{ + @ingroup private +*/ +extern void * enet_malloc (size_t); +extern void enet_free (void *); +extern int enet_rand (void); + +/** @} */ + +#endif /* __ENET_CALLBACKS_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/enet.h b/project/jni/application/enigma/lib-src/enet/include/enet/enet.h new file mode 100644 index 000000000..4c48a9265 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/enet.h @@ -0,0 +1,432 @@ +/** + @file enet.h + @brief ENet public header file +*/ +#ifndef __ENET_ENET_H__ +#define __ENET_ENET_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#ifdef WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif + +#include "enet/types.h" +#include "enet/protocol.h" +#include "enet/list.h" +#include "enet/callbacks.h" + +typedef enum +{ + ENET_VERSION = 1 +} ENetVersion; + +typedef enum +{ + ENET_SOCKET_TYPE_STREAM = 1, + ENET_SOCKET_TYPE_DATAGRAM = 2 +} ENetSocketType; + +typedef enum +{ + ENET_SOCKET_WAIT_NONE = 0, + ENET_SOCKET_WAIT_SEND = (1 << 0), + ENET_SOCKET_WAIT_RECEIVE = (1 << 1) +} ENetSocketWait; + +enum +{ + ENET_HOST_ANY = 0, /**< specifies the default server host */ + ENET_HOST_BROADCAST = 0xFFFFFFFF /**< specifies a subnet-wide broadcast */ +}; + +/** + * Portable internet address structure. + * + * The host must be specified in network byte-order, and the port must be in host + * byte-order. The constant ENET_HOST_ANY may be used to specify the default + * server host. The constant ENET_HOST_BROADCAST may be used to specify the + * broadcast address (255.255.255.255). This makes sense for enet_host_connect, + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. + */ +typedef struct _ENetAddress +{ + enet_uint32 host; + enet_uint16 port; +} ENetAddress; + +/** + * Packet flag bit constants. + * + * The host must be specified in network byte-order, and the port must be in + * host byte-order. The constant ENET_HOST_ANY may be used to specify the + * default server host. + + @sa ENetPacket +*/ +typedef enum +{ + /** packet must be received by the target peer and resend attempts should be + * made until the packet is delivered */ + ENET_PACKET_FLAG_RELIABLE = (1 << 0), + /** packet will not be sequenced with other packets + * not supported for reliable packets + */ + ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1) +} ENetPacketFlag; + +/** + * ENet packet structure. + * + * An ENet data packet that may be sent to or received from a peer. The shown + * fields should only be read and never modified. The data field contains the + * allocated data for the packet. The dataLength fields specifies the length + * of the allocated data. The flags field is either 0 (specifying no flags), + * or a bitwise-or of any combination of the following flags: + * + * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer + * and resend attempts should be made until the packet is delivered + + @sa ENetPacketFlag + */ +typedef struct _ENetPacket +{ + size_t referenceCount; /**< internal use only */ + enet_uint32 flags; /**< bitwise or of ENetPacketFlag constants */ + enet_uint8 * data; /**< allocated data for packet */ + size_t dataLength; /**< length of data */ +} ENetPacket; + +typedef struct _ENetAcknowledgement +{ + ENetListNode acknowledgementList; + enet_uint32 sentTime; + ENetProtocol command; +} ENetAcknowledgement; + +typedef struct _ENetOutgoingCommand +{ + ENetListNode outgoingCommandList; + enet_uint32 reliableSequenceNumber; + enet_uint32 unreliableSequenceNumber; + enet_uint32 sentTime; + enet_uint32 roundTripTimeout; + enet_uint32 roundTripTimeoutLimit; + enet_uint32 fragmentOffset; + enet_uint16 fragmentLength; + ENetProtocol command; + ENetPacket * packet; +} ENetOutgoingCommand; + +typedef struct _ENetIncomingCommand +{ + ENetListNode incomingCommandList; + enet_uint32 reliableSequenceNumber; + enet_uint32 unreliableSequenceNumber; + ENetProtocol command; + enet_uint32 fragmentCount; + enet_uint32 fragmentsRemaining; + enet_uint32 * fragments; + ENetPacket * packet; +} ENetIncomingCommand; + +typedef enum +{ + ENET_PEER_STATE_DISCONNECTED = 0, + ENET_PEER_STATE_CONNECTING = 1, + ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, + ENET_PEER_STATE_CONNECTION_PENDING = 3, + ENET_PEER_STATE_CONNECTED = 4, + ENET_PEER_STATE_DISCONNECTING = 5, + ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 6, + ENET_PEER_STATE_ZOMBIE = 7 +} ENetPeerState; + +#ifndef ENET_BUFFER_MAXIMUM +#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) +#endif + +enum +{ + ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, + ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, + ENET_HOST_DEFAULT_MTU = 1400, + + ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, + ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, + ENET_PEER_PACKET_THROTTLE_SCALE = 32, + ENET_PEER_PACKET_THROTTLE_COUNTER = 7, + ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, + ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, + ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, + ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), + ENET_PEER_PACKET_LOSS_INTERVAL = 10000, + ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, + ENET_PEER_TIMEOUT_LIMIT = 32, + ENET_PEER_TIMEOUT_MINIMUM = 5000, + ENET_PEER_TIMEOUT_MAXIMUM = 30000, + ENET_PEER_PING_INTERVAL = 500, + ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 4 * 32, +}; + +typedef struct _ENetChannel +{ + enet_uint32 outgoingReliableSequenceNumber; + enet_uint32 outgoingUnreliableSequenceNumber; + enet_uint32 incomingReliableSequenceNumber; + enet_uint32 incomingUnreliableSequenceNumber; + ENetList incomingReliableCommands; + ENetList incomingUnreliableCommands; +} ENetChannel; + +/** + * An ENet peer which data packets may be sent or received from. + * + * No fields should be modified unless otherwise specified. + */ +typedef struct _ENetPeer +{ + struct _ENetHost * host; + enet_uint16 outgoingPeerID; + enet_uint16 incomingPeerID; + enet_uint32 challenge; + ENetAddress address; /**< Internet address of the peer */ + void * data; /**< Application private data, may be freely modified */ + ENetPeerState state; + ENetChannel * channels; + size_t channelCount; /**< Number of channels allocated for communication with peer */ + enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ + enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ + enet_uint32 incomingBandwidthThrottleEpoch; + enet_uint32 outgoingBandwidthThrottleEpoch; + enet_uint32 incomingDataTotal; + enet_uint32 outgoingDataTotal; + enet_uint32 lastSendTime; + enet_uint32 lastReceiveTime; + enet_uint32 nextTimeout; + enet_uint32 earliestTimeout; + enet_uint32 packetLossEpoch; + enet_uint32 packetsSent; + enet_uint32 packetsLost; + enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ + enet_uint32 packetLossVariance; + enet_uint32 packetThrottle; + enet_uint32 packetThrottleLimit; + enet_uint32 packetThrottleCounter; + enet_uint32 packetThrottleEpoch; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 packetThrottleInterval; + enet_uint32 lastRoundTripTime; + enet_uint32 lowestRoundTripTime; + enet_uint32 lastRoundTripTimeVariance; + enet_uint32 highestRoundTripTimeVariance; + enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ + enet_uint32 roundTripTimeVariance; + enet_uint16 mtu; + enet_uint32 windowSize; + enet_uint32 reliableDataInTransit; + enet_uint32 outgoingReliableSequenceNumber; + ENetList acknowledgements; + ENetList sentReliableCommands; + ENetList sentUnreliableCommands; + ENetList outgoingReliableCommands; + ENetList outgoingUnreliableCommands; + enet_uint32 incomingUnsequencedGroup; + enet_uint32 outgoingUnsequencedGroup; + enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; + enet_uint32 disconnectData; +} ENetPeer; + +/** An ENet host for communicating with peers. + * + * No fields should be modified. + + @sa enet_host_create() + @sa enet_host_destroy() + @sa enet_host_connect() + @sa enet_host_service() + @sa enet_host_flush() + @sa enet_host_broadcast() + @sa enet_host_bandwidth_limit() + @sa enet_host_bandwidth_throttle() + */ +typedef struct _ENetHost +{ + ENetSocket socket; + ENetAddress address; /**< Internet address of the host */ + enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ + enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ + enet_uint32 bandwidthThrottleEpoch; + enet_uint32 mtu; + int recalculateBandwidthLimits; + ENetPeer * peers; /**< array of peers allocated for this host */ + size_t peerCount; /**< number of peers allocated for this host */ + ENetPeer * lastServicedPeer; + size_t packetSize; + ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; + size_t commandCount; + ENetBuffer buffers [ENET_BUFFER_MAXIMUM]; + size_t bufferCount; + ENetAddress receivedAddress; + enet_uint8 receivedData [ENET_PROTOCOL_MAXIMUM_MTU]; + size_t receivedDataLength; +} ENetHost; + +/** + * An ENet event type, as specified in @ref ENetEvent. + */ +typedef enum +{ + /** no event occurred within the specified time limit */ + ENET_EVENT_TYPE_NONE = 0, + + /** a connection request initiated by enet_host_connect has completed. + * The peer field contains the peer which successfully connected. + */ + ENET_EVENT_TYPE_CONNECT = 1, + + /** a peer has disconnected. This event is generated on a successful + * completion of a disconnect initiated by enet_pper_disconnect, if + * a peer has timed out, or if a connection request intialized by + * enet_host_connect has timed out. The peer field contains the peer + * which disconnected. The data field contains user supplied data + * describing the disconnection, or 0, if none is available. + */ + ENET_EVENT_TYPE_DISCONNECT = 2, + + /** a packet has been received from a peer. The peer field specifies the + * peer which sent the packet. The channelID field specifies the channel + * number upon which the packet was received. The packet field contains + * the packet that was received; this packet must be destroyed with + * enet_packet_destroy after use. + */ + ENET_EVENT_TYPE_RECEIVE = 3 +} ENetEventType; + +/** + * An ENet event as returned by enet_host_service(). + + @sa enet_host_service + */ +typedef struct _ENetEvent +{ + ENetEventType type; /**< type of the event */ + ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ + enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ + enet_uint32 data; /**< data associated with the event, if appropriate */ + ENetPacket * packet; /**< packet associated with the event, if appropriate */ +} ENetEvent; + +/** @defgroup global ENet global functions + @{ +*/ + +/** + Initializes ENet globally. Must be called prior to using any functions in + ENet. + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize (void); + +ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits); + +/** + Shuts down ENet globally. Should be called when a program that has + initialized ENet exits. +*/ +ENET_API void enet_deinitialize (void); + +/** @} */ + +/** @defgroup private ENet private implementation functions */ + +/** + Returns the wall-time in milliseconds. Its initial value is unspecified + unless otherwise set. + */ +ENET_API enet_uint32 enet_time_get (void); +/** + Sets the current wall-time in milliseconds. + */ +ENET_API void enet_time_set (enet_uint32); + +/** @defgroup socket ENet socket functions + @{ + @ingroup private +*/ +extern ENetSocket enet_socket_create (ENetSocketType, const ENetAddress *); +extern ENetSocket enet_socket_accept (ENetSocket, ENetAddress *); +extern int enet_socket_connect (ENetSocket, const ENetAddress *); +extern int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); +extern int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t); +extern int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32); +extern void enet_socket_destroy (ENetSocket); + +/** @} */ + +/** @defgroup Address ENet address functions + @{ +*/ +/** Attempts to resolve the host named by the parameter hostName and sets + the host field in the address parameter if successful. + @param address destination to store resolved address + @param hostName host name to lookup + @retval 0 on success + @retval < 0 on failure + @returns the address of the given hostName in address on success +*/ +ENET_API int enet_address_set_host (ENetAddress *address, const char *hostName ); + +/** Attempts to do a reserve lookup of the host field in the address parameter. + @param address address used for reverse lookup + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host (const ENetAddress *address, char *hostName, size_t nameLength ); + +/** @} */ + +ENET_API ENetPacket * enet_packet_create (const void *dataContents, size_t dataLength, enet_uint32 flags); +ENET_API void enet_packet_destroy (ENetPacket *packet ); +ENET_API int enet_packet_resize (ENetPacket *packet, size_t dataLength ); + +ENET_API ENetHost * enet_host_create (const ENetAddress *address, size_t peerCount, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth ); +ENET_API void enet_host_destroy (ENetHost *host ); +ENET_API ENetPeer * enet_host_connect (ENetHost *host, const ENetAddress *address, size_t channelCount ); +ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32); +ENET_API void enet_host_flush (ENetHost *); +ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *); +ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); +extern void enet_host_bandwidth_throttle (ENetHost *); + +ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); +ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8); +ENET_API void enet_peer_ping (ENetPeer *); +ENET_API void enet_peer_reset (ENetPeer *); +ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32); +ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +extern int enet_peer_throttle (ENetPeer *, enet_uint32); +extern void enet_peer_reset_queues (ENetPeer *); +extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); +extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32); +extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint32); + +#ifdef __cplusplus +} +#endif + +#endif /* __ENET_ENET_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/list.h b/project/jni/application/enigma/lib-src/enet/include/enet/list.h new file mode 100644 index 000000000..99bc4aeee --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/list.h @@ -0,0 +1,42 @@ +/** + @file list.h + @brief ENet list management +*/ +#ifndef __ENET_LIST_H__ +#define __ENET_LIST_H__ + +#include + +typedef struct _ENetListNode +{ + struct _ENetListNode * next; + struct _ENetListNode * previous; +} ENetListNode; + +typedef ENetListNode * ENetListIterator; + +typedef struct _ENetList +{ + ENetListNode sentinel; +} ENetList; + +extern void enet_list_clear (ENetList *); + +extern ENetListIterator enet_list_insert (ENetListIterator, void *); +extern void * enet_list_remove (ENetListIterator); + +extern size_t enet_list_size (ENetList *); + +#define enet_list_begin(list) ((list) -> sentinel.next) +#define enet_list_end(list) (& (list) -> sentinel) + +#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) + +#define enet_list_next(iterator) ((iterator) -> next) +#define enet_list_previous(iterator) ((iterator) -> previous) + +#define enet_list_front(list) ((void *) (list) -> sentinel.next) +#define enet_list_back(list) ((void *) (list) -> sentinel.previous) + +#endif /* __ENET_LIST_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/protocol.h b/project/jni/application/enigma/lib-src/enet/include/enet/protocol.h new file mode 100644 index 000000000..6d4dae979 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/protocol.h @@ -0,0 +1,167 @@ +/** + @file protocol.h + @brief ENet protocol +*/ +#ifndef __ENET_PROTOCOL_H__ +#define __ENET_PROTOCOL_H__ + +#include "enet/types.h" + +enum +{ + ENET_PROTOCOL_MINIMUM_MTU = 576, + ENET_PROTOCOL_MAXIMUM_MTU = 4096, + ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, + ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 32768, + ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, + ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255 +}; + +typedef enum +{ + ENET_PROTOCOL_COMMAND_NONE = 0, + ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, + ENET_PROTOCOL_COMMAND_CONNECT = 2, + ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, + ENET_PROTOCOL_COMMAND_DISCONNECT = 4, + ENET_PROTOCOL_COMMAND_PING = 5, + ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, + ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, + ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 9, + ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 10, + ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 11 +} ENetProtocolCommand; + +typedef enum +{ + ENET_PROTOCOL_FLAG_ACKNOWLEDGE = (1 << 0), + ENET_PROTOCOL_FLAG_UNSEQUENCED = (1 << 1) +} ENetProtocolFlag; + +typedef struct +{ + enet_uint16 peerID; + enet_uint8 flags; + enet_uint8 commandCount; + enet_uint32 sentTime; + enet_uint32 challenge; +} ENetProtocolHeader; + +typedef struct +{ + enet_uint8 command; + enet_uint8 channelID; + enet_uint8 flags; + enet_uint8 reserved; + enet_uint32 commandLength; + enet_uint32 reliableSequenceNumber; +} ENetProtocolCommandHeader; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 receivedReliableSequenceNumber; + enet_uint32 receivedSentTime; +} ENetProtocolAcknowledge; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint16 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENetProtocolConnect; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint16 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENetProtocolVerifyConnect; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; +} ENetProtocolBandwidthLimit; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENetProtocolThrottleConfigure; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 data; +} ENetProtocolDisconnect; + +typedef struct +{ + ENetProtocolCommandHeader header; +} ENetProtocolPing; + +typedef struct +{ + ENetProtocolCommandHeader header; +} ENetProtocolSendReliable; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 unreliableSequenceNumber; +} ENetProtocolSendUnreliable; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 unsequencedGroup; +} ENetProtocolSendUnsequenced; + +typedef struct +{ + ENetProtocolCommandHeader header; + enet_uint32 startSequenceNumber; + enet_uint32 fragmentCount; + enet_uint32 fragmentNumber; + enet_uint32 totalLength; + enet_uint32 fragmentOffset; +} ENetProtocolSendFragment; + +typedef union +{ + ENetProtocolCommandHeader header; + ENetProtocolAcknowledge acknowledge; + ENetProtocolConnect connect; + ENetProtocolVerifyConnect verifyConnect; + ENetProtocolDisconnect disconnect; + ENetProtocolPing ping; + ENetProtocolSendReliable sendReliable; + ENetProtocolSendUnreliable sendUnreliable; + ENetProtocolSendUnsequenced sendUnsequenced; + ENetProtocolSendFragment sendFragment; + ENetProtocolBandwidthLimit bandwidthLimit; + ENetProtocolThrottleConfigure throttleConfigure; +} ENetProtocol; + +#endif /* __ENET_PROTOCOL_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/time.h b/project/jni/application/enigma/lib-src/enet/include/enet/time.h new file mode 100644 index 000000000..c82a54603 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/time.h @@ -0,0 +1,18 @@ +/** + @file time.h + @brief ENet time constants and macros +*/ +#ifndef __ENET_TIME_H__ +#define __ENET_TIME_H__ + +#define ENET_TIME_OVERFLOW 86400000 + +#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) +#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) + +#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) + +#endif /* __ENET_TIME_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/types.h b/project/jni/application/enigma/lib-src/enet/include/enet/types.h new file mode 100644 index 000000000..ab010a4b1 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/types.h @@ -0,0 +1,13 @@ +/** + @file types.h + @brief type definitions for ENet +*/ +#ifndef __ENET_TYPES_H__ +#define __ENET_TYPES_H__ + +typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ +typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ +typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ + +#endif /* __ENET_TYPES_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/unix.h b/project/jni/application/enigma/lib-src/enet/include/enet/unix.h new file mode 100644 index 000000000..b20fecd44 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/unix.h @@ -0,0 +1,36 @@ +/** + @file unix.h + @brief ENet Unix header +*/ +#ifndef __ENET_UNIX_H__ +#define __ENET_UNIX_H__ + +#include +#include +#include + +typedef int ENetSocket; + +enum +{ + ENET_SOCKET_NULL = -1 +}; + +#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ +#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ + +typedef struct +{ + void * data; + size_t dataLength; +} ENetBuffer; + +#define ENET_CALLBACK + +#define ENET_API extern + +#endif /* __ENET_UNIX_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/utility.h b/project/jni/application/enigma/lib-src/enet/include/enet/utility.h new file mode 100644 index 000000000..e48a476be --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/utility.h @@ -0,0 +1,12 @@ +/** + @file utility.h + @brief ENet utility header +*/ +#ifndef __ENET_UTILITY_H__ +#define __ENET_UTILITY_H__ + +#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#endif /* __ENET_UTILITY_H__ */ + diff --git a/project/jni/application/enigma/lib-src/enet/include/enet/win32.h b/project/jni/application/enigma/lib-src/enet/include/enet/win32.h new file mode 100644 index 000000000..310ecd531 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/include/enet/win32.h @@ -0,0 +1,51 @@ +/** + @file win32.h + @brief ENet Win32 header +*/ +#ifndef __ENET_WIN32_H__ +#define __ENET_WIN32_H__ + +#ifdef ENET_BUILDING_LIB +#pragma warning (disable: 4996) // 'strncpy' was declared deprecated +#pragma warning (disable: 4267) // size_t to int conversion +#pragma warning (disable: 4244) // 64bit to 32bit int +#pragma warning (disable: 4018) // signed/unsigned mismatch +#endif + +#include +#include + +typedef SOCKET ENetSocket; + +enum +{ + ENET_SOCKET_NULL = INVALID_SOCKET +}; + +#define ENET_HOST_TO_NET_16(value) (htons (value)) +#define ENET_HOST_TO_NET_32(value) (htonl (value)) + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) + +typedef struct +{ + size_t dataLength; + void * data; +} ENetBuffer; + +#define ENET_CALLBACK __cdecl + +#if defined ENET_DLL +#if defined ENET_BUILDING_LIB +#define ENET_API __declspec( dllexport ) +#else +#define ENET_API __declspec( dllimport ) +#endif /* ENET_BUILDING_LIB */ +#else /* !ENET_DLL */ +#define ENET_API extern +#endif /* ENET_DLL */ + +#endif /* __ENET_WIN32_H__ */ + + diff --git a/project/jni/application/enigma/lib-src/enet/list.c b/project/jni/application/enigma/lib-src/enet/list.c new file mode 100644 index 000000000..1a4aa3ae7 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/list.c @@ -0,0 +1,57 @@ +/** + @file list.c + @brief ENet linked list functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/list.h" + +/** + @defgroup list ENet linked list utility functions + @ingroup private + @{ +*/ +void +enet_list_clear (ENetList * list) +{ + list -> sentinel.next = & list -> sentinel; + list -> sentinel.previous = & list -> sentinel; +} + +ENetListIterator +enet_list_insert (ENetListIterator position, void * data) +{ + ENetListIterator result = (ENetListIterator) data; + + result -> previous = position -> previous; + result -> next = position; + + result -> previous -> next = result; + position -> previous = result; + + return result; +} + +void * +enet_list_remove (ENetListIterator position) +{ + position -> previous -> next = position -> next; + position -> next -> previous = position -> previous; + + return position; +} + +size_t +enet_list_size (ENetList * list) +{ + size_t size = 0; + ENetListIterator position; + + for (position = enet_list_begin (list); + position != enet_list_end (list); + position = enet_list_next (position)) + ++ size; + + return size; +} + +/** @} */ diff --git a/project/jni/application/enigma/lib-src/enet/packet.c b/project/jni/application/enigma/lib-src/enet/packet.c new file mode 100644 index 000000000..5226550db --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/packet.c @@ -0,0 +1,74 @@ +/** + @file packet.c + @brief ENet packet management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup Packet ENet packet functions + @{ +*/ + +/** Creates a packet that may be sent to a peer. + @param dataContents initial contents of the packet's data; the packet's data will remain uninitialized if dataContents is NULL. + @param dataLength size of the data allocated for this packet + @param flags flags for this packet as described for the ENetPacket structure. + @returns the packet on success, NULL on failure +*/ +ENetPacket * +enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) +{ + ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); + + packet -> data = (enet_uint8 *) enet_malloc (dataLength); + + if (data != NULL) + memcpy (packet -> data, data, dataLength); + + packet -> referenceCount = 0; + packet -> flags = flags; + packet -> dataLength = dataLength; + + return packet; +} + +/** Destroys the packet and deallocates its data. + @param packet packet to be destroyed +*/ +void +enet_packet_destroy (ENetPacket * packet) +{ + enet_free (packet -> data); + enet_free (packet); +} + +/** Attempts to resize the data in the packet to length specified in the + dataLength parameter + @param packet packet to resize + @param dataLength new size for the packet data + @returns 0 on success, < 0 on failure +*/ +int +enet_packet_resize (ENetPacket * packet, size_t dataLength) +{ + enet_uint8 * newData; + + if (dataLength <= packet -> dataLength) + { + packet -> dataLength = dataLength; + + return 0; + } + + newData = (enet_uint8 *) enet_malloc (dataLength); + memcpy (newData, packet -> data, packet -> dataLength); + enet_free (packet -> data); + + packet -> data = newData; + packet -> dataLength = dataLength; + + return 0; +} + +/** @} */ diff --git a/project/jni/application/enigma/lib-src/enet/peer.c b/project/jni/application/enigma/lib-src/enet/peer.c new file mode 100644 index 000000000..754239f01 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/peer.c @@ -0,0 +1,657 @@ +/** + @file peer.c + @brief ENet peer management functions +*/ +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup peer ENet peer functions + @{ +*/ + +/** Configures throttle parameter for a peer. + + Unreliable packets are dropped by ENet in response to the varying conditions + of the Internet connection to the peer. The throttle represents a probability + that an unreliable packet should not be dropped and thus sent by ENet to the peer. + The lowest mean round trip time from the sending of a reliable packet to the + receipt of its acknowledgement is measured over an amount of time specified by + the interval parameter in milliseconds. If a measured round trip time happens to + be significantly less than the mean round trip time measured over the interval, + then the throttle probability is increased to allow more traffic by an amount + specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE + constant. If a measured round trip time happens to be significantly greater than + the mean round trip time measured over the interval, then the throttle probability + is decreased to limit traffic by an amount specified in the deceleration parameter, which + is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has + a value of ENET_PEER_PACKET_THROTTLE_SCALE, on unreliable packets are dropped by + ENet, and so 100% of all unreliable packets will be sent. When the throttle has a + value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable + packets will be sent. Intermediate values for the throttle represent intermediate + probabilities between 0% and 100% of unreliable packets being sent. The bandwidth + limits of the local and foreign hosts are taken into account to determine a + sensible limit for the throttle probability above which it should not raise even in + the best of conditions. + + @param peer peer to configure + @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. + @param acceleration rate at which to increase the throttle probability as mean RTT declines + @param deceleration rate at which to decrease the throttle probability as mean RTT increases +*/ +void +enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) +{ + ENetProtocol command; + + peer -> packetThrottleInterval = interval; + peer -> packetThrottleAcceleration = acceleration; + peer -> packetThrottleDeceleration = deceleration; + + command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolThrottleConfigure); + + command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval); + command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration); + command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +int +enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) +{ + if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance) + { + peer -> packetThrottle = peer -> packetThrottleLimit; + } + else + if (rtt < peer -> lastRoundTripTime) + { + peer -> packetThrottle += peer -> packetThrottleAcceleration; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + return 1; + } + else + if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance) + { + if (peer -> packetThrottle > peer -> packetThrottleDeceleration) + peer -> packetThrottle -= peer -> packetThrottleDeceleration; + else + peer -> packetThrottle = 0; + + return -1; + } + + return 0; +} + +/** Queues a packet to be sent. + @param peer destination for the packet + @param channelID channel on which to send + @param packet packet to send + @retval 0 on success + @retval < 0 on failure +*/ +int +enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) +{ + ENetChannel * channel = & peer -> channels [channelID]; + ENetProtocol command; + size_t fragmentLength; + + if (peer -> state != ENET_PEER_STATE_CONNECTED || + channelID >= peer -> channelCount) + return -1; + + fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); + + if (packet -> dataLength > fragmentLength) + { + enet_uint32 fragmentCount = ENET_HOST_TO_NET_32 ((packet -> dataLength + fragmentLength - 1) / fragmentLength), + startSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingReliableSequenceNumber + 1), + fragmentNumber, + fragmentOffset; + + packet -> flags = ENET_PACKET_FLAG_RELIABLE; + + for (fragmentNumber = 0, + fragmentOffset = 0; + fragmentOffset < packet -> dataLength; + ++ fragmentNumber, + fragmentOffset += fragmentLength) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT; + command.header.channelID = channelID; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolSendFragment); + command.sendFragment.startSequenceNumber = startSequenceNumber; + command.sendFragment.fragmentCount = fragmentCount; + command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); + command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); + command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); + + if (packet -> dataLength - fragmentOffset < fragmentLength) + fragmentLength = packet -> dataLength - fragmentOffset; + + enet_peer_queue_outgoing_command (peer, & command, packet, fragmentOffset, fragmentLength); + } + + return 0; + } + + command.header.channelID = channelID; + + if (packet -> flags & ENET_PACKET_FLAG_RELIABLE) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolSendReliable); + } + else + if (packet -> flags & ENET_PACKET_FLAG_UNSEQUENCED) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED; + command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED; + command.header.commandLength = sizeof (ENetProtocolSendUnsequenced); + command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_32 (peer -> outgoingUnsequencedGroup + 1); + } + else + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; + command.header.flags = 0; + command.header.commandLength = sizeof (ENetProtocolSendUnreliable); + command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_32 (channel -> outgoingUnreliableSequenceNumber + 1); + } + + enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength); + + return 0; +} + +/** Attempts to dequeue any incoming queued packet. + @param peer peer to dequeue packets from + @param channelID channel on which to receive + @returns a pointer to the packet, or NULL if there are no available incoming queued packets +*/ +ENetPacket * +enet_peer_receive (ENetPeer * peer, enet_uint8 channelID) +{ + ENetChannel * channel = & peer -> channels [channelID]; + ENetIncomingCommand * incomingCommand = NULL; + ENetPacket * packet; + + if (enet_list_empty (& channel -> incomingUnreliableCommands) == 0) + { + incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingUnreliableCommands); + + if (incomingCommand -> unreliableSequenceNumber > 0) + { + if (incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber) + incomingCommand = NULL; + else + channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; + } + } + + if (incomingCommand == NULL && + enet_list_empty (& channel -> incomingReliableCommands) == 0) + { + do + { + incomingCommand = (ENetIncomingCommand *) enet_list_front (& channel -> incomingReliableCommands); + + if (incomingCommand -> fragmentsRemaining > 0 || + incomingCommand -> reliableSequenceNumber > channel -> incomingReliableSequenceNumber + 1) + return NULL; + + if (incomingCommand -> reliableSequenceNumber <= channel -> incomingReliableSequenceNumber) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_list_remove (& incomingCommand -> incomingCommandList); + + enet_free (incomingCommand); + + incomingCommand = NULL; + } + } while (incomingCommand == NULL && + enet_list_empty (& channel -> incomingReliableCommands) == 0); + + if (incomingCommand == NULL) + return NULL; + + channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber; + + if (incomingCommand -> fragmentCount > 0) + channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1; + } + + if (incomingCommand == NULL) + return NULL; + + enet_list_remove (& incomingCommand -> incomingCommandList); + + packet = incomingCommand -> packet; + + -- packet -> referenceCount; + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + + return packet; +} + +static void +enet_peer_reset_outgoing_commands (ENetList * queue) +{ + ENetOutgoingCommand * outgoingCommand; + + while (enet_list_empty (queue) == 0) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue)); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + } +} + +static void +enet_peer_reset_incoming_commands (ENetList * queue) +{ + ENetIncomingCommand * incomingCommand; + + while (enet_list_empty (queue) == 0) + { + incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (queue)); + + if (incomingCommand -> packet != NULL) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + } + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + } +} + +void +enet_peer_reset_queues (ENetPeer * peer) +{ + ENetChannel * channel; + + while (enet_list_empty (& peer -> acknowledgements) == 0) + enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); + + enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands); + + if (peer -> channels != NULL && peer -> channelCount > 0) + { + for (channel = peer -> channels; + channel < & peer -> channels [peer -> channelCount]; + ++ channel) + { + enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands); + enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands); + } + + enet_free (peer -> channels); + } + + peer -> channels = NULL; + peer -> channelCount = 0; +} + +/** Forcefully disconnects a peer. + @param peer peer to forcefully disconnect + @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout + on its connection to the local host. +*/ +void +enet_peer_reset (ENetPeer * peer) +{ + peer -> outgoingPeerID = 0xFFFF; + peer -> challenge = 0; + + peer -> address.host = ENET_HOST_ANY; + peer -> address.port = 0; + + peer -> state = ENET_PEER_STATE_DISCONNECTED; + + peer -> incomingBandwidth = 0; + peer -> outgoingBandwidth = 0; + peer -> incomingBandwidthThrottleEpoch = 0; + peer -> outgoingBandwidthThrottleEpoch = 0; + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + peer -> lastSendTime = 0; + peer -> lastReceiveTime = 0; + peer -> nextTimeout = 0; + peer -> earliestTimeout = 0; + peer -> packetLossEpoch = 0; + peer -> packetsSent = 0; + peer -> packetsLost = 0; + peer -> packetLoss = 0; + peer -> packetLossVariance = 0; + peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; + peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; + peer -> packetThrottleCounter = 0; + peer -> packetThrottleEpoch = 0; + peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; + peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; + peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; + peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lastRoundTripTimeVariance = 0; + peer -> highestRoundTripTimeVariance = 0; + peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> roundTripTimeVariance = 0; + peer -> mtu = peer -> host -> mtu; + peer -> reliableDataInTransit = 0; + peer -> outgoingReliableSequenceNumber = 0; + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + peer -> incomingUnsequencedGroup = 0; + peer -> outgoingUnsequencedGroup = 0; + peer -> disconnectData = 0; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + + enet_peer_reset_queues (peer); +} + +/** Sends a ping request to a peer. + @param peer destination for the ping request + @remarks ping requests factor into the mean round trip time as designated by the + roundTripTime field in the ENetPeer structure. Enet automatically pings all connected + peers at regular intervals, however, this function may be called to ensure more + frequent ping requests. +*/ +void +enet_peer_ping (ENetPeer * peer) +{ + ENetProtocol command; + + if (peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + command.header.command = ENET_PROTOCOL_COMMAND_PING; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + command.header.commandLength = sizeof (ENetProtocolPing); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +/** Force an immediate disconnection from a peer. + @param peer peer to disconnect + @param data data describing the disconnection + @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not + guarenteed to receive the disconnect notification, and is reset immediately upon + return from this function. +*/ +void +enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED) + return; + + if (peer -> state != ENET_PEER_STATE_ZOMBIE && + peer -> state != ENET_PEER_STATE_DISCONNECTING) + { + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED; + command.header.commandLength = sizeof (ENetProtocolDisconnect); + command.disconnect.data = data; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + enet_host_flush (peer -> host); + } + + enet_peer_reset (peer); +} + +/** Request a disconnection from a peer. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTING || + peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ZOMBIE) + return; + + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; + command.header.channelID = 0xFF; + command.header.flags = ENET_PROTOCOL_FLAG_UNSEQUENCED; + command.header.commandLength = sizeof (ENetProtocolDisconnect); + command.disconnect.data = data; + + if (peer -> state == ENET_PEER_STATE_CONNECTED) + command.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + if (peer -> state == ENET_PEER_STATE_CONNECTED) + peer -> state = ENET_PEER_STATE_DISCONNECTING; + else + { + enet_host_flush (peer -> host); + enet_peer_reset (peer); + } +} + +ENetAcknowledgement * +enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint32 sentTime) +{ + ENetAcknowledgement * acknowledgement; + + peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge); + + acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement)); + + acknowledgement -> sentTime = sentTime; + acknowledgement -> command = * command; + + enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement); + + return acknowledgement; +} + +ENetOutgoingCommand * +enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length) +{ + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + ENetOutgoingCommand * outgoingCommand; + + peer -> outgoingDataTotal += command -> header.commandLength + length; + + outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + + if (command -> header.channelID == 0xFF) + { + ++ peer -> outgoingReliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (command -> header.flags & ENET_PROTOCOL_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } + + outgoingCommand -> sentTime = 0; + outgoingCommand -> roundTripTimeout = 0; + outgoingCommand -> roundTripTimeoutLimit = 0; + outgoingCommand -> fragmentOffset = offset; + outgoingCommand -> fragmentLength = length; + outgoingCommand -> packet = packet; + outgoingCommand -> command = * command; + outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_32 (outgoingCommand -> reliableSequenceNumber); + + if (packet != NULL) + ++ packet -> referenceCount; + + if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) + enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand); + + return outgoingCommand; +} + +ENetIncomingCommand * +enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 fragmentCount) +{ + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint32 unreliableSequenceNumber = 0; + ENetIncomingCommand * incomingCommand; + ENetListIterator currentCommand; + + switch (command -> header.command) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (incomingCommand -> reliableSequenceNumber <= command -> header.reliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < command -> header.reliableSequenceNumber) + break; + + goto freePacket; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + unreliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendUnreliable.unreliableSequenceNumber); + + if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + goto freePacket; + + if (unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + goto freePacket; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber) + break; + + goto freePacket; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + currentCommand = enet_list_end (& channel -> incomingUnreliableCommands); + break; + + default: + goto freePacket; + } + + incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand)); + + incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber; + incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber; + incomingCommand -> command = * command; + incomingCommand -> fragmentCount = fragmentCount; + incomingCommand -> fragmentsRemaining = fragmentCount; + incomingCommand -> packet = packet; + incomingCommand -> fragments = NULL; + + if (fragmentCount > 0) + { + incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32)); + memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32)); + } + + if (packet != NULL) + ++ packet -> referenceCount; + + enet_list_insert (enet_list_next (currentCommand), incomingCommand); + + return incomingCommand; + +freePacket: + if (packet != NULL) + { + if (packet -> referenceCount == 0) + enet_packet_destroy (packet); + } + + return NULL; +} + +/** @} */ diff --git a/project/jni/application/enigma/lib-src/enet/protocol.c b/project/jni/application/enigma/lib-src/enet/protocol.c new file mode 100644 index 000000000..75cdd6405 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/protocol.c @@ -0,0 +1,1349 @@ +/** + @file protocol.c + @brief ENet protocol functions +*/ +#include +#include +#define ENET_BUILDING_LIB 1 +#include "enet/utility.h" +#include "enet/time.h" +#include "enet/enet.h" + +static enet_uint32 timeCurrent; + +static int +enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) +{ + ENetPeer * currentPeer = host -> lastServicedPeer; + ENetChannel * channel; + + do + { + ++ currentPeer; + + if (currentPeer >= & host -> peers [host -> peerCount]) + currentPeer = host -> peers; + + switch (currentPeer -> state) + { + case ENET_PEER_STATE_CONNECTION_PENDING: + currentPeer -> state = ENET_PEER_STATE_CONNECTED; + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = currentPeer; + + return 1; + + case ENET_PEER_STATE_ZOMBIE: + host -> recalculateBandwidthLimits = 1; + + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = currentPeer; + event -> data = currentPeer -> disconnectData; + + enet_peer_reset (currentPeer); + + host -> lastServicedPeer = currentPeer; + + return 1; + } + + if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [currentPeer -> channelCount]; + ++ channel) + { + if (enet_list_empty (& channel -> incomingReliableCommands) && + enet_list_empty (& channel -> incomingUnreliableCommands)) + continue; + + event -> packet = enet_peer_receive (currentPeer, channel - currentPeer -> channels); + if (event -> packet == NULL) + continue; + + event -> type = ENET_EVENT_TYPE_RECEIVE; + event -> peer = currentPeer; + event -> channelID = (enet_uint8) (channel - currentPeer -> channels); + + host -> lastServicedPeer = currentPeer; + + return 1; + } + } while (currentPeer != host -> lastServicedPeer); + + return 0; +} + +static void +enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + host -> recalculateBandwidthLimits = 1; + + if (event == NULL) + peer -> state = ENET_PEER_STATE_CONNECTION_PENDING; + else + { + peer -> state = ENET_PEER_STATE_CONNECTED; + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + } +} + +static void +enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING) + host -> recalculateBandwidthLimits = 1; + + if (peer -> state < ENET_PEER_STATE_CONNECTED) + enet_peer_reset (peer); + else + if (event == NULL) + peer -> state = ENET_PEER_STATE_ZOMBIE; + else + { + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = 0; + + enet_peer_reset (peer); + } +} + +static void +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +{ + ENetOutgoingCommand * outgoingCommand; + + while (enet_list_empty (& peer -> sentUnreliableCommands) == 0) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + } +} + +static ENetProtocolCommand +enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint32 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + ENetProtocolCommand commandNumber; + + for (currentCommand = enet_list_begin (& peer -> sentReliableCommands); + currentCommand != enet_list_end (& peer -> sentReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) + return ENET_PROTOCOL_COMMAND_NONE; + + commandNumber = outgoingCommand -> command.header.command; + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + + if (enet_list_empty (& peer -> sentReliableCommands)) + return commandNumber; + + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands); + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + + return commandNumber; +} + +static ENetPeer * +enet_protocol_handle_connect (ENetHost * host, const ENetProtocolHeader * header, const ENetProtocol * command) +{ + enet_uint16 mtu; + enet_uint32 windowSize; + ENetChannel * channel; + size_t channelCount; + ENetPeer * currentPeer; + ENetProtocol verifyCommand; + + if (command -> header.commandLength < sizeof (ENetProtocolConnect)) + return NULL; + + channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || + channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + return NULL; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_DISCONNECTED && + currentPeer -> address.host == host -> receivedAddress.host && + currentPeer -> address.port == host -> receivedAddress.port && + currentPeer -> challenge == header -> challenge) + return NULL; + } + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + currentPeer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; + currentPeer -> challenge = header -> challenge; + currentPeer -> address = host -> receivedAddress; + currentPeer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); + currentPeer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); + currentPeer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); + currentPeer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); + currentPeer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); + currentPeer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + currentPeer -> channelCount = channelCount; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + } + + mtu = ENET_NET_TO_HOST_16 (command -> connect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + currentPeer -> mtu = mtu; + + if (host -> outgoingBandwidth == 0 && + currentPeer -> incomingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, currentPeer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (host -> incomingBandwidth == 0) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) + windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT; + verifyCommand.header.channelID = 0xFF; + verifyCommand.header.flags = ENET_PROTOCOL_FLAG_ACKNOWLEDGE; + verifyCommand.header.commandLength = sizeof (ENetProtocolVerifyConnect); + verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_16 (currentPeer -> mtu); + verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); + verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + + enet_peer_queue_outgoing_command (currentPeer, & verifyCommand, NULL, 0, 0); + + return currentPeer; +} + +static void +enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + ENetPacket * packet; + + if (command -> header.commandLength <= sizeof (ENetProtocolSendReliable) || + command -> header.channelID >= peer -> channelCount || + peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), + command -> header.commandLength - sizeof (ENetProtocolSendReliable), + ENET_PACKET_FLAG_RELIABLE); + + enet_peer_queue_incoming_command (peer, command, packet, 0); +} + +static void +enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + ENetPacket * packet; + enet_uint32 unsequencedGroup, index; + + if (command -> header.commandLength <= sizeof (ENetProtocolSendUnsequenced) || + command -> header.channelID >= peer -> channelCount || + peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + unsequencedGroup = ENET_NET_TO_HOST_32 (command -> sendUnsequenced.unsequencedGroup); + index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; + + if (unsequencedGroup >= peer -> incomingUnsequencedGroup + ENET_PEER_UNSEQUENCED_WINDOW_SIZE) + { + peer -> incomingUnsequencedGroup = unsequencedGroup - index; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + } + else + if (unsequencedGroup < peer -> incomingUnsequencedGroup || + peer -> unsequencedWindow [index / 32] & (1 << (index % 32))) + return; + + peer -> unsequencedWindow [index / 32] |= 1 << (index % 32); + + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), + command -> header.commandLength - sizeof (ENetProtocolSendUnsequenced), + ENET_PACKET_FLAG_UNSEQUENCED); + + enet_peer_queue_incoming_command (peer, command, packet, 0); +} + +static void +enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + ENetPacket * packet; + + if (command -> header.commandLength <= sizeof (ENetProtocolSendUnreliable) || + command -> header.channelID >= peer -> channelCount || + peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + packet = enet_packet_create ((const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), + command -> header.commandLength - sizeof (ENetProtocolSendUnreliable), + 0); + + enet_peer_queue_incoming_command (peer, command, packet, 0); +} + +static void +enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + startSequenceNumber, + totalLength; + ENetChannel * channel; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand; + + if (command -> header.commandLength <= sizeof (ENetProtocolSendFragment) || + command -> header.channelID >= peer -> channelCount || + peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + startSequenceNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.startSequenceNumber); + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + fragmentLength = command -> header.commandLength - sizeof (ENetProtocolSendFragment); + + if (fragmentOffset >= totalLength || + fragmentOffset + fragmentLength > totalLength || + fragmentNumber >= fragmentCount) + return; + + channel = & peer -> channels [command -> header.channelID]; + + if (startSequenceNumber <= channel -> incomingReliableSequenceNumber) + return; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + startCommand = (ENetIncomingCommand *) currentCommand; + + if (startCommand -> command.header.command == ENET_PROTOCOL_COMMAND_SEND_FRAGMENT && + startCommand -> command.sendFragment.startSequenceNumber == startSequenceNumber) + break; + } + + if (currentCommand == enet_list_end (& channel -> incomingReliableCommands)) + { + ENetProtocol hostCommand = * command; + + hostCommand.header.reliableSequenceNumber = startSequenceNumber; + hostCommand.sendFragment.startSequenceNumber = startSequenceNumber; + hostCommand.sendFragment.fragmentNumber = fragmentNumber; + hostCommand.sendFragment.fragmentCount = fragmentCount; + hostCommand.sendFragment.fragmentOffset = fragmentOffset; + hostCommand.sendFragment.totalLength = totalLength; + + startCommand = enet_peer_queue_incoming_command (peer, + & hostCommand, + enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_RELIABLE), + fragmentCount); + } + else + if (totalLength != startCommand -> packet -> dataLength || + fragmentCount != startCommand -> fragmentCount) + return; + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + } +} + +static void +enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (command -> header.commandLength < sizeof (ENetProtocolPing)) + return; +} + +static void +enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (command -> header.commandLength < sizeof (ENetProtocolBandwidthLimit)) + return; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth); + + if (peer -> incomingBandwidth == 0 && + host -> outgoingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; +} + +static void +enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (command -> header.commandLength < sizeof (ENetProtocolThrottleConfigure)) + return; + + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration); +} + +static void +enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (command -> header.commandLength < sizeof (ENetProtocolDisconnect)) + return; + + enet_peer_reset_queues (peer); + + if (peer -> state != ENET_PEER_STATE_CONNECTED) + { + if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1; + + enet_peer_reset (peer); + } + else + if (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) + peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT; + else + peer -> state = ENET_PEER_STATE_ZOMBIE; + + peer -> disconnectData = command -> disconnect.data; +} + +static int +enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 roundTripTime, + receivedSentTime, + receivedReliableSequenceNumber; + ENetProtocolCommand commandNumber; + + if (command -> header.commandLength < sizeof (ENetProtocolAcknowledge)) + return 0; + + receivedSentTime = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedSentTime); + + if (ENET_TIME_LESS (timeCurrent, receivedSentTime)) + return 0; + + peer -> lastReceiveTime = timeCurrent; + peer -> earliestTimeout = 0; + + roundTripTime = ENET_TIME_DIFFERENCE (timeCurrent, receivedSentTime); + + enet_peer_throttle (peer, roundTripTime); + + peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4; + + if (roundTripTime >= peer -> roundTripTime) + { + peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8; + peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4; + } + else + { + peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8; + peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4; + } + + if (peer -> roundTripTime < peer -> lowestRoundTripTime) + peer -> lowestRoundTripTime = peer -> roundTripTime; + + if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + + if (peer -> packetThrottleEpoch == 0 || + ENET_TIME_DIFFERENCE(timeCurrent, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval) + { + peer -> lastRoundTripTime = peer -> lowestRoundTripTime; + peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance; + peer -> lowestRoundTripTime = peer -> roundTripTime; + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + peer -> packetThrottleEpoch = timeCurrent; + } + + receivedReliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> acknowledge.receivedReliableSequenceNumber); + + commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID); + + switch (peer -> state) + { + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) + return 0; + + enet_protocol_notify_connect (host, peer, event); + + return 1; + + case ENET_PEER_STATE_DISCONNECTING: + if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) + return 0; + + enet_protocol_notify_disconnect (host, peer, event); + + return 1; + } + + return 0; +} + +static void +enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint16 mtu; + enet_uint32 windowSize; + + if (event == NULL || + command -> header.commandLength < sizeof (ENetProtocolVerifyConnect) || + peer -> state != ENET_PEER_STATE_CONNECTING) + return; + + if (ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount) != peer -> channelCount || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration) + { + peer -> state = ENET_PEER_STATE_ZOMBIE; + + return; + } + + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID); + + mtu = ENET_NET_TO_HOST_16 (command -> verifyConnect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + if (mtu < peer -> mtu) + peer -> mtu = mtu; + + windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (windowSize < peer -> windowSize) + peer -> windowSize = windowSize; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth); + + enet_protocol_notify_connect (host, peer, event); +} + +static int +enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +{ + ENetProtocolHeader * header; + ENetProtocol * command; + ENetPeer * peer; + enet_uint8 * currentData; + size_t commandCount; + + if (host -> receivedDataLength < sizeof (ENetProtocolHeader)) + return 0; + + header = (ENetProtocolHeader *) host -> receivedData; + + header -> peerID = ENET_NET_TO_HOST_16 (header -> peerID); + header -> sentTime = ENET_NET_TO_HOST_32 (header -> sentTime); + + if (header -> peerID == 0xFFFF) + peer = NULL; + else + if (header -> peerID >= host -> peerCount) + return 0; + else + { + peer = & host -> peers [header -> peerID]; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ZOMBIE || + (host -> receivedAddress.host != peer -> address.host && + peer -> address.host != ENET_HOST_BROADCAST) || + header -> challenge != peer -> challenge) + return 0; + else + { + peer -> address.host = host -> receivedAddress.host; + peer -> address.port = host -> receivedAddress.port; + } + } + + if (peer != NULL) + peer -> incomingDataTotal += host -> receivedDataLength; + + commandCount = header -> commandCount; + currentData = host -> receivedData + sizeof (ENetProtocolHeader); + + while (commandCount > 0 && + currentData < & host -> receivedData [host -> receivedDataLength]) + { + command = (ENetProtocol *) currentData; + + if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength]) + break; + + command -> header.commandLength = ENET_NET_TO_HOST_32 (command -> header.commandLength); + + if (command -> header.commandLength <= 0 || + command -> header.commandLength > & host -> receivedData [host -> receivedDataLength] - currentData) + break; + + -- commandCount; + currentData += command -> header.commandLength; + + if (peer == NULL && command -> header.command != ENET_PROTOCOL_COMMAND_CONNECT) + break; + + command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_32 (command -> header.reliableSequenceNumber); + + switch (command -> header.command) + { + case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: + enet_protocol_handle_acknowledge (host, event, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_CONNECT: + peer = enet_protocol_handle_connect (host, header, command); + + break; + + case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: + enet_protocol_handle_verify_connect (host, event, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_DISCONNECT: + enet_protocol_handle_disconnect (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_PING: + enet_protocol_handle_ping (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + enet_protocol_handle_send_reliable (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + enet_protocol_handle_send_unreliable (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + enet_protocol_handle_send_unsequenced (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + enet_protocol_handle_send_fragment (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: + enet_protocol_handle_bandwidth_limit (host, peer, command); + + break; + + case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: + enet_protocol_handle_throttle_configure (host, peer, command); + + break; + + default: + break; + } + + if (peer != NULL && + (command -> header.flags & ENET_PROTOCOL_FLAG_ACKNOWLEDGE) != 0) + { + switch (peer -> state) + { + case ENET_PEER_STATE_DISCONNECTING: + break; + + case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: + if (command -> header.command != ENET_PROTOCOL_COMMAND_DISCONNECT) + break; + + default: + enet_peer_queue_acknowledgement (peer, command, header -> sentTime); + + break; + } + } + } + + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + return 0; +} + +static int +enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) +{ + for (;;) + { + int receivedLength; + ENetBuffer buffer; + + buffer.data = host -> receivedData; + buffer.dataLength = sizeof (host -> receivedData); + + receivedLength = enet_socket_receive (host -> socket, + & host -> receivedAddress, + & buffer, + 1); + + if (receivedLength < 0) + return -1; + + if (receivedLength == 0) + return 0; + + host -> receivedDataLength = receivedLength; + + switch (enet_protocol_handle_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + return -1; + + default: + break; + } + } + + return -1; +} + +static void +enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetAcknowledgement * acknowledgement; + ENetListIterator currentAcknowledgement; + + currentAcknowledgement = enet_list_begin (& peer -> acknowledgements); + + while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements)) + { + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) + break; + + acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; + + currentAcknowledgement = enet_list_next (currentAcknowledgement); + + buffer -> data = command; + buffer -> dataLength = sizeof (ENetProtocolAcknowledge); + + host -> packetSize += buffer -> dataLength; + + command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; + command -> header.channelID = acknowledgement -> command.header.channelID; + command -> header.flags = 0; + command -> header.commandLength = ENET_HOST_TO_NET_32 (sizeof (ENetProtocolAcknowledge)); + command -> acknowledge.receivedReliableSequenceNumber = ENET_HOST_TO_NET_32 (acknowledgement -> command.header.reliableSequenceNumber); + command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_32 (acknowledgement -> sentTime); + + if (acknowledgement -> command.header.command == ENET_PROTOCOL_COMMAND_DISCONNECT) + peer -> state = ENET_PEER_STATE_ZOMBIE; + + enet_list_remove (& acknowledgement -> acknowledgementList); + enet_free (acknowledgement); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static void +enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength || + (outgoingCommand -> packet != NULL && + peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength + + outgoingCommand -> packet -> dataLength)) + break; + + currentCommand = enet_list_next (currentCommand); + + if (outgoingCommand -> packet != NULL) + { + peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; + peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> packetThrottleCounter > peer -> packetThrottle) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + enet_free (outgoingCommand); + + continue; + } + } + + buffer -> data = command; + buffer -> dataLength = outgoingCommand -> command.header.commandLength; + + host -> packetSize += buffer -> dataLength; + + * command = outgoingCommand -> command; + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data; + buffer -> dataLength = outgoingCommand -> packet -> dataLength; + + command -> header.commandLength += buffer -> dataLength; + + host -> packetSize += buffer -> dataLength; + + enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + } + else + enet_free (outgoingCommand); + + command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static int +enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> sentReliableCommands); + + while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + if (ENET_TIME_DIFFERENCE (timeCurrent, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout) + continue; + + if(peer -> earliestTimeout == 0 || + ENET_TIME_LESS(outgoingCommand -> sentTime, peer -> earliestTimeout)) + peer -> earliestTimeout = outgoingCommand -> sentTime; + + if (peer -> earliestTimeout != 0 && + (ENET_TIME_DIFFERENCE(timeCurrent, peer -> earliestTimeout) >= ENET_PEER_TIMEOUT_MAXIMUM || + (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ENET_TIME_DIFFERENCE(timeCurrent, peer -> earliestTimeout) >= ENET_PEER_TIMEOUT_MINIMUM))) + { + enet_protocol_notify_disconnect (host, peer, event); + + return 1; + } + + if (outgoingCommand -> packet != NULL) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + ++ peer -> packetsLost; + + outgoingCommand -> roundTripTimeout *= 2; + + enet_list_insert (enet_list_begin (& peer -> outgoingReliableCommands), + enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && + enet_list_empty (& peer -> sentReliableCommands) == 0) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + } + } + + return 0; +} + +static void +enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < outgoingCommand -> command.header.commandLength) + break; + + currentCommand = enet_list_next (currentCommand); + + if (outgoingCommand -> packet != NULL) + { + if ((enet_uint16) (peer -> mtu - host -> packetSize) < + (enet_uint16) (outgoingCommand -> command.header.commandLength + + outgoingCommand -> fragmentLength) || + peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > peer -> windowSize) + break; + } + + if (outgoingCommand -> roundTripTimeout == 0) + { + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; + outgoingCommand -> roundTripTimeoutLimit = ENET_PEER_TIMEOUT_LIMIT * outgoingCommand -> roundTripTimeout; + } + + if (enet_list_empty (& peer -> sentReliableCommands)) + peer -> nextTimeout = timeCurrent + outgoingCommand -> roundTripTimeout; + + enet_list_insert (enet_list_end (& peer -> sentReliableCommands), + enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + outgoingCommand -> sentTime = timeCurrent; + + buffer -> data = command; + buffer -> dataLength = outgoingCommand -> command.header.commandLength; + + host -> packetSize += buffer -> dataLength; + + * command = outgoingCommand -> command; + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + command -> header.commandLength += outgoingCommand -> fragmentLength; + + host -> packetSize += outgoingCommand -> fragmentLength; + + peer -> reliableDataInTransit += outgoingCommand -> fragmentLength; + } + + command -> header.commandLength = ENET_HOST_TO_NET_32 (command -> header.commandLength); + + ++ peer -> packetsSent; + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static int +enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) +{ + size_t packetsSent = 1; + ENetProtocolHeader header; + ENetPeer * currentPeer; + int sentLength; + + while (packetsSent > 0) + for (currentPeer = host -> peers, + packetsSent = 0; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || + currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + continue; + + host -> commandCount = 0; + host -> bufferCount = 1; + host -> packetSize = sizeof (ENetProtocolHeader); + + if (enet_list_empty (& currentPeer -> acknowledgements) == 0) + enet_protocol_send_acknowledgements (host, currentPeer); + + if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol)) + { + if (checkForTimeouts != 0 && + enet_list_empty (& currentPeer -> sentReliableCommands) == 0 && + ENET_TIME_GREATER_EQUAL (timeCurrent, currentPeer -> nextTimeout) && + enet_protocol_check_timeouts (host, currentPeer, event) == 1) + return 1; + } + if (enet_list_empty (& currentPeer -> outgoingReliableCommands) == 0) + enet_protocol_send_reliable_outgoing_commands (host, currentPeer); + else + if (enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> lastReceiveTime) >= ENET_PEER_PING_INTERVAL && + currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) + { + enet_peer_ping (currentPeer); + enet_protocol_send_reliable_outgoing_commands (host, currentPeer); + } + + if (host -> commandCount < sizeof (host -> commands) / sizeof (ENetProtocol) && + enet_list_empty (& currentPeer -> outgoingUnreliableCommands) == 0) + enet_protocol_send_unreliable_outgoing_commands (host, currentPeer); + + if (host -> commandCount == 0) + continue; + + if (currentPeer -> packetLossEpoch == 0) + currentPeer -> packetLossEpoch = timeCurrent; + else + if (ENET_TIME_DIFFERENCE (timeCurrent, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && + currentPeer -> packetsSent > 0) + { + enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; + +#ifdef ENET_DEBUG +#ifdef WIN32 + printf ( +#else + fprintf (stderr, +#endif + "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands)); +#endif + + currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4; + + if (packetLoss >= currentPeer -> packetLoss) + { + currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8; + currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4; + } + else + { + currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8; + currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4; + } + + currentPeer -> packetLossEpoch = timeCurrent; + currentPeer -> packetsSent = 0; + currentPeer -> packetsLost = 0; + } + + header.peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID); + header.flags = 0; + header.commandCount = host -> commandCount; + header.sentTime = ENET_HOST_TO_NET_32 (timeCurrent); + header.challenge = currentPeer -> challenge; + + host -> buffers -> data = & header; + host -> buffers -> dataLength = sizeof (ENetProtocolHeader); + + currentPeer -> lastSendTime = timeCurrent; + + ++ packetsSent; + + sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); + + enet_protocol_remove_sent_unreliable_commands (currentPeer); + + if (sentLength < 0) + return -1; + } + + return 0; +} + +/** Sends any queued packets on the host specified to its designated peers. + + @param host host to flush + @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). + @ingroup host +*/ +void +enet_host_flush (ENetHost * host) +{ + timeCurrent = enet_time_get (); + + enet_protocol_send_outgoing_commands (host, NULL, 0); +} + +/** Waits for events on the host specified and shuttles packets between + the host and its peers. + + @param host host to service + @param event an event structure where event details will be placed if one occurs + if event == NULL then no events will be delivered + @param timeout number of milliseconds that ENet should wait for events + @retval > 0 if an event occurred within the specified time limit + @retval 0 if no event occurred + @retval < 0 on failure + @remarks enet_host_service should be called fairly regularly for adequate performance + @ingroup host +*/ +int +enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout) +{ + enet_uint32 waitCondition; + + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error dispatching incoming packets"); + + return -1; + + default: + break; + } + } + + timeCurrent = enet_time_get (); + + timeout += timeCurrent; + + do + { + if (ENET_TIME_DIFFERENCE (timeCurrent, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + enet_host_bandwidth_throttle (host); + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: + perror ("Error sending outgoing packets"); + + return -1; + + default: + break; + } + + switch (enet_protocol_receive_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error receiving incoming packets"); + + return -1; + + default: + break; + } + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: + perror ("Error sending outgoing packets"); + + return -1; + + default: + break; + } + + if (event != NULL) + { + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + perror ("Error dispatching incoming packets"); + + return -1; + + default: + break; + } + } + + timeCurrent = enet_time_get (); + + if (ENET_TIME_GREATER_EQUAL (timeCurrent, timeout)) + return 0; + + waitCondition = ENET_SOCKET_WAIT_RECEIVE; + + if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, timeCurrent)) != 0) + return -1; + + timeCurrent = enet_time_get (); + } while (waitCondition == ENET_SOCKET_WAIT_RECEIVE); + + return 0; +} + diff --git a/project/jni/application/enigma/lib-src/enet/unix.c b/project/jni/application/enigma/lib-src/enet/unix.c new file mode 100644 index 000000000..5e0e5e80c --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/unix.c @@ -0,0 +1,407 @@ +/** + @file unix.c + @brief ENet Unix system specific functions +*/ +#ifndef WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID +#define HAS_SOCKLEN_T 1 +#define HAS_POLL 1 +#define HAS_FCNTL 1 +#define HAS_INET_PTON 1 +// #define HAS_GETHOSTBYNAME_R 1 // Incompatible argument list +#define HAS_INET_NTOP 1 +#define HAS_MSGHDR_FLAGS 1 +#endif + +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +#ifdef HAS_FCNTL +#include +#endif + +#ifdef __APPLE__ +#undef HAS_POLL +#endif + +#ifdef HAS_POLL +#include +#endif + +#ifndef HAS_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + return 0; +} + +void +enet_deinitialize (void) +{ +} + +enet_uint32 +enet_time_get (void) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYNAME_R + struct hostent hostData; + char buffer [2048]; + int errnum; + +#if defined(linux) || defined(__GLIBC__) || defined(__GNU__) + gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + hostEntry = gethostbyname (name); +#endif + + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + { +#ifdef HAS_INET_PTON + if (! inet_pton (AF_INET, name, & address -> host)) +#else + if (! inet_aton (name, (struct in_addr *) & address -> host)) +#endif + return -1; + return 0; + } + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYADDR_R + struct hostent hostData; + char buffer [2048]; + int errnum; + + in.s_addr = address -> host; + +#if defined(linux) || defined(__GLIBC__) || defined(__GNU__) + gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); +#endif + + if (hostEntry == NULL) + { +#ifdef HAS_INET_NTOP + if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) +#else + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr != NULL) + strncpy (name, addr, nameLength); + else +#endif + return -1; + return 0; + } + + strncpy (name, hostEntry -> h_name, nameLength); + + return 0; +} + +ENetSocket +enet_socket_create (ENetSocketType type, const ENetAddress * address) +{ + ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); + int receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE, + allowBroadcasting = 1; +#ifndef HAS_FCNTL + int nonBlocking = 1; +#endif + struct sockaddr_in sin; + + if (newSocket == ENET_SOCKET_NULL) + return ENET_SOCKET_NULL; + + if (type == ENET_SOCKET_TYPE_DATAGRAM) + { +#ifdef HAS_FCNTL + fcntl (newSocket, F_SETFL, O_NONBLOCK | fcntl (newSocket, F_GETFL)); +#else + ioctl (newSocket, FIONBIO, & nonBlocking); +#endif + + setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int)); + setsockopt (newSocket, SOL_SOCKET, SO_BROADCAST, (char *) & allowBroadcasting, sizeof (int)); + } + + if (address == NULL) + return newSocket; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + if (bind (newSocket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)) == -1 || + (type == ENET_SOCKET_TYPE_STREAM && + listen (newSocket, SOMAXCONN) == -1)) + { + close (newSocket); + + return ENET_SOCKET_NULL; + } + + return newSocket; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + int result; + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == -1) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +void +enet_socket_destroy (ENetSocket socket) +{ + close (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int sentLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (sentLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + + return sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int recvLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (recvLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + +#ifdef HAS_MSGHDR_FLAGS + if (msgHdr.msg_flags & MSG_TRUNC) + return -1; +#endif + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return recvLength; +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ +#ifdef HAS_POLL + struct pollfd pollSocket; + int pollCount; + + pollSocket.fd = socket; + pollSocket.events = 0; + + if (* condition & ENET_SOCKET_WAIT_SEND) + pollSocket.events |= POLLOUT; + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + pollSocket.events |= POLLIN; + + pollCount = poll (& pollSocket, 1, timeout); + + if (pollCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (pollCount == 0) + return 0; + + if (pollSocket.revents & POLLOUT) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (pollSocket.revents & POLLIN) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#else + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#endif +} + +#endif + diff --git a/project/jni/application/enigma/lib-src/enet/win32.c b/project/jni/application/enigma/lib-src/enet/win32.c new file mode 100644 index 000000000..230007160 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enet/win32.c @@ -0,0 +1,309 @@ +/** + @file win32.c + @brief ENet Win32 system specific functions +*/ +#ifdef WIN32 + +#include +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + WORD versionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + + if (WSAStartup (versionRequested, & wsaData)) + return -1; + + if (LOBYTE (wsaData.wVersion) != 1|| + HIBYTE (wsaData.wVersion) != 1) + { + WSACleanup (); + + return -1; + } + + timeBeginPeriod (1); + + return 0; +} + +void +enet_deinitialize (void) +{ + timeEndPeriod (1); + + WSACleanup (); +} + +enet_uint32 +enet_time_get (void) +{ + return (enet_uint32) timeGetTime () - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + timeBase = (enet_uint32) timeGetTime () - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry; + + hostEntry = gethostbyname (name); + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + { + unsigned long host = inet_addr (name); + if (host == INADDR_NONE) + return -1; + address -> host = host; + return 0; + } + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry; + + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); + if (hostEntry == NULL) + { + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr == NULL) + return -1; + strncpy (name, addr, nameLength); + return 0; + } + + strncpy (name, hostEntry -> h_name, nameLength); + + return 0; +} + +ENetSocket +enet_socket_create (ENetSocketType type, const ENetAddress * address) +{ + ENetSocket newSocket = socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); + u_long nonBlocking = 1; + int receiveBufferSize = ENET_HOST_RECEIVE_BUFFER_SIZE, + allowBroadcasting = 1; + struct sockaddr_in sin; + + if (newSocket == ENET_SOCKET_NULL) + return ENET_SOCKET_NULL; + + if (type == ENET_SOCKET_TYPE_DATAGRAM) + { + ioctlsocket (newSocket, FIONBIO, & nonBlocking); + + setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *) & receiveBufferSize, sizeof (int)); + setsockopt (newSocket, SOL_SOCKET, SO_BROADCAST, (char *) & allowBroadcasting, sizeof (int)); + } + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + if (bind (newSocket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)) == SOCKET_ERROR || + (type == ENET_SOCKET_TYPE_STREAM && + address != NULL && + listen (newSocket, SOMAXCONN) == SOCKET_ERROR)) + { + closesocket (newSocket); + + return ENET_SOCKET_NULL; + } + + return newSocket; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + return connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + SOCKET result; + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == INVALID_SOCKET) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +void +enet_socket_destroy (ENetSocket socket) +{ + closesocket (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct sockaddr_in sin; + DWORD sentLength; + + if (address != NULL) + { + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + + if (WSASendTo (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & sentLength, + 0, + address != NULL ? (struct sockaddr *) & sin : 0, + address != NULL ? sizeof (struct sockaddr_in) : 0, + NULL, + NULL) == SOCKET_ERROR) + { + if (WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + return -1; + } + + return (int) sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + INT sinLength = sizeof (struct sockaddr_in); + DWORD flags = 0, + recvLength; + struct sockaddr_in sin; + + if (WSARecvFrom (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & recvLength, + & flags, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL, + NULL, + NULL) == SOCKET_ERROR) + { + switch (WSAGetLastError ()) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + return 0; + } + + return -1; + } + + if (flags & MSG_PARTIAL) + return -1; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return (int) recvLength; +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +} + +#endif + diff --git a/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.c b/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.c new file mode 100644 index 000000000..3565d08fd --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.c @@ -0,0 +1,151 @@ +#include "SDL.h" +#include "IMG_SavePNG.h" +#include "png.h" +#include +#include + +#define IMG_SetError(a) SDL_SetError(a) + +/* Save a PNG type image to an SDL datasource */ +static void png_write_data(png_structp ctx, png_bytep area, png_size_t size) +{ + SDL_RWops *src; + + src = (SDL_RWops *)png_get_io_ptr(ctx); + SDL_RWwrite(src, area, size, 1); +} + +static void png_io_flush(png_structp ctx) +{ + SDL_RWops *src; + + src = (SDL_RWops *)png_get_io_ptr(ctx); + /* how do I flush src? */ +} + +static int png_colortype_from_surface(SDL_Surface *surface) +{ + int colortype = PNG_COLOR_MASK_COLOR; /* grayscale not supported */ + + if (surface->format->palette) + colortype |= PNG_COLOR_MASK_PALETTE; + else if (surface->format->Amask) + colortype |= PNG_COLOR_MASK_ALPHA; + + return colortype; +} + +static void png_user_warn(png_structp ctx, png_const_charp str) +{ + fprintf(stderr, "libpng: warning: %s\n", str); +} + +static void png_user_error(png_structp ctx, png_const_charp str) +{ + fprintf(stderr, "libpng: error: %s\n", str); +} + + + +int IMG_SavePNG_RW(SDL_Surface *face, SDL_RWops *src) { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + const int rmask = 0x00ff0000; + const int gmask = 0x0000ff00; + const int bmask = 0x000000ff; + const int amask = 0x00000000; +#else + const int rmask = 0x000000ff; + const int gmask = 0x0000ff00; + const int bmask = 0x00ff0000; + const int amask = 0x00000000; +#endif + + int result = -1; + SDL_Surface *surface = SDL_CreateRGBSurface(SDL_SWSURFACE, face->w, face->h, 24, + rmask, gmask, bmask, amask); + + if (surface) { + png_structp png_ptr; + + SDL_BlitSurface(face, NULL, surface, NULL); + SDL_LockSurface(surface); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_user_error, png_user_warn); + if (!png_ptr) { + IMG_SetError("Couldn't allocate memory for PNG file"); + } + else { + /* Allocate/initialize the image information data. REQUIRED */ + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + IMG_SetError("Couldn't create image information for PNG file"); + } + else { + png_bytep *row_pointers = 0; + + /* Set error handling. */ + if (setjmp(png_ptr->jmpbuf)) { + IMG_SetError("Error writing the PNG file"); + } + else { + int colortype; + png_set_write_fn(png_ptr, src, png_write_data, png_io_flush); + /* Set the image information here. Width and height are up to 2^31, + * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED + */ + colortype = png_colortype_from_surface(surface); + png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, + colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* Write the file header information. REQUIRED */ + png_write_info(png_ptr, info_ptr); + + /* pack pixels into bytes */ + png_set_packing(png_ptr); + + /* Create the array of pointers to image data */ + row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h); + if (!row_pointers) { + IMG_SetError("Couldn't allocate PNG row pointers"); + } + else { + int i; + for (i = 0; i < surface->h; i++) + row_pointers[i] = (png_bytep)(Uint8 *)surface->pixels + i*surface->pitch; + + /* write out the entire image data in one call */ + png_write_image(png_ptr, row_pointers); + png_write_end(png_ptr, info_ptr); + result = 0; /* success! */ + } + } + if (row_pointers) + free(row_pointers); + + png_destroy_info_struct(png_ptr, &info_ptr); + } + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + } + + SDL_UnlockSurface(surface); + SDL_FreeSurface(surface); + } + + return result; +} + +int IMG_SavePNG(SDL_Surface *surface, const char *file) +{ + SDL_RWops *out = SDL_RWFromFile(file, "wb"); + int ret; + if(!out) + return -1; + ret = IMG_SavePNG_RW(surface, out); + SDL_RWclose(out); + return ret; +} diff --git a/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.h b/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.h new file mode 100644 index 000000000..cfb4ec961 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/IMG_SavePNG.h @@ -0,0 +1,28 @@ +/* + IMG_SavePNG.h + + Originally a patch to SDL_image, LGPL + + (c) 2001 Darren Grant + Endian fixes by J. Fortmann + +*/ +#ifndef _IMG_SavePNG_h +#define _IMG_SavePNG_h + +#include "SDL.h" + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +int IMG_SavePNG_RW(SDL_Surface *face, SDL_RWops *src); +int IMG_SavePNG(SDL_Surface *surface, const char *file); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +}; +#endif + +#endif // _IMG_SavePNG_h diff --git a/project/jni/application/enigma/lib-src/enigma-core/README b/project/jni/application/enigma/lib-src/enigma-core/README new file mode 100644 index 000000000..b5f4a98bc --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/README @@ -0,0 +1,47 @@ +This directory contains the reusable parts of Enigma, i.e., all the +code that I had the time and leisure to write more or less properly. +Some of the classes and functions are rather complete and useful but +others are only useful for certain applications and would need further +features or refinements to make genuinely reusable. Anyway, if you +find any of it useful, feel free to use it in your game or +application. + +Here is a short rundown of the files and the services they provide: + +File Description +-------------------------------------------------------------------- + +alist.hh Associative lists + +argp.hh Parser for command line (POSIX and GNU-style options) + +buffer.hh Stream-like buffer for binary data, takes care of + endianness + +cache.hh Simple facility for resource caching + +callback.hh Generic callbacks, turned out not to be that useful + +dict.hh std::map alike hash table; useful but a bit incomplete + +font.hh Bitmap fonts, to be used in connection with the stuff + in video.hh + +geom.hh Rectangles and rectangle lists and some geometric + operations like intersection, bounding box, etc; + fairly complete + +math.hh Generic vectors in 2 and 3 dimensions + +sdl.hh Some utilities and bindings for SDL + +tools.hh General utilities like delete_sequence() and delete_map() + +video.hh Class wrappers and some higher level operations SDL's + video operations; useful, but incomplete and in need of + a better design. + +windows.hh Attempt at a reusable window manager that keeps track + of the stacking order, dirty rectangles, etc. Needs a + little more work to make it really useful. + diff --git a/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.c b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.c new file mode 100644 index 000000000..09b220ccb --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.c @@ -0,0 +1,3387 @@ +/* + + SDL_gfxPrimitives - Graphics primitives for SDL surfaces + + LGPL (c) A. Schiffler + +*/ + +#include +#include +#include +#include + +#include "SDL.h" + +#include "SDL_gfxPrimitives.h" +#include "SDL_gfxPrimitives_font.h" + +/* -===================- */ + +/* Define this flag to use surface blits for alpha blended drawing. */ +/* This is usually slower that direct surface calculations. */ + +#undef SURFACE_ALPHA_PIXEL + +/* ----- Defines for pixel clipping tests */ + +#define clip_xmin(surface) surface->clip_rect.x +#define clip_xmax(surface) surface->clip_rect.x+surface->clip_rect.w-1 +#define clip_ymin(surface) surface->clip_rect.y +#define clip_ymax(surface) surface->clip_rect.y+surface->clip_rect.h-1 + +/* ----- Pixel - fast, no blending, no locking, clipping */ + +int fastPixelColorNolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + int bpp; + Uint8 *p; + + /* + * Honor clipping setup at pixel level + */ + if ((x >= clip_xmin(dst)) && (x <= clip_xmax(dst)) && (y >= clip_ymin(dst)) && (y <= clip_ymax(dst))) { + + /* + * Get destination format + */ + bpp = dst->format->BytesPerPixel; + p = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + switch (bpp) { + case 1: + *p = color; + break; + case 2: + *(Uint16 *) p = color; + break; + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (color >> 16) & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = color & 0xff; + } else { + p[0] = color & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = (color >> 16) & 0xff; + } + break; + case 4: + *(Uint32 *) p = color; + break; + } /* switch */ + + + } + + return (0); +} + +/* ----- Pixel - fast, no blending, no locking, no clipping */ + +/* (faster but dangerous, make sure we stay in surface bounds) */ + +int fastPixelColorNolockNoclip(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + int bpp; + Uint8 *p; + + /* + * Get destination format + */ + bpp = dst->format->BytesPerPixel; + p = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + switch (bpp) { + case 1: + *p = color; + break; + case 2: + *(Uint16 *) p = color; + break; + case 3: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + p[0] = (color >> 16) & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = color & 0xff; + } else { + p[0] = color & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = (color >> 16) & 0xff; + } + break; + case 4: + *(Uint32 *) p = color; + break; + } /* switch */ + + return (0); +} + +/* ----- Pixel - fast, no blending, locking, clipping */ + +int fastPixelColor(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + int result; + + /* + * Lock the surface + */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + result = fastPixelColorNolock(dst, x, y, color); + + /* + * Unlock surface + */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +/* ----- Pixel - fast, no blending, locking, RGB input */ + +int fastPixelRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Uint32 color; + + /* + * Setup color + */ + color = SDL_MapRGBA(dst->format, r, g, b, a); + + /* + * Draw + */ + return (fastPixelColor(dst, x, y, color)); + +} + +/* ----- Pixel - fast, no blending, no locking RGB input */ + +int fastPixelRGBANolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Uint32 color; + + /* + * Setup color + */ + color = SDL_MapRGBA(dst->format, r, g, b, a); + + /* + * Draw + */ + return (fastPixelColorNolock(dst, x, y, color)); +} + +#ifdef SURFACE_ALPHA_PIXEL + +/* ----- Pixel - using single pixel blit with blending enabled if a<255 */ + +/* Old, slower routine - normally disabled */ + +static SDL_Surface *gfxPrimitivesSinglePixel = NULL; + +int pixelColor(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + SDL_Rect srect; + SDL_Rect drect; + int result; + + /* + * Setup source rectangle for pixel + */ + srect.x = 0; + srect.y = 0; + srect.w = 1; + srect.h = 1; + + /* + * Setup destination rectangle for pixel + */ + drect.x = x; + drect.y = y; + drect.w = 1; + drect.h = 1; + + /* + * Create single pixel in 32bit RGBA format + */ + if (gfxPrimitivesSinglePixel == NULL) { + gfxPrimitivesSinglePixel = + SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_HWSURFACE | SDL_SRCALPHA, 1, 1, + 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + } + + /* + * Draw color into pixel + */ + SDL_FillRect(gfxPrimitivesSinglePixel, &srect, color); + + /* + * Draw pixel onto destination surface + */ + result = SDL_BlitSurface(gfxPrimitivesSinglePixel, &srect, dst, &drect); + + return (result); +} + +#else + +/* PutPixel routine with alpha blending, input color in destination format */ + +/* New, faster routine - default blending pixel */ + +int _putPixelAlpha(SDL_Surface * surface, Sint16 x, Sint16 y, Uint32 color, Uint8 alpha) +{ + Uint32 Rmask = surface->format->Rmask, Gmask = + surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask; + Uint32 R, G, B, A = 0; + + if (x >= clip_xmin(surface) && x <= clip_xmax(surface) + && y >= clip_ymin(surface) && y <= clip_ymax(surface)) { + + switch (surface->format->BytesPerPixel) { + case 1:{ /* Assuming 8-bpp */ + if (alpha == 255) { + *((Uint8 *) surface->pixels + y * surface->pitch + x) = color; + } else { + Uint8 *pixel = (Uint8 *) surface->pixels + y * surface->pitch + x; + + Uint8 dR = surface->format->palette->colors[*pixel].r; + Uint8 dG = surface->format->palette->colors[*pixel].g; + Uint8 dB = surface->format->palette->colors[*pixel].b; + Uint8 sR = surface->format->palette->colors[color].r; + Uint8 sG = surface->format->palette->colors[color].g; + Uint8 sB = surface->format->palette->colors[color].b; + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + + *pixel = SDL_MapRGB(surface->format, dR, dG, dB); + } + } + break; + + case 2:{ /* Probably 15-bpp or 16-bpp */ + if (alpha == 255) { + *((Uint16 *) surface->pixels + y * surface->pitch / 2 + x) = color; + } else { + Uint16 *pixel = (Uint16 *) surface->pixels + y * surface->pitch / 2 + x; + Uint32 dc = *pixel; + + R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >> 8)) & Rmask; + G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >> 8)) & Gmask; + B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >> 8)) & Bmask; + if (Amask) + A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha >> 8)) & Amask; + + *pixel = R | G | B | A; + } + } + break; + + case 3:{ /* Slow 24-bpp mode, usually not used */ + Uint8 *pix = (Uint8 *) surface->pixels + y * surface->pitch + x * 3; + Uint8 rshift8 = surface->format->Rshift / 8; + Uint8 gshift8 = surface->format->Gshift / 8; + Uint8 bshift8 = surface->format->Bshift / 8; + Uint8 ashift8 = surface->format->Ashift / 8; + + + if (alpha == 255) { + *(pix + rshift8) = color >> surface->format->Rshift; + *(pix + gshift8) = color >> surface->format->Gshift; + *(pix + bshift8) = color >> surface->format->Bshift; + *(pix + ashift8) = color >> surface->format->Ashift; + } else { + Uint8 dR, dG, dB, dA = 0; + Uint8 sR, sG, sB, sA = 0; + + pix = (Uint8 *) surface->pixels + y * surface->pitch + x * 3; + + dR = *((pix) + rshift8); + dG = *((pix) + gshift8); + dB = *((pix) + bshift8); + dA = *((pix) + ashift8); + + sR = (color >> surface->format->Rshift) & 0xff; + sG = (color >> surface->format->Gshift) & 0xff; + sB = (color >> surface->format->Bshift) & 0xff; + sA = (color >> surface->format->Ashift) & 0xff; + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + dA = dA + ((sA - dA) * alpha >> 8); + + *((pix) + rshift8) = dR; + *((pix) + gshift8) = dG; + *((pix) + bshift8) = dB; + *((pix) + ashift8) = dA; + } + } + break; + + case 4:{ /* Probably 32-bpp */ + if (alpha == 255) { + *((Uint32 *) surface->pixels + y * surface->pitch / 4 + x) = color; + } else { + Uint32 *pixel = (Uint32 *) surface->pixels + y * surface->pitch / 4 + x; + Uint32 dc = *pixel; + + R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >> 8)) & Rmask; + G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >> 8)) & Gmask; + B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >> 8)) & Bmask; + if (Amask) + A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha >> 8)) & Amask; + + *pixel = R | G | B | A; + } + } + break; + } + } + + return (0); +} + +/* ----- Pixel - pixel draw with blending enabled if a<255 */ + +int pixelColor(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + Uint8 alpha; + Uint32 mcolor; + int result = 0; + + /* + * Lock the surface + */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Setup color + */ + alpha = color & 0x000000ff; + mcolor = + SDL_MapRGBA(dst->format, (color & 0xff000000) >> 24, + (color & 0x00ff0000) >> 16, (color & 0x0000ff00) >> 8, alpha); + + /* + * Draw + */ + result = _putPixelAlpha(dst, x, y, mcolor, alpha); + + /* + * Unlock the surface + */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +int pixelColorNolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color) +{ + Uint8 alpha; + Uint32 mcolor; + int result = 0; + + /* + * Setup color + */ + alpha = color & 0x000000ff; + mcolor = + SDL_MapRGBA(dst->format, (color & 0xff000000) >> 24, + (color & 0x00ff0000) >> 16, (color & 0x0000ff00) >> 8, alpha); + + /* + * Draw + */ + result = _putPixelAlpha(dst, x, y, mcolor, alpha); + + return (result); +} + + +/* Filled rectangle with alpha blending, color in destination format */ + +int _filledRectAlpha(SDL_Surface * surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color, Uint8 alpha) +{ + Uint32 Rmask = surface->format->Rmask, Gmask = + surface->format->Gmask, Bmask = surface->format->Bmask, Amask = surface->format->Amask; + Uint32 R, G, B, A = 0; + Sint16 x, y; + + switch (surface->format->BytesPerPixel) { + case 1:{ /* Assuming 8-bpp */ + Uint8 *row, *pixel; + Uint8 dR, dG, dB; + + Uint8 sR = surface->format->palette->colors[color].r; + Uint8 sG = surface->format->palette->colors[color].g; + Uint8 sB = surface->format->palette->colors[color].b; + + for (y = y1; y <= y2; y++) { + row = (Uint8 *) surface->pixels + y * surface->pitch; + for (x = x1; x <= x2; x++) { + pixel = row + x; + + dR = surface->format->palette->colors[*pixel].r; + dG = surface->format->palette->colors[*pixel].g; + dB = surface->format->palette->colors[*pixel].b; + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + + *pixel = SDL_MapRGB(surface->format, dR, dG, dB); + } + } + } + break; + + case 2:{ /* Probably 15-bpp or 16-bpp */ + Uint16 *row, *pixel; + Uint32 dR = (color & Rmask), dG = (color & Gmask), dB = (color & Bmask), dA = (color & Amask); + + for (y = y1; y <= y2; y++) { + row = (Uint16 *) surface->pixels + y * surface->pitch / 2; + for (x = x1; x <= x2; x++) { + pixel = row + x; + + R = ((*pixel & Rmask) + ((dR - (*pixel & Rmask)) * alpha >> 8)) & Rmask; + G = ((*pixel & Gmask) + ((dG - (*pixel & Gmask)) * alpha >> 8)) & Gmask; + B = ((*pixel & Bmask) + ((dB - (*pixel & Bmask)) * alpha >> 8)) & Bmask; + if (Amask) + A = ((*pixel & Amask) + ((dA - (*pixel & Amask)) * alpha >> 8)) & Amask; + + *pixel = R | G | B | A; + } + } + } + break; + + case 3:{ /* Slow 24-bpp mode, usually not used */ + Uint8 *row, *pix; + Uint8 dR, dG, dB, dA; + Uint8 rshift8 = surface->format->Rshift / 8; + Uint8 gshift8 = surface->format->Gshift / 8; + Uint8 bshift8 = surface->format->Bshift / 8; + Uint8 ashift8 = surface->format->Ashift / 8; + + Uint8 sR = (color >> surface->format->Rshift) & 0xff; + Uint8 sG = (color >> surface->format->Gshift) & 0xff; + Uint8 sB = (color >> surface->format->Bshift) & 0xff; + Uint8 sA = (color >> surface->format->Ashift) & 0xff; + + for (y = y1; y <= y2; y++) { + row = (Uint8 *) surface->pixels + y * surface->pitch; + for (x = x1; x <= x2; x++) { + pix = row + x * 3; + + dR = *((pix) + rshift8); + dG = *((pix) + gshift8); + dB = *((pix) + bshift8); + dA = *((pix) + ashift8); + + dR = dR + ((sR - dR) * alpha >> 8); + dG = dG + ((sG - dG) * alpha >> 8); + dB = dB + ((sB - dB) * alpha >> 8); + dA = dA + ((sA - dA) * alpha >> 8); + + *((pix) + rshift8) = dR; + *((pix) + gshift8) = dG; + *((pix) + bshift8) = dB; + *((pix) + ashift8) = dA; + } + } + + } + break; + + case 4:{ /* Probably 32-bpp */ + Uint32 *row, *pixel; + Uint32 dR = (color & Rmask), dG = (color & Gmask), dB = (color & Bmask), dA = (color & Amask); + + for (y = y1; y <= y2; y++) { + row = (Uint32 *) surface->pixels + y * surface->pitch / 4; + for (x = x1; x <= x2; x++) { + pixel = row + x; + + R = ((*pixel & Rmask) + ((dR - (*pixel & Rmask)) * alpha >> 8)) & Rmask; + G = ((*pixel & Gmask) + ((dG - (*pixel & Gmask)) * alpha >> 8)) & Gmask; + B = ((*pixel & Bmask) + ((dB - (*pixel & Bmask)) * alpha >> 8)) & Bmask; + if (Amask) + A = ((*pixel & Amask) + ((dA - (*pixel & Amask)) * alpha >> 8)) & Amask; + + *pixel = R | G | B | A; + } + } + } + break; + } + + return (0); +} + +/* Draw rectangle with alpha enabled from RGBA color. */ + +int filledRectAlpha(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 alpha; + Uint32 mcolor; + int result = 0; + + /* + * Lock the surface + */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Setup color + */ + alpha = color & 0x000000ff; + mcolor = + SDL_MapRGBA(dst->format, (color & 0xff000000) >> 24, + (color & 0x00ff0000) >> 16, (color & 0x0000ff00) >> 8, alpha); + + /* + * Draw + */ + result = _filledRectAlpha(dst, x1, y1, x2, y2, mcolor, alpha); + + /* + * Unlock the surface + */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +/* Draw horizontal line with alpha enabled from RGBA color */ + +int HLineAlpha(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color) +{ + return (filledRectAlpha(dst, x1, y, x2, y, color)); +} + + +/* Draw vertical line with alpha enabled from RGBA color */ + +int VLineAlpha(SDL_Surface * dst, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color) +{ + return (filledRectAlpha(dst, x, y1, x, y2, color)); +} + +#endif + + +/* Pixel - using alpha weight on color for AA-drawing */ + +int pixelColorWeight(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color, Uint32 weight) +{ + Uint32 a; + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Modify Alpha by weight + */ + a = ((a * weight) >> 8); + + return (pixelColor(dst, x, y, (color & (Uint32) 0xffffff00) | (Uint32) a)); +} + +/* Pixel - using alpha weight on color for AA-drawing - no locking */ + +int pixelColorWeightNolock(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color, Uint32 weight) +{ + Uint32 a; + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Modify Alpha by weight + */ + a = ((a * weight) >> 8); + + return (pixelColorNolock(dst, x, y, (color & (Uint32) 0xffffff00) | (Uint32) a)); +} + +int pixelRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + Uint32 color; + + /* + * Check Alpha + */ + if (a == 255) { + /* + * No alpha blending required + */ + /* + * Setup color + */ + color = SDL_MapRGBA(dst->format, r, g, b, a); + /* + * Draw + */ + return (fastPixelColor(dst, x, y, color)); + } else { + /* + * Alpha blending required + */ + /* + * Draw + */ + return (pixelColor(dst, x, y, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); + } +} + +/* ----- Horizontal line */ + +#ifdef SURFACE_ALPHA_PIXEL +static SDL_Surface *gfxPrimitivesHline = NULL; +#endif + +int hlineColor(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color) +{ + Sint16 left, right, top, bottom; + Uint8 *pixel, *pixellast; + int dx; + int pixx, pixy; + Sint16 w; + Sint16 xtmp; + int result = -1; + Uint8 *colorptr; + +#ifdef SURFACE_ALPHA_PIXEL + Uint32 a; + SDL_Rect srect; + SDL_Rect drect; +#endif + + /* + * Get clipping boundary + */ + left = dst->clip_rect.x; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + top = dst->clip_rect.y; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + xtmp = x1; + x1 = x2; + x2 = xtmp; + } + + /* + * Visible + */ + if ((x1 > right) || (x2 < left) || (y < top) || (y > bottom)) { + return (0); + } + + /* + * Clip x + */ + if (x1 < left) { + x1 = left; + } + if (x2 > right) { + x2 = right; + } + + /* + * Calculate width + */ + w = x2 - x1; + + /* + * Sanity check on width + */ + if (w < 0) { + return (0); + } + + /* + * Alpha check + */ + if ((color & 255) == 255) { + + /* + * No alpha-blending required + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + /* + * Lock surface + */ + SDL_LockSurface(dst); + + /* + * More variable setup + */ + dx = w; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y; + + /* + * Draw + */ + switch (dst->format->BytesPerPixel) { + case 1: + memset(pixel, color, dx); + break; + case 2: + pixellast = pixel + dx + dx; + for (; pixel <= pixellast; pixel += pixx) { + *(Uint16 *) pixel = color; + } + break; + case 3: + pixellast = pixel + dx + dx + dx; + for (; pixel <= pixellast; pixel += pixx) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + } + break; + default: /* case 4 */ + dx = dx + dx; + pixellast = pixel + dx + dx; + for (; pixel <= pixellast; pixel += pixx) { + *(Uint32 *) pixel = color; + } + break; + } + + /* + * Unlock surface + */ + SDL_UnlockSurface(dst); + + /* + * Set result code + */ + result = 0; + + } else { + + /* + * Alpha blending blit + */ + +#ifdef SURFACE_ALPHA_PIXEL + + /* + * Adjust width for Rect setup + */ + w++; + + /* + * Setup source rectangle for pixel + */ + srect.x = 0; + srect.y = 0; + srect.w = w; + srect.h = 1; + + /* + * Setup rectangle for destination line + */ + drect.x = x1; + drect.y = y; + drect.w = w; + drect.h = 1; + + /* + * Maybe deallocate existing surface if size is too small + */ + if ((gfxPrimitivesHline != NULL) && (gfxPrimitivesHline->w < w)) { + SDL_FreeSurface(gfxPrimitivesHline); + gfxPrimitivesHline = NULL; + } + + /* + * Create horizontal line surface in destination format if necessary + */ + if (gfxPrimitivesHline == NULL) { + gfxPrimitivesHline = + SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_HWSURFACE | SDL_SRCALPHA, w, + 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + } + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Toggle alpha blending if necessary, reset otherwise + */ + if (a != 255) { + SDL_SetAlpha(gfxPrimitivesHline, SDL_SRCALPHA, 255); + } else { + SDL_SetAlpha(gfxPrimitivesHline, 0, 255); + } + + /* + * Draw color into pixel + */ + SDL_FillRect(gfxPrimitivesHline, &srect, color); + + /* + * Draw pixel onto destination surface + */ + result = SDL_BlitSurface(gfxPrimitivesHline, &srect, dst, &drect); + +#else + + result = HLineAlpha(dst, x1, x1 + w, y, color); + +#endif + + } + + return (result); +} + +int hlineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (hlineColor(dst, x1, x2, y, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- Vertical line */ + +#ifdef SURFACE_ALPHA_PIXEL +static SDL_Surface *gfxPrimitivesVline = NULL; +#endif + +int vlineColor(SDL_Surface * dst, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color) +{ + Sint16 left, right, top, bottom; + Uint8 *pixel, *pixellast; + int dy; + int pixx, pixy; + Sint16 h; + Sint16 ytmp; + int result = -1; + Uint8 *colorptr; + +#ifdef SURFACE_ALPHA_PIXEL + SDL_Rect srect; + SDL_Rect drect; + Uint32 a; +#endif + + /* + * Get clipping boundary + */ + left = dst->clip_rect.x; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + top = dst->clip_rect.y; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + ytmp = y1; + y1 = y2; + y2 = ytmp; + } + + /* + * Visible + */ + if ((y2 < top) || (y1 > bottom) || (x < left) || (x > right)) { + return (0); + } + + /* + * Clip y + */ + if (y1 < top) { + y1 = top; + } + if (y2 > bottom) { + y2 = bottom; + } + + /* + * Calculate height + */ + h = y2 - y1; + + /* + * Sanity check on height + */ + if (h < 0) { + return (0); + } + + /* + * Alpha check + */ + if ((color & 255) == 255) { + + /* + * No alpha-blending required + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + /* + * Lock surface + */ + SDL_LockSurface(dst); + + /* + * More variable setup + */ + dy = h; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x + pixy * (int) y1; + pixellast = pixel + pixy * dy; + + /* + * Draw + */ + switch (dst->format->BytesPerPixel) { + case 1: + for (; pixel <= pixellast; pixel += pixy) { + *(Uint8 *) pixel = color; + } + break; + case 2: + for (; pixel <= pixellast; pixel += pixy) { + *(Uint16 *) pixel = color; + } + break; + case 3: + for (; pixel <= pixellast; pixel += pixy) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + } + break; + default: /* case 4 */ + for (; pixel <= pixellast; pixel += pixy) { + *(Uint32 *) pixel = color; + } + break; + } + + /* + * Unlock surface + */ + SDL_UnlockSurface(dst); + + /* + * Set result code + */ + result = 0; + + } else { + + /* + * Alpha blending blit + */ + +#ifdef SURFACE_ALPHA_PIXEL + + /* + * Adjust height for Rect setup + */ + h++; + + /* + * Setup source rectangle for pixel + */ + srect.x = 0; + srect.y = 0; + srect.w = 1; + srect.h = h; + + /* + * Setup rectangle for line + */ + drect.x = x; + drect.y = y1; + drect.w = 1; + drect.h = h; + + /* + * Maybe deallocate existing surface if size is too small + */ + if ((gfxPrimitivesVline != NULL) && (gfxPrimitivesVline->h < h)) { + SDL_FreeSurface(gfxPrimitivesVline); + gfxPrimitivesVline = NULL; + } + + /* + * Create horizontal line surface in destination format if necessary + */ + if (gfxPrimitivesVline == NULL) { + gfxPrimitivesVline = + SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_HWSURFACE | SDL_SRCALPHA, 1, + h, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + } + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Toggle alpha blending if necessary, reset otherwise + */ + if (a != 255) { + SDL_SetAlpha(gfxPrimitivesVline, SDL_SRCALPHA, 255); + } else { + SDL_SetAlpha(gfxPrimitivesVline, 0, 255); + } + + /* + * Draw color into pixel + */ + SDL_FillRect(gfxPrimitivesVline, &srect, color); + + /* + * Draw Vline onto destination surface + */ + result = SDL_BlitSurface(gfxPrimitivesVline, &srect, dst, &drect); + +#else + + result = VLineAlpha(dst, x, y1, y1 + h, color); + +#endif + + } + + return (result); +} + +int vlineRGBA(SDL_Surface * dst, Sint16 x, Sint16 y1, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (vlineColor(dst, x, y1, y2, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- Rectangle */ + +int rectangleColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + int result; + Sint16 w, h, xtmp, ytmp; + + + /* + * Swap x1, x2 if required + */ + if (x1 > x2) { + xtmp = x1; + x1 = x2; + x2 = xtmp; + } + + /* + * Swap y1, y2 if required + */ + if (y1 > y2) { + ytmp = y1; + y1 = y2; + y2 = ytmp; + } + + /* + * Calculate width&height + */ + w = x2 - x1; + h = y2 - y1; + + /* + * Sanity check + */ + if ((w < 0) || (h < 0)) { + return (0); + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 == y2) { + return (pixelColor(dst, x1, y1, color)); + } else { + return (vlineColor(dst, x1, y1, y2, color)); + } + } else { + if (y1 == y2) { + return (hlineColor(dst, x1, x2, y1, color)); + } + } + + /* + * Draw rectangle + */ + result = 0; + result |= vlineColor(dst, x1, y1, y2, color); + result |= vlineColor(dst, x2, y1, y2, color); + result |= hlineColor(dst, x1, x2, y1, color); + result |= hlineColor(dst, x1, x2, y2, color); + + return (result); + +} + +int rectangleRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (rectangleColor + (dst, x1, y1, x2, y2, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* --------- Clipping routines for box/line */ + +/* Clipping based heavily on code from */ + +/* http://www.ncsa.uiuc.edu/Vis/Graphics/src/clipCohSuth.c */ + +#define CLIP_LEFT_EDGE 0x1 +#define CLIP_RIGHT_EDGE 0x2 +#define CLIP_BOTTOM_EDGE 0x4 +#define CLIP_TOP_EDGE 0x8 +#define CLIP_INSIDE(a) (!a) +#define CLIP_REJECT(a,b) (a&b) +#define CLIP_ACCEPT(a,b) (!(a|b)) + +static int clipEncode(Sint16 x, Sint16 y, Sint16 left, Sint16 top, Sint16 right, Sint16 bottom) +{ + int code = 0; + + if (x < left) { + code |= CLIP_LEFT_EDGE; + } else if (x > right) { + code |= CLIP_RIGHT_EDGE; + } + if (y < top) { + code |= CLIP_TOP_EDGE; + } else if (y > bottom) { + code |= CLIP_BOTTOM_EDGE; + } + return code; +} + +static int clipLine(SDL_Surface * dst, Sint16 * x1, Sint16 * y1, Sint16 * x2, Sint16 * y2) +{ + Sint16 left, right, top, bottom; + int code1, code2; + int draw = 0; + Sint16 swaptmp; + float m; + + /* + * Get clipping boundary + */ + left = dst->clip_rect.x; + right = dst->clip_rect.x + dst->clip_rect.w - 1; + top = dst->clip_rect.y; + bottom = dst->clip_rect.y + dst->clip_rect.h - 1; + + while (1) { + code1 = clipEncode(*x1, *y1, left, top, right, bottom); + code2 = clipEncode(*x2, *y2, left, top, right, bottom); + if (CLIP_ACCEPT(code1, code2)) { + draw = 1; + break; + } else if (CLIP_REJECT(code1, code2)) + break; + else { + if (CLIP_INSIDE(code1)) { + swaptmp = *x2; + *x2 = *x1; + *x1 = swaptmp; + swaptmp = *y2; + *y2 = *y1; + *y1 = swaptmp; + swaptmp = code2; + code2 = code1; + code1 = swaptmp; + } + if (*x2 != *x1) { + m = (*y2 - *y1) / (float) (*x2 - *x1); + } else { + m = 1.0f; + } + if (code1 & CLIP_LEFT_EDGE) { + *y1 += (Sint16) ((left - *x1) * m); + *x1 = left; + } else if (code1 & CLIP_RIGHT_EDGE) { + *y1 += (Sint16) ((right - *x1) * m); + *x1 = right; + } else if (code1 & CLIP_BOTTOM_EDGE) { + if (*x2 != *x1) { + *x1 += (Sint16) ((bottom - *y1) / m); + } + *y1 = bottom; + } else if (code1 & CLIP_TOP_EDGE) { + if (*x2 != *x1) { + *x1 += (Sint16) ((top - *y1) / m); + } + *y1 = top; + } + } + } + + return draw; +} + +/* ----- Filled rectangle (Box) */ + +#ifdef SURFACE_ALPHA_PIXEL +static SDL_Surface *gfxPrimitivesBox = NULL; +#endif + +int boxColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + Uint8 *pixel, *pixellast; + int x, dx; + int dy; + int pixx, pixy; + Sint16 w, h, tmp; + int result; + Uint8 *colorptr; + +#ifdef SURFACE_ALPHA_PIXEL + Uint32 a; + SDL_Rect srect; + SDL_Rect drect; +#endif + + /* + * Clip diagonal and test if we have to draw + */ + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 < y2) { + return (vlineColor(dst, x1, y1, y2, color)); + } else if (y1 > y2) { + return (vlineColor(dst, x1, y2, y1, color)); + } else { + return (pixelColor(dst, x1, y1, color)); + } + } + if (y1 == y2) { + if (x1 < x2) { + return (hlineColor(dst, x1, x2, y1, color)); + } else if (x1 > x2) { + return (hlineColor(dst, x2, x1, y1, color)); + } + } + + /* + * Order coordinates + */ + if (x1 > x2) { + tmp = x1; + x1 = x2; + x2 = tmp; + } + if (y1 > y2) { + tmp = y1; + y1 = y2; + y2 = tmp; + } + + /* + * Calculate width&height + */ + w = x2 - x1; + h = y2 - y1; + + /* + * Alpha check + */ + if ((color & 255) == 255) { + + /* + * No alpha-blending required + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + /* + * Lock surface + */ + SDL_LockSurface(dst); + + /* + * More variable setup + */ + dx = w; + dy = h; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y1; + pixellast = pixel + pixx * dx + pixy * dy; + + /* + * Draw + */ + switch (dst->format->BytesPerPixel) { + case 1: + for (; pixel <= pixellast; pixel += pixy) { + memset(pixel, (Uint8) color, dx); + } + break; + case 2: + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + *(Uint16 *) pixel = color; + pixel += pixx; + } + } + break; + case 3: + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + pixel += pixx; + } + } + break; + default: /* case 4 */ + pixy -= (pixx * dx); + for (; pixel <= pixellast; pixel += pixy) { + for (x = 0; x < dx; x++) { + *(Uint32 *) pixel = color; + pixel += pixx; + } + } + break; + } + + /* + * Unlock surface + */ + SDL_UnlockSurface(dst); + + result = 0; + + } else { + +#ifdef SURFACE_ALPHA_PIXEL + + /* + * Setup source rectangle for pixel + */ + srect.x = 0; + srect.y = 0; + srect.w = w; + srect.h = h; + + /* + * Setup rectangle for line + */ + drect.x = x1; + drect.y = y1; + drect.w = w; + drect.h = h; + + /* + * Maybe deallocate existing surface if size is too small + */ + if ((gfxPrimitivesBox != NULL) + && ((gfxPrimitivesBox->w < w) || (gfxPrimitivesBox->h < h))) { + SDL_FreeSurface(gfxPrimitivesBox); + gfxPrimitivesBox = NULL; + } + + /* + * Create box surface in destination format if necessary + */ + if (gfxPrimitivesBox == NULL) { + gfxPrimitivesBox = + SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_HWSURFACE | SDL_SRCALPHA, w, + h, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + } + + /* + * Get alpha + */ + a = (color & (Uint32) 0x000000ff); + + /* + * Toggle alpha blending if necessary, reset otherwise + */ + if (a != 255) { + SDL_SetAlpha(gfxPrimitivesBox, SDL_SRCALPHA, 255); + } else { + SDL_SetAlpha(gfxPrimitivesBox, 0, 255); + } + + /* + * Draw color into pixel + */ + SDL_FillRect(gfxPrimitivesBox, &srect, color); + + /* + * Draw pixel onto destination surface + */ + result = SDL_BlitSurface(gfxPrimitivesBox, &srect, dst, &drect); + +#else + + result = filledRectAlpha(dst, x1, y1, x1 + w, y1 + h, color); + +#endif + + } + + return (result); +} + +int boxRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (boxColor(dst, x1, y1, x2, y2, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- Line */ + +/* Non-alpha line drawing code adapted from routine */ +/* by Pete Shinners, pete@shinners.org */ +/* Originally from pygame, http://pygame.seul.org */ + +#define ABS(a) (((a)<0) ? -(a) : (a)) + +int lineColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + int pixx, pixy; + int x, y; + int dx, dy; + int ax, ay; + int sx, sy; + int swaptmp; + Uint8 *pixel; + Uint8 *colorptr; + + /* + * Clip line and test if we have to draw + */ + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Test for special cases of straight lines or single point + */ + if (x1 == x2) { + if (y1 < y2) { + return (vlineColor(dst, x1, y1, y2, color)); + } else if (y1 > y2) { + return (vlineColor(dst, x1, y2, y1, color)); + } else { + return (pixelColor(dst, x1, y1, color)); + } + } + if (y1 == y2) { + if (x1 < x2) { + return (hlineColor(dst, x1, x2, y1, color)); + } else if (x1 > x2) { + return (hlineColor(dst, x2, x1, y1, color)); + } + } + + /* + * Variable setup + */ + dx = x2 - x1; + dy = y2 - y1; + sx = (dx >= 0) ? 1 : -1; + sy = (dy >= 0) ? 1 : -1; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Check for alpha blending + */ + if ((color & 255) == 255) { + + /* + * No alpha blending - use fast pixel routines + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + /* + * More variable setup + */ + dx = sx * dx + 1; + dy = sy * dy + 1; + pixx = dst->format->BytesPerPixel; + pixy = dst->pitch; + pixel = ((Uint8 *) dst->pixels) + pixx * (int) x1 + pixy * (int) y1; + pixx *= sx; + pixy *= sy; + if (dx < dy) { + swaptmp = dx; + dx = dy; + dy = swaptmp; + swaptmp = pixx; + pixx = pixy; + pixy = swaptmp; + } + + /* + * Draw + */ + x = 0; + y = 0; + switch (dst->format->BytesPerPixel) { + case 1: + for (; x < dx; x++, pixel += pixx) { + *pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 2: + for (; x < dx; x++, pixel += pixx) { + *(Uint16 *) pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + case 3: + for (; x < dx; x++, pixel += pixx) { + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + pixel[0] = (color >> 16) & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = color & 0xff; + } else { + pixel[0] = color & 0xff; + pixel[1] = (color >> 8) & 0xff; + pixel[2] = (color >> 16) & 0xff; + } + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + default: /* case 4 */ + for (; x < dx; x++, pixel += pixx) { + *(Uint32 *) pixel = color; + y += dy; + if (y >= dx) { + y -= dx; + pixel += pixy; + } + } + break; + } + + } else { + + /* + * Alpha blending required - use single-pixel blits + */ + + ax = ABS(dx) << 1; + ay = ABS(dy) << 1; + x = x1; + y = y1; + if (ax > ay) { + int d = ay - (ax >> 1); + + while (x != x2) { + pixelColorNolock (dst, x, y, color); + if (d > 0 || (d == 0 && sx == 1)) { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } else { + int d = ax - (ay >> 1); + + while (y != y2) { + pixelColorNolock (dst, x, y, color); + if (d > 0 || ((d == 0) && (sy == 1))) { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } + pixelColorNolock (dst, x, y, color); + + } + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (0); +} + +int lineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (lineColor(dst, x1, y1, x2, y2, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* AA Line */ + +#define AAlevels 256 +#define AAbits 8 + +/* + +This implementation of the Wu antialiasing code is based on Mike Abrash's +DDJ article which was reprinted as Chapter 42 of his Graphics Programming +Black Book, but has been optimized to work with SDL and utilizes 32-bit +fixed-point arithmetic. (A. Schiffler). + +*/ + +int aalineColorInt(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color, int draw_endpoint) +{ + Sint32 xx0, yy0, xx1, yy1; + int result; + Uint32 intshift, erracc, erradj; + Uint32 erracctmp, wgt, wgtcompmask; + int dx, dy, tmp, xdir, y0p1, x0pxdir; + + /* + * Clip line and test if we have to draw + */ + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Keep on working with 32bit numbers + */ + xx0 = x1; + yy0 = y1; + xx1 = x2; + yy1 = y2; + + /* + * Reorder points if required + */ + if (yy0 > yy1) { + tmp = yy0; + yy0 = yy1; + yy1 = tmp; + tmp = xx0; + xx0 = xx1; + xx1 = tmp; + } + + /* + * Calculate distance + */ + dx = xx1 - xx0; + dy = yy1 - yy0; + + /* + * Adjust for negative dx and set xdir + */ + if (dx >= 0) { + xdir = 1; + } else { + xdir = -1; + dx = (-dx); + } + + /* + * Check for special cases + */ + if (dx == 0) { + /* + * Vertical line + */ + return (vlineColor(dst, x1, y1, y2, color)); + } else if (dy == 0) { + /* + * Horizontal line + */ + return (hlineColor(dst, x1, x2, y1, color)); + } else if (dx == dy) { + /* + * Diagonal line + */ + return (lineColor(dst, x1, y1, x2, y2, color)); + } + + /* + * Line is not horizontal, vertical or diagonal + */ + result = 0; + + /* + * Zero accumulator + */ + erracc = 0; + + /* + * # of bits by which to shift erracc to get intensity level + */ + intshift = 32 - AAbits; + /* + * Mask used to flip all bits in an intensity weighting + */ + wgtcompmask = AAlevels - 1; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Draw the initial pixel in the foreground color + */ + result |= pixelColorNolock(dst, x1, y1, color); + + /* + * x-major or y-major? + */ + if (dy > dx) { + + /* + * y-major. Calculate 16-bit fixed point fractional part of a pixel that + * X advances every time Y advances 1 pixel, truncating the result so that + * we won't overrun the endpoint along the X axis + */ + /* + * Not-so-portable version: erradj = ((Uint64)dx << 32) / (Uint64)dy; + */ + erradj = ((dx << 16) / dy) << 16; + + /* + * draw all pixels other than the first and last + */ + x0pxdir = xx0 + xdir; + while (--dy) { + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * rollover in error accumulator, x coord advances + */ + xx0 = x0pxdir; + x0pxdir += xdir; + } + yy0++; /* y-major so always advance Y */ + + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + result |= pixelColorWeightNolock (dst, xx0, yy0, color, 255 - wgt); + result |= pixelColorWeightNolock (dst, x0pxdir, yy0, color, wgt); + } + + } else { + + /* + * x-major line. Calculate 16-bit fixed-point fractional part of a pixel + * that Y advances each time X advances 1 pixel, truncating the result so + * that we won't overrun the endpoint along the X axis. + */ + /* + * Not-so-portable version: erradj = ((Uint64)dy << 32) / (Uint64)dx; + */ + erradj = ((dy << 16) / dx) << 16; + + /* + * draw all pixels other than the first and last + */ + y0p1 = yy0 + 1; + while (--dx) { + + erracctmp = erracc; + erracc += erradj; + if (erracc <= erracctmp) { + /* + * Accumulator turned over, advance y + */ + yy0 = y0p1; + y0p1++; + } + xx0 += xdir; /* x-major so always advance X */ + /* + * the AAbits most significant bits of erracc give us the intensity + * weighting for this pixel, and the complement of the weighting for + * the paired pixel. + */ + wgt = (erracc >> intshift) & 255; + result |= pixelColorWeightNolock (dst, xx0, yy0, color, 255 - wgt); + result |= pixelColorWeightNolock (dst, xx0, y0p1, color, wgt); + } + } + + /* + * Do we have to draw the endpoint + */ + if (draw_endpoint) { + /* + * Draw final pixel, always exactly intersected by the line and doesn't + * need to be weighted. + */ + result |= pixelColorNolock (dst, x2, y2, color); + } + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +int aalineColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color) +{ + return (aalineColorInt(dst, x1, y1, x2, y2, color, 1)); +} + +int aalineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return (aalineColorInt + (dst, x1, y1, x2, y2, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a, 1)); +} + + +/* ----- Circle */ + +/* Note: Based on algorithm from sge library, modified by A. Schiffler */ + +/* with multiple pixel-draw removal and other minor speedup changes. */ + +int circleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color) +{ + int result; + Sint16 x1, y1, x2, y2; + Sint16 cx = 0; + Sint16 cy = r; + Sint16 ocx = (Sint16) 0xffff; + Sint16 ocy = (Sint16) 0xffff; + Sint16 df = 1 - r; + Sint16 d_e = 3; + Sint16 d_se = -2 * r + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + Uint8 *colorptr; + + /* + * Sanity check radius + */ + if (r < 0) { + return (-1); + } + + /* + * Special case for r=0 - draw a point + */ + if (r == 0) { + return (pixelColor(dst, x, y, color)); + } + + /* + * Test if bounding box of circle is visible + */ + x1 = x - r; + y1 = y - r; + x2 = x + r; + y2 = y + r; + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Draw circle + */ + result = 0; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Alpha Check + */ + if ((color & 255) == 255) { + + /* + * No Alpha - direct memory writes + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + /* + * Draw + */ + do { + if ((ocy != cy) || (ocx != cx)) { + xpcx = x + cx; + xmcx = x - cx; + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + result |= fastPixelColorNolock(dst, xmcx, ypcy, color); + result |= fastPixelColorNolock(dst, xpcx, ypcy, color); + result |= fastPixelColorNolock(dst, xmcx, ymcy, color); + result |= fastPixelColorNolock(dst, xpcx, ymcy, color); + } else { + result |= fastPixelColorNolock(dst, xmcx, y, color); + result |= fastPixelColorNolock(dst, xpcx, y, color); + } + ocy = cy; + xpcy = x + cy; + xmcy = x - cy; + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + result |= fastPixelColorNolock(dst, xmcy, ypcx, color); + result |= fastPixelColorNolock(dst, xpcy, ypcx, color); + result |= fastPixelColorNolock(dst, xmcy, ymcx, color); + result |= fastPixelColorNolock(dst, xpcy, ymcx, color); + } else { + result |= fastPixelColorNolock(dst, xmcy, y, color); + result |= fastPixelColorNolock(dst, xpcy, y, color); + } + ocx = cx; + } + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); + + /* + * Unlock surface + */ + SDL_UnlockSurface(dst); + + } else { + + /* + * Using Alpha - blended pixel blits + */ + + do { + /* + * Draw + */ + if ((ocy != cy) || (ocx != cx)) { + xpcx = x + cx; + xmcx = x - cx; + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + result |= pixelColorNolock (dst, xmcx, ypcy, color); + result |= pixelColorNolock (dst, xpcx, ypcy, color); + result |= pixelColorNolock (dst, xmcx, ymcy, color); + result |= pixelColorNolock (dst, xpcx, ymcy, color); + } else { + result |= pixelColorNolock (dst, xmcx, y, color); + result |= pixelColorNolock (dst, xpcx, y, color); + } + ocy = cy; + xpcy = x + cy; + xmcy = x - cy; + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + result |= pixelColorNolock (dst, xmcy, ypcx, color); + result |= pixelColorNolock (dst, xpcy, ypcx, color); + result |= pixelColorNolock (dst, xmcy, ymcx, color); + result |= pixelColorNolock (dst, xpcy, ymcx, color); + } else { + result |= pixelColorNolock (dst, xmcy, y, color); + result |= pixelColorNolock (dst, xpcy, y, color); + } + ocx = cx; + } + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); + + } /* Alpha check */ + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +int circleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (circleColor(dst, x, y, rad, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- AA Circle */ + +/* AA circle is based on AAellipse */ + +int aacircleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color) +{ + return (aaellipseColor(dst, x, y, r, r, color)); +} + +int aacircleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (aaellipseColor + (dst, x, y, rad, rad, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- Filled Circle */ + +/* Note: Based on algorithm from sge library with multiple-hline draw removal */ + +/* and other speedup changes. */ + +int filledCircleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color) +{ + int result; + Sint16 x1, y1, x2, y2; + Sint16 cx = 0; + Sint16 cy = r; + Sint16 ocx = (Sint16) 0xffff; + Sint16 ocy = (Sint16) 0xffff; + Sint16 df = 1 - r; + Sint16 d_e = 3; + Sint16 d_se = -2 * r + 5; + Sint16 xpcx, xmcx, xpcy, xmcy; + Sint16 ypcy, ymcy, ypcx, ymcx; + + /* + * Sanity check radius + */ + if (r < 0) { + return (-1); + } + + /* + * Special case for r=0 - draw a point + */ + if (r == 0) { + return (pixelColor(dst, x, y, color)); + } + + /* + * Test bounding box + */ + x1 = x - r; + y1 = y - r; + x2 = x + r; + y2 = y + r; + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Draw + */ + result = 0; + do { + xpcx = x + cx; + xmcx = x - cx; + xpcy = x + cy; + xmcy = x - cy; + if (ocy != cy) { + if (cy > 0) { + ypcy = y + cy; + ymcy = y - cy; + result |= hlineColor(dst, xmcx, xpcx, ypcy, color); + result |= hlineColor(dst, xmcx, xpcx, ymcy, color); + } else { + result |= hlineColor(dst, xmcx, xpcx, y, color); + } + ocy = cy; + } + if (ocx != cx) { + if (cx != cy) { + if (cx > 0) { + ypcx = y + cx; + ymcx = y - cx; + result |= hlineColor(dst, xmcy, xpcy, ymcx, color); + result |= hlineColor(dst, xmcy, xpcy, ypcx, color); + } else { + result |= hlineColor(dst, xmcy, xpcy, y, color); + } + } + ocx = cx; + } + /* + * Update + */ + if (df < 0) { + df += d_e; + d_e += 2; + d_se += 2; + } else { + df += d_se; + d_e += 2; + d_se += 4; + cy--; + } + cx++; + } while (cx <= cy); + + return (result); +} + +int filledCircleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (filledCircleColor + (dst, x, y, rad, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + + +/* ----- Ellipse */ + +/* Note: Based on algorithm from sge library with multiple-hline draw removal */ + +/* and other speedup changes. */ + +int ellipseColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) +{ + int result; + Sint16 x1, y1, x2, y2; + int ix, iy; + int h, i, j, k; + int oh, oi, oj, ok; + int xmh, xph, ypk, ymk; + int xmi, xpi, ymj, ypj; + int xmj, xpj, ymi, ypi; + int xmk, xpk, ymh, yph; + Uint8 *colorptr; + + /* + * Sanity check radii + */ + if ((rx < 0) || (ry < 0)) { + return (-1); + } + + /* + * Special case for rx=0 - draw a vline + */ + if (rx == 0) { + return (vlineColor(dst, x, y - ry, y + ry, color)); + } + /* + * Special case for ry=0 - draw a hline + */ + if (ry == 0) { + return (hlineColor(dst, x - rx, x + rx, y, color)); + } + + /* + * Test bounding box + */ + x1 = x - rx; + y1 = y - ry; + x2 = x + rx; + y2 = y + ry; + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Init vars + */ + oh = oi = oj = ok = 0xFFFF; + + /* + * Draw + */ + result = 0; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* + * Check alpha + */ + if ((color & 255) == 255) { + + /* + * No Alpha - direct memory writes + */ + + /* + * Setup color + */ + colorptr = (Uint8 *) & color; + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { + color = SDL_MapRGBA(dst->format, colorptr[0], colorptr[1], colorptr[2], colorptr[3]); + } else { + color = SDL_MapRGBA(dst->format, colorptr[3], colorptr[2], colorptr[1], colorptr[0]); + } + + + if (rx > ry) { + ix = 0; + iy = rx * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + ypk = y + k; + ymk = y - k; + result |= fastPixelColorNolock(dst, xmh, ypk, color); + result |= fastPixelColorNolock(dst, xph, ypk, color); + result |= fastPixelColorNolock(dst, xmh, ymk, color); + result |= fastPixelColorNolock(dst, xph, ymk, color); + } else { + result |= fastPixelColorNolock(dst, xmh, y, color); + result |= fastPixelColorNolock(dst, xph, y, color); + } + ok = k; + xpi = x + i; + xmi = x - i; + if (j > 0) { + ypj = y + j; + ymj = y - j; + result |= fastPixelColorNolock(dst, xmi, ypj, color); + result |= fastPixelColorNolock(dst, xpi, ypj, color); + result |= fastPixelColorNolock(dst, xmi, ymj, color); + result |= fastPixelColorNolock(dst, xpi, ymj, color); + } else { + result |= fastPixelColorNolock(dst, xmi, y, color); + result |= fastPixelColorNolock(dst, xpi, y, color); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + ypi = y + i; + ymi = y - i; + result |= fastPixelColorNolock(dst, xmj, ypi, color); + result |= fastPixelColorNolock(dst, xpj, ypi, color); + result |= fastPixelColorNolock(dst, xmj, ymi, color); + result |= fastPixelColorNolock(dst, xpj, ymi, color); + } else { + result |= fastPixelColorNolock(dst, xmj, y, color); + result |= fastPixelColorNolock(dst, xpj, y, color); + } + oi = i; + xmk = x - k; + xpk = x + k; + if (h > 0) { + yph = y + h; + ymh = y - h; + result |= fastPixelColorNolock(dst, xmk, yph, color); + result |= fastPixelColorNolock(dst, xpk, yph, color); + result |= fastPixelColorNolock(dst, xmk, ymh, color); + result |= fastPixelColorNolock(dst, xpk, ymh, color); + } else { + result |= fastPixelColorNolock(dst, xmk, y, color); + result |= fastPixelColorNolock(dst, xpk, y, color); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + } else { + + if (rx > ry) { + ix = 0; + iy = rx * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if (((ok != k) && (oj != k)) || ((oj != j) && (ok != j)) || (k != j)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + ypk = y + k; + ymk = y - k; + result |= pixelColorNolock (dst, xmh, ypk, color); + result |= pixelColorNolock (dst, xph, ypk, color); + result |= pixelColorNolock (dst, xmh, ymk, color); + result |= pixelColorNolock (dst, xph, ymk, color); + } else { + result |= pixelColorNolock (dst, xmh, y, color); + result |= pixelColorNolock (dst, xph, y, color); + } + ok = k; + xpi = x + i; + xmi = x - i; + if (j > 0) { + ypj = y + j; + ymj = y - j; + result |= pixelColorNolock (dst, xmi, ypj, color); + result |= pixelColorNolock (dst, xpi, ypj, color); + result |= pixelColorNolock (dst, xmi, ymj, color); + result |= pixelColor(dst, xpi, ymj, color); + } else { + result |= pixelColorNolock (dst, xmi, y, color); + result |= pixelColorNolock (dst, xpi, y, color); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if (((oi != i) && (oh != i)) || ((oh != h) && (oi != h) && (i != h))) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + ypi = y + i; + ymi = y - i; + result |= pixelColorNolock (dst, xmj, ypi, color); + result |= pixelColorNolock (dst, xpj, ypi, color); + result |= pixelColorNolock (dst, xmj, ymi, color); + result |= pixelColorNolock (dst, xpj, ymi, color); + } else { + result |= pixelColorNolock (dst, xmj, y, color); + result |= pixelColorNolock (dst, xpj, y, color); + } + oi = i; + xmk = x - k; + xpk = x + k; + if (h > 0) { + yph = y + h; + ymh = y - h; + result |= pixelColorNolock (dst, xmk, yph, color); + result |= pixelColorNolock (dst, xpk, yph, color); + result |= pixelColorNolock (dst, xmk, ymh, color); + result |= pixelColorNolock (dst, xpk, ymh, color); + } else { + result |= pixelColorNolock (dst, xmk, y, color); + result |= pixelColorNolock (dst, xpk, y, color); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + } /* Alpha check */ + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +int ellipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (ellipseColor(dst, x, y, rx, ry, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- AA Ellipse */ + +/* Based on code from Anders Lindstroem, based on code from SGE, based on code from TwinLib */ + +int aaellipseColor(SDL_Surface * dst, Sint16 xc, Sint16 yc, Sint16 rx, Sint16 ry, Uint32 color) +{ + int i; + int a2, b2, ds, dt, dxt, t, s, d; + Sint16 x, y, xs, ys, dyt, xx, yy, xc2, yc2; + float cp; + Uint8 weight, iweight; + int result; + + /* Sanity check radius */ + if (rx < 1) + rx = 1; + if (ry < 1) + ry = 1; + + /* Variable setup */ + a2 = rx * rx; + b2 = ry * ry; + + ds = 2 * a2; + dt = 2 * b2; + + xc2 = 2 * xc; + yc2 = 2 * yc; + + dxt = (int) (a2 / sqrt(a2 + b2)); + + t = 0; + s = -2 * a2 * ry; + d = 0; + + x = xc; + y = yc - ry; + + + /* Draw */ + result = 0; + + /* Lock surface */ + if (SDL_MUSTLOCK(dst)) { + if (SDL_LockSurface(dst) < 0) { + return (-1); + } + } + + /* "End points" */ + result |= pixelColorNolock(dst, x, y, color); + result |= pixelColorNolock(dst, xc2 - x, y, color); + result |= pixelColorNolock(dst, x, yc2 - y, color); + result |= pixelColorNolock(dst, xc2 - x, yc2 - y, color); + + for (i = 1; i <= dxt; i++) { + x--; + d += t - b2; + + if (d >= 0) + ys = y - 1; + else if ((d - s - a2) > 0) { + if ((2 * d - s - a2) >= 0) + ys = y + 1; + else { + ys = y; + y++; + d -= s + a2; + s += ds; + } + } else { + y++; + ys = y + 1; + d -= s + a2; + s += ds; + } + + t -= dt; + + /* Calculate alpha */ + if (s != 0.0) { + cp = (float) abs(d) / (float) abs(s); + if (cp > 1.0) { + cp = 1.0; + } + } else { + cp = 1.0; + } + + /* Calculate weights */ + weight = (Uint8) (cp * 255); + iweight = 255 - weight; + + /* Upper half */ + xx = xc2 - x; + result |= pixelColorWeightNolock(dst, x, y, color, iweight); + result |= pixelColorWeightNolock(dst, xx, y, color, iweight); + + result |= pixelColorWeightNolock(dst, x, ys, color, weight); + result |= pixelColorWeightNolock(dst, xx, ys, color, weight); + + /* Lower half */ + yy = yc2 - y; + result |= pixelColorWeightNolock(dst, x, yy, color, iweight); + result |= pixelColorWeightNolock(dst, xx, yy, color, iweight); + + yy = yc2 - ys; + result |= pixelColorWeightNolock(dst, x, yy, color, weight); + result |= pixelColorWeightNolock(dst, xx, yy, color, weight); + } + + dyt = abs(y - yc); + + for (i = 1; i <= dyt; i++) { + y++; + d -= s + a2; + + if (d <= 0) + xs = x + 1; + else if ((d + t - b2) < 0) { + if ((2 * d + t - b2) <= 0) + xs = x - 1; + else { + xs = x; + x--; + d += t - b2; + t -= dt; + } + } else { + x--; + xs = x - 1; + d += t - b2; + t -= dt; + } + + s += ds; + + /* Calculate alpha */ + if (t != 0.0) { + cp = (float) abs(d) / (float) abs(t); + if (cp > 1.0) { + cp = 1.0; + } + } else { + cp = 1.0; + } + + /* Calculate weight */ + weight = (Uint8) (cp * 255); + iweight = 255 - weight; + + /* Left half */ + xx = xc2 - x; + yy = yc2 - y; + result |= pixelColorWeightNolock(dst, x, y, color, iweight); + result |= pixelColorWeightNolock(dst, xx, y, color, iweight); + + result |= pixelColorWeightNolock(dst, x, yy, color, iweight); + result |= pixelColorWeightNolock(dst, xx, yy, color, iweight); + + /* Right half */ + xx = 2 * xc - xs; + result |= pixelColorWeightNolock(dst, xs, y, color, weight); + result |= pixelColorWeightNolock(dst, xx, y, color, weight); + + result |= pixelColorWeightNolock(dst, xs, yy, color, weight); + result |= pixelColorWeightNolock(dst, xx, yy, color, weight); + + + } + + /* Unlock surface */ + if (SDL_MUSTLOCK(dst)) { + SDL_UnlockSurface(dst); + } + + return (result); +} + +int aaellipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (aaellipseColor + (dst, x, y, rx, ry, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ---- Filled Ellipse */ + +/* Note: */ +/* Based on algorithm from sge library with multiple-hline draw removal */ +/* and other speedup changes. */ + +int filledEllipseColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color) +{ + int result; + Sint16 x1, y1, x2, y2; + int ix, iy; + int h, i, j, k; + int oh, oi, oj, ok; + int xmh, xph; + int xmi, xpi; + int xmj, xpj; + int xmk, xpk; + + /* + * Sanity check radii + */ + if ((rx < 0) || (ry < 0)) { + return (-1); + } + + /* + * Special case for rx=0 - draw a vline + */ + if (rx == 0) { + return (vlineColor(dst, x, y - ry, y + ry, color)); + } + /* + * Special case for ry=0 - draw a hline + */ + if (ry == 0) { + return (hlineColor(dst, x - rx, x + rx, y, color)); + } + + /* + * Test bounding box + */ + x1 = x - rx; + y1 = y - ry; + x2 = x + rx; + y2 = y + ry; + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Init vars + */ + oh = oi = oj = ok = 0xFFFF; + + /* + * Draw + */ + result = 0; + if (rx > ry) { + ix = 0; + iy = rx * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * ry) / rx; + k = (i * ry) / rx; + + if ((ok != k) && (oj != k)) { + xph = x + h; + xmh = x - h; + if (k > 0) { + result |= hlineColor(dst, xmh, xph, y + k, color); + result |= hlineColor(dst, xmh, xph, y - k, color); + } else { + result |= hlineColor(dst, xmh, xph, y, color); + } + ok = k; + } + if ((oj != j) && (ok != j) && (k != j)) { + xmi = x - i; + xpi = x + i; + if (j > 0) { + result |= hlineColor(dst, xmi, xpi, y + j, color); + result |= hlineColor(dst, xmi, xpi, y - j, color); + } else { + result |= hlineColor(dst, xmi, xpi, y, color); + } + oj = j; + } + + ix = ix + iy / rx; + iy = iy - ix / rx; + + } while (i > h); + } else { + ix = 0; + iy = ry * 64; + + do { + h = (ix + 32) >> 6; + i = (iy + 32) >> 6; + j = (h * rx) / ry; + k = (i * rx) / ry; + + if ((oi != i) && (oh != i)) { + xmj = x - j; + xpj = x + j; + if (i > 0) { + result |= hlineColor(dst, xmj, xpj, y + i, color); + result |= hlineColor(dst, xmj, xpj, y - i, color); + } else { + result |= hlineColor(dst, xmj, xpj, y, color); + } + oi = i; + } + if ((oh != h) && (oi != h) && (i != h)) { + xmk = x - k; + xpk = x + k; + if (h > 0) { + result |= hlineColor(dst, xmk, xpk, y + h, color); + result |= hlineColor(dst, xmk, xpk, y - h, color); + } else { + result |= hlineColor(dst, xmk, xpk, y, color); + } + oh = h; + } + + ix = ix + iy / ry; + iy = iy - ix / ry; + + } while (i > h); + } + + return (result); +} + + +int filledEllipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (filledEllipseColor + (dst, x, y, rx, ry, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ----- filled pie */ + +/* Low-speed float pie-calc implementation by drawing polygons. */ + +int filledpieColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, Sint16 start, Sint16 end, Uint32 color) +{ + int result; + Sint16 x1, y1, x2, y2; + double angle, start_angle, end_angle; + double deltaAngle; + double dr; + int posX, posY; + int numpoints, i; + Sint16 *vx, *vy; + + /* + * Sanity check radii + */ + if (rad < 0) { + return (-1); + } + + /* + * Fixup angles + */ + start = start % 360; + end = end % 360; + + /* + * Special case for rad=0 - draw a point + */ + if (rad == 0) { + return (pixelColor(dst, x, y, color)); + } + + /* + * Test bounding box for visibility + */ + x1 = x - rad; + y1 = y - rad; + x2 = x + rad; + y2 = y + rad; + if (!(clipLine(dst, &x1, &y1, &x2, &y2))) { + return (0); + } + + /* + * Variable setup + */ + dr = (double) rad; + deltaAngle = 3.0 / dr; + start_angle = (double) start *(2.0 * M_PI / 360.0); + end_angle = (double) end *(2.0 * M_PI / 360.0); + if (start > end) { + end_angle += (2.0 * M_PI); + } + + /* Count points (rather than calculate it) */ + numpoints = 1; + angle = start_angle; + while (angle <= end_angle) { + angle += deltaAngle; + numpoints++; + } + + /* Check size of array */ + if (numpoints == 1) { + return (pixelColor(dst, x, y, color)); + } else if (numpoints == 2) { + posX = x + (int) (dr * cos(start_angle)); + posY = y + (int) (dr * sin(start_angle)); + return (lineColor(dst, x, y, posX, posY, color)); + } + + /* Allocate vertex array */ + vx = vy = (Sint16 *) malloc(2 * sizeof(Sint16) * numpoints); + if (vx == NULL) { + return (-1); + } + vy += numpoints; + + /* Center */ + vx[0] = x; + vy[0] = y; + + /* Calculate and store vertices */ + i = 1; + angle = start_angle; + while (angle <= end_angle) { + vx[i] = x + (int) (dr * cos(angle)); + vy[i] = y + (int) (dr * sin(angle)); + angle += deltaAngle; + i++; + } + + /* Draw */ + result = filledPolygonColor(dst, vx, vy, numpoints, color); + + /* Free vertex */ + free(vx); + + return (result); +} + +int filledpieRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + return (filledpieColor(dst, x, y, rad, start, end, + ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); + +} + +/* ---- Polygon */ + +int polygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint32 color) +{ + int result; + int i; + Sint16 *x1, *y1, *x2, *y2; + + /* + * Sanity check + */ + if (n < 3) { + return (-1); + } + + /* + * Pointer setup + */ + x1 = x2 = vx; + y1 = y2 = vy; + x2++; + y2++; + + /* + * Draw + */ + result = 0; + for (i = 1; i < n; i++) { + result |= lineColor(dst, *x1, *y1, *x2, *y2, color); + x1 = x2; + y1 = y2; + x2++; + y2++; + } + result |= lineColor(dst, *x1, *y1, *vx, *vy, color); + + return (result); +} + +int polygonRGBA(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (polygonColor(dst, vx, vy, n, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ---- AA-Polygon */ + +int aapolygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint32 color) +{ + int result; + int i; + Sint16 *x1, *y1, *x2, *y2; + + /* + * Sanity check + */ + if (n < 3) { + return (-1); + } + + /* + * Pointer setup + */ + x1 = x2 = vx; + y1 = y2 = vy; + x2++; + y2++; + + /* + * Draw + */ + result = 0; + for (i = 1; i < n; i++) { + result |= aalineColorInt(dst, *x1, *y1, *x2, *y2, color, 0); + x1 = x2; + y1 = y2; + x2++; + y2++; + } + result |= aalineColorInt(dst, *x1, *y1, *vx, *vy, color, 0); + + return (result); +} + +int aapolygonRGBA(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (aapolygonColor(dst, vx, vy, n, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +/* ---- Filled Polygon */ + +int gfxPrimitivesCompareInt(const void *a, const void *b); + +static int *gfxPrimitivesPolyInts = NULL; +static int gfxPrimitivesPolyAllocated = 0; + +int filledPolygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, int color) +{ + int result; + int i; + int y; + int miny, maxy; + int x1, y1; + int x2, y2; + int ind1, ind2; + int ints; + + /* + * Sanity check + */ + if (n < 3) { + return -1; + } + + /* + * Allocate temp array, only grow array + */ + if (!gfxPrimitivesPolyAllocated) { + gfxPrimitivesPolyInts = (int *) malloc(sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } else { + if (gfxPrimitivesPolyAllocated < n) { + gfxPrimitivesPolyInts = (int *) realloc(gfxPrimitivesPolyInts, sizeof(int) * n); + gfxPrimitivesPolyAllocated = n; + } + } + + /* + * Determine Y maxima + */ + miny = vy[0]; + maxy = vy[0]; + for (i = 1; (i < n); i++) { + if (vy[i] < miny) { + miny = vy[i]; + } else if (vy[i] > maxy) { + maxy = vy[i]; + } + } + + /* + * Draw, scanning y + */ + result = 0; + for (y = miny; (y <= maxy); y++) { + ints = 0; + for (i = 0; (i < n); i++) { + if (!i) { + ind1 = n - 1; + ind2 = 0; + } else { + ind1 = i - 1; + ind2 = i; + } + y1 = vy[ind1]; + y2 = vy[ind2]; + if (y1 < y2) { + x1 = vx[ind1]; + x2 = vx[ind2]; + } else if (y1 > y2) { + y2 = vy[ind1]; + y1 = vy[ind2]; + x2 = vx[ind1]; + x1 = vx[ind2]; + } else { + continue; + } + if ((y >= y1) && (y < y2)) { + gfxPrimitivesPolyInts[ints++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1; + } else if ((y == maxy) && (y > y1) && (y <= y2)) { + gfxPrimitivesPolyInts[ints++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1; + } + } + qsort(gfxPrimitivesPolyInts, ints, sizeof(int), gfxPrimitivesCompareInt); + + for (i = 0; (i < ints); i += 2) { + result |= hlineColor(dst, gfxPrimitivesPolyInts[i], gfxPrimitivesPolyInts[i + 1], y, color); + } + } + + return (result); +} + +int filledPolygonRGBA(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (filledPolygonColor + (dst, vx, vy, n, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +int gfxPrimitivesCompareInt(const void *a, const void *b) +{ + return (*(const int *) a) - (*(const int *) b); +} + +/* ---- Character (8x8 internal font) */ + +static SDL_Surface *gfxPrimitivesFont[256]; +static Uint32 gfxPrimitivesFontColor[256]; + +int characterColor(SDL_Surface * dst, Sint16 x, Sint16 y, char c, Uint32 color) +{ + SDL_Rect srect; + SDL_Rect drect; + int result; + int ix, iy, k; + unsigned char *charpos; + unsigned char bits[8] = { 128, 64, 32, 16, 8, 4, 2, 1 }; + unsigned char *bitpos; + Uint8 *curpos; + int forced_redraw; + + /* + * Setup source rectangle for 8x8 bitmap + */ + srect.x = 0; + srect.y = 0; + srect.w = 8; + srect.h = 8; + + /* + * Setup destination rectangle for 8x8 bitmap + */ + drect.x = x; + drect.y = y; + drect.w = 8; + drect.h = 8; + + /* + * Create new 8x8 bitmap surface if not already present + */ + if (gfxPrimitivesFont[(unsigned char) c] == NULL) { + gfxPrimitivesFont[(unsigned char) c] = + SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_HWSURFACE | SDL_SRCALPHA, 8, 8, + 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + /* + * Check pointer + */ + if (gfxPrimitivesFont[(unsigned char) c] == NULL) { + return (-1); + } + /* + * Definitely redraw + */ + forced_redraw = 1; + } else { + forced_redraw = 0; + } + + /* + * Check if color has changed + */ + if ((gfxPrimitivesFontColor[(unsigned char) c] != color) || (forced_redraw)) { + /* + * Redraw character + */ + SDL_SetAlpha(gfxPrimitivesFont[(unsigned char) c], SDL_SRCALPHA, 255); + gfxPrimitivesFontColor[(unsigned char) c] = color; + + /* + * Variable setup + */ + k = (unsigned char) c; + k *= 8; + charpos = gfxPrimitivesFontdata; + charpos += k; + + /* + * Clear bitmap + */ + curpos = (Uint8 *) gfxPrimitivesFont[(unsigned char) c]->pixels; + memset(curpos, 0, 8 * 8 * 4); + + /* + * Drawing loop + */ + for (iy = 0; iy < 8; iy++) { + bitpos = bits; + for (ix = 0; ix < 8; ix++) { + if ((*charpos & *bitpos) == *bitpos) { + memcpy(curpos, &color, 4); + } + bitpos++; + curpos += 4;; + } + charpos++; + } + } + + /* + * Draw bitmap onto destination surface + */ + result = SDL_BlitSurface(gfxPrimitivesFont[(unsigned char) c], &srect, dst, &drect); + + return (result); +} + +int characterRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (characterColor(dst, x, y, c, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} + +int stringColor(SDL_Surface * dst, Sint16 x, Sint16 y, char *c, Uint32 color) +{ + int result; + int i, length; + char *curchar; + int curx; + + length = strlen(c); + curchar = c; + curx = x; + result = 0; + for (i = 0; i < length; i++) { + result |= characterColor(dst, curx, y, *curchar, color); + curchar++; + curx += 8; + } + + return (result); +} + +int stringRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, char *c, Uint8 r, Uint8 g, Uint8 b, Uint8 a) +{ + /* + * Draw + */ + return (stringColor(dst, x, y, c, ((Uint32) r << 24) | ((Uint32) g << 16) | ((Uint32) b << 8) | (Uint32) a)); +} diff --git a/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.h b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.h new file mode 100644 index 000000000..955375c65 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives.h @@ -0,0 +1,143 @@ + +/* + + SDL_gfxPrimitives: graphics primitives for SDL + + LGPL (c) A. Schiffler + +*/ + +#ifndef _SDL_gfxPrimitives_h +#define _SDL_gfxPrimitives_h + +#include +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +#include "SDL.h" + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* ----- Versioning */ + +#define SDL_GFXPRIMITIVES_MAJOR 1 +#define SDL_GFXPRIMITIVES_MINOR 5 + +/* ----- Prototypes */ + +/* Note: all ___Color routines expect the color to be in format 0xRRGGBBAA */ + +/* Pixel */ + + int pixelColor(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color); + int pixelRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Horizontal line */ + + int hlineColor(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color); + int hlineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Vertical line */ + + int vlineColor(SDL_Surface * dst, Sint16 x, Sint16 y1, Sint16 y2, Uint32 color); + int vlineRGBA(SDL_Surface * dst, Sint16 x, Sint16 y1, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Rectangle */ + + int rectangleColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color); + int rectangleRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, + Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Filled rectangle (Box) */ + + int boxColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color); + int boxRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, + Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Line */ + + int lineColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color); + int lineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, + Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* AA Line */ + int aalineColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color); + int aalineRGBA(SDL_Surface * dst, Sint16 x1, Sint16 y1, + Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Circle */ + + int circleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color); + int circleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* AA Circle */ + + int aacircleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color); + int aacircleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, + Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Filled Circle */ + + int filledCircleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color); + int filledCircleRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, + Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Ellipse */ + + int ellipseColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color); + int ellipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, + Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* AA Ellipse */ + + int aaellipseColor(SDL_Surface * dst, Sint16 xc, Sint16 yc, Sint16 rx, Sint16 ry, Uint32 color); + int aaellipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, + Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Filled Ellipse */ + + int filledEllipseColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint32 color); + int filledEllipseRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, + Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a); +/* Filled Pie */ + + int filledpieColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint32 color); + int filledpieRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 rad, + Sint16 start, Sint16 end, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Polygon */ + + int polygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint32 color); + int polygonRGBA(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, + int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* AA-Polygon */ + + int aapolygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, Uint32 color); + int aapolygonRGBA(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, + int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Filled Polygon */ + + int filledPolygonColor(SDL_Surface * dst, Sint16 * vx, Sint16 * vy, int n, int color); + int filledPolygonRGBA(SDL_Surface * dst, Sint16 * vx, + Sint16 * vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* 8x8 Characters/Strings */ + + int characterColor(SDL_Surface * dst, Sint16 x, Sint16 y, char c, Uint32 color); + int characterRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, char c, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + int stringColor(SDL_Surface * dst, Sint16 x, Sint16 y, char *c, Uint32 color); + int stringRGBA(SDL_Surface * dst, Sint16 x, Sint16 y, char *c, Uint8 r, Uint8 g, Uint8 b, Uint8 a); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +}; +#endif + +#endif /* _SDL_gfxPrimitives_h */ diff --git a/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives_font.h b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives_font.h new file mode 100644 index 000000000..9dde03250 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/SDL_gfxPrimitives_font.h @@ -0,0 +1,3082 @@ + +/* ---- 8x8 font definition ---- */ + +/* LGPL (c) A. Schiffler */ + +#define GFX_FONTDATAMAX (8*256) + +static unsigned char gfxPrimitivesFontdata[GFX_FONTDATAMAX] = { + + /* + * 0 0x00 '^@' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 1 0x01 '^A' + */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + + /* + * 2 0x02 '^B' + */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + + /* + * 3 0x03 '^C' + */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* + * 4 0x04 '^D' + */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* + * 5 0x05 '^E' + */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* + * 6 0x06 '^F' + */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* + * 7 0x07 '^G' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 8 0x08 '^H' + */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* + * 9 0x09 '^I' + */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 10 0x0a '^J' + */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + + /* + * 11 0x0b '^K' + */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + + /* + * 12 0x0c '^L' + */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + + /* + * 13 0x0d '^M' + */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + + /* + * 14 0x0e '^N' + */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + + /* + * 15 0x0f '^O' + */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + + /* + * 16 0x10 '^P' + */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* + * 17 0x11 '^Q' + */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* + * 18 0x12 '^R' + */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + + /* + * 19 0x13 '^S' + */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* + * 20 0x14 '^T' + */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + + /* + * 21 0x15 '^U' + */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + + /* + * 22 0x16 '^V' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 23 0x17 '^W' + */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + + /* + * 24 0x18 '^X' + */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 25 0x19 '^Y' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 26 0x1a '^Z' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 27 0x1b '^[' + */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 28 0x1c '^\' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 29 0x1d '^]' + */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 30 0x1e '^^' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 31 0x1f '^_' + */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 32 0x20 ' ' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 33 0x21 '!' + */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 34 0x22 '"' + */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 35 0x23 '#' + */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* + * 36 0x24 '$' + */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x60, /* 01100000 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 37 0x25 '%' + */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 38 0x26 '&' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 39 0x27 ''' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 40 0x28 '(' + */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* + * 41 0x29 ')' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* + * 42 0x2a '*' + */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 43 0x2b '+' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 44 0x2c ',' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* + * 45 0x2d '-' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 46 0x2e '.' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 47 0x2f '/' + */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* + * 48 0x30 '0' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* + * 49 0x31 '1' + */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 50 0x32 '2' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 51 0x33 '3' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 52 0x34 '4' + */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* + * 53 0x35 '5' + */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 54 0x36 '6' + */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 55 0x37 '7' + */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* + * 56 0x38 '8' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 57 0x39 '9' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* + * 58 0x3a ':' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 59 0x3b ';' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* + * 60 0x3c '<' + */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + + /* + * 61 0x3d '=' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 62 0x3e '>' + */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* + * 63 0x3f '?' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 64 0x40 '@' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* + * 65 0x41 'A' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 66 0x42 'B' + */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* + * 67 0x43 'C' + */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 68 0x44 'D' + */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* + * 69 0x45 'E' + */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 70 0x46 'F' + */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* + * 71 0x47 'G' + */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + + /* + * 72 0x48 'H' + */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 73 0x49 'I' + */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 74 0x4a 'J' + */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* + * 75 0x4b 'K' + */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* + * 76 0x4c 'L' + */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 77 0x4d 'M' + */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 78 0x4e 'N' + */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 79 0x4f 'O' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 80 0x50 'P' + */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* + * 81 0x51 'Q' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xce, /* 11001110 */ + 0x7c, /* 01111100 */ + 0x0e, /* 00001110 */ + + /* + * 82 0x52 'R' + */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* + * 83 0x53 'S' + */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 84 0x54 'T' + */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 85 0x55 'U' + */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 86 0x56 'V' + */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* + * 87 0x57 'W' + */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* + * 88 0x58 'X' + */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 89 0x59 'Y' + */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 90 0x5a 'Z' + */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x8c, /* 10001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 91 0x5b '[' + */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 92 0x5c '\' + */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* + * 93 0x5d ']' + */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 94 0x5e '^' + */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 95 0x5f '_' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + + /* + * 96 0x60 '`' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 97 0x61 'a' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 98 0x62 'b' + */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + + /* + * 99 0x63 'c' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 100 0x64 'd' + */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 101 0x65 'e' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 102 0x66 'f' + */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf8, /* 11111000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* + * 103 0x67 'g' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + + /* + * 104 0x68 'h' + */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* + * 105 0x69 'i' + */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 106 0x6a 'j' + */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + + /* + * 107 0x6b 'k' + */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* + * 108 0x6c 'l' + */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 109 0x6d 'm' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0x00, /* 00000000 */ + + /* + * 110 0x6e 'n' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* + * 111 0x6f 'o' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 112 0x70 'p' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + + /* + * 113 0x71 'q' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + + /* + * 114 0x72 'r' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* + * 115 0x73 's' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* + * 116 0x74 't' + */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* + * 117 0x75 'u' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 118 0x76 'v' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* + * 119 0x77 'w' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* + * 120 0x78 'x' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 121 0x79 'y' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* + * 122 0x7a 'z' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x4c, /* 01001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 123 0x7b '{' + */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* + * 124 0x7c '|' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 125 0x7d '}' + */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* + * 126 0x7e '~' + */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 127 0x7f '' + */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 128 0x80 '' + */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + + /* + * 129 0x81 '' + */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 130 0x82 '' + */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 131 0x83 '' + */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 132 0x84 '' + */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 133 0x85 '' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 134 0x86 '' + */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 135 0x87 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + + /* + * 136 0x88 '' + */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 137 0x89 '' + */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 138 0x8a '' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 139 0x8b '' + */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 140 0x8c '' + */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 141 0x8d '' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 142 0x8e '' + */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 143 0x8f '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 144 0x90 '' + */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 145 0x91 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 146 0x92 '' + */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* + * 147 0x93 '' + */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 148 0x94 '' + */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 149 0x95 '' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 150 0x96 '' + */ + 0x78, /* 01111000 */ + 0x84, /* 10000100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 151 0x97 '' + */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 152 0x98 '' + */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* + * 153 0x99 '' + */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* + * 154 0x9a '' + */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 155 0x9b '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 156 0x9c '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* + * 157 0x9d '' + */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 158 0x9e '' + */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfa, /* 11111010 */ + 0xc6, /* 11000110 */ + 0xcf, /* 11001111 */ + 0xc6, /* 11000110 */ + 0xc7, /* 11000111 */ + + /* + * 159 0x9f '' + */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* + * 160 0xa0 '' + */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 161 0xa1 '' + */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 162 0xa2 '' + */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* + * 163 0xa3 '' + */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 164 0xa4 '' + */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* + * 165 0xa5 '' + */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* + * 166 0xa6 '' + */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 167 0xa7 '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 168 0xa8 '' + */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x63, /* 01100011 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + + /* + * 169 0xa9 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 170 0xaa '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 171 0xab '' + */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7e, /* 01111110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x0f, /* 00001111 */ + + /* + * 172 0xac '' + */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7a, /* 01111010 */ + 0x36, /* 00110110 */ + 0x6a, /* 01101010 */ + 0xdf, /* 11011111 */ + 0x06, /* 00000110 */ + + /* + * 173 0xad '' + */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 174 0xae '' + */ + 0x00, /* 00000000 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 175 0xaf '' + */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 176 0xb0 '' + */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + + /* + * 177 0xb1 '' + */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* + * 178 0xb2 '' + */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + + /* + * 179 0xb3 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 180 0xb4 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 181 0xb5 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 182 0xb6 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 183 0xb7 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 184 0xb8 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 185 0xb9 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 186 0xba '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 187 0xbb '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 188 0xbc '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 189 0xbd '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 190 0xbe '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 191 0xbf '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 192 0xc0 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 193 0xc1 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 194 0xc2 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 195 0xc3 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 196 0xc4 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 197 0xc5 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 198 0xc6 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 199 0xc7 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 200 0xc8 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 201 0xc9 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 202 0xca '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 203 0xcb '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 204 0xcc '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 205 0xcd '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 206 0xce '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 207 0xcf '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 208 0xd0 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 209 0xd1 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 210 0xd2 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 211 0xd3 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 212 0xd4 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 213 0xd5 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 214 0xd6 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 215 0xd7 '' + */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* + * 216 0xd8 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 217 0xd9 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 218 0xda '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 219 0xdb '' + */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* + * 220 0xdc '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* + * 221 0xdd '' + */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* + * 222 0xde '' + */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* + * 223 0xdf '' + */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 224 0xe0 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xc8, /* 11001000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* + * 225 0xe1 '' + */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* + * 226 0xe2 '' + */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* + * 227 0xe3 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* + * 228 0xe4 '' + */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* + * 229 0xe5 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* + * 230 0xe6 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0xc0, /* 11000000 */ + + /* + * 231 0xe7 '' + */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* + * 232 0xe8 '' + */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + + /* + * 233 0xe9 '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* + * 234 0xea '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + + /* + * 235 0xeb '' + */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* + * 236 0xec '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 237 0xed '' + */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + + /* + * 238 0xee '' + */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* + * 239 0xef '' + */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* + * 240 0xf0 '' + */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 241 0xf1 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 242 0xf2 '' + */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 243 0xf3 '' + */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* + * 244 0xf4 '' + */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* + * 245 0xf5 '' + */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + + /* + * 246 0xf6 '' + */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 247 0xf7 '' + */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 248 0xf8 '' + */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 249 0xf9 '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 250 0xfa '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 251 0xfb '' + */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + + /* + * 252 0xfc '' + */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 253 0xfd '' + */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 254 0xfe '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* + * 255 0xff '' + */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; diff --git a/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.c b/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.c new file mode 100644 index 000000000..132b4d4f8 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.c @@ -0,0 +1,1223 @@ +/* + + SDL_rotozoom.c - rotozoomer for 32bit or 8bit surfaces + + LGPL (c) A. Schiffler + + - Better +*/ + +#ifdef WIN32 +#include +#endif + +#include +#include +#include + +#include "SDL_rotozoom.h" + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#ifdef _MSC_VER +#define INLINE __inline +#else +#define INLINE inline +#endif + +INLINE int clamp (int x, int xmin, int xmax) +{ + if (x < xmin) + return xmin; + else if (x > xmax) + return xmax; + return x; +} + +INLINE double sinc(double x) +{ + if (x == 0) + return 1; + x *= M_PI; + return sin(x)/x; +} + +INLINE float lanczos2(float x) +{ +/* return sinc(x); */ + if (x < -2 || x > 2) + return 0; + else + return sinc(x) * sinc(x/2); +/* sinc_table [(int)((x+4)*100. )] * sinc_table[(int)((x/2+4)*100.)]; */ +} + +INLINE float lanczos3(float x) +{ +/* return sinc(x); */ + if (x < -3 || x > 3) + return 0; + else + return sinc(x) * sinc(x/3); +} + +/* inline float lanczos4(float x) */ +/* { */ +/* /\* return sinc(x); *\/ */ +/* if (x < -4 || x > 4) */ +/* return 0; */ +/* else */ +/* return sinc_table [(int)((x+4)*100. )] * sinc_table[(int)((x/4+4)*100.)]; */ +/* } */ + +/* static void */ +/* resample (Uint8 *s, int slen, int spitch, */ +/* Uint8 *d, int dlen, int dpitch) */ +/* { */ +/* float f = (float) slen / dlen; */ + +/* int i,j; */ +/* Uint8 *sp; */ +/* float r, g, b, a; */ +/* int support = 2; */ + +/* for (i=0; iw / dst->w; + float dx = MAX(factor, 1.0); + float *F = (float*) malloc(sizeof(float)*(2*support+1)); + + for (x=0; xw; ++x) + { + float c = 0; + float center = (x+0.5)*factor; /* kernel center in the source image */ + int start = (int) MAX(0, center-support + 0.5); + int stop = (int) MIN(src->w, center+support+0.5); + int n = stop-start; + + for (j=0; jpixels + x*4; + for (y=0; yh; ++y) { + sp = (Uint8*)src->pixels + y*src->pitch + start*4; + r = g = b = a = 0; + for (j=0; jpitch; + } + } + free(F); +} + +static void +resample_vert (SDL_Surface *src, SDL_Surface *dst, FilterFunc filter, int support) +{ + int x, y; + int i,j; + Uint8 *sp, *dp; + int r, g, b, a; + float factor = (float) src->h / dst->h; + float dx = MAX(factor, 1.0); + float *F = (float*) malloc(sizeof(float)*(2*support+1)); + + for (y=0; yh; ++y) + { + float c = 0; + float center = (y+0.5)*factor; /* kernel center in the source image */ + int start = (int) MAX(0, center-support + 0.5); + int stop = (int) MIN(src->h, center+support+0.5); + int n = stop-start; + + for (j=0; jpixels + y*dst->pitch; + for (x=0; xw; ++x) + { + sp = (Uint8*)src->pixels + start*src->pitch + x*4; + r = g = b = a = 0; + for (j=0; jpitch; + } + dp[0] = (Uint8) clamp (r, 0, 255); + dp[1] = (Uint8) clamp (g, 0, 255); + dp[2] = (Uint8) clamp (b, 0, 255); + dp[3] = (Uint8) clamp (a, 0, 255); + dp += 4; +/* dp += src->pitch; */ + } + } + free(F); +} + +/* FIXME: Can currently only shrink images!!!! */ + +static void zoomLanczos (SDL_Surface *src, SDL_Surface *dst) +{ +#if 1 + resample_horiz(src, dst, lanczos2, 2); + resample_vert(src, dst, lanczos2, 2); +#else + resample_horiz(src, dst, lanczos3, 3); + resample_vert(src, dst, lanczos3, 3); +#endif +} + +/* + + 32bit Zoomer with optional anti-aliasing by bilinear interpolation. + + Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth) +{ + if (!smooth) { + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey; + tColorRGBA *sp, *csp, *dp; + int sgap, dgap; + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + + /* + * Allocate memory for row increments + */ + if ((sax = (int *) malloc((dst->w + smooth) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (int *) malloc((dst->h + smooth) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + for (x = 0; x < dst->w + smooth; x++) { + csax[x] = csx; + csx &= 0xffff; + csx += sx; + if ((csx >> 16) >= src->w) + csx -= sx; + } + csy = 0; + csay = say; + for (y = 0; y < dst->h + smooth; y++) { + csay[y] = csy; + csy &= 0xffff; + csy += sy; + if ((csy >> 16) >= src->h) + csy -= sy; + } + + /* + * Pointer setup + */ + sp = csp = (tColorRGBA *) src->pixels; + dp = (tColorRGBA *) dst->pixels; + sgap = src->pitch - src->w * 4; + dgap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + csay = say; + for (y = 0; y < dst->h; y++) { + sp = csp; + csax = sax; + for (x = 0; x < dst->w; x++, dp++) { + *dp = *sp; + csax++; + sp += (*csax >> 16); + } + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + + free(sax); + free(say); + return (0); + } + else if (dst->w < src->w && dst->h <=src->h) { + zoomLanczos (src, dst); + } + else + { + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep; + tColorRGBA *sp, *csp, *dp; + int sgap, dgap; + + /* + * Variable setup + */ + if (smooth) { + /* + * For interpolation: assume source dimension is `smooth' + * pixels smaller to avoid overflow on right and bottom edge. + */ + smooth = 1; + if (src->w > dst->w) // if the image gets shrinked -> use stronger blur : + smooth = (int)((double)(src->w)/dst->w+0.5); + + sx = (int) (65536.0 * (float) (src->w) / (float) dst->w); + sy = (int) (65536.0 * (float) (src->h) / (float) dst->h); + } else { + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + } + + /* + * Allocate memory for row increments + */ + if ((sax = (int *) malloc((dst->w + smooth) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (int *) malloc((dst->h + smooth) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + int sum = 0; + for (x = 0; x < dst->w + smooth; x++) { + csax[x] = csx; + csx &= 0xffff; + csx += sx; + sum += sx; + if ((sum >> 16) >= src->w - smooth) + csx -= sx; + } + csy = 0; + csay = say; + sum = 0; + for (y = 0; y < dst->h + smooth; y++) { + csay[y] = csy; + csy &= 0xffff; + csy += sy; + sum += sy; + if ((sum >> 16) >= src->h - smooth) + csy -= sy; + } + + /* + * Pointer setup + */ + sp = csp = (tColorRGBA *) src->pixels; + dp = (tColorRGBA *) dst->pixels; + sgap = src->pitch - src->w * 4; + dgap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + csay = say; + if (smooth == 1) { + /* Interpolating Zoom */ + for (y = 0; y < dst->h; y++) { + /* Setup color source pointers */ + tColorRGBA *c00, *c01, *c10, *c11; + + c00 = csp; + c01 = csp+1; + c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch); + c11 = c10+1; + csax = sax; + for (x = 0; x < dst->w; x++, dp++) { + + /* Interpolate colors */ + ex = *csax & 0xffff; + ey = *csay & 0xffff; + t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; + t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; + dp->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; + t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; + dp->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; + t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; + dp->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; + t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; + dp->a = (((t2 - t1) * ey) >> 16) + t1; + + /* Advance source pointers */ + csax++; + sstep = (*csax >> 16); + c00 += sstep; + c01 += sstep; + c10 += sstep; + c11 += sstep; + } + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + } + else if (smooth > 1) { + // stronger blur for image shrinking + for (y = 0; y < dst->h; y++) { + tColorRGBA *c = csp; + int smooth2 = smooth*smooth; + csax = sax; + + for (x = 0; x < dst->w; x++, dp++) { + int xo, yo; + tColorRGBA *co = c; + int r = 0, g = 0, b = 0, a = 0; + + for (yo = 0; yo= csp); + r += co->r; + g += co->g; + b += co->b; + a += co->a; + co++; + } + co = (tColorRGBA *) ((Uint8 *) co + src->pitch); + co -= smooth; + } + + dp->r = r/smooth2; + dp->g = g/smooth2; + dp->b = b/smooth2; + dp->a = a/smooth2; + + csax++; + sstep = (*csax >> 16); + c += sstep; + } + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + } + else { /* Non-Interpolating Zoom */ + for (y = 0; y < dst->h; y++) { + sp = csp; + csax = sax; + for (x = 0; x < dst->w; x++, dp++) { + *dp = *sp; + csax++; + sp += (*csax >> 16); + } + csay++; + csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + + } + + free(sax); + free(say); + return (0); + } + return 0; +} + +/* + + 8bit Zoomer without smoothing. + + Zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst) +{ + Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy; + Uint8 *sp, *dp, *csp; + int dgap; + + /* + * Variable setup + */ + sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w); + sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h); + + /* + * Allocate memory for row increments + */ + if ((sax = (Uint32 *) malloc(dst->w * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (Uint32 *) malloc(dst->h * sizeof(Uint32))) == NULL) { + if (sax != NULL) { + free(sax); + } + return (-1); + } + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + for (x = 0; x < dst->w; x++) { + csx += sx; + *csax = (csx >> 16); + csx &= 0xffff; + csax++; + } + csy = 0; + csay = say; + for (y = 0; y < dst->h; y++) { + csy += sy; + *csay = (csy >> 16); + csy &= 0xffff; + csay++; + } + + csx = 0; + csax = sax; + for (x = 0; x < dst->w; x++) { + csx += (*csax); + csax++; + } + csy = 0; + csay = say; + for (y = 0; y < dst->h; y++) { + csy += (*csay); + csay++; + } + + /* + * Pointer setup + */ + sp = csp = (Uint8 *) src->pixels; + dp = (Uint8 *) dst->pixels; + dgap = dst->pitch - dst->w; + + /* + * Draw + */ + csay = say; + for (y = 0; y < dst->h; y++) { + csax = sax; + sp = csp; + for (x = 0; x < dst->w; x++) { + /* + * Draw + */ + *dp = *sp; + /* + * Advance source pointers + */ + sp += (*csax); + csax++; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer (for row) + */ + csp += ((*csay) * src->pitch); + csay++; + /* + * Advance destination pointers + */ + dp += dgap; + } + + /* + * Remove temp arrays + */ + free(sax); + free(say); + + return (0); +} + +/* + + 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation. + + Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int smooth) +{ + int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; + tColorRGBA c00, c01, c10, c11; + tColorRGBA *pc, *sp, *spb; + int gap; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + if (smooth) { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (sdx >> 16); + dy = (sdy >> 16); + if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) { + if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + sp += 1; + c01 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + sp -= 1; + c10 = *sp; + sp += 1; + c11 = *sp; + } else if ((dx == sw) && (dy == sh)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == -1) && (dy == -1)) { + sp = (tColorRGBA *) (src->pixels); + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == -1) && (dy == sh)) { + sp = (tColorRGBA *) (src->pixels); + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if ((dx == sw) && (dy == -1)) { + sp = (tColorRGBA *) (src->pixels); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } else if (dx == -1) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + c00 = *sp; + c01 = *sp; + c10 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + c11 = *sp; + } else if (dy == -1) { + sp = (tColorRGBA *) (src->pixels); + sp += dx; + c00 = *sp; + c01 = *sp; + c10 = *sp; + sp += 1; + c11 = *sp; + } else if (dx == sw) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + c01 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + c10 = *sp; + c11 = *sp; + } else if (dy == sh) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + sp += 1; + c01 = *sp; + c10 = *sp; + c11 = *sp; + } + /* + * Interpolate colors + */ + ex = (sdx & 0xffff); + ey = (sdy & 0xffff); + t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; + t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; + pc->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; + t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; + pc->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; + t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; + pc->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; + t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; + pc->a = (((t2 - t1) * ey) >> 16) + t1; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } else { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } +} + +/* + + 8bit Rotozoomer without smoothing + + Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos) +{ + int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh; + tColorY *pc, *sp; + int gap; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w; + /* + * Clear surface to colorkey + */ + memset(pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h); + /* + * Iterate through destination surface + */ + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorY *) (src->pixels); + sp += (src->pitch * dy + dx); + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc += gap; + } +} + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + + +/* Local rotozoom-size function with trig result return */ + +void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight, + double *canglezoom, double *sanglezoom) +{ + double x, y, cx, cy, sx, sy; + double radangle; + int dstwidthhalf, dstheighthalf; + + /* + * Determine destination width and height by rotating a centered source box + */ + radangle = angle * (M_PI / 180.0); + *sanglezoom = sin(radangle); + *canglezoom = cos(radangle); + *sanglezoom *= zoom; + *canglezoom *= zoom; + x = width / 2; + y = height / 2; + cx = *canglezoom * x; + cy = *canglezoom * y; + sx = *sanglezoom * x; + sy = *sanglezoom * y; + dstwidthhalf = MAX((int) + ceil(MAX(MAX(MAX(fabs(cx + sy), fabs(cx - sy)), fabs(-cx + sy)), fabs(-cx - sy))), 1); + dstheighthalf = MAX((int) + ceil(MAX(MAX(MAX(fabs(sx + cy), fabs(sx - cy)), fabs(-sx + cy)), fabs(-sx - cy))), 1); + *dstwidth = 2 * dstwidthhalf; + *dstheight = 2 * dstheighthalf; +} + + +/* Publically available rotozoom-size function */ + +void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight) +{ + double dummy_sanglezoom, dummy_canglezoom; + + rotozoomSurfaceSizeTrig(width, height, angle, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom); +} + + +/* Publically available rotozoom function */ + +SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + double zoominv; + double sanglezoom, canglezoom, sanglezoominv, canglezoominv; + int dstwidthhalf, dstwidth, dstheighthalf, dstheight; + double x, y, cx, cy; + int is32bit; + int i, src_converted; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + /* + * Sanity check zoom factor + */ + if (zoom < VALUE_LIMIT) { + zoom = VALUE_LIMIT; + } + zoominv = 65536.0 / (zoom * zoom); + + /* + * Check if we have a rotozoom or just a zoom + */ + if (fabs(angle) > VALUE_LIMIT) { + + /* + * Angle!=0: full rotozoom + */ + /* + * ----------------------- + */ + + /* Determine target size */ + rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoom, &dstwidth, &dstheight, &canglezoom, &sanglezoom); + + /* + * Calculate target factors from sin/cos and zoom + */ + sanglezoominv = sanglezoom; + canglezoominv = canglezoom; + sanglezoominv *= zoominv; + canglezoominv *= zoominv; + + /* Calculate half size */ + dstwidthhalf = dstwidth / 2; + dstheighthalf = dstheight / 2; + + /* + * Alloc space to completely contain the rotated surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the rotation (using alpha) + */ + transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv), smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the rotation + */ + transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv)); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + } else { + + /* + * Angle=0: Just a zoom + */ + /* + * -------------------- + */ + + /* + * Calculate target size + */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoom, zoom, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + } + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + +void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight) +{ + /* + * Sanity check zoom factors + */ + if (zoomx < VALUE_LIMIT) { + zoomx = VALUE_LIMIT; + } + if (zoomy < VALUE_LIMIT) { + zoomy = VALUE_LIMIT; + } + + /* + * Calculate target size + */ + *dstwidth = (int) ((double) width * zoomx); + *dstheight = (int) ((double) height * zoomy); + if (*dstwidth < 1) { + *dstwidth = 1; + } + if (*dstheight < 1) { + *dstheight = 1; + } +} + +SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + int dstwidth, dstheight; + int is32bit; + int i, src_converted; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + /* Get size if target */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} diff --git a/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.h b/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.h new file mode 100644 index 000000000..0a17c3f42 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/SDL_rotozoom.h @@ -0,0 +1,88 @@ + +/* + + SDL_rotozoom - rotozoomer + + LGPL (c) A. Schiffler + +*/ + +#ifndef _SDL_rotozoom_h +#define _SDL_rotozoom_h + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +#ifndef M_PI +#define M_PI 3.141592654 +#endif +#include "SDL.h" + + +/* ---- Defines */ + +#define SMOOTHING_OFF 0 +#define SMOOTHING_ON 1 + +/* ---- Structures */ + + typedef struct tColorRGBA { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 a; + } tColorRGBA; + + typedef struct tColorY { + Uint8 y; + } tColorY; + + +/* ---- Prototypes */ + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + + SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth); + + +/* Returns the size of the target surface for a rotozoomSurface() call */ + + void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, + int *dstheight); + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + + SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth); + +/* Returns the size of the target surface for a zoomSurface() call */ + + void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +}; +#endif + +#endif /* _SDL_rotozoom_h */ diff --git a/project/jni/application/enigma/lib-src/enigma-core/ecl.hh b/project/jni/application/enigma/lib-src/enigma-core/ecl.hh new file mode 100644 index 000000000..69b572ccb --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/ecl.hh @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ECL_HH +#define ECL_HH + +#include "ecl_array2.hh" +#include "ecl_buffer.hh" +#include "ecl_cache.hh" +#include "ecl_callback.hh" +#include "ecl_font.hh" +#include "ecl_math.hh" +#include "ecl_system.hh" +#include "ecl_video.hh" +#include "ecl_util.hh" + +#endif diff --git a/project/jni/application/enigma/lib-src/enigma-core/ecl_alist.hh b/project/jni/application/enigma/lib-src/enigma-core/ecl_alist.hh new file mode 100644 index 000000000..c762cd56d --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/ecl_alist.hh @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2002 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef ECL_ALIST_HH +#define ECL_ALIST_HH + +/* + * This file defines STL-like associative lists (similar to the ones + * found in Lisp dialects, but with a C++-ish look and feel. They can + * be used as a std::map replacement when linear searches aren't + * expensive. + */ + +#include +#include +#include + +namespace ecl +{ + template + class AssocList : public std::list > { + public: + typedef KEY key_type; + typedef std::pair value_type; + typedef typename std::list::iterator iterator; + typedef typename std::list::const_iterator const_iterator; + + // + // Lookup of keys + // + iterator find (const key_type &key) { + iterator i=this->begin(), e=this->end(); + for (; i!=e; ++i) + if (i->first == key) + break; + return i; + } + + const_iterator find (const key_type &key) const { + const_iterator i=this->begin(), e=this->end(); + for (; i!=e; ++i) + if (i->first == key) + break; + return i; + } + + VAL &operator[] (const key_type &key) { + iterator i=find(key); + if (i==this->end()) + i=insert(this->end(), make_pair(key, VAL())); + return i->second; + } + }; +} +#endif + diff --git a/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.cpp b/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.cpp new file mode 100644 index 000000000..ce56e24e3 --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2000,2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "ecl_argp.hh" +#include + +using namespace std; +using ecl::ArgParser; + + +ArgParser::ArgParser() { +} + +ArgParser::~ArgParser(){ +} + +void ArgParser::on_error (ErrorType t, const std::string &arg) { + cerr << "Error: " << errormsg(t, arg) << endl; +} + +void ArgParser::on_argument (const std::string &/*arg*/) { +} + +void ArgParser::on_option (int /*id*/, const std::string &/*param*/) +{ +} + +void ArgParser::def (int id, const std::string &name, + char abbr, bool takesparam) +{ + m_opts.push_back (Option(id, abbr, string("--") + name, takesparam)); +} + +void ArgParser::def (bool *boolvar, const std::string &name, + char abbr) +{ + Option opt(-1, abbr, string("--") + name, false); + opt.boolvar = boolvar; + m_opts.push_back(opt); +} + +string ArgParser::errormsg(ErrorType t, const std::string &arg) const { + string errmsg; + switch (t) { + case InvalidParam: errmsg = "Invalid parameter for option `"; break; + case AmbiguousOpt: errmsg = "Ambiguous command line option `"; break; + case UnknownOpt: errmsg = "Unknown command line option `"; break; + case MissingParam: errmsg = "Missing parameter for option `"; break; + default: + return string(); + } + errmsg += arg; + errmsg += "'."; + return errmsg; +} + +void ArgParser::process_option(Option &opt, const std::string ¶m) { + if (opt.boolvar) + *opt.boolvar = true; + else + on_option(opt.id, param); +} + +void ArgParser::getlongopt (const std::string &arg) { + string::const_iterator eqpos = find(arg.begin(), arg.end(), '='); + string optname(arg.begin(), eqpos); + + option_iterator i, j; + j = i = find_prefix (m_opts.begin(), m_opts.end(), optname); + + if (i == m_opts.end()) { + on_error (UnknownOpt, optname); + } + else if (find_prefix(++j, m_opts.end(), optname) != m_opts.end()) { + on_error (AmbiguousOpt, optname); + } + else if (i->takesparam) { + if (eqpos != arg.end()) { + process_option (*i, string(eqpos+1, arg.end())); + } + else if (!m_arglist.empty()) { + process_option (*i, m_arglist.front()); + m_arglist.pop_front(); + } + else + on_error (MissingParam, optname); + } + else if (eqpos != arg.end()) { + on_error (InvalidParam, optname); + } + else + process_option(*i, ""); +} + +void ArgParser::getshortopt (const std::string &arg) { + option_iterator i = m_opts.begin(); + for (; i != m_opts.end(); ++i) + if (i->shortopt == arg[1]) + break; + + string option = arg.substr(0, 2); + + if (i == m_opts.end()) { + on_error (UnknownOpt, option); + } + else if (i->takesparam) { + string param = arg.substr(2); + if (param.empty()) { + if (!m_arglist.empty()) { + param = m_arglist.front(); + m_arglist.pop_front(); + process_option (*i, param); + } + else + on_error (MissingParam, option); + } else + process_option (*i, param); + } + else { + process_option (*i, ""); + + // The following characters can be options in their own right + // (single-character options can be grouped, as in -hvx), so + // put them back into the argument list. + std::string newarg("-"); + newarg.append(arg.begin()+2, arg.end()); + if (newarg != "-") + m_arglist.push_front(newarg); + } +} + +void ArgParser::parse () { + bool no_more_opts = false; + while (!m_arglist.empty()) { + std::string arg = m_arglist.front(); + m_arglist.pop_front(); + + if (!no_more_opts && arg.size() >= 2 && arg[0] == '-') { + // Note: "-" is treated as an ordinary argument + if (arg[1] == '-') { + if (arg.size() == 2) // "--" terminates the option list + no_more_opts = true; + else + getlongopt (arg); + } + else + getshortopt (arg); + } + else + on_argument (arg); + } +} diff --git a/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.hh b/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.hh new file mode 100644 index 000000000..393c6c2fe --- /dev/null +++ b/project/jni/application/enigma/lib-src/enigma-core/ecl_argp.hh @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000,2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ECL_ARGP_HH +#define ECL_ARGP_HH + +#include "ecl_util.hh" +#include +#include + +namespace ecl +{ + class ArgParser : public ecl::Nocopy { + public: + // Constructors. + ArgParser (); + + template + ArgParser(Iter beg, Iter end) { + feed (beg, end); + } + + // Destructor. + virtual ~ArgParser(); + + // Types. + enum ErrorType { + UnknownOpt, // Unkown option found + AmbiguousOpt, // Abbreviation matches two options + MissingParam, // Expected parameter is missing + InvalidParam // Option does not take parameter + }; + + // + // Public interface. + // + + virtual void on_error (ErrorType t, const std::string &arg); + virtual void on_argument (const std::string &arg); + virtual void on_option (int id, const std::string ¶m); + + // + // Functions. + // + + /* Feed new arguments into the parser. */ + template + void feed (ForwardIterator begin, ForwardIterator end) { + std::copy(begin, end, std::back_inserter(m_arglist)); + } + + /* Define a new option. */ + void def (int id, const std::string &name, char abbr = 0, bool param = false); + void def (bool *boolvar, const std::string &name, char abbr = 0); + + /* Parse all command line arguments, calling the appropriate + handlers at the right time. */ + void parse(); + + std::string errormsg(ErrorType t, const std::string &arg) const; + + private: + struct Option { + Option (int id_, char s='\0', const std::string& l="", bool param=false) + : id(id_), shortopt(s), longopt(l), takesparam(param), boolvar(0) + {} + + int id; + char shortopt; + std::string longopt; + bool takesparam; + bool *boolvar; + }; + + + // Private Variables. + typedef std::listGNU Lesser General Public + License. + + \section status Status + + Spanned archives are not supported, and support is not planned. + + + The library has been tested and appears to be working with +
    +
  • FreeBSD stable and current / gcc 2.95.3
  • +
  • Red Hat Linux release 7.0 / gcc 2.96
  • +
  • Red Hat Linux release 6.2 (Zoot) / egcs-2.91.66
  • +
  • Linux Mandrake release 7.0 (Air) / gcc 2.95.2
  • +
  • SGI IRIX64 6.5 / gcc 2.95.2
  • +
  • SGI IRIX64 6.5 / MIPSpro Compilers: Version 7.30
  • +
+ + If you are aware of any other platforms that Zipios++ works on, + please let me know (thomass@deltadata.dk). + + \section documentation Documentation + This web page is the front page to the library documentation which + is generated from the source files using Doxygen. Use + the links at the top of the page to browse the API + documentation. The documentation is also available in + a printer-friendly format [pdf]. + + \subsection zipfiles Zip file access + The two most important classes are \ref zipfile_anchor "ZipFile" and + \ref ZipInputStream_anchor "ZipInputStream". ZipInputStream is an istream + for reading zipfiles. It can be instantiated directly, without the + use of ZipFile. A new ZipInputStream reads from the first entry, and + the user can skip to the next entry by calling + \ref ZipInputStream_getnextentry_anchor "ZipInputStream::getNextEntry()". + + ZipFile scans the central directory of a zipfile and provides an + interface to access that directory. The user may search for entries + with a particular filename using \ref fcoll_getentry_anchor "ZipFile::getEntry()", + or simply get the complete list of entries + with \ref fcoll_entries_anchor "ZipFile::entries()". To get an + istream (ZipInputStream) to a particular entry simply use + \ref fcoll_getinputstream "ZipFile::getInputStream()". + + \ref example_zip_anchor "example_zip.cpp" demonstrates the central + elements of Zipios++. + + A Zip file appended to another file, e.g. a binary program, with the program + \ref appendzip_anchor "appendzip", can be read with + \ref zipfile_openembeddedzipfile "ZipFile::openEmbeddedZipFile()". + + \subsection filecollections FileCollections + + A ZipFile is actually just a special kind of + \ref fcoll_anchor "FileCollection" that + obtains its entries from a .zip Zip archive. Zipios++ also implements + a \ref dircol_anchor "DirectoryCollection" that obtains its entries + from a specified directory, and a \ref collcoll_anchor "CollectionCollection" + that obtains its entries from + other collections. Using a single CollectionCollection any number of + other FileCollections can be placed under its control and accessed + through the same single interface that is used to access a ZipFile or + a DirectoryCollection. A singleton (a unique global instance) + CollectionCollection can be obtained through + + \ref collcoll_inst_anchor "CollectionCollection::inst()" ; + + To save typing CollectionCollection has been typedef'ed to CColl. In + the initialization part of an application FileCollections can be + created, and placed under CColll::inst()'s control using + + \ref collcoll_addcoll_anchor "CColl::inst().addCollection()" + + and later an istream can be obtained using + + \ref fcoll_getinputstream "CColl::inst().getInputStream()". + + \section download Download + Go to Zipios++ project page on SourceForge for tar balls and ChangeLog. + + http://sourceforge.net/project/?group_id=5418 + + \section links Links + zlib. + The compression library that Zipios++ uses to perform the actual + decompression. + + + Java 2 Platform, Standard Edition, v 1.3 API Specification + . Zipios++ is heavily inspired by the java.util.zip package. + + + PKWARE zip format + . A description of the zip file format. + + \section bugs Bugs + Submit bug reports and patches to thomass@deltadata.dk + + + + \htmlonly + Project hosted by + +

+ Logo created with + + + \endhtmlonly */ + + +/** \file + Header file that defines FileCollection. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/fcollexceptions.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/fcollexceptions.h new file mode 100644 index 000000000..d4dad1922 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/fcollexceptions.h @@ -0,0 +1,100 @@ +#ifndef FCOLLEXCEPTIONS_H +#define FCOLLEXCEPTIONS_H + +#include "zipios++/zipios-config.h" + +#include +#include + +namespace zipios { + +using std::string ; +using std::exception ; + +/** An IOException is used to signal an I/O error. + */ +class IOException : public exception { +public: + IOException() throw () ; + explicit IOException( const string &msg ) throw () ; + IOException( const IOException &src ) throw () ; + IOException &operator= ( const IOException &src ) throw () ; + + virtual const char *what() const throw () ; + virtual ~IOException() throw () ; +private: + string _what ; +}; + +/** An FCollException is used to signal a problem with a + FileCollection. */ +class FCollException : public exception { +public: + FCollException() throw () ; + explicit FCollException( const string &msg ) throw () ; + FCollException( const FCollException &src ) throw () ; + FCollException &operator= ( const FCollException &src ) throw () ; + + virtual const char *what() const throw () ; + virtual ~FCollException() throw () ; +private: + string _what ; +}; + +/** An object member function may throw this exception, if the + operation it normally performs is inappropriate or impossible to + perform because of the current state of the object. */ +class InvalidStateException : public exception { +public: + InvalidStateException() throw () ; + explicit InvalidStateException( const string &msg ) throw () ; + InvalidStateException( const InvalidStateException &src ) throw () ; + InvalidStateException &operator= ( const InvalidStateException &src ) throw () ; + + virtual const char *what() const throw () ; + virtual ~InvalidStateException() throw () ; +private: + string _what ; +}; + +/** Basic exception */ +class Exception : public exception { +public: + Exception() throw () ; + explicit Exception( const string &msg ) throw () ; + Exception( const Exception &src ) throw () ; + Exception &operator= ( const Exception &src ) throw () ; + + virtual const char *what() const throw () ; + virtual ~Exception() throw () ; +private: + string _what ; +}; + + +} // namespace +#endif + +/** \file + Header file that defines a number of exceptions used by FileCollection and + its subclasses. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/fileentry.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/fileentry.h new file mode 100644 index 000000000..542e06166 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/fileentry.h @@ -0,0 +1,240 @@ +#ifndef FILEENTRY_H +#define FILEENTRY_H + +#include "zipios++/zipios-config.h" + +#include +#include +#include "zipios++/meta-iostreams.h" + +#include "zipios++/simplesmartptr.h" +#include "zipios++/zipios_defs.h" + +namespace zipios { + +using std::vector ; +using std::ostream ; +using std::istream ; +using std::string ; + +/** The types used with FileEntry::setMethod and + FileEntry::getMethod. The current entries are the types supported + by the zip format. The numbering also matches the numbering used + in the zip file format, ie STORED is indicated by a 0 in the + method field in a zip file and so on. */ +enum StorageMethod { STORED = 0, SHRUNK, REDUCED1, REDUCED2, + REDUCED3, REDUCED4, IMPLODED, RESERVED, + DEFLATED } ; + +class FileEntry ; + +/** \typedef typedef SimpleSmartPointer< FileEntry > EntryPointer + EntryPointer is a SimpleSmartPointer for FileEntry's. */ +typedef SimpleSmartPointer< FileEntry > EntryPointer ; + + +/** ConstEntryPointer is a SimpleSmartPointer for const FileEntry's. */ +typedef SimpleSmartPointer< const FileEntry > ConstEntryPointer ; + +/** Entries is a vector of EntryPointer's */ +typedef vector< EntryPointer > Entries ; + +/** ConstEntries is a vector of ConstEntryPointer's */ +typedef vector< EntryPointer > ConstEntries ; + + + +/** A FileEntry represents an entry in a FileCollection. The interface + is a copy of the ZipEntry interface from the java.util.zip + package. The name has been changed to FileEntry, as FileCollection + is a more general abstraction, that covers other types of file + collections than just zip files. */ +class FileEntry { +public: + + /* Default construcotr, copy constructor and copy assignment + operator are sufficient. */ + + /** Returns the comment of the entry, if it has one. Otherwise it + returns an empty string. + @return the comment associated with the entry, if there is one. + */ + virtual string getComment() const = 0 ; + /** Returns the compressed size of the entry. If the entry is not + stored in a compressed format, the uncompressed size is + returned. + @return the compressed size of the entry. If the entry is stored without + compression the uncompressed size is returned. + */ + virtual uint32 getCompressedSize() const = 0 ; + /** Returns the Crc for the entry, if it has one. FIXME: what is + returned if it doesn't have one? + @return the Crc for the entry, if it has one. + */ + virtual uint32 getCrc() const = 0 ; + /** Returns a vector of bytes of extra data that may be stored with + the entry. + @return A vector< unsigned char > of extra bytes that may potentially + be associated with an entry. + */ + virtual vector< unsigned char > getExtra() const = 0 ; + /** Returns the method used to store the entry in the FileCollection. + @return the storage method used to store the entry in the collection. + @see StorageMethod. + */ + virtual StorageMethod getMethod() const = 0 ; + /** Returns the full filename of the entry, including a path if the + entry is stored in a subfolder. + @return the filename of the entry, including path if the entry is stored + in a sub-folder. + */ + virtual string getName() const = 0 ; + /** Returns the filename of the entry. + @return Returns the filename of the entry. + */ + virtual string getFileName() const = 0 ; + /** Returns the (uncompressed) size of the entry data. + @return Returns the (uncompressed) size of the entry. + */ + virtual uint32 getSize() const = 0 ; + /** Returns the date and time of FIXME: what? + @return the date and time of the entry. + */ + virtual int getTime() const = 0 ; + /** Any method or operator that initializes a FileEntry may set a + flag, that specifies whether the read entry is valid or not. If + it isn't this method returns false. + @return true if the FileEntry has been parsed succesfully. + */ + virtual bool isValid() const = 0 ; + // virtual int hashCode() const = 0 ; + /** Returns true if the entry is a directory. A directory entry is + an entry which name ends with a separator ('/' for Unix systems, + '\' for Windows and DOS systems. + @return true if the entry is a directory. + */ + virtual bool isDirectory() const = 0 ; + + /** Sets the comment field for the FileEntry. + @param comment string with the new comment. + */ + virtual void setComment( const string &comment ) = 0 ; + /** Set the compressed size field of the entry. + @param size value to set the compressed size field of the entry to. + */ + virtual void setCompressedSize( uint32 size ) = 0 ; + /** Sets the crc field. + @param crc value to set the crc field to. + */ + virtual void setCrc( uint32 crc ) = 0 ; + /** Sets the extra field. + @param extra the extra field is set to this value. + */ + virtual void setExtra( const vector< unsigned char > &extra ) = 0 ; + /** Sets the storage method field for the entry. + @param method the method field is set to the specified value. + */ + virtual void setMethod( StorageMethod method ) = 0 ; + /** Sets the name field for the entry. + @param name the name field is set to the specified value. + */ + virtual void setName( const string &name ) = 0 ; + /** Sets the size field for the entry. + @param size the size field is set to this value. + */ + virtual void setSize( uint32 size ) = 0 ; + /** Sets the time field for the entry. + @param time the time field is set to the specified value. + */ + virtual void setTime( int time ) = 0 ; + + /** Returns a human-readable string representation of the entry. + @return a human-readable string representation of the entry. + */ + virtual string toString() const = 0 ; + + /** Create a heap allocated clone of the object this method is called for. The + caller is responsible for deallocating the clone when he is done with it. + @return A heap allocated copy of the object this method is called for. + */ + virtual FileEntry *clone() const = 0 ; + + /** FileEntry destructor. */ + virtual ~FileEntry() {} + +// protected: + class MatchName ; + class MatchFileName ; +protected: + friend class SimpleSmartPointer< FileEntry > ; + friend class SimpleSmartPointer< const FileEntry > ; + void ref() const { _refcount.ref() ; } + unsigned int unref() const { return _refcount.unref() ; } + + ReferenceCount< FileEntry > _refcount ; +}; + +/** Function object to be used with the STL find_if algorithm to + find a FileEntry in a container, which name (as obtained with + FileEntry::getName()) is identical to the name specified in the + MatchName constructor. */ +class FileEntry::MatchName { +public: + explicit MatchName( const string &name ) : _name( name ) {} + bool operator() ( const ConstEntryPointer &entry ) { + return entry->getName() == _name ; + } +private: + string _name ; +}; + +/** Function object to be used with the STL find_if algorithm to + find a FileEntry in a container, which name (as obtained with + FileEntry::getFileName()) is identical to the name specified in the + MatchName constructor. */ +class FileEntry::MatchFileName { +public: + explicit MatchFileName( const string &name ) : _name( name ) {} + bool operator() ( const ConstEntryPointer &entry ) { + return entry->getFileName() == _name ; + } +private: + string _name ; +}; + +ostream &operator<< ( ostream &os, const FileEntry &entry ) ; + +inline ostream &operator<< ( ostream &os, const ConstEntryPointer &entry ) { + os << *entry ; + return os ; +} + + + +} // namespace + +#endif + + +/** \file + Header file that defines FileEntry. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/filepath.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/filepath.h new file mode 100644 index 000000000..8ec9ad5d9 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/filepath.h @@ -0,0 +1,202 @@ +#ifndef FILEPATH_H +#define FILEPATH_H + +#include "zipios++/zipios-config.h" + +#include +#include + +namespace zipios { + +using namespace std ; + +/** FilePath represents a path to a file or directory name. FilePath has + member functions to check if the file path is a valid file system entity, + and to check what kind of file system entity it is, e.g. is it a file, a + directory, a pipe etc. +*/ +class FilePath { +public: + /** Constructor. + @param path A string representation of the path. + @param check_exists If true is specified the constructor will + check the existence and type of the path immidiately, instead of + deferring that task until it is needed. */ + FilePath( const string &path = "", bool check_exists = false ) ; + + inline FilePath &operator= ( const string &rhs ) ; + + inline operator string() const ; + + /** Concatenates FilePath objects. A file separator is inserted + if appropriate. */ + inline FilePath operator+ ( const FilePath &name ) const ; + + /** Returns filename of the FilePath object by pruning the path + off. */ + inline FilePath filename() const ; + + + /** @return true If the path is a valid file system entity. */ + inline bool exists() const ; + + /** @return true if the path is a regular file. */ + inline bool isRegular() const ; + + /** @return true if the path is a directory. */ + inline bool isDirectory() const ; + + /** @return true if the path is character special (a character + device file). */ + inline bool isCharSpecial() const ; + + /** @return true if the path is block special (a block device + file). */ + inline bool isBlockSpecial() const ; + + /** @return true if the path is a socket. */ + inline bool isSocket() const ; + + /** @return true if the path is a Fifo (a pipe). */ + inline bool isFifo() const ; + +protected: + + /** Prunes the trailing separator of a specified path. */ + inline void pruneTrailingSeparator() ; + + /** This function sets _checked to true, stats the path, to see if + it exists and to determine what type of file it is. All the query + functions check if _checked is true, and if it isn't they call + check(). This means stat'ing is deferred until it becomes + necessary. */ + void check() const ; + + static const char _separator; + + // FIXME: Should be bitfield + mutable bool _checked ; + mutable bool _exists ; + mutable bool _is_reg ; + mutable bool _is_dir ; + mutable bool _is_char ; + mutable bool _is_block ; + mutable bool _is_socket ; + mutable bool _is_fifo ; + string _path ; +}; + + +// +// Inline member functions +// + +FilePath &FilePath::operator= ( const string &rhs ) { + _path = rhs ; + pruneTrailingSeparator() ; + return *this ; +} + +void FilePath::pruneTrailingSeparator() { + if ( _path.size() > 0 ) + if ( _path[ _path.size() -1 ] == _separator ) + _path.erase( _path.size() - 1 ) ; +} + +FilePath::operator string() const { + return _path ; +} + + +FilePath FilePath::operator+ ( const FilePath &name ) const { + if ( _path.size() > 0 ) + return _path + _separator + name._path ; + else + return name._path ; +} + + +FilePath FilePath::filename() const { + string::size_type pos ; + pos = _path.find_last_of( _separator ) ; + if ( pos != string::npos ) + return _path.substr( pos + 1); + else + return _path ; +} + + +bool FilePath::exists() const { + if ( ! _checked ) + check() ; + return _exists ; +} + + +bool FilePath::isRegular() const { + if ( ! _checked ) + check() ; + return _is_reg ; +} + + +bool FilePath::isDirectory() const { + if ( ! _checked ) + check() ; + return _is_dir ; +} + + +bool FilePath::isCharSpecial() const { + if ( ! _checked ) + check() ; + return _is_char ; +} + + +bool FilePath::isBlockSpecial() const { + if ( ! _checked ) + check() ; + return _is_block ; +} + + +bool FilePath::isSocket() const { + if ( ! _checked ) + check() ; + return _is_socket ; +} + + +bool FilePath::isFifo() const { + if ( ! _checked ) + check() ; + return _is_fifo ; +} + + +} // namespace +#endif + +/** \file + Header file that defines FilePath. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/filterinputstreambuf.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/filterinputstreambuf.h new file mode 100644 index 000000000..fbe86ce1e --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/filterinputstreambuf.h @@ -0,0 +1,66 @@ +#ifndef FILTERINPUTSTREAMBUF_H +#define FILTERINPUTSTREAMBUF_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" + +namespace zipios { + +using std::streambuf ; + +/** An input streambuf filter is a streambuf that filters the input it + gets from the streambuf it is attached to. FilterInputStreambuf is a base class to + derive input streambuf filters from. */ +class FilterInputStreambuf : public streambuf { +public: + /** Constructor. + @param inbuf the streambuf to use for input. + @param del_inbuf if true is specified inbuf will be deleted, when + the FilterInputStreambuf is destructed. + */ + explicit FilterInputStreambuf( streambuf *inbuf, bool del_inbuf = false ) ; + /** Destructor. */ + virtual ~FilterInputStreambuf() ; + +protected: + int _s_pos ; // Position in this streambuf - FIXME: is this used? + streambuf *_inbuf ; + bool _del_inbuf ; +private: + + /** Copy-constructor is private to prevent copying. */ + FilterInputStreambuf( const FilterInputStreambuf &src ) ; + + /** Copy-assignment operator is private to prevent copying. */ + const FilterInputStreambuf &operator= ( const FilterInputStreambuf &src ) ; +}; + + +} // namespace + + +#endif + +/** \file + Header file that defines FilterInputStreambuf. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/filteroutputstreambuf.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/filteroutputstreambuf.h new file mode 100644 index 000000000..32cd7a0c3 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/filteroutputstreambuf.h @@ -0,0 +1,58 @@ +#ifndef FILTEROUTPUTSTREAMBUF_H +#define FILTEROUTPUTSTREAMBUF_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" + +namespace zipios { + +using std::streambuf ; + +/** A FilterOutputStreambuf is a streambuf that filters the data that is written + to it before it passes it on to the output streambuf it is connected to. */ +class FilterOutputStreambuf : public streambuf { +public: + + /** Constructor. + @param outbuf the streambuf to pass the filtered data on to. + @param del_outbuf if true is specified outbuf will be deleted, when + the FilterOutputStreambuf is destructed. */ + explicit FilterOutputStreambuf( streambuf *outbuf, bool del_outbuf = false ) ; + + /** Destructor. */ + virtual ~FilterOutputStreambuf() ; + +protected: + streambuf *_outbuf ; + bool _del_outbuf ; +}; + + +} // namespace + + +#endif + +/** \file + Header file that defines FilterOutputStreambuf. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/inflateinputstreambuf.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/inflateinputstreambuf.h new file mode 100644 index 000000000..b0264fbc6 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/inflateinputstreambuf.h @@ -0,0 +1,89 @@ +#ifndef INFLATEINPUTSTREAMBUF_H +#define INFLATEINPUTSTREAMBUF_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" +#include + +#include + +#include "zipios++/filterinputstreambuf.h" + +namespace zipios { + +using std::vector ; + +/** InflateInputStreambuf is an input stream filter, that inflates the input + from the attached input stream. Deflation/Inflation is a + compression/decompression method used in gzip and zip. The zlib + library is used to perform the actual inflation, this class only + wraps the functionality in an input stream filter. */ +class InflateInputStreambuf : public FilterInputStreambuf { +public: + /** InflateInputStreambuf constructor. + @param inbuf the streambuf to use for input. + @param s_pos a position to reset the inbuf to before reading. Specify + -1 to read from the current position. + @param del_inbuf if true is specified inbuf will be deleted, when + the InflateInputStreambuf is destructed. + */ + explicit InflateInputStreambuf( streambuf *inbuf, int s_pos = -1, bool del_inbuf = false ) ; + virtual ~InflateInputStreambuf() ; + + /** Resets the zlib stream and purges input and output buffers. + repositions the input streambuf at stream_position. + @param stream_position a position to reset the inbuf to before reading. Specify + -1 to read from the current position. + */ + bool reset( int stream_position = -1 ) ; +protected: + virtual int underflow() ; +private: + z_stream _zs ; + bool _zs_initialized ; + const int _invecsize ; + vector< char > _invec ; +protected: // FIXME: reconsider design? + const int _outvecsize ; + vector< char > _outvec ; + +private: + + /** Copy-constructor is private to prevent copying. */ + InflateInputStreambuf( const InflateInputStreambuf &src ) ; + + /** Copy-assignment operator is private to prevent copying. */ + const InflateInputStreambuf &operator= ( const InflateInputStreambuf &src ) ; + +}; + + +} // namespace + + + +#endif + +/** \file + Header file that defines InflateInputStreambuf. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/meta-iostreams.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/meta-iostreams.h new file mode 100644 index 000000000..f1866ae87 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/meta-iostreams.h @@ -0,0 +1,17 @@ +#ifndef META_IOSTREAMS_H +#define META_IOSTREAMS_H + +// Includes the different iostream libraries + +#include "zipios++/zipios-config.h" + +#include +#include + +#if defined (HAVE_STD_IOSTREAM) && defined (USE_STD_IOSTREAM) +#include +#else +#include +#endif + +#endif diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/simplesmartptr.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/simplesmartptr.h new file mode 100644 index 000000000..2d853fc05 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/simplesmartptr.h @@ -0,0 +1,173 @@ +#ifndef SIMPLESMARTPTR_H +#define SIMPLESMARTPTR_H + +#include "zipios++/zipios-config.h" + +namespace zipios { + +/** SimpleSmartPointer is a simple reference counting smart pointer + template. The type pointed to must keep a reference count that is + accessible through the two methods void ref() const and unsigned + int unref() const. The type must also handle the reference count + properly. The easiest way to do that is to use the ReferenceCount + template class. */ +template< class Type > +class SimpleSmartPointer { +public: + Type *operator-> () const { return _p ; } + + Type &operator* () const { return *_p ; } + + SimpleSmartPointer( Type *p = 0 ) : _p( p ) { ref() ; } + + template< class T2 > SimpleSmartPointer( const SimpleSmartPointer< T2 > &src ) + : _p( src.get() ) { ref() ; } + + SimpleSmartPointer( const SimpleSmartPointer &src ) : _p( src.get() ) { + ref() ; + } + + ~SimpleSmartPointer () { if ( unref() == 0 ) deleteIt() ; } + + template< class T2 > + SimpleSmartPointer &operator= ( const SimpleSmartPointer< T2 > &src ) { + ref( src.get() ) ; + if ( unref() == 0 ) + deleteIt() ; + _p = src.get() ; + return *this ; + } + + SimpleSmartPointer &operator= ( const SimpleSmartPointer &src ) { + ref( src.get() ) ; + if ( unref() == 0 ) + deleteIt() ; + _p = src.get() ; + return *this ; + } + + SimpleSmartPointer &operator=( Type *src ) { + _p = src ; + ref() ; + return *this ; + } + + bool operator== ( const Type *p ) const { return _p == p ; } + bool operator!= ( const Type *p ) const { return _p != p ; } + bool operator== ( const SimpleSmartPointer &sp ) const { return _p == sp.get() ; } + bool operator!= ( const SimpleSmartPointer &sp ) const { return _p != sp.get() ; } + bool operator! () const { return ! _p ; } + // This next method is inspired by iostream, and is for use with + // if ( some_smart_pointer ). + operator void*() const { return _p ? (void *)(-1) : (void *)(0) ; } + + Type *get() const { return _p ; } + + /** Returns the reference count - For debugging purposes. */ + unsigned int getReferenceCount() const { return _p->getReferenceCount(); } + + +private: + template< class T2 > + void ref( const T2 *ptr ) { if ( ptr ) ptr->ref() ; } + + void ref() const { if ( _p ) _p->ref() ; } + unsigned int unref() const { + if ( _p ) + return _p->unref(); + else + return 0 ; + } + void deleteIt() { +// if( _p ) +// cerr << "SimpleSmartPointer: Deleting object!" << endl ; + delete _p ; + } + Type *_p ; +}; + + +/** ReferenceCount is useful to ensure proper handling of the + reference count for (objects of) classes handled through a + SimpleSmartPointer. Subclassing ReferenceCount is all a class + needs to become ready for being handled by + SimpleSmartPointer. Another way is to add a ReferenceCount member + variable to a class and write two methods 'void ref() const' and + 'unsigned int unref() const' that invoke the same methods in the + ReferenceCount variable. */ +template< class Type > +class ReferenceCount { + /** SimpleSmartPointer needs to be a friend to invoke the private + ref() and unref() methods. */ + friend class SimpleSmartPointer< Type > ; + friend class SimpleSmartPointer< const Type > ; + /** Type also needs to be a friend to invoke the private ref() and + unref() methods, in case Type doesn't want to inherit + ReferenceCount and thus needs to invoke ref() and unref() + through forwarding member functions. */ + // + // Originally the following template parameter was made a friend. + // This is not allowed by the standard so comment it out: + // + // friend Type ; + // + // Initially hack things by making the necessary classes friends + // even though we don't know really which they are. This is an + // Hideous Hack. + friend class FileEntry ; + friend class Bogus ; + +public: + /** Constructor intializes count to zero. */ + ReferenceCount() : _ref_count( 0 ) {} + + /** Copy-constructor intializes count to zero. It doesn't copy it + from src. */ + ReferenceCount( const ReferenceCount & /*src*/ ) : _ref_count( 0 ) {} + + /** The assignment operator doesn't copy the reference count, it + leaves it unchanged. */ + const ReferenceCount &operator= ( const ReferenceCount &src ) {} +private: + + /** Increases the reference count. */ + void ref() const { ++_ref_count ; } + + /** Decreases the reference count. */ + unsigned int unref() const { return --_ref_count ; } + + /** Returns the reference count - For debugging purposes. */ + unsigned int getReferenceCount() const { return _ref_count; } + + /** Holds the actual reference count */ + mutable unsigned short _ref_count ; +}; + + + +} // namespace + +#endif + +/** \file + Header file that defines SimpleSmartPointer and ReferenceCount. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/virtualseeker.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/virtualseeker.h new file mode 100644 index 000000000..e79a0cc05 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/virtualseeker.h @@ -0,0 +1,105 @@ +#ifndef VIRTUALSEEKER_H +#define VIRTUALSEEKER_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" + + +namespace zipios { + +using std::ios ; +using std::cerr ; +using std::endl ; + +/** VirtualSeeker is a simple class that keeps track of a set of + specified 'virtual' file endings that mark a subset of a real + file. An example of its use (and its reason for existence) is to + keep track of the file endings of a Zip file embedded in another + file. */ +class VirtualSeeker { +public: + inline VirtualSeeker( int start_offset = 0, int end_offset = 0) ; + inline void setOffsets( int start_offset, int end_offset ) ; + inline void getOffsets( int &start_offset, int &end_offset ) const ; + inline int startOffset() const ; + inline int endOffset() const ; + inline void vseekg( istream &is, int offset, ios::seekdir sd ) const ; + inline int vtellg( istream &is ) const ; +private: + // start and end offsets + int _s_off, _e_off ; +}; + + + +VirtualSeeker::VirtualSeeker( int start_offset, int end_offset ) + : _s_off( start_offset ), + _e_off( end_offset ) +{} + + +void VirtualSeeker::setOffsets( int start_offset, int end_offset ) { + _s_off = start_offset ; + _e_off = end_offset ; +} + + +void VirtualSeeker::getOffsets( int &start_offset, int &end_offset ) const { + start_offset = _s_off ; + end_offset = _e_off ; +} + + +int VirtualSeeker::startOffset() const { + return _s_off ; +} + + +int VirtualSeeker::endOffset() const { + return _e_off ; +} + +void VirtualSeeker::vseekg( istream &is, int offset, ios::seekdir sd ) const { + if ( sd == ios::cur ) + is.seekg( offset, sd ) ; + else if ( sd == ios::beg ) + is.seekg( offset + _s_off, sd ) ; + else if ( sd == ios::end ) + is.seekg( offset - _e_off, sd ) ; + else + cerr << "VirtualSeekManager::seekg: error - not supposed to happen!" << endl ; +} + + +int VirtualSeeker::vtellg( istream &is ) const { + return static_cast< int >( is.tellg() ) - _s_off ; +} + + +} // namespace + +#endif + +/** \file + Header file that defines VirtualSeeker. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipfile.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipfile.h new file mode 100644 index 000000000..9d203e36a --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipfile.h @@ -0,0 +1,105 @@ +#ifndef ZIPFILE_H +#define ZIPFILE_H + +#include "zipios++/zipios-config.h" + +#include +#include "zipios++/meta-iostreams.h" + +#include "zipios++/fcoll.h" +#include "zipios++/ziphead.h" +#include "zipios++/virtualseeker.h" + +namespace zipios { + +using std::ifstream ; + +/** \anchor zipfile_anchor + ZipFile is a FileCollection, where the files are stored + in a .zip file. */ +class ZipFile : public FileCollection { +public: + /** \anchor zipfile_openembeddedzipfile + Opens a Zip archive embedded in another file, by writing the zip + archive to the end of the file followed by the start offset of + the zip file. The offset must be written in zip-file byte-order + (little endian). The program appendzip, which is part of the + Zipios++ distribution can be used to append a Zip archive to a + file, e.g. a binary program. + @throw FColException Thrown if the specified file name is not a valid zip + archive. + @throw IOException Thrown if an I/O problem is encountered, while the directory + of the specified zip archive is being read. */ + static ZipFile openEmbeddedZipFile( const string &name ) ; + + /** Default constructor. + */ + ZipFile() {} + + /* Default Copy constructor and copy assignment operator are sufficient. */ + + /** Constructor. Opens the zip file name. If the zip "file" is + embedded in a file that contains other data, e.g. a binary + program, the offset of the zip file start and end must be + specified. + @param name The filename of the zip file to open. + @param s_off Offset relative to the start of the file, that + indicates the beginning of the zip file. + @param e_off Offset relative to the end of the file, that + indicates the end of the zip file. The offset is a positive number, + even though the offset is towards the beginning of the file. + @throw FColException Thrown if the specified file name is not a valid zip + archive. + @throw IOException Thrown if an I/O problem is encountered, while the directory + of the specified zip archive is being read. */ + explicit ZipFile( const string &name, int s_off = 0, int e_off = 0 + /* , ios::open_mode mode = ios::in | ios::binary */ ) ; + + virtual FileCollection *clone() const ; + + /** Destructor. */ + virtual ~ZipFile() ; + + virtual void close() ; + + virtual istream *getInputStream( const ConstEntryPointer &entry ) ; + virtual istream *getInputStream( const string &entry_name, + MatchPath matchpath = MATCH ) ; +private: + VirtualSeeker _vs ; + EndOfCentralDirectory _eocd ; + + bool init( istream &_zipfile ) ; + bool readCentralDirectory( istream &_zipfile ) ; + bool readEndOfCentralDirectory( istream &_zipfile ) ; + bool confirmLocalHeaders( istream &_zipfile ) ; + void setError( string error_str ) ; +}; + + +} + +#endif + +/** \file + Header file that defines ZipFile. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/ziphead.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/ziphead.h new file mode 100644 index 000000000..ea1d0d3d3 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/ziphead.h @@ -0,0 +1,271 @@ +#ifndef ZIPHEAD_H +#define ZIPHEAD_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" +#include +#include + +#include "zipios++/fileentry.h" +#include "zipios++/zipios_defs.h" + +namespace zipios { + +using std::streampos ; + +class ZipCDirEntry ; + +/** A concrete implementation of the abstract FileEntry base class for + ZipFile entries, specifically for representing the information + present in the local headers of file entries in a zip file. */ +class ZipLocalEntry : public FileEntry { + friend istream &operator>> ( istream &is, ZipLocalEntry &zcdh ) ; + friend ostream &operator<< ( ostream &os, const ZipLocalEntry &zlh ) ; + friend bool operator== ( const ZipLocalEntry &zlh, const ZipCDirEntry &ze ) ; +public: + inline ZipLocalEntry( const string &_filename = "", + const vector< unsigned char > &_extra_field = + vector< unsigned char >() ) + : gp_bitfield( 0 ), + _valid( false ) { + setDefaultExtract() ; + setName( _filename ) ; + setExtra( _extra_field ) ; + } + + void setDefaultExtract() ; + inline ZipLocalEntry &operator=( const class ZipLocalEntry &src ) ; + virtual string getComment() const ; + virtual uint32 getCompressedSize() const ; + virtual uint32 getCrc() const ; + virtual vector< unsigned char > getExtra() const ; + virtual StorageMethod getMethod() const ; + virtual string getName() const ; + virtual string getFileName() const ; + virtual uint32 getSize() const ; + virtual int getTime() const ; + virtual bool isValid() const ; + + virtual bool isDirectory() const ; + + virtual void setComment( const string &comment ) ; + virtual void setCompressedSize( uint32 size ) ; + virtual void setCrc( uint32 crc ) ; + virtual void setExtra( const vector< unsigned char > &extra ) ; + virtual void setMethod( StorageMethod method ) ; + virtual void setName( const string &name ) ; + virtual void setSize( uint32 size ) ; + virtual void setTime( int time ) ; + + virtual string toString() const ; + + int getLocalHeaderSize() const ; + + bool trailingDataDescriptor() const ; + + virtual FileEntry *clone() const ; + + virtual ~ZipLocalEntry() {} +protected: + static const uint32 signature ; + uint16 extract_version ; + uint16 gp_bitfield ; + uint16 compress_method ; + uint16 last_mod_ftime ; + uint16 last_mod_fdate ; + uint32 crc_32 ; + uint32 compress_size ; + uint32 uncompress_size ; + uint16 filename_len ; + uint16 extra_field_len ; + + string filename ; + vector< unsigned char > extra_field ; + + bool _valid ; +}; + +/** A struct containing fields for the entries in a zip file data + descriptor, that trails the compressed data in files that were + created by streaming, ie where the zip compressor cannot seek back + to the local header and store the data. */ +struct DataDescriptor { + uint32 crc_32 ; + uint32 compress_size ; + uint32 uncompress_size ; +}; + +/** Specialization of ZipLocalEntry, that add fields for storing the + extra information, that is only present in the entries in the zip + central directory and not in the local entry headers. */ +class ZipCDirEntry : public ZipLocalEntry { +friend istream &operator>> ( istream &is, ZipCDirEntry &zcdh ) ; +friend ostream &operator<< ( ostream &os, const ZipCDirEntry &zcdh ) ; +friend bool operator== ( const ZipLocalEntry &zlh, const ZipCDirEntry &ze ) ; +public: + + inline ZipCDirEntry( const string &_filename = "", + const string &_file_comment = "", + const vector< unsigned char > &_extra_field = + vector< unsigned char >() ) + : ZipLocalEntry ( _filename, _extra_field ), + disk_num_start ( 0x0 ), + intern_file_attr( 0x0 ), + extern_file_attr( 0x81B40000 ) + // FIXME: I don't understand the external mapping, simply + // copied value for a file with -rw-rw-r-- permissions + // compressed with info-zip + { + setComment( _file_comment ) ; + setDefaultWriter() ; + } + + void setDefaultWriter() ; + + inline ZipCDirEntry &operator=( const class ZipCDirEntry &src ) ; + virtual string toString() const ; + + virtual string getComment() const ; + + virtual void setComment( const string &comment ) ; + + virtual uint32 getLocalHeaderOffset() const ; + virtual void setLocalHeaderOffset( uint32 offset ) ; + + int getCDirHeaderSize() const ; + + virtual FileEntry *clone() const ; + + virtual ~ZipCDirEntry() {} +private: + static const uint32 signature ; + uint16 writer_version ; + + uint16 file_comment_len ; + uint16 disk_num_start ; + uint16 intern_file_attr ; + uint32 extern_file_attr ; + + uint32 rel_offset_loc_head ; + + string file_comment ; +}; + +/** The end of the Central directory structure. This structure is + stored in the end of the zipfile, and contains information about + the zipfile, including the position of the start of the central + directory. */ +class EndOfCentralDirectory { + friend ostream &operator<< ( ostream &os, const EndOfCentralDirectory &eocd ) ; +public: + explicit EndOfCentralDirectory( const string &_zip_comment = "", + uint16 _disk_num = 0, uint16 _cdir_disk_num = 0, + uint16 _cdir_entries = 0, + uint16 _cdir_tot_entries = 0, + uint32 _cdir_size = 0, uint32 _cdir_offset = 0 ) + : disk_num ( _disk_num ), + cdir_disk_num ( _cdir_disk_num ), + cdir_entries ( _cdir_entries ), + cdir_tot_entries ( _cdir_tot_entries ), + cdir_size ( _cdir_size ), + cdir_offset ( _cdir_offset ), + zip_comment_len ( _zip_comment.size() ), + zip_comment ( _zip_comment ) {} + + uint32 offset() const { return cdir_offset ; } + uint16 totalCount() const { return cdir_tot_entries ; } + void setCDirSize( uint32 size ) { cdir_size = size ; } + void setOffset( uint32 offset ) { cdir_offset = offset ; } + + void setTotalCount( uint16 c ) { cdir_entries = c ; cdir_tot_entries = c ; } + int eocdOffSetFromEnd() const { return eocd_offset_from_end ; } + bool read( vector &buf, int pos ) ; +private: + static const uint32 signature; + uint16 disk_num ; + uint16 cdir_disk_num ; + uint16 cdir_entries ; + uint16 cdir_tot_entries ; + uint32 cdir_size ; + uint32 cdir_offset ; + uint16 zip_comment_len ; + + streampos eocd_offset_from_end ; // Not a Zip defined field + string zip_comment; + bool checkSignature( unsigned char *buf ) const ; + inline bool checkSignature( uint32 sig ) const ; +}; + + +bool operator== ( const ZipLocalEntry &zlh, const ZipCDirEntry &ze ) ; +inline bool operator== ( const ZipCDirEntry &ze, const ZipLocalEntry &zlh ) { + return zlh == ze ; +} +inline bool operator!= ( const ZipLocalEntry &zlh, const ZipCDirEntry &ze ) { + return ! ( zlh == ze ) ; +} +inline bool operator!= ( const ZipCDirEntry &ze, const ZipLocalEntry &zlh ) { + return ! ( zlh == ze ) ; +} + +// Inline member functions + +ZipCDirEntry &ZipCDirEntry::operator=( const class ZipCDirEntry &src ) { + writer_version = src.writer_version ; + extract_version = src.extract_version ; + gp_bitfield = src.gp_bitfield ; + compress_method = src.compress_method ; + last_mod_ftime = src.last_mod_ftime ; + last_mod_fdate = src.last_mod_fdate ; + crc_32 = src.crc_32 ; + compress_size = src.compress_size ; + uncompress_size = src.uncompress_size ; + filename_len = src.filename_len ; + extra_field_len = src.extra_field_len ; + file_comment_len = src.file_comment_len ; + disk_num_start = src.disk_num_start ; + intern_file_attr = src.intern_file_attr ; + extern_file_attr = src.extern_file_attr ; + rel_offset_loc_head = src.rel_offset_loc_head ; + + filename = src.filename ; + extra_field = src.extra_field ; + file_comment = src.file_comment ; + + return *this ; +} + +bool EndOfCentralDirectory::checkSignature ( uint32 sig ) const { + return signature == sig ; +} + + +} // namespace + +#endif + + +/** \file + Header file containing classes and functions for reading the central + directory and local header fields in a zip archive. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipheadio.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipheadio.h new file mode 100644 index 000000000..101443a1a --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipheadio.h @@ -0,0 +1,192 @@ +#ifndef ZIPHEADIO_H +#define ZIPHEADIO_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" +#include +#include + +#include "zipios++/ziphead.h" +#include "zipios++/zipios_defs.h" + +namespace zipios { + +// byte order conversion functions. +// ztohs (zip-to-host-short) +#ifdef MY_BIG_ENDIAN + +inline uint16 ztohs ( unsigned char *buf ) { + uint16 out ; +// *( reinterpret_cast( &out ) ) = *( buf + 1 ); +// *( reinterpret_cast( &out ) + 1 ) = *( buf ); + out = ( static_cast< uint16 >( buf[ 0 ] ) << 8 ) + + ( static_cast< uint16 >( buf[ 1 ] ) ) ; + + return out; +} + +// ztohl (zip-to-host-long) +inline uint32 ztohl ( unsigned char *buf ) { + uint32 out; + out = ( static_cast< uint32 >( buf[ 0 ] ) << 24 ) + + ( static_cast< uint32 >( buf[ 1 ] ) << 16 ) + + ( static_cast< uint32 >( buf[ 2 ] ) << 8 ) + + ( static_cast< uint32 >( buf[ 3 ] ) ) ; + + return out; +} + +#else + +inline uint16 ztohs ( unsigned char *buf ) { + uint16 out ; + out = ( static_cast< uint16 >( buf[ 1 ] ) << 8 ) + + ( static_cast< uint16 >( buf[ 0 ] ) ) ; + return out; +} + +// ztohl (zip-to-host-long) +inline uint32 ztohl ( unsigned char *buf ) { + uint32 out; + out = ( static_cast< uint32 >( buf[ 3 ] ) << 24 ) + + ( static_cast< uint32 >( buf[ 2 ] ) << 16 ) + + ( static_cast< uint32 >( buf[ 1 ] ) << 8 ) + + ( static_cast< uint32 >( buf[ 0 ] ) ) ; +// cerr << "buf : " << static_cast< int >( buf[ 0 ] ) ; +// cerr << " " << static_cast< int >( buf[ 1 ] ) ; +// cerr << " " << static_cast< int >( buf[ 2 ] ) ; +// cerr << " " << static_cast< int >( buf[ 3 ] ) << endl ; +// cerr << "uint32 " << out << endl ; + return out; +} + + +#endif + +// htozl (host-to-zip-long) +inline uint32 htozl ( unsigned char *buf ) { + return ztohl( buf ) ; +} + +// htozs (host-to-zip-short) +inline uint16 htozs ( unsigned char *buf ) { + return ztohs( buf ) ; +} + + +inline uint32 readUint32 ( istream &is ) { + static const int buf_len = sizeof ( uint32 ) ; + unsigned char buf [ buf_len ] ; + int rsf = 0 ; + while ( rsf < buf_len ) { + is.read ( reinterpret_cast< char * >( buf ) + rsf, buf_len - rsf ) ; + rsf += is.gcount () ; + } + return ztohl ( buf ) ; +} + +inline void writeUint32 ( uint32 host_val, ostream &os ) { + uint32 val = htozl( reinterpret_cast< unsigned char * >( &host_val ) ) ; + os.write( reinterpret_cast< char * >( &val ), sizeof( uint32 ) ) ; +} + +inline uint16 readUint16 ( istream &is ) { + static const int buf_len = sizeof ( uint16 ) ; + unsigned char buf [ buf_len ] ; + int rsf = 0 ; + while ( rsf < buf_len ) { + is.read ( reinterpret_cast< char * >( buf ) + rsf, buf_len - rsf ) ; + rsf += is.gcount () ; + } + return ztohs ( buf ) ; +} + +inline void writeUint16 ( uint16 host_val, ostream &os ) { + uint16 val = htozl( reinterpret_cast< unsigned char * >( &host_val ) ) ; + os.write( reinterpret_cast< char * >( &val ), sizeof( uint16 ) ) ; +} + +inline void readByteSeq ( istream &is, string &con, int count ) { + char *buf = new char [ count + 1 ] ; + int rsf = 0 ; + while ( rsf < count && is ) { + is.read ( buf + rsf, count - rsf ) ; + rsf += is.gcount() ; + } + buf [ count ] = '\0' ; + + con = buf ; + delete [] buf ; +} + +inline void writeByteSeq( ostream &os, const string &con ) { + os << con ; +} + +inline void readByteSeq ( istream &is, unsigned char *buf, int count ) { + int rsf = 0 ; + + while ( rsf < count && is ) { + is.read ( reinterpret_cast< char * >( buf ) + rsf, count - rsf ) ; + rsf += is.gcount() ; + } +} + +inline void writeByteSeq ( ostream &os, const unsigned char *buf, int count ) { + os.rdbuf()->sputn( reinterpret_cast< const char * >( buf ), count ) ; +} + +inline void readByteSeq ( istream &is, vector < unsigned char > &vec, int count ) { + unsigned char *buf = new unsigned char [ count ] ; + int rsf = 0 ; + while ( rsf < count && is ) { + is.read ( reinterpret_cast< char * >( buf ) + rsf, count - rsf ) ; + rsf += is.gcount() ; + } + + vec.insert ( vec.end (), buf, buf + count ) ; + delete [] buf ; +} + +inline void writeByteSeq ( ostream &os, const vector < unsigned char > &vec ) { + os.rdbuf()->sputn( reinterpret_cast< const char * >( &( vec[ 0 ] ) ), vec.size() ) ; +} + +istream& operator>> ( istream &is, ZipLocalEntry &zlh ) ; +istream& operator>> ( istream &is, DataDescriptor &dd ) ; +istream& operator>> ( istream &is, ZipCDirEntry &zcdh ) ; +// istream& operator>> ( istream &is, EndOfCentralDirectory &eocd ) ; + +ostream &operator<< ( ostream &os, const ZipLocalEntry &zlh ) ; +ostream &operator<< ( ostream &os, const ZipCDirEntry &zcdh ) ; +ostream &operator<< ( ostream &os, const EndOfCentralDirectory &eocd ) ; + + +} // namespace + +#endif + +/** \file + Header file that defines I/O functions for the header structures + defined in ziphead.h. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstream.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstream.h new file mode 100644 index 000000000..2849cfc03 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstream.h @@ -0,0 +1,91 @@ +#ifndef ZIPINPUTSTREAM_H +#define ZIPINPUTSTREAM_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" +#include + +#include "zipios++/ziphead.h" +#include "zipios++/zipinputstreambuf.h" + +namespace zipios { + +using std::ifstream ; + +/** \anchor ZipInputStream_anchor + ZipInputStream is an istream that gets it's input from a zip file. The + interface approximates the interface of the Java + ZipInputStream. */ +class ZipInputStream : public istream { +public: + + /** ZipInputStream constructor. + @param is istream from which the compressed zip archive can be read. + @param pos position to reposition the istream to before reading. */ + explicit ZipInputStream( istream &is, streampos pos = 0 ) ; + + /** ZipInputStream constructor. + @filename filename of a valid zip file. + @param pos position to reposition the istream to before reading. */ + explicit ZipInputStream( const string &filename, streampos pos = 0 ) ; + + int available() ; + /** Closes the current entry, and positions the stream read pointer at + the beginning of the next entry (if there is one). */ + void closeEntry() ; + + /** Closes the istream. */ + void close() ; + +// ZipLocalEntry *createZipCDirEntry( const string &name ) ; + + /** \anchor ZipInputStream_getnextentry_anchor + Opens the next entry in the zip archive and returns a const pointer to a + FileEntry object for the entry. + @return a const FileEntry * containing information about the (now) current + entry. + */ + ConstEntryPointer getNextEntry() ; + + /** Destructor. */ + virtual ~ZipInputStream() ; + +private: + ifstream *ifs ; + ZipInputStreambuf *izf ; + + /** Copy-constructor is private to prevent copying. */ + ZipInputStream( const ZipInputStream &src ) ; + + /** Copy-assignment operator is private to prevent copying. */ + const ZipInputStream &operator= ( const ZipInputStream &src ) ; + +}; + +} // namespace. + +#endif + +/** \file + Header file that defines ZipInputStream. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstreambuf.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstreambuf.h new file mode 100644 index 000000000..79d314b6d --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipinputstreambuf.h @@ -0,0 +1,89 @@ +#ifndef ZIPINPUTSTREAMBUF_H +#define ZIPINPUTSTREAMBUF_H + +#include "zipios++/zipios-config.h" + +#include + +#include + +#include "zipios++/fcoll.h" +#include "zipios++/inflateinputstreambuf.h" +#include "zipios++/ziphead.h" + +namespace zipios { + +/** ZipInputStreambuf is a zip input streambuf filter. + */ +class ZipInputStreambuf : public InflateInputStreambuf { +public: + /** ZipInputStreambuf constructor. + @param inbuf the streambuf to use for input. + @param s_pos a position to reset the inbuf to before reading. Specify + -1 to read from the current position. + @param del_inbuf if true is specified inbuf will be deleted, when + the ZipInputStreambuf is destructed. + */ + explicit ZipInputStreambuf( streambuf *inbuf, int s_pos = -1, bool del_inbuf = false ) ; + + /** Closes the current entry, and positions the stream read pointer at + the beginning of the next entry (if there is one). */ + void closeEntry() ; + /** Closes the streambuf. */ + void close() ; + + /** Opens the next entry in the zip archive and returns a const pointer to a + FileEntry object for the entry. + @return a const FileEntry * containing information about the (now) current + entry. + */ + ConstEntryPointer getNextEntry() ; + + /** Destructor. */ + virtual ~ZipInputStreambuf() ; +protected: + virtual int underflow() ; +private: + bool _open_entry ; + ZipLocalEntry _curr_entry ; + int _data_start ; // Don't forget entry header has a length too. + int _remain ; // For STORED entry only. the number of bytes that + // hasn't been put in the _outvec yet. + + /** Copy-constructor is private to prevent copying. */ + ZipInputStreambuf( const ZipInputStreambuf &src ) ; + + /** Copy-assignment operator is private to prevent copying. */ + const ZipInputStreambuf &operator= ( const ZipInputStreambuf &src ) ; + +}; + + +} // namespace + + + +#endif + +/** \file + Header file that defines ZipInputStreambuf. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h new file mode 100644 index 000000000..3d4a6d85a --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h @@ -0,0 +1,80 @@ +/* zipios++/zipios-config.h. Generated by configure. */ +/* zipios++/zipios-config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* define if the compiler implements namespaces */ +#define HAVE_NAMESPACES + +/* define if the compiler supports ISO C++ standard library */ +#define HAVE_STD + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* define if the compiler has std compliant iostream library */ +#define HAVE_STD_IOSTREAM + +/* define if the compiler supports Standard Template Library */ +#define HAVE_STL + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if zlib has zError */ +#define HAVE_ZERROR 1 + +/* Name of package */ +#define PACKAGE "zipios++" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if the std compliant iostream library should be used (if present) */ +#define USE_STD_IOSTREAM 1 + +/* Version number of package */ +#define VERSION "0.1.5" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h.in b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h.in new file mode 100644 index 000000000..273ec75b0 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios-config.h.in @@ -0,0 +1,79 @@ +/* zipios++/zipios-config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* define if the compiler implements namespaces */ +#undef HAVE_NAMESPACES + +/* define if the compiler supports ISO C++ standard library */ +#undef HAVE_STD + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* define if the compiler has std compliant iostream library */ +#undef HAVE_STD_IOSTREAM + +/* define if the compiler supports Standard Template Library */ +#undef HAVE_STL + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if zlib has zError */ +#undef HAVE_ZERROR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if the std compliant iostream library should be used (if present) */ +#undef USE_STD_IOSTREAM + +/* Version number of package */ +#undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios_defs.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios_defs.h new file mode 100644 index 000000000..41398f521 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipios_defs.h @@ -0,0 +1,34 @@ +#ifndef ZIPIOS_DEFS_H +#define ZIPIOS_DEFS_H + +namespace zipios { + +typedef unsigned short uint16 ; +typedef unsigned long uint32 ; + +} // namespace + +#endif + +/** \file + Header file that defines some simple data types. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstream.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstream.h new file mode 100644 index 000000000..e9abde557 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstream.h @@ -0,0 +1,101 @@ +#ifndef ZIPOUTPUTSTREAM_H +#define ZIPOUTPUTSTREAM_H + +#include "zipios++/zipios-config.h" + +#include "zipios++/meta-iostreams.h" + +#include + +#include "zipios++/ziphead.h" +#include "zipios++/zipoutputstreambuf.h" + +namespace zipios { + +/** \anchor ZipOutputStream_anchor + ZipOutputStream is an ostream that writes the output to a zip file. The + interface approximates the interface of the Java ZipOutputStream. */ +class ZipOutputStream : public std::ostream { +public: + + /** ZipOutputStream constructor. + @param os ostream to which the compressed zip archive is written. + @param pos position to reposition the ostream to before reading. */ + explicit ZipOutputStream( std::ostream &os ) ; + + /** ZipOutputStream constructor. + @filename filename to write the zip archive to. */ + explicit ZipOutputStream( const std::string &filename ) ; + + /** Closes the current entry updates its header with the relevant + size information and positions the stream write pointer for the + next entry header. Puts the stream in EOF state. Call + putNextEntry() to clear the EOF stream state flag. */ + void closeEntry() ; + + /** Calls finish and if the ZipOutputStream was created with a + filename as a parameter that file is closed as well. If the + ZipOutputStream was created with an ostream as its first + parameter nothing but the call to finish happens. */ + void close() ; + + /** Closes the current entry (if one is open), then writes the Zip + Central Directory Structure closing the ZipOutputStream. The + output stream that the zip archive is being written to is not + closed. */ + void finish() ; + + /** \anchor ZipOutputStream_putnextentry_anchor + Begins writing the next entry. + */ + void putNextEntry( const ZipCDirEntry &entry ) ; + + /** \anchor ZipOutputStream_putnextentry2_anchor + Begins writing the next entry. + */ + void putNextEntry(const std::string& entryName); + + /** Sets the global comment for the Zip archive. */ + void setComment( const std::string& comment ) ; + + /** Sets the compression level to be used for subsequent entries. */ + void setLevel( int level ) ; + + /** Sets the compression method to be used. only STORED and DEFLATED are + supported. */ + void setMethod( StorageMethod method ) ; + + /** Destructor. */ + virtual ~ZipOutputStream() ; + +private: + std::ofstream *ofs ; + ZipOutputStreambuf *ozf ; +}; + +} // namespace. + +#endif + +/** \file + Header file that defines ZipOutputStream. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstreambuf.h b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstreambuf.h new file mode 100644 index 000000000..606db63f6 --- /dev/null +++ b/project/jni/application/enigma/lib-src/zipios++/zipios++/zipoutputstreambuf.h @@ -0,0 +1,117 @@ +#ifndef ZIPOUTPUTSTREAMBUF_H +#define ZIPOUTPUTSTREAMBUF_H + +#include "zipios++/zipios-config.h" + +#include + +#include + +#include "zipios++/fcoll.h" +#include "zipios++/deflateoutputstreambuf.h" +#include "zipios++/ziphead.h" + + +namespace zipios { + +/** ZipOutputStreambuf is a zip input streambuf filter. */ +class ZipOutputStreambuf : public DeflateOutputStreambuf { +public: + + enum CompressionLevels { NO_COMPRESSION = Z_NO_COMPRESSION, + BEST_SPEED = Z_BEST_SPEED, + BEST_COMPRESSION = Z_BEST_COMPRESSION, + DEFAULT_COMPRESSION = Z_DEFAULT_COMPRESSION } ; + + /** ZipOutputStreambuf constructor. A newly constructed ZipOutputStreambuf + is not ready to accept data, putNextEntry() must be invoked first. + @param outbuf the streambuf to use for input. + @param del_outbuf if true is specified outbuf will be deleted, when + the ZipOutputStreambuf is destructed. */ + explicit ZipOutputStreambuf( streambuf *outbuf, bool del_outbuf = false ) ; + + /** Closes the current entry, and positions the stream read pointer at + the beginning of the next entry (if there is one). */ + void closeEntry() ; + + /** Calls finish. */ + void close() ; + + /** Closes the current entry (if one is open), then writes the Zip + Central Directory Structure closing the ZipOutputStream. The + output stream that the zip archive is being written to is not + closed. */ + void finish() ; + + /** Begins writing the next entry. + Opens the next entry in the zip archive and returns a const pointer to a + FileEntry object for the entry. + @return a const FileEntry * containing information about the (now) current + entry. */ + void putNextEntry( const ZipCDirEntry &entry ) ; + + /** Sets the global comment for the Zip archive. */ + void setComment( const string &comment ) ; + + /** Sets the compression level to be used for subsequent entries. */ + void setLevel( int level ) ; + + /** Sets the compression method to be used. only STORED and DEFLATED are + supported. */ + void setMethod( StorageMethod method ) ; + + /** Destructor. */ + virtual ~ZipOutputStreambuf() ; + +protected: + virtual int overflow( int c = EOF ) ; + virtual int sync() ; + + void setEntryClosedState() ; + void updateEntryHeaderInfo() ; + + // Should/could be moved to zipheadio.h ?! + static void writeCentralDirectory( const vector< ZipCDirEntry > &entries, + EndOfCentralDirectory eocd, + ostream &os ) ; + + + +private: + string _zip_comment ; + vector< ZipCDirEntry > _entries ; + bool _open_entry ; + bool _open ; + StorageMethod _method ; + int _level ; +}; + + +} // namespace + + + +#endif + +/** \file + Header file that defines ZipOutputStreambuf. +*/ + +/* + Zipios++ - a small C++ library that provides easy access to .zip files. + Copyright (C) 2000 Thomas Sndergaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ diff --git a/project/jni/application/enigma/src/DOMErrorReporter.cpp b/project/jni/application/enigma/src/DOMErrorReporter.cpp new file mode 100644 index 000000000..3eb8a40d8 --- /dev/null +++ b/project/jni/application/enigma/src/DOMErrorReporter.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "DOMErrorReporter.hh" +#include "main.hh" +#include "XMLtoLocal.hh" +#include +#include +#include +#include + + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + DOMErrorReporter::DOMErrorReporter(std::ostream *aLogStream) : + sawErrors(false), severity(DOMError::DOM_SEVERITY_WARNING), + logStream(aLogStream), reportStream(aLogStream) { + } + + DOMErrorReporter::~DOMErrorReporter() { + } + + // DOMCountHandlers: Overrides of the DOM ErrorHandler interface + bool DOMErrorReporter::handleError(const DOMError& domError) { + sawErrors = true; + if (domError.getSeverity() == DOMError::DOM_SEVERITY_WARNING) { + if(reportStream) {*reportStream << "\nWarning ";} + } + else if (domError.getSeverity() == DOMError::DOM_SEVERITY_ERROR) { + if(reportStream) {*reportStream << "\nError ";} + if (severity == DOMError::DOM_SEVERITY_WARNING) + severity = DOMError::DOM_SEVERITY_ERROR; + } + else { + if(reportStream) {*reportStream << "\nFatal Error ";} + severity = DOMError::DOM_SEVERITY_FATAL_ERROR; + } + + if(reportStream) { + const XMLCh * const fileURI = domError.getLocation()->getURI(); + if( fileURI && (XMLString::stringLen(fileURI) > 0)) { + *reportStream << "at file " + << XMLtoLocal(fileURI) + << ", line " << domError.getLocation()->getLineNumber() + << ", char " << domError.getLocation()->getColumnNumber(); + } + *reportStream << "\n Message: " << XMLtoLocal(domError.getMessage()) + << std::endl; + } + + // try to continue + return true; + } + + bool DOMErrorReporter::getSawErrors() const { + return sawErrors; + } + + DOMError::ErrorSeverity DOMErrorReporter::getSeverity() const { + return severity; + } + + void DOMErrorReporter::resetErrors() { + sawErrors = false; + severity = DOMError::DOM_SEVERITY_WARNING; + } + + void DOMErrorReporter::reportToLog() { + reportStream = logStream; + } + + void DOMErrorReporter::reportToErr() { + reportStream = &std::cerr; + } + + void DOMErrorReporter::reportToNull() { + reportStream = NULL; + } + + void DOMErrorReporter::reportToOstream(std::ostream *anOstream) { + reportStream = anOstream; + } + +} // namespace enigma + diff --git a/project/jni/application/enigma/src/DOMErrorReporter.hh b/project/jni/application/enigma/src/DOMErrorReporter.hh new file mode 100644 index 000000000..c55feaea4 --- /dev/null +++ b/project/jni/application/enigma/src/DOMErrorReporter.hh @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_DOMERRORREPORTER_HH +#define ENIGMA_DOMERRORREPORTER_HH + +#include +#include +#include + +namespace enigma +{ + /** + * Error handler for DOM parser and serializer. It reports the error + * messages according to there severity as a log note or an alert panel + */ + class DOMErrorReporter : public XERCES_CPP_NAMESPACE_QUALIFIER DOMErrorHandler + { + public: + /** + * Main constructor. + * @param aLogStream the stream for error reports + */ + DOMErrorReporter(std::ostream *aLogStream); + ~DOMErrorReporter(); + + /** + * Implements the DOM ErrorHandler interface. The error is reported + * and the maximum error typ is stored for polling evalutaion. + * @param domError the error description + * @return true to continue parse or serialization + */ + bool handleError(const XERCES_CPP_NAMESPACE_QUALIFIER DOMError& domError); + + /** + * Returns true if error or warning occured since last reset + */ + bool getSawErrors() const; + + /** + * Returns maximum severity of error since last reset + */ + XERCES_CPP_NAMESPACE_QUALIFIER DOMError::ErrorSeverity getSeverity() const; + + /** + * Resets error flag and severity + */ + void resetErrors(); + + /** + * Switch on report output to log stream + */ + void reportToLog(); + + /** + * Switch on report output to err stream + */ + void reportToErr(); + + /** + * Switch off report output + */ + void reportToNull(); + + /** + * Switch on report output to given stream + */ + void reportToOstream(std::ostream *anOstream); + + private : + // Unimplemented constructors and operators + DOMErrorReporter(const DOMErrorReporter&); + void operator=(const DOMErrorReporter&); + + /** + * Flag for warnings or errors that occured since last reset + */ + bool sawErrors; + + /** + * Most significant error type since last reset + */ + XERCES_CPP_NAMESPACE_QUALIFIER DOMError::ErrorSeverity severity; + + /** + * Current output stream for error reports + */ + std::ostream *reportStream; + + /** + * The configured log output stream + */ + std::ostream *logStream; + }; + +} // namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/DOMSchemaResolver.cpp b/project/jni/application/enigma/src/DOMSchemaResolver.cpp new file mode 100644 index 000000000..2fba8484b --- /dev/null +++ b/project/jni/application/enigma/src/DOMSchemaResolver.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "DOMSchemaResolver.hh" +#include "main.hh" +#include "file.hh" +#include "XMLtoLocal.hh" +#include "LocalToXML.hh" +#include +#if _XERCES_VERSION >= 30000 +#include +#include + +#else +#include +#include +#endif + +#include + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + DOMSchemaResolver::DOMSchemaResolver():resolveStatus(false) { + } + + DOMSchemaResolver::~DOMSchemaResolver() { + } + + void DOMSchemaResolver::resetResolver() { + substitutions.clear(); + resolveStatus = false; + } + + bool DOMSchemaResolver::didResolveSchema() { + return resolveStatus; + } + + void DOMSchemaResolver::resetResolveStatus() { + resolveStatus = false; + } + + void DOMSchemaResolver::addSchemaId(const std::string &schemaSystemId, + const std::string &schemaFilename) { + substitutions.insert(std::make_pair(schemaSystemId, schemaFilename)); + } + +#if _XERCES_VERSION >= 30000 + DOMLSInput * DOMSchemaResolver::resolveResource ( + const XMLCh* const resourceType, const XMLCh* const namespaceUri, + const XMLCh *const publicId, const XMLCh *const systemId, + const XMLCh *const baseURI) { + + std::string schemaName = XMLtoLocal(systemId).c_str(); + std::map::iterator i = substitutions.find(schemaName); + if (i == substitutions.end()) { + Log << "DOMSchemaResolver: no schema substitution found for '" + << schemaName << "'\n"; + // let the parser try to resolve potential external entities + return NULL; + } else { + std::string schemaPath; + bool result = app.systemFS->findFile( std::string("schemas/") + + i->second , schemaPath); + if (result) { + DOMLSInput * inSrc = new Wrapper4InputSource(new LocalFileInputSource( + LocalToXML(&schemaPath).x_str())); +// W3C implementation: +// DOMLSInput * inSrc = app.domImplementationLS->createLSInput(); +// inSrc->setSystemId(LocalToXML(&schemaPath).x_str())); + return inSrc; + } else { + Log << "DOMSchemaResolver: schema file '" + << i->second << "' not found\n"; + // let the parser try to resolve the schema + return NULL; + } + } + } +#else + DOMInputSource * DOMSchemaResolver::resolveEntity ( + const XMLCh *const publicId, const XMLCh *const systemId, + const XMLCh *const baseURI) { + + std::string schemaName = XMLtoLocal(systemId).c_str(); + std::map::iterator i = substitutions.find(schemaName); + if (i == substitutions.end()) { + Log << "DOMSchemaResolver: no schema substitution found for '" + << schemaName << "'\n"; + // let the parser try to resolve potential external entities + return NULL; + } else { + std::string schemaPath; + bool result = app.systemFS->findFile( std::string("schemas/") + + i->second , schemaPath); + if (result) { + DOMInputSource * inSrc = new Wrapper4InputSource(new LocalFileInputSource( + LocalToXML(&schemaPath).x_str())); + return inSrc; + } else { + Log << "DOMSchemaResolver: schema file '" + << i->second << "' not found\n"; + // let the parser try to resolve the schema + return NULL; + } + } + } +#endif +} // namespace enigma + diff --git a/project/jni/application/enigma/src/DOMSchemaResolver.hh b/project/jni/application/enigma/src/DOMSchemaResolver.hh new file mode 100644 index 000000000..a7d17239f --- /dev/null +++ b/project/jni/application/enigma/src/DOMSchemaResolver.hh @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_DOMSCHEMARESOLVER_HH +#define ENIGMA_DOMSCHEMARESOLVER_HH + +#include +#include +#include +#if _XERCES_VERSION >= 30000 +#include +#include +#include +#else +#include +#include +#include +#endif + +namespace enigma +{ + /** + * An XML schema system id to local schema storage path resolver. + * The resolver can be configured with several pairs of system id and + * paths. This should allow usage of versioned schema system ids like + * http://enigma/1.0/levels.xsd . All entities without local substitution + * are resolved by the parser using the given system id. A flag allows to + * check if the XML file did adhere to a given schema. + */ +#if _XERCES_VERSION >= 30000 + class DOMSchemaResolver : public XERCES_CPP_NAMESPACE_QUALIFIER DOMLSResourceResolver { + public: + XERCES_CPP_NAMESPACE_QUALIFIER DOMLSInput * resolveResource ( + const XMLCh* const resourceType, const XMLCh* const namespaceUri, + const XMLCh *const publicId, const XMLCh *const systemId, + const XMLCh *const baseURI); +#else + class DOMSchemaResolver : public XERCES_CPP_NAMESPACE_QUALIFIER DOMEntityResolver { + public: + XERCES_CPP_NAMESPACE_QUALIFIER DOMInputSource * resolveEntity ( + const XMLCh *const publicId, const XMLCh *const systemId, + const XMLCh *const baseURI); +#endif + DOMSchemaResolver(); + ~DOMSchemaResolver(); + + /** + * clear system id substitution paths and reset result flag. + */ + void resetResolver(); + + /** + * add a system id substituion path. + * @param schemaSystemId as used in XML files + * @param schemaFilename the local substitution filename of the schema. + * Just the filename without any path components. + */ + void addSchemaId(const std::string &schemaSystemId, const std::string &schemaFilename); + + /** + * true if at least one local substitution occured since last reset. + */ + bool didResolveSchema(); + + /** + * just reset status without deletion of substitution info. + */ + void resetResolveStatus(); + private: + std::map substitutions; + bool resolveStatus; + }; +} // namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/Inventory.cpp b/project/jni/application/enigma/src/Inventory.cpp new file mode 100644 index 000000000..b013f5771 --- /dev/null +++ b/project/jni/application/enigma/src/Inventory.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "Inventory.hh" +#include "items.hh" +#include "ecl_util.hh" +#include + +using enigma::Inventory; +using world::Item; + +typedef std::vector ItemList; + +/** The maximum number of items that may be stored in an inventory. + For compatibility with Oxyd this should be always 12. */ +unsigned const Inventory::max_items = 12; + + +Inventory::Inventory() : m_items() +{} + + +Inventory::~Inventory() +{ + clear(); +} + + +size_t Inventory::size() const +{ + return m_items.size(); +} + + +void Inventory::clear() +{ + ecl::delete_sequence(m_items.begin(), m_items.end()); + m_items.clear(); +} + + +Item * Inventory::get_item (size_t idx) const +{ + return idx >= size() ? 0 : m_items[idx]; +} + + +Item * Inventory::yield_item (size_t idx) +{ + if (idx < size()) { + Item *it = m_items[idx]; + m_items.erase(m_items.begin()+ idx); + return it; + } + return 0; +} + + +Item * Inventory::yield_first() +{ + return yield_item(0); +} + + +Item * Inventory::remove_item(Item *wanted) +{ + ItemList::iterator e = m_items.end(); + for (ItemList::iterator i = m_items.begin(); i != e; ++i) { + if (*i == wanted) { + m_items.erase(i); + return wanted; + } + } + return 0; +} + + +bool Inventory::is_full() const +{ + ItemHolder *holder = dynamic_cast(get_item(0)); + if (holder && !holder->is_full()) + return false; + return m_items.size() >= max_items; +} + +bool Inventory::is_empty() const { + return m_items.size() == 0; +} + +void Inventory::add_item(Item *i) +{ + ItemHolder *firstHolder = dynamic_cast(get_item(0)); + ItemHolder *addHolder = dynamic_cast(i); + if (firstHolder && !firstHolder->is_full() && + (addHolder == NULL || !addHolder->is_empty())) { + // first item is a bag and not full and add item is not an empty bag + firstHolder->add_item (i); + } + else { + m_items.insert (m_items.begin(), i); + } +} + +void Inventory::takeItemsFrom(ItemHolder *ih) { + ItemHolder *holder = dynamic_cast(get_item(0)); + if (holder && !holder->is_full()) { + // first item is a bag and not full - do not refill items form one + // itemholder into another + return; + } + else { + while (m_items.size() < max_items && !ih->is_empty()) { + Item * it = ih->yield_first(); + m_items.insert (m_items.begin(), it); + } + return; + } +} + +void Inventory::rotate_left () +{ + if (!m_items.empty()) + std::rotate(m_items.begin(), m_items.begin()+1, m_items.end()); +} + + +void Inventory::rotate_right () +{ + if (!m_items.empty()) + std::rotate(m_items.begin(), m_items.end()-1, m_items.end()); +} + +bool Inventory::willAddItem(Item *it) { + ItemHolder *holder = dynamic_cast(it); + if (is_full()) { + return false; + } else if (holder != NULL && holder->is_empty() && + (m_items.size() >= max_items || dynamic_cast(get_item(0)) != NULL)) { + // should add a bag that is empty, but first item in Inventory is itself + // a bag or Inventory is full -- avoid recursive bags + return false; + } + return true; // we have space and item is not critical +} + + +int Inventory::find(const std::string& kind, size_t start_idx) const +{ + size_t size_ = size(); + for (size_t i = start_idx; iis_kind(kind)) + return static_cast (i); + } + return -1; +} diff --git a/project/jni/application/enigma/src/Inventory.hh b/project/jni/application/enigma/src/Inventory.hh new file mode 100644 index 000000000..bf562810e --- /dev/null +++ b/project/jni/application/enigma/src/Inventory.hh @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef INVENTORY_HH_INCLUDED +#define INVENTORY_HH_INCLUDED + +#include "ItemHolder.hh" +#include +#include +#include + +namespace enigma +{ + using world::Item; // TODO: remove this after moving Item into enigma namespace + + class Inventory : public ItemHolder { + public: + Inventory(); + ~Inventory(); + + // ---------- ItemHolder interface ---------- + bool is_full() const; + virtual bool is_empty() const; + void add_item (Item *i); + virtual void takeItemsFrom(ItemHolder *ih); + virtual Item *yield_first(); + + // ---------- Methods ---------- + + //! The number of items currently in the inventory + size_t size() const; + void clear(); + + void rotate_left(); + void rotate_right(); + Item *get_item (size_t idx) const; + Item *yield_item (size_t idx); + bool willAddItem(Item *it); + + int find(const std::string& kind, size_t start_idx = 0) const; + + private: + // Private methods. + Item *remove_item(Item *it); + + // Private variables. + static const unsigned max_items; + std::vector m_items; + }; +} + +#endif diff --git a/project/jni/application/enigma/src/ItemHolder.hh b/project/jni/application/enigma/src/ItemHolder.hh new file mode 100644 index 000000000..4e9f76a91 --- /dev/null +++ b/project/jni/application/enigma/src/ItemHolder.hh @@ -0,0 +1,29 @@ +#ifndef ITEMHOLDER_HH_INCLUDED +#define ITEMHOLDER_HH_INCLUDED + +#include "items.hh" + +namespace enigma +{ + /** + * A base class for all entities that can hold multiple items + * during the game. Currently this is only the inventory that is + * displayed at the bottom of the screen (there is one inventory + * for every player) and the it-bag item. + */ + class ItemHolder { + public: + virtual ~ItemHolder() {} + + //! Return true if not further object can be picked up + virtual bool is_full() const = 0; + virtual bool is_empty() const = 0; + + //! Add another item + virtual void add_item (world::Item *it) = 0; + virtual world::Item *yield_first() = 0; + virtual void takeItemsFrom(ItemHolder *ih) {return;} + }; +} + +#endif diff --git a/project/jni/application/enigma/src/LocalToXML.cpp b/project/jni/application/enigma/src/LocalToXML.cpp new file mode 100644 index 000000000..42c8a782c --- /dev/null +++ b/project/jni/application/enigma/src/LocalToXML.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "LocalToXML.hh" + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + LocalToXML::LocalToXML(const char * const toTranscode) { + // Use XML for transcoding -- the returned string is owned by us + // but managed by XMLString! + xmlString = XMLString::transcode(toTranscode); + } + + LocalToXML::LocalToXML(const std::string * const toTranscode) { + // Use XML for transcoding -- the returned string is owned by us + // but managed by XMLString! + xmlString = XMLString::transcode(toTranscode->c_str()); + } + + LocalToXML::~LocalToXML() { + XMLString::release(&xmlString); + } + + const XMLCh * LocalToXML::x_str() const { + return xmlString; + } + +} //namespace enigma + diff --git a/project/jni/application/enigma/src/LocalToXML.hh b/project/jni/application/enigma/src/LocalToXML.hh new file mode 100644 index 000000000..8242b5916 --- /dev/null +++ b/project/jni/application/enigma/src/LocalToXML.hh @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_LOCALTOXML_HH +#define ENIGMA_LOCALTOXML_HH + +#include +#include + + +namespace enigma +{ + /** + * Transcoding utility for local code page strings to XMLCh strings. + * Provides a simple interface for the memory management paradigm shift + * even though not terribly efficient. Make sure all LocalToXML objects + * exist only temporarily, f.e. + * DOMNode::setNodeValue(LocalToXML(char *toTranscode).x_str()) or + * XMLString::replicate(LocalToXML(char *toTranscode).x_str()) + * Xerces should be initialized before using this class and all objects + * should be deleted before terminating. + */ + class LocalToXML { + public : + /** + * Makes a transcoding to XML. + * + * @param toTranscode local code page string + */ + LocalToXML(const char * const toTranscode); + + /** + * Makes a transcoding to XML. + * + * @param toTranscode local code page string + */ + LocalToXML(const std::string * const toTranscode); + ~LocalToXML(); + + /** + * Returns the string coded in the local page. It remains owner of + * the string. + */ + const XMLCh * x_str() const; + + private : + /** + * A XML copy. We are the owner. + */ + XMLCh * xmlString; + void init(const char * const toTranscode); + }; + +} //namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/PreferenceManager.cpp b/project/jni/application/enigma/src/PreferenceManager.cpp new file mode 100644 index 000000000..c51094116 --- /dev/null +++ b/project/jni/application/enigma/src/PreferenceManager.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "PreferenceManager.hh" +#include "main.hh" +#include "DOMErrorReporter.hh" +#include "DOMSchemaResolver.hh" +#include "LocalToXML.hh" +#include "nls.hh" +#include "Utf8ToXML.hh" +#include "utilXML.hh" +#include "options.hh" +#include "XMLtoLocal.hh" +#include "XMLtoUtf8.hh" +#include "ecl_system.hh" +#include "gui/ErrorMenu.hh" + +#include +#include +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#endif + + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +namespace enigma { + PreferenceManager *PreferenceManager::theSingleton = 0; + + PreferenceManager* PreferenceManager::instance() { + if (theSingleton == 0) { + theSingleton = new PreferenceManager(); + } + return theSingleton; + } + + PreferenceManager::PreferenceManager() { + std::string prefTemplatePath; + bool haveXMLProperties = (ecl::FileExists(app.prefPath)) ? true : false; + + if (!app.systemFS->findFile( std::string("schemas/") + PREFFILENAME , prefTemplatePath)) { + cerr << "Preferences: no template found\n"; + exit(-1); + } + + try { + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToErr(); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("preferences.xsd","preferences.xsd"); + + if (haveXMLProperties) { + // update existing XML prefs from possibly newer template: + // use user prefs and copy new properties from template + doc = app.domParser->parseURI(app.prefPath.c_str()); + propertiesElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("properties").x_str())->item(0)); + // The following algorithm is not optimized - O(n^2)! + DOMDocument * prefTemplate = app.domParser->parseURI(prefTemplatePath.c_str()); + DOMNodeList * tmplPropList = prefTemplate->getElementsByTagName( + Utf8ToXML("property").x_str()); + for (int i = 0, l = tmplPropList-> getLength(); i < l; i++) { + DOMElement *tmplProperty = dynamic_cast(tmplPropList->item(i)); + const XMLCh * key = tmplProperty->getAttribute(Utf8ToXML("key").x_str()); + DOMElement * lastUserProperty; + if ((key[0] != chUnderscore) && !hasProperty(key, &lastUserProperty)) { + // a new property in the template + Log << "Preferences: add new Property \"" << XMLtoLocal(key) << "\"\n"; + // make a copy + DOMNode * newProperty = doc->importNode(tmplProperty, false); + if (newProperty == NULL) { + Log << "Preferences: copy new Property failed!\n"; + } else { + // insert it at the end of the existing user properties + propertiesElem->appendChild(dynamic_cast(newProperty)); + } + } + } + prefTemplate->release(); + } else { + // update from LUA options to XML preferences: + // use the template, copy LUA option values and save it later as prefs + doc = app.domParser->parseURI(prefTemplatePath.c_str()); + propertiesElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("properties").x_str())->item(0)); + DOMNodeList * propList = propertiesElem->getElementsByTagName(Utf8ToXML("property").x_str()); + for (int i = 0, l = propList-> getLength(); i < l; i++) { + DOMElement * property = dynamic_cast(propList->item(i)); + const XMLCh * key = property->getAttribute(Utf8ToXML("key").x_str()); + std::string optionValue; + if (options::HasOption(XMLtoLocal(key).c_str(), optionValue)) { + Log << "Preferences: copy LUA option \"" << XMLtoLocal(key) << "\"\n"; + property->setAttribute(Utf8ToXML("value").x_str(), + LocalToXML(&optionValue).x_str()); + } else { + Log << "Preferences: no LUA option \"" << XMLtoLocal(key) << "\"\n"; + } + } + } + } + catch (const XMLException& toCatch) { + char* message = XMLString::transcode(toCatch.getMessage()); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + exit (-1); + } + catch (const DOMException& toCatch) { + char* message = XMLString::transcode(toCatch.msg); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + exit (-1); + } + catch (...) { + cerr << "Unexpected Exception on load of preferences\n" ; + exit (-1); + } + } + + PreferenceManager::~PreferenceManager() { + if (doc != NULL) + shutdown(); + } + + bool PreferenceManager::save() { + bool result = true; + std::string errMessage; + + if (doc == NULL) + return true; + + stripIgnorableWhitespace(doc->getDocumentElement()); + + try { +#if _XERCES_VERSION >= 30000 + result = app.domSer->writeToURI(doc, LocalToXML(& app.prefPath).x_str()); +#else + XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(app.prefPath.c_str()); + result = app.domSer->writeNode(myFormTarget, *doc); + delete myFormTarget; // flush +#endif + } catch (const XMLException& toCatch) { + errMessage = std::string("Exception on save of preferences: \n") + + XMLtoUtf8(toCatch.getMessage()).c_str() + "\n"; + result = false; + } catch (const DOMException& toCatch) { + errMessage = std::string("Exception on save of preferences: \n") + + XMLtoUtf8(toCatch.getMessage()).c_str() + "\n"; + result = false; + } catch (...) { + errMessage = "Unexpected exception on save of preferences\n" ; + result = false; + } + + if (!result) { + cerr << errMessage; + gui::ErrorMenu m(errMessage, N_("Continue")); + m.manage(); + } else + Log << "Preferences save o.k.\n"; + + return result; + } + + void PreferenceManager::shutdown() { + save(); + if (doc != NULL) + doc->release(); + doc = NULL; + } + +} // namespace enigma + diff --git a/project/jni/application/enigma/src/PreferenceManager.hh b/project/jni/application/enigma/src/PreferenceManager.hh new file mode 100644 index 000000000..7c196a682 --- /dev/null +++ b/project/jni/application/enigma/src/PreferenceManager.hh @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PREFMGR_HH_INCLUDED +#define PREFMGR_HH_INCLUDED + +#include "PropertyManager.hh" + +namespace enigma +{ + /** + * A singelton manager for preferences stored in XML format. + *

The singleton can be accessed via standard static instance() method or + * the application public ivar app.prefs.

+ *

During initialization old LUA stored options are convereted. New + * preferences with given default values are introduced to the user via the + * template in the systen data directory (data/enigmarc.xml).

+ *

The storage location of the users preferences is determined by the + * Application object.

+ *

The set and get methods for preferences take utf8 encoded preference + * names and operate with utf8 encoded string values. Set methods create new + * preference property elements if necessary. Get methods do not modify + * reference values and return C++ default values if the named preference + * does not exist.

+ */ + class PreferenceManager : public PropertyManager { + public: + static PreferenceManager *instance(); + ~PreferenceManager(); + virtual bool save(); + void shutdown(); + + protected: + PreferenceManager(); + private: + static PreferenceManager *theSingleton; + }; +} // namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/PropertyManager.cpp b/project/jni/application/enigma/src/PropertyManager.cpp new file mode 100644 index 000000000..a2d28610c --- /dev/null +++ b/project/jni/application/enigma/src/PropertyManager.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "PropertyManager.hh" +#include "errors.hh" +#include "main.hh" +#include "DOMErrorReporter.hh" +#include "DOMSchemaResolver.hh" +#include "LocalToXML.hh" +#include "Utf8ToXML.hh" +#include "options.hh" +#include "XMLtoLocal.hh" +#include "XMLtoUtf8.hh" +#include "ecl_system.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#endif + + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +namespace enigma { + PropertyManager::PropertyManager() : doc (NULL), propertiesElem (NULL) { + } + + PropertyManager::~PropertyManager() { + } + + void PropertyManager::setProperty(const char *prefName, const std::string &value) { + DOMElement * property = getPropertyElement(prefName); + + property->setAttribute(Utf8ToXML("value").x_str(), + Utf8ToXML(&value).x_str()); + } + + void PropertyManager::setProperty(const char *prefName, const char *value) { + setProperty(prefName, std::string(value)); + } + + void PropertyManager::getProperty(const char *prefName, std::string &value) { + DOMElement * property; + bool propFound = hasProperty(prefName, &property); + if (propFound) { + value = XMLtoUtf8(property->getAttribute(Utf8ToXML("value").x_str())).c_str(); + } + } + + std::string PropertyManager::getString(const char *prefName) { + std::string value; + getProperty(prefName, value); + return value; + } + + void PropertyManager::setProperty(const char *prefName, const double &value) { + char printedValue[20]; + sprintf(printedValue, "%.7g", value); + DOMElement * property = getPropertyElement(prefName); + property->setAttribute(Utf8ToXML("value").x_str(), + Utf8ToXML(printedValue).x_str()); + } + + void PropertyManager::getProperty(const char *prefName, double &value) { + DOMElement * property; + bool propFound = hasProperty(prefName, &property); + if (propFound) { + XMLDouble * result = new XMLDouble(property->getAttribute(Utf8ToXML("value").x_str())); + value = result->getValue(); + delete result; + } + } + + double PropertyManager::getDouble(const char *prefName) { + double value = 0; + getProperty (prefName, value); + return value; + } + + void PropertyManager::setProperty(const char *prefName, const int &value) { + char printedValue[20]; + sprintf(printedValue, "%d", value); + DOMElement * property = getPropertyElement(prefName); + + property->setAttribute(Utf8ToXML("value").x_str(), + Utf8ToXML(printedValue).x_str()); + } + + void PropertyManager::getProperty(const char *prefName, int &value) { + DOMElement * property; + bool propFound = hasProperty(prefName, &property); + if (propFound) { + value = XMLString::parseInt(property->getAttribute(Utf8ToXML("value").x_str())); + } + } + + int PropertyManager::getInt(const char *prefName) { + int value = 0; + getProperty (prefName, value); + return value; + } + + void PropertyManager::setProperty(const char *prefName, const bool &value) { + int i = (value ? 1: 0); + setProperty(prefName, i); + } + + void PropertyManager::getProperty(const char *prefName, bool &value) { + int result = 0; + getProperty(prefName, result); + value = ((result == 0) ? false: true); + } + + bool PropertyManager::getBool(const char *prefName) { + bool value = false; + getProperty (prefName, value); + return value; + } + + DOMElement * PropertyManager::getPropertyElement(const char *prefName) { + ASSERT(propertiesElem != NULL, XFrontend, ""); + ASSERT(doc != NULL, XFrontend, ""); + XMLCh * key = XMLString::replicate(Utf8ToXML(prefName).x_str()); + DOMElement * property; + bool propFound = hasProperty(prefName, &property); + + if (!propFound) { + DOMElement * newProperty = doc->createElement (Utf8ToXML("property").x_str()); + newProperty->setAttribute(Utf8ToXML("key").x_str(), key); + // insert it at the end of the existing properties + propertiesElem->appendChild(newProperty); + property = newProperty; + } + XMLString::release(&key); + return property; + } + + bool PropertyManager::hasProperty(const char *prefName, DOMElement ** element) { + return hasProperty(Utf8ToXML(prefName).x_str(), element); + } + + bool PropertyManager::hasProperty(const XMLCh * key, DOMElement ** element) { + ASSERT(propertiesElem != NULL, XFrontend, ""); + bool propFound = false; + DOMElement * property; + +// Xerces 3.0 has no full XPath support - otherwise the following simple +// statement would suffice and not be aborted with NOT_SUPPORTED_ERR. +// doc->evaluate(Utf8ToXML("//property[@key='...']").x_str(), doc, NULL, 0, NULL); + + DOMNodeList * propList = propertiesElem->getElementsByTagName(Utf8ToXML("property").x_str()); + for (int i = 0, l = propList-> getLength(); i < l && !propFound; i++) { + property = dynamic_cast(propList->item(i)); + if (XMLString::equals(key, + property->getAttribute(Utf8ToXML("key").x_str()))) { + propFound = true; + } + } + * element = property; + return propFound; + } +} // namespace enigma diff --git a/project/jni/application/enigma/src/PropertyManager.hh b/project/jni/application/enigma/src/PropertyManager.hh new file mode 100644 index 000000000..c219812f3 --- /dev/null +++ b/project/jni/application/enigma/src/PropertyManager.hh @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PROPERTYMGR_HH_INCLUDED +#define PROPERTYMGR_HH_INCLUDED + +#include +#include +#include + +namespace enigma +{ + /** + * An abstact Superclass for key/value property access. + */ + class PropertyManager { + public: + ~PropertyManager(); + virtual bool save() = 0; // mark the class as abstract + + void setProperty(const char *prefName, const std::string &value); + void setProperty(const char *prefName, const char *value); + void getProperty(const char *prefName, std::string &value); + std::string getString(const char *prefName); + void setProperty(const char *prefName, const double &value); + void getProperty(const char *prefName, double &value); + double getDouble(const char *prefName); + void setProperty(const char *prefName, const int &value); + void getProperty(const char *prefName, int &value); + int getInt(const char *prefName); + void setProperty(const char *prefName, const bool &value); + void getProperty(const char *prefName, bool &value); + bool getBool(const char *prefName); + + protected: + PropertyManager(); + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *propertiesElem; + + /** + * force a return of a property element with the given name as key. + * @param prefName the name of the searched or new property + * @return the property element with the given key name. + */ + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getPropertyElement(const char *prefName); + + /** + * checks if a property exists. + * @param prefName the preference name. + * @param element the searched property element or the last property + * element found in the preference list (for append usage). + * @return validity of element. + */ + bool hasProperty(const char *prefName, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement ** element); + /** + * checks if a property exists. + * @param key the preference name. + * @param element the searched property element or the last property + * element found in the preference list (for append usage). + * @return validity of element. + */ + bool hasProperty(const XMLCh * key, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement ** element); + }; +} // namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/StateManager.cpp b/project/jni/application/enigma/src/StateManager.cpp new file mode 100644 index 000000000..95efc0a07 --- /dev/null +++ b/project/jni/application/enigma/src/StateManager.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "StateManager.hh" +#include "errors.hh" +#include "main.hh" +#include "DOMErrorReporter.hh" +#include "DOMSchemaResolver.hh" +#include "LocalToXML.hh" +#include "nls.hh" +#include "options.hh" +#include "Utf8ToXML.hh" +#include "utilXML.hh" +#include "XMLtoLocal.hh" +#include "XMLtoUtf8.hh" +#include "ecl_system.hh" +#include "gui/ErrorMenu.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#endif + + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +namespace enigma { + StateManager *StateManager::theSingleton = 0; + + StateManager* StateManager::instance() { + if (theSingleton == 0) { + theSingleton = new StateManager(); + } + return theSingleton; + } + + StateManager::StateManager() { + std::string statePath; + std::string errMessage; + + if (!app.resourceFS->findFile( "state.xml" , statePath)) { + if (!app.systemFS->findFile( "schemas/state.xml" , statePath)) { + throw XFrontend("Cannot load application state template!"); + } + } + + try { + std::ostringstream errStream; + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToOstream(&errStream); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("state.xsd","state.xsd"); + + doc = app.domParser->parseURI(statePath.c_str()); + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + propertiesElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("properties").x_str())->item(0)); + groupsElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("groups").x_str())->item(0)); + groupList = groupsElem->getElementsByTagName( + Utf8ToXML("group").x_str()); + indicesElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("indices").x_str())->item(0)); + indexList = indicesElem->getElementsByTagName( + Utf8ToXML("index").x_str()); + levelsElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("levels").x_str())->item(0)); + } + if(app.domParserErrorHandler->getSawErrors()) { + errMessage = errStream.str(); + } + app.domParserErrorHandler->reportToNull(); // do not report to errStream any more + } + catch (...) { + errMessage = "Unexpected XML Exception on load of state\n"; + } + if (!errMessage.empty()) { + throw XFrontend("Cannot load application state file: " + statePath + + "\nError: " + errMessage); + } + } + + StateManager::~StateManager() { + if (doc != NULL) + shutdown(); + } + + bool StateManager::save() { + bool result = true; + std::string errMessage; + + if (doc == NULL) + return true; + + int count = getInt("Count"); + setProperty("Count", ++count); + + stripIgnorableWhitespace(doc->getDocumentElement()); + std::string path = app.userPath + "/state.xml"; + std::string pathBackup = app.userPath + "/backup/state.xml"; + + // backup state every 10th save + if (count%10 == 0) { + std::remove((pathBackup + "~2").c_str()); + std::remove((path + "~2").c_str()); // 1.00 bakups + if (ecl::FileExists(path + "~1")) { + if (std::difftime(ecl::FileModTime(path + "~1"), + ecl::FileModTime(pathBackup + "~1")) > 0) { + // backup 1 from 1.00 is newer than backup 1 on backup path + if (Copyfile(path + "~1", pathBackup + "~2")) + std::remove((path + "~1").c_str()); // 1.00 bakup + } else { + // just in case off previous copy failure + std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str()); + std::remove((path + "~1").c_str()); // 1.00 bakup + } + } else { + std::rename((pathBackup + "~1").c_str(), (pathBackup + "~2").c_str()); + } + Copyfile(path, pathBackup + "~1"); + } + + try { +#if _XERCES_VERSION >= 30000 + result = app.domSer->writeToURI(doc, LocalToXML(& path).x_str()); +#else + XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(path.c_str()); + result = app.domSer->writeNode(myFormTarget, *doc); + delete myFormTarget; // flush +#endif + } catch (const XMLException& toCatch) { + errMessage = std::string("Exception on save of state: \n") + + XMLtoUtf8(toCatch.getMessage()).c_str() + "\n"; + result = false; + } catch (const DOMException& toCatch) { + errMessage = std::string("Exception on save of state: \n") + + XMLtoUtf8(toCatch.getMessage()).c_str() + "\n"; + result = false; + } catch (...) { + errMessage = "Unexpected exception on save of state\n" ; + result = false; + } + + if (!result) { + if (count%10 == 0) { + // restore backup in case of error + if (Copyfile(pathBackup + "~1", path)) { + std::remove((pathBackup + "~1").c_str()); + std::rename((pathBackup + "~2").c_str(), (pathBackup + "~1").c_str()); + } + } + cerr << XMLtoLocal(Utf8ToXML(errMessage.c_str()).x_str()).c_str(); + gui::ErrorMenu m(errMessage, N_("Continue")); + m.manage(); + } else + Log << "State save o.k.\n"; + + return result; + } + + void StateManager::shutdown() { + save(); + if (doc != NULL) + doc->release(); + doc = NULL; + } + + void StateManager::getGroupNames(std::vector *names) { + for (int i = 0, l = groupList-> getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + names->push_back(XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()); + } + } + + std::string StateManager::getGroupSelectedIndex(std::string groupName) { + for (int i = 0, l = groupList-> getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) + return XMLtoUtf8(group->getAttribute(Utf8ToXML("curindex").x_str())).c_str(); + } + return ""; + } + + std::string StateManager::getGroupSelectedColumn(std::string groupName) { + for (int i = 0, l = groupList-> getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) + return XMLtoUtf8(group->getAttribute(Utf8ToXML("curcolumn").x_str())).c_str(); + } + return 0; + } + + void StateManager::setGroupSelectedIndex(std::string groupName, std::string indexName) { + for (int i = 0, l = groupList-> getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str()); + return; + } + } + } + + void StateManager::setGroupSelectedColumn(std::string groupName, std::string column) { + for (int i = 0, l = groupList-> getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str()); + return; + } + } + } + + void StateManager::addGroup(std::string groupName, std::string indexName, int column) { + // check if group exists - update attributes only + for (int i = 0, l = groupList->getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + group->setAttribute(Utf8ToXML("curcolumn").x_str(), + Utf8ToXML(ecl::strf("%d",column)).x_str()); + group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str()); + return; + } + } + // if group does not exist add a new element + DOMElement * group = doc->createElement (Utf8ToXML("group").x_str()); + group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str()); + group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str()); + group->setAttribute(Utf8ToXML("curcolumn").x_str(), + Utf8ToXML(ecl::strf("%d",column)).x_str()); + groupsElem->appendChild(group); + } + + void StateManager::insertGroup(int pos, std::string groupName, + std::string indexName, std::string column) { + DOMElement * group = doc->createElement (Utf8ToXML("group").x_str()); + group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(groupName).x_str()); + group->setAttribute(Utf8ToXML("curindex").x_str(), Utf8ToXML(indexName).x_str()); + group->setAttribute(Utf8ToXML("curcolumn").x_str(), Utf8ToXML(column).x_str()); + if (pos < 0 || pos >= groupList->getLength()) + groupsElem->appendChild(group); + else { + DOMElement * nextGroup = dynamic_cast(groupList->item(pos)); + groupsElem->insertBefore(group, nextGroup); + } + } + + void StateManager::deleteGroup(std::string groupName) { + for (int i = 0, l = groupList->getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (groupName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + groupsElem->removeChild(group); + return; + } + } + } + + void StateManager::renameGroup(std::string oldName, std::string newName) { + // rename group element + for (int i = 0, l = groupList->getLength(); i < l; i++) { + DOMElement * group = dynamic_cast(groupList->item(i)); + if (oldName == XMLtoUtf8(group->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + group->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str()); + break; + } + } + } + + + void StateManager::addIndex(std::string indexName, std::string &groupName, + double &location, int &curpos, int &curfirst) { + // check if index exists - get user attributes + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + groupName = XMLtoUtf8(index->getAttribute(Utf8ToXML("group").x_str())).c_str(); + XMLDouble * result = new XMLDouble(index->getAttribute(Utf8ToXML("location").x_str())); + location = result->getValue(); + delete result; + curpos = XMLString::parseInt(index->getAttribute(Utf8ToXML("curposition").x_str())); + curfirst = XMLString::parseInt(index->getAttribute(Utf8ToXML("curfirst").x_str())); + return; + } + } + // if index does not exist add a new element with default values + DOMElement * index = doc->createElement (Utf8ToXML("index").x_str()); + index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(indexName).x_str()); + index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str()); + index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%g",location)).x_str()); + index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str()); + index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",0)).x_str()); + indicesElem->appendChild(index); + } + + void StateManager::setIndexName(std::string oldName, std::string newName) { + // search index and set attribute + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (oldName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + index->setAttribute(Utf8ToXML("title").x_str(), Utf8ToXML(newName).x_str()); + return; + } + } + } + + void StateManager::setIndexLocation(std::string indexName, double location) { + // search index and set attribute + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + index->setAttribute(Utf8ToXML("location").x_str(), Utf8ToXML(ecl::strf("%.15g",location)).x_str()); + return; + } + } + } + + void StateManager::setIndexCurpos(std::string indexName, int curpos) { + // search index and set attribute + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + index->setAttribute(Utf8ToXML("curposition").x_str(), Utf8ToXML(ecl::strf("%d",curpos)).x_str()); + return; + } + } + } + + void StateManager::setIndexCurfirst(std::string indexName, int curfirst) { + // search index and set attribute + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + index->setAttribute(Utf8ToXML("curfirst").x_str(), Utf8ToXML(ecl::strf("%d",curfirst)).x_str()); + return; + } + } + } + + void StateManager::setIndexGroup(std::string indexName, std::string groupName) { + // search index and set attribute + for (int i = 0, l = indexList-> getLength(); i < l; i++) { + DOMElement * index = dynamic_cast(indexList->item(i)); + if (indexName == XMLtoUtf8(index->getAttribute(Utf8ToXML("title").x_str())).c_str()) { + index->setAttribute(Utf8ToXML("group").x_str(), Utf8ToXML(groupName).x_str()); + return; + } + } + } + + + std::string StateManager::getAnnotation(std::string id) { + DOMElement * level = getLevel(id); + if (level != NULL) + return XMLtoUtf8(level->getAttribute(Utf8ToXML("annotation").x_str())).c_str(); + else + return ""; + } + + void StateManager::setAnnotation(std::string id, std::string annotation) { + DOMElement * level = getLevel(id); + if (level == NULL) { + level = doc->createElement (Utf8ToXML("level").x_str()); + level->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(id).x_str()); + levelsElem->appendChild(level); + } + level->setAttribute(Utf8ToXML("annotation").x_str(), Utf8ToXML(annotation).x_str()); + } + + DOMElement * StateManager::getLevel(std::string id) { + XMLCh * xmlId = XMLString::replicate(Utf8ToXML(id).x_str()); + bool levelFound = false; + DOMElement * level; + DOMNodeList * levelList = levelsElem->getElementsByTagName(Utf8ToXML("level").x_str()); + for (int i = 0, l = levelList-> getLength(); i < l && !levelFound; i++) { + level = dynamic_cast(levelList->item(i)); + if (XMLString::equals(xmlId, + level->getAttribute(Utf8ToXML("id").x_str()))) { + levelFound = true; + } + } + XMLString::release(&xmlId); + return levelFound ? level : NULL; + } +} // namespace enigma diff --git a/project/jni/application/enigma/src/StateManager.hh b/project/jni/application/enigma/src/StateManager.hh new file mode 100644 index 000000000..0e47561a1 --- /dev/null +++ b/project/jni/application/enigma/src/StateManager.hh @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef STATEMGR_HH_INCLUDED +#define STATEMGR_HH_INCLUDED + +#include "PropertyManager.hh" +#include +#include +#include + +namespace enigma +{ + /** + * A singelton manager for state info stored in XML format. + *

The singleton can be accessed via standard static instance() method or + * the application public ivar app.state.

+ * + *

The storage location of the state is determined by the + * Application object.

+ *

The set and get methods for properties take utf8 encoded property + * names and operate with utf8 encoded string values. Set methods create new + * state property elements if necessary. Get methods do not modify + * reference values and return C++ default values if the named property + * does not exist.

+ */ + class StateManager : public PropertyManager { + public: + static StateManager *instance(); + ~StateManager(); + virtual bool save(); + void shutdown(); + + void getGroupNames(std::vector *names); + std::string getGroupSelectedIndex(std::string groupName); + std::string getGroupSelectedColumn(std::string groupName); + void setGroupSelectedIndex(std::string groupName, std::string indexName); + void setGroupSelectedColumn(std::string groupName, std::string column); + void addGroup(std::string groupName, std::string indexName, int column); // update or append + void insertGroup(int pos, std::string groupName, std::string indexName, std::string column); // no duplicate check, pos -1 is append + void deleteGroup(std::string groupName); + void renameGroup(std::string oldName, std::string newName); + + void addIndex(std::string indexName, std::string &groupName, double &location, + int &curpos, int &curfirst); + void setIndexName(std::string oldName, std::string newName); + void setIndexLocation(std::string indexName, double location); + void setIndexCurpos(std::string indexName, int curpos); + void setIndexCurfirst(std::string indexName, int curfirst); + void setIndexGroup(std::string indexName, std::string groupName); + + std::string getAnnotation(std::string id); + void setAnnotation(std::string id, std::string annotation); + + protected: + StateManager(); + private: + static StateManager *theSingleton; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *groupsElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *groupList; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *indicesElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *indexList; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem; + + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *getLevel(std::string id); + }; +} // namespace enigma +#endif diff --git a/project/jni/application/enigma/src/Utf8ToXML.cpp b/project/jni/application/enigma/src/Utf8ToXML.cpp new file mode 100644 index 000000000..064b23e99 --- /dev/null +++ b/project/jni/application/enigma/src/Utf8ToXML.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include "Utf8ToXML.hh" +#include "main.hh" +#include +#include + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + Utf8ToXML::Utf8ToXML(const char * const toTranscode) { + init(toTranscode); + } + + Utf8ToXML::Utf8ToXML(const std::string * const toTranscode) { + init(toTranscode->c_str()); + } + + Utf8ToXML::Utf8ToXML(const std::string toTranscode) { + init(toTranscode.c_str()); + } + + void Utf8ToXML::init(const char * const toTranscode) { +#if _XERCES_VERSION >= 30000 + XMLSize_t srcLength = std::strlen(toTranscode) + 1; + // make safe assumptions on utf-16 size + XMLSize_t maxDestLength = srcLength; + XMLSize_t charsEaten; + XMLSize_t destLength; +#else + unsigned int srcLength = std::strlen(toTranscode) + 1; + // make safe assumptions on utf-16 size + unsigned int maxDestLength = srcLength; + unsigned int charsEaten; + unsigned int destLength; +#endif + unsigned char *charSizes = new unsigned char[maxDestLength]; // just junk + // make a buffer - size does not matter - the object is temporary + xmlString = new XMLCh[maxDestLength]; + // transcode to utf-8 -- there are no unrepresentable chars + destLength = app.xmlUtf8Transcoder->transcodeFrom((XMLByte *)toTranscode, + srcLength, + xmlString, maxDestLength, + charsEaten, charSizes); + delete[] charSizes; + if (charsEaten < srcLength) + // an assert - should never occur + Log << "Utf8toXML: incomplete transcoding - only "<< charsEaten << + " of " << srcLength << "bytes were processed!" << std::endl; + } + + + Utf8ToXML::~Utf8ToXML() { + delete [] xmlString; + } + + const XMLCh * Utf8ToXML::x_str() const { + return xmlString; + }; +} //namespace enigma + diff --git a/project/jni/application/enigma/src/Utf8ToXML.hh b/project/jni/application/enigma/src/Utf8ToXML.hh new file mode 100644 index 000000000..64b4dafb3 --- /dev/null +++ b/project/jni/application/enigma/src/Utf8ToXML.hh @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_UTF8TOXML_HH +#define ENIGMA_UTF8TOXML_HH + +#include +#include + + +namespace enigma +{ + /** + * Transcoding utility for utf-8 strings to XMLCh strings + * Provides a simple interface for the memory management paradigm shift + * even though not terribly efficient. Make sure all Utf8toXML objects + * exist only temporarily, f.e. + * DOMNode::setNodeValue(Utf8ToXML(char *toTranscode).x_str()) or + * XMLString::replicate(Utf8ToXML(char *toTranscode).x_str()) + * Xerces should be initialized before using this class and all objects + * should be deleted before terminating. + */ + class Utf8ToXML { + public : + /** + * Makes a transcoding to XML. + * + * @param toTranscode utf-8 coded string + */ + Utf8ToXML(const char * const toTranscode); + /** + * Makes a transcoding to the local code page. + * + * @param toTranscode utf-8 coded string + */ + Utf8ToXML(const std::string * const toTranscode); + /** + * Makes a transcoding to the local code page. + * + * @param toTranscode utf-8 coded string + */ + Utf8ToXML(const std::string toTranscode); + ~Utf8ToXML(); + + /** + * Returns the XML string. It remains owner of + * the string. + */ + const XMLCh * x_str() const; + + private : + /** + * A XML copy. We are the owner. + */ + XMLCh * xmlString; + void init(const char * const toTranscode); + }; +} //namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/XMLtoLocal.cpp b/project/jni/application/enigma/src/XMLtoLocal.cpp new file mode 100644 index 000000000..8594748fe --- /dev/null +++ b/project/jni/application/enigma/src/XMLtoLocal.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "XMLtoLocal.hh" + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + XMLtoLocal::XMLtoLocal(const XMLCh* const toTranscode) { + // Use XML for transcoding -- the returned string is owned by us + // but managed by XMLString! + localString = XMLString::transcode(toTranscode); + } + + XMLtoLocal::~XMLtoLocal() { + XMLString::release(&localString); + } + + const char* XMLtoLocal::c_str() const { + return localString; + } + +} //namespace enigma diff --git a/project/jni/application/enigma/src/XMLtoLocal.hh b/project/jni/application/enigma/src/XMLtoLocal.hh new file mode 100644 index 000000000..3757c13aa --- /dev/null +++ b/project/jni/application/enigma/src/XMLtoLocal.hh @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_XMLTOLOCAL_HH +#define ENIGMA_XMLTOLOCAL_HH + +#include +#include + +namespace enigma +{ + /** + * Transcoding utility for XMLCh strings to local code page strings. + * Provides a simple interface for the memory management paradigm shift + * even though not terribly efficient. Make sure all XMLtoLocal objects + * exist only temporarily, f.e. + * new String(XMLtoLocal(XMLCh *toTranscode).c_str()) or + * Log << XMLtoLocal(XMLCh *toTranscode) + * Xerces should be initialized before using this class and all objects + * should be deleted before terminating. + */ + class XMLtoLocal { + public : + /** + * Makes a transcoding to the local code page. + * + * @param toTranscode XML managed string + */ + XMLtoLocal(const XMLCh* const toTranscode); + ~XMLtoLocal(); + + /** + * Returns the string coded in the local page. It remains owner of + * the string. + */ + const char* c_str() const; + + private : + /** + * A copy coded in the local code page. + * The string is managed by XMLString - we are the owner. + */ + char* localString; + }; + + /** + * Enables efficient stream output of XMLCh * strings. Use it as + * Log << XML2Local(XMLCh *toTranscode). All resources + * are managed and released. + */ + inline std::ostream& operator<<(std::ostream& target, const XMLtoLocal& toDump) { + target << toDump.c_str(); + return target; + } +} //namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/XMLtoUtf8.cpp b/project/jni/application/enigma/src/XMLtoUtf8.cpp new file mode 100644 index 000000000..c09c8082b --- /dev/null +++ b/project/jni/application/enigma/src/XMLtoUtf8.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005, 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "XMLtoUtf8.hh" +#include "main.hh" +#include +#include + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + XMLtoUtf8::XMLtoUtf8(const XMLCh* const toTranscode) { +#if _XERCES_VERSION >= 30000 + XMLSize_t srcLength = XMLString::stringLen(toTranscode) + 1; + // make safe assumptions on utf-8 size + XMLSize_t maxDestLength = 3 * srcLength; + XMLSize_t charsEaten; + XMLSize_t destLength; +#else + unsigned int srcLength = XMLString::stringLen(toTranscode) + 1; + // make safe assumptions on utf-8 size + unsigned int maxDestLength = 3 * srcLength; + unsigned int charsEaten; + unsigned int destLength; +#endif + // make a buffer - size does not matter - the object is temporary + utf8String = new char[maxDestLength]; + // transcode to utf-8 -- there are no unrepresentable chars + destLength = app.xmlUtf8Transcoder->transcodeTo(toTranscode, srcLength, + (XMLByte *)utf8String, maxDestLength, + charsEaten, XMLTranscoder::UnRep_RepChar); + if (charsEaten < srcLength) + // an assert - should never occur + Log << "XMLtoUtf8: incomplete transcoding - only "<< charsEaten << + " of " << srcLength << "characters were processed!" << std::endl; + } + + XMLtoUtf8::~XMLtoUtf8() { + delete [] utf8String; + } + + const char* XMLtoUtf8::c_str() const { + return utf8String; + }; +} //namespace enigma + diff --git a/project/jni/application/enigma/src/XMLtoUtf8.hh b/project/jni/application/enigma/src/XMLtoUtf8.hh new file mode 100644 index 000000000..b17486a63 --- /dev/null +++ b/project/jni/application/enigma/src/XMLtoUtf8.hh @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_XMLTOUTF8_HH +#define ENIGMA_XMLTOUTF8_HH + +#include +#include + +namespace enigma +{ + /** + * Transcoding utility for XMLCh strings to utf-8 strings. + * Provides a simple interface for the memory management paradigm shift + * even though not terribly efficient. Make sure all XMLtoUtf8 objects + * exist only temporarily, f.e. + * new string(XMLtoUtf8(XMLCh *toTranscode).c_str()) + * Xerces should be initialized before using this class and all objects + * should be deleted before terminating. + */ + class XMLtoUtf8 { + public : + /** + * Makes a transcoding to utf-8 + * + * @param toTranscode XML managed string + */ + XMLtoUtf8(const XMLCh* const toTranscode); + ~XMLtoUtf8(); + + /** + * Returns the string coded in utf-8. It remains owner of + * the string. + */ + const char * c_str() const; + + private : + /** + * A copy coded in utf-8. We are the owner. + */ + char * utf8String; + }; +} //namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/actors.cpp b/project/jni/application/enigma/src/actors.cpp new file mode 100644 index 000000000..a25d2ddf7 --- /dev/null +++ b/project/jni/application/enigma/src/actors.cpp @@ -0,0 +1,1182 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "enigma.hh" +#include "player.hh" +#include "sound.hh" +#include "server.hh" +#include "world.hh" + +#include +#include + +using namespace std; +using namespace enigma; +using namespace world; +using ecl::V2; + +#include "actors_internal.hh" + + + +/* -------------------- Helper functions -------------------- */ + + +#define DECL_TRAITS \ + static ActorTraits traits; \ + const ActorTraits &get_traits() const { return traits; } \ + + + +/* -------------------- Actor -------------------- */ + +Actor::Actor (const ActorTraits &tr) +: Object(tr.name), + m_actorinfo(), + m_sprite(), + startingpos(), + respawnpos(), use_respawnpos(false), + spikes(false), controllers (0) +{ + set_attrib("mouseforce", 0.0); + + // copy default properties to dynamic properties + m_actorinfo.mass = tr.default_mass; + m_actorinfo.radius = tr.radius; + + ASSERT(m_actorinfo.radius <= get_max_radius(), XLevelRuntime, "Actor: radius of actor too large"); +} + +void Actor::on_collision (Actor *) +{ +} + +ActorInfo *Actor::get_actorinfo() { + return &m_actorinfo; +} + +const ActorInfo &Actor::get_actorinfo() const { + return m_actorinfo; +} + +const ecl::V2 &Actor::get_pos() const +{ + return m_actorinfo.pos; +} + + +double Actor::get_max_radius() { + return 24/64.0; +} + +void Actor::think(double /*dtime*/) { + const Field *f = GetField (get_gridpos()); + if (f) { + Floor *fl = f->floor; + Item *it = f->item; + bool item_covers_floor = (it && it->covers_floor(m_actorinfo.pos)); + if (!item_covers_floor && fl && this->is_on_floor()) + fl->actor_contact(this); + } +} + +void Actor::set_respawnpos(const V2& p) +{ + respawnpos = p; + use_respawnpos = true; +} + +void Actor::remove_respawnpos() { + use_respawnpos = false; +} + +void Actor::find_respawnpos() { + V2& what_pos = use_respawnpos ? respawnpos : startingpos; + FreeRespawnLocationFinder unblocked(what_pos, *this); + what_pos = unblocked.get_position(); +} + +const V2& Actor::get_respawnpos() const { + return use_respawnpos ? respawnpos : startingpos; +} + +const V2 &Actor::get_startpos() const { + return startingpos; +} + +void Actor::respawn() { + V2 p =(use_respawnpos) ? respawnpos : startingpos; + warp (p); + on_respawn(p); +} + +void Actor::add_force (const ecl::V2 &f) { + m_actorinfo.forceacc += f; +} + +void Actor::init() { + m_sprite = display::AddSprite(get_pos()); +} + +void Actor::on_creation(const ecl::V2 &p) +{ + set_model(get_kind()); + m_sprite.move (p); + move (); +} + +void Actor::on_respawn (const ecl::V2 &/*pos*/) { +} + +void Actor::warp(const ecl::V2 &newpos) { + m_actorinfo.pos = newpos; + m_actorinfo.vel = V2(); + m_sprite.move (newpos); + move (); +} + +void Actor::move () +{ + GridPos ofield = gridpos; + gridpos = GridPos (m_actorinfo.pos); + + if (const Field *f = GetField (gridpos)) { + if (gridpos != ofield) { + // Actor entered a new field -> notify floor and item objects + if (Floor *fl = f->floor) + fl->actor_enter (this); + if (Item *it = f->item) + it->actor_enter (this); + + if (const Field *of = GetField (ofield)) { + if (Floor *fl = of->floor) + fl->actor_leave (this); + if (Item *it = of->item) + it->actor_leave (this); + } + } + + Item *it = f->item; + if (it && it->actor_hit(this)) + player::PickupItem (this, gridpos); + + if (Stone *st = f->stone) + st->actor_inside (this); + } + // move the actor and save the position + m_actorinfo.oldpos = m_actorinfo.pos; +} + +void Actor::move_screen () { + m_sprite.move (m_actorinfo.pos); +} + +void Actor::set_model(const string &name) { + m_sprite.replace_model (display::MakeModel(name)); +} + +void Actor::animcb () { +} + + +void Actor::hide() { + m_sprite.hide(); +} + +void Actor::show() { + m_sprite.show(); +} + +void Actor::set_anim (const string &modelname) { + set_model(modelname.c_str()); + get_sprite().set_callback (this); +} + +bool Actor::can_move() const { + if (Stone *st = GetStone (get_gridpos())) { + if (!server::NoCollisions) + return !st->is_sticky(this); + } + return true; +} + +Value Actor::message(const string &m, const Value &) { + if (m == "init") { + startingpos = get_pos(); + } + return Value(); +} + +bool Actor::sound_event (const char *name, double vol) { + return sound::EmitSoundEvent (name, get_pos(), getVolume(name, this, vol)); +} + +void Actor::set_attrib(const string& key, const Value &val) +{ + if (key == "controllers") + controllers = to_int (val); + else if (key == "mouseforce") + mouseforce = to_double (val); + Object::set_attrib (key, val); +} + + + + +/* -------------------- RotorBase -------------------- */ + +namespace +{ + /*! The base class for rotors and spinning tops. */ + class RotorBase : public Actor { + protected: + RotorBase(const ActorTraits &tr); + private: + // Actor interface. + void think (double dtime); + bool is_dead() const { return false; } +// bool is_flying() const { return true; } + bool is_flying() const { return false; } +// bool is_on_floor() const { return false; } we need friction! + + void on_collision (Actor *a) { + SendMessage(a, "shatter"); + } + + bool attackCurrentOnly; + double timeKeepAttackStrategy; + }; +} + +RotorBase::RotorBase(const ActorTraits &tr) +: Actor(tr), attackCurrentOnly(false), timeKeepAttackStrategy(0) +{ + set_attrib ("range", 5.0); + set_attrib ("force", 10.0); + set_attrib ("gohome", 1.0); + set_attrib ("attacknearest", 1.0); + set_attrib ("prefercurrent", 0.0); +} + +void RotorBase::think (double dtime) +{ + double range = 0, force = 0; + double_attrib("range", &range); + double_attrib("force", &force); + + force /= 6; + + Actor *target = 0; + V2 target_vec; + bool attack_nearest = (int_attrib ("attacknearest") != 0); + + double preferCurrent; + double_attrib("prefercurrent", &preferCurrent); + timeKeepAttackStrategy -= dtime; + if (timeKeepAttackStrategy < 0) { + timeKeepAttackStrategy = enigma::DoubleRand(0.8, 1.6); + attackCurrentOnly = (enigma::DoubleRand(0.0, 1.0) < preferCurrent); + } + + vector actors; + GetActorsInRange (get_pos(), range, actors); + for (size_t i=0; iis_movable() && !a->is_invisible()) + { + V2 v = a->get_pos() - get_pos(); + if (attack_nearest && !attackCurrentOnly || + attackCurrentOnly && a == player::GetMainActor( + player::CurrentPlayer())) { + if (!target || (length(v) < length(target_vec))) { + target = a; + target_vec = v; + } + } else if (!attackCurrentOnly) { + target = a; + target_vec += normalize(v); + } + } + } + + if (!target && int_attrib("gohome")) { + // no actors focussed -> return to start position + target_vec = get_respawnpos()-get_pos(); + } + + double target_dist = length(target_vec); + + if (target_dist > 0.2) + target_vec.normalize(); + add_force (target_vec * force); + + Actor::think(dtime); +} + + +/* -------------------- Rotor -------------------- */ +namespace +{ + class Rotor : public RotorBase { + CLONEACTOR(Rotor); + DECL_TRAITS; + public: + Rotor() : RotorBase (traits) + { + } + }; +} + +ActorTraits Rotor::traits = { + "ac-rotor", // name + ac_rotor, // id + 22.0f/64, // radius + 0.8f // mass +}; + + +/* -------------------- Spinning top -------------------- */ +namespace +{ + class Top : public RotorBase { + CLONEACTOR(Top); + DECL_TRAITS; + public: + Top() : RotorBase (traits) + { + } + }; +} + +ActorTraits Top::traits = { + "ac-top", // name + ac_top, // id + 16.0f/64, // radius + 0.8f // mass +}; + + +/* -------------------- Bug -------------------- */ + +namespace +{ + class Bug : public Actor { + CLONEACTOR(Bug); + DECL_TRAITS; + public: + Bug() : Actor(traits) {} + bool is_flying() const { return true; } + bool is_dead() const { return false; } + int get_id() const { return ac_bug; } + }; +} + +ActorTraits Bug::traits = { + "ac-bug", // name + ac_bug, // id + 12.0f/64, // radius + 0.7f // mass +}; + + + +/* -------------------- Horse -------------------- */ + +namespace +{ + class Horse : public Actor { + CLONEACTOR(Horse); + DECL_TRAITS; + + int get_id() const { return ac_horse; } + bool is_flying() const { return true; } + bool is_dead() const { return false; } + + void think (double dtime); + + // Private methods + void update_target (); + bool try_target (int idx); + + // Variables + int m_targetidx; + V2 m_target; + public: + Horse(); + }; +} + +ActorTraits Horse::traits = { + "ac-horse", // name + ac_horse, // id + 24.0f/64, // radius + 1.2f // mass +}; + +Horse::Horse() +: Actor(traits), + m_targetidx(-1), + m_target() +{ + set_attrib("force", Value(10.0)); + set_attrib("target1", Value()); + set_attrib("target2", Value()); + set_attrib("target3", Value()); + set_attrib("target4", Value()); +} + +void Horse::think (double /* dtime */) +{ + double force = 0; + double_attrib("force", &force); + update_target (); + if (m_targetidx != -1) { + add_force (normalize(m_target - get_pos()) * force); + } +} + +bool Horse::try_target (int idx) { + if (idx <= 0 || idx > 4) + return false; + + const char *attrs[] = { + "target1", "target2", "target3", "target4", + }; + string targetstr; + GridLoc loc; + + if (!string_attrib (attrs[idx-1], &targetstr)) + return false; + + to_gridloc(targetstr.c_str(), loc); + m_target = loc.pos.center(); + m_targetidx = idx; + return true; +} + +void Horse::update_target () { + + if (m_targetidx == -1) { + // no target defined so far + try_target(1); + } + else if (length(m_target - get_pos()) < 0.2) { + // target reached or? try next one + if (!try_target (m_targetidx + 1)) + m_targetidx = -1; // failed -> start anew + } +} + + + + +/* -------------------- CannonBall -------------------- */ + +namespace +{ + class CannonBall : public Actor { + CLONEACTOR(CannonBall); + DECL_TRAITS; + public: + CannonBall(); + bool is_flying() const { return true; } + bool is_dead() const { return false; } + bool is_on_floor() const { return false; } + void on_creation(const ecl::V2 &p); + bool can_move() const { return true; } + + void animcb(); + }; +} + +ActorTraits CannonBall::traits = { + "ac-cannonball", // name + ac_cannonball, // id + 24.0/64, // radius + 1.0 // mass +}; + +CannonBall::CannonBall() +: Actor(traits) +{ + get_actorinfo()->ignore_contacts = true; +} + +void CannonBall::animcb() +{ + const GridPos &p = get_gridpos(); + + if (Stone *st = GetStone (p)) { + SendMessage (st, "spitter"); + } + else if (Item *it = GetItem(p)) { + if (!has_flags(it, itf_indestructible)) + SetItem (p, it_explosion3); + } + else if (Floor *fl = GetFloor(p)) { + if (fl->is_destructible()) + SetItem (p, it_explosion3); + } + KillActor (this); +} + +void CannonBall::on_creation(const ecl::V2 &p) +{ + Actor::on_creation(p); + + display::SpriteHandle &sprite = get_sprite(); + sprite.kill(); + sprite = display::AddEffect(p, "ac-cannonball"); + sprite.set_callback(this); +} + + + +/* -------------------- BasicBall -------------------- */ +namespace +{ + /*! The base class for all marbles. */ + class BasicBall : public Actor { + protected: + BasicBall(const ActorTraits &tr); + + enum State { + NO_STATE, + NORMAL, + SHATTERING, + DROWNING, + BUBBLING, + FALLING, // falling into abyss + JUMPING, + DEAD, // marble is dead + RESURRECTED, // has been resurrected; about to respawn + APPEARING, // appearing when level starts/after respawn + DISAPPEARING, // disappearing when level finished + FALLING_VORTEX, // falling into vortex + RISING_VORTEX, // appear in vortex + JUMP_VORTEX, // jump out of vortex (here player controls actor) + }; + + enum HaloState { + NOHALO, HALOBLINK, HALONORMAL + }; + + void sink (double dtime); + void disable_shield(); + void change_state_noshield (State newstate); + void change_state(State newstate); + + // Model management + void update_model(); + void set_sink_model(const string &m); + void set_shine_model (bool shinep); + void update_halo(); + virtual void hide(); + + /* ---------- Actor interface ---------- */ + + virtual void think (double dtime); + virtual void move_screen (); + + void on_creation(const ecl::V2 &p); + void on_respawn (const ecl::V2 &/*pos*/) + { + change_state(APPEARING); + } + + bool is_dead() const; + bool is_movable() const; + bool is_flying() const { return state == JUMPING; } + bool is_on_floor() const; + bool is_drunken() const { return m_drunk_rest_time>0; } + bool is_invisible() const { return m_invisible_rest_time>0; } + + bool can_drop_items() const; + bool can_pickup_items() const; + bool has_shield() const; + + bool can_be_warped() const { return state==NORMAL; } + + // Object interface. + virtual Value message(const string &m, const Value &); + + // ModelCallback interface. + void animcb(); + + /* ---------- Variables ---------- */ + + State state; // The marble's current state + + static const int minSinkDepth = 0; // normal level + int maxSinkDepth; // actor dies at this depth + double sinkDepth; // how deep actor has sunk + int sinkModel; // current model + bool lastshinep; + + double vortex_normal_time; // while jumping out of vortex: time at normal level + + display::SpriteHandle m_halosprite; + double m_shield_rest_time; + static const double SHIELD_TIME; + HaloState m_halostate; + + double m_drunk_rest_time; + double m_invisible_rest_time; + }; + + const double BasicBall::SHIELD_TIME = 10.0; +} + +BasicBall::BasicBall(const ActorTraits &tr) +: Actor (tr), + state (NO_STATE), + maxSinkDepth (7), + sinkDepth (minSinkDepth), + sinkModel (-1), + lastshinep (false), + vortex_normal_time (0), + m_halosprite (), + m_shield_rest_time (0), + m_halostate (NOHALO), + m_drunk_rest_time (0), + m_invisible_rest_time (0) +{ +} + +void BasicBall::on_creation(const ecl::V2 &p) +{ + Actor::on_creation(p); + if (server::CreatingPreview) + change_state(NORMAL); + else + change_state(APPEARING); +} + + +void BasicBall::move_screen () +{ + update_model(); + update_halo(); + Actor::move_screen(); +} + +bool BasicBall::is_movable() const +{ + return (state!=DEAD && state!=RESURRECTED && state!=APPEARING && state!=DISAPPEARING); +} + +bool BasicBall::is_dead() const { + return state == DEAD; +} + +bool BasicBall::is_on_floor() const { + return state == NORMAL || state == JUMP_VORTEX || state==APPEARING; +} + +bool BasicBall::can_drop_items() const { + return state == NORMAL || state == JUMP_VORTEX || state==JUMPING; +} + +bool BasicBall::can_pickup_items() const { + return state == NORMAL || state == JUMP_VORTEX; +} + +void BasicBall::change_state_noshield (State newstate) +{ + if (!has_shield()) + change_state(newstate); +} + +Value BasicBall::message(const string &m, const Value &v) +{ + switch (state) { + case NORMAL: + if (m == "shatter") change_state_noshield(SHATTERING); + else if (m == "suicide") change_state(SHATTERING); + else if (m == "laserhit") change_state_noshield(SHATTERING); + else if (m == "drown") change_state_noshield(DROWNING); + else if (m == "fall") change_state_noshield(FALLING); + else if (m == "fallvortex") change_state(FALLING_VORTEX); + else if (m == "jump") change_state(JUMPING); + else if (m == "appear") change_state(APPEARING); + else if (m == "disappear") change_state(DISAPPEARING); + break; + case JUMPING: + if (m == "shatter") change_state_noshield(SHATTERING); + else if (m == "disappear") change_state(DISAPPEARING); + break; + case DEAD: + if (m == "resurrect") change_state(RESURRECTED); + break; + case FALLING_VORTEX: + if (m == "rise") change_state(RISING_VORTEX); // vortex->vortex teleportation + else if (m == "appear") change_state(APPEARING); // vortex->non-vortex teleportation + break; + case JUMP_VORTEX: + if (m == "laserhit") change_state(SHATTERING); + break; + case APPEARING: + // ugly hack + if (m == "init") + Actor::message (m, v); + else if (m == "shatter") change_state (SHATTERING); + break; + default: + break; + } + + + // Shield, booze and invisibility can be activated in all states except DEAD + + if (state != DEAD) { + if (m == "shield") { + m_shield_rest_time += SHIELD_TIME; + update_halo(); + } + else if (m == "invisibility") { + m_invisible_rest_time += 8.0; + } + else if (m == "booze") { + m_drunk_rest_time += 5.0; // Drunken for 5 more seconds + } + } + return Value(); +} + +void BasicBall::set_sink_model(const string &m) +{ + int modelnum = ecl::round_down(sinkDepth); + + if (!has_shield() && modelnum != sinkModel) { + ASSERT(modelnum >= minSinkDepth && modelnum < maxSinkDepth, XLevelRuntime, + "BasicBall: set_sink_model called though modelnum incorrect"); + + string img = m+"-sink"; + img.append(1, static_cast('0'+modelnum)); + set_model(img); + + sinkModel = modelnum; + } +} + +void BasicBall::set_shine_model (bool shinep) +{ + if (shinep != lastshinep) { + string modelname = get_kind(); + if (shinep) + set_model (modelname + "-shine"); + else + set_model (modelname); + lastshinep = shinep; + } +} + +void BasicBall::update_model() +{ + if (m_invisible_rest_time > 0) + get_sprite().hide(); + else + get_sprite().show(); + + switch (state) { + case NORMAL: + if (sinkDepth > minSinkDepth && sinkDepth < maxSinkDepth) { + set_sink_model(get_kind()); + } + else { + ActorInfo *ai = get_actorinfo(); + int xpos = ecl::round_nearest (ai->pos[0] * 32.0); + int ypos = ecl::round_nearest (ai->pos[1] * 32.0); + + bool shinep = ((xpos + ypos) % 2) != 0; + set_shine_model (shinep); + } + break; + default: + break; + } +} + +void BasicBall::sink (double dtime) +{ + double sink_speed = 0.0; + double raise_speed = 0.0; // at this velocity don't sink; above: raise + + Item *it = GetItem(get_gridpos()); + Floor *fl = GetFloor (get_gridpos()); + if (!(it != NULL && it->covers_floor(get_pos())) && fl != NULL) + fl->get_sink_speed (sink_speed, raise_speed); + + if (sink_speed == 0.0 || has_shield()) { + sinkDepth = minSinkDepth; + sinkModel = -1; + } + else { + ActorInfo *ai = get_actorinfo(); + double sinkSpeed = sink_speed * (1 - length(ai->vel) / raise_speed); + sinkDepth += sinkSpeed*dtime; + + if (sinkDepth >= maxSinkDepth) { + set_model(string(get_kind())+"-sunk"); + ai->vel = V2(); // stop! + sound_event ("swamp"); + change_state(BUBBLING); + } + else { + if (sinkDepth < minSinkDepth) + sinkDepth = minSinkDepth; + } + } +} + +void BasicBall::think (double dtime) +{ + if (m_invisible_rest_time > 0) + m_invisible_rest_time -= dtime; + + // Update protection shield + if (m_shield_rest_time > 0) + m_shield_rest_time -= dtime; + + switch (state) { + case NORMAL: + if (m_drunk_rest_time > 0) + m_drunk_rest_time -= dtime; + sink (dtime); + break; + case JUMP_VORTEX: + vortex_normal_time += dtime; + if (vortex_normal_time > 0.025) // same time as appear animation + if (vortex_normal_time > dtime) // ensure min. one tick in state JUMP_VORTEX! + change_state(JUMPING); // end of short control over actor + break; + default: + + break; + } + + Actor::think(dtime); +} + +void BasicBall::animcb() +{ + string kind=get_kind(); + + switch (state) { + case SHATTERING: + set_model(kind+"-shattered"); + change_state(DEAD); + break; + case DROWNING: + case BUBBLING: + set_model("invisible"); + change_state(DEAD); + break; + case FALLING: + set_model(kind+"-fallen"); // invisible + if (get_id (this) == ac_meditation) + sound_event ("shattersmall"); + else + sound_event ("shatter"); + change_state(DEAD); + break; + case JUMPING: + set_model(kind); + change_state(NORMAL); + break; + case APPEARING: + set_model(kind); + change_state(NORMAL); + break; + case DISAPPEARING: + set_model("ring-anim"); + break; + case FALLING_VORTEX: { + set_model(kind+"-fallen"); // invisible + break; + } + case RISING_VORTEX: { + set_model(kind); + if (Item *it = GetItem(get_gridpos())) { + world::ItemID id = get_id(it); + if (id == world::it_vortex_open || id == world::it_vortex_closed) + SendMessage(it, "arrival"); // closes some vortex + } + change_state(JUMP_VORTEX); + break; + } + default: + break; + } +} + +void BasicBall::change_state(State newstate) { + if (newstate == state) + return; + + string kind = get_kind(); + State oldstate = state; + + state = newstate; + switch (newstate) { + case NORMAL: + if (oldstate == APPEARING) { + ActorInfo *ai = get_actorinfo(); + ai->forceacc = V2(); + } + world::ReleaseActor(this); + break; + + case SHATTERING: + if (get_id (this) == ac_meditation) + sound_event ("shattersmall"); + else + sound_event ("shatter"); + world::GrabActor(this); + set_anim (kind+"-shatter"); + break; + + case DROWNING: + // @@@ FIXME: use same animation as SINKING ? + world::GrabActor(this); +// sound::PlaySound("drown"); + sound_event("drown"); +// set_anim ("ring-anim"); + set_anim ("ac-drowned"); + break; + case BUBBLING: + world::GrabActor(this); +// sound::PlaySound("drown"); + set_anim ("ac-drowned"); + break; + case FALLING: + case FALLING_VORTEX: + world::GrabActor(this); + set_anim(kind+"-fall"); + break; + case DEAD: + disable_shield(); + m_drunk_rest_time = 0; + m_invisible_rest_time = 0; + break; + case JUMPING: + sound_event ("jump"); + set_anim(kind+"-jump"); + break; + case APPEARING: + case RISING_VORTEX: + set_anim(kind+"-appear"); + world::GrabActor(this); + break; + case JUMP_VORTEX: + ASSERT(oldstate == RISING_VORTEX, XLevelRuntime, + "BasicBall: change to state JUMP_VORTEX but not RISING_VORTEX"); + vortex_normal_time = 0; + set_model(kind); + world::ReleaseActor(this); + break; + case DISAPPEARING: + world::GrabActor(this); + disable_shield(); + set_anim(kind+"-disappear"); + break; + case RESURRECTED: + disable_shield(); + sinkDepth = minSinkDepth; + break; + default: + break; + } +} + +void BasicBall::disable_shield() { + if (has_shield()) { + m_shield_rest_time = 0; + update_halo(); + } +} + +bool BasicBall::has_shield() const { + return m_shield_rest_time > 0; +} + +void BasicBall::update_halo() { + HaloState newstate = m_halostate; + + double radius = get_actorinfo()->radius; + string halokind; + + // Determine which halomodel has to be used: + if (radius == 19.0/64) { // Halo for normal balls + halokind="halo"; + }else if (radius == 13.0f/64) { // Halo for small balls + halokind="halo-small"; + } + + if (m_shield_rest_time <= 0) + newstate = NOHALO; + else if (m_shield_rest_time <= 3.0) + newstate = HALOBLINK; + else + newstate = HALONORMAL; + + if (newstate != m_halostate) { + if (m_halostate == NOHALO){ + m_halosprite = display::AddSprite (get_pos(), halokind.c_str()); + } + switch (newstate) { + case NOHALO: + // remove halo + m_halosprite.kill(); + m_halosprite = display::SpriteHandle(); + break; + case HALOBLINK: + // blink for the last 3 seconds + m_halosprite.replace_model (display::MakeModel (halokind+"-blink")); + break; + case HALONORMAL: + m_halosprite.replace_model (display::MakeModel (halokind)); + break; + } + m_halostate = newstate; + } + else if (m_halostate != NOHALO) { + m_halosprite.move (get_pos()); + } +} + +void BasicBall::hide() { + Actor::hide(); + disable_shield(); +} + +//---------------------------------------- +// Balls of different sorts +//---------------------------------------- + +namespace +{ + class BlackBall : public BasicBall { + CLONEACTOR(BlackBall); + DECL_TRAITS; + public: + BlackBall() : BasicBall(traits) + { + set_attrib("mouseforce", Value(1.0)); + set_attrib("color", Value(0.0)); + set_attrib("blackball", Value(true)); + set_attrib("player", Value(0.0)); + set_attrib("controllers", Value(1.0)); + } + }; + + class WhiteBall : public BasicBall { + CLONEACTOR(WhiteBall); + DECL_TRAITS; + public: + WhiteBall() : BasicBall(traits) + { + set_attrib("mouseforce", Value(1.0)); + set_attrib("color", Value(1.0)); + set_attrib("whiteball", Value(true)); + set_attrib("player", Value(1.0)); + set_attrib("controllers", Value(2.0)); + } + }; + + class WhiteBall_Small : public BasicBall { + CLONEACTOR(WhiteBall_Small); + DECL_TRAITS; + public: + WhiteBall_Small() : BasicBall(traits) + { + set_attrib("mouseforce", Value(1.0)); + set_attrib("color", Value(1.0)); + set_attrib("whiteball", Value(true)); + set_attrib("controllers", Value(3.0)); + maxSinkDepth = 4; + } + }; + + class Killerball : public Actor { + CLONEACTOR(Killerball); + DECL_TRAITS; + public: + + Killerball() : Actor (traits) + { + set_attrib("mouseforce", Value(2.0)); + set_attrib("color", Value(1.0)); + set_attrib("whiteball", Value(true)); + set_attrib("controllers", Value(3.0)); + } + bool is_dead() const { return false; } + + void on_collision(Actor *a) { + SendMessage(a, "shatter"); + } + }; +} + +ActorTraits BlackBall::traits = { + "ac-blackball", // name + ac_blackball, // id + 19.0/64, // radius + 1.0 // mass +}; + +ActorTraits WhiteBall::traits = { + "ac-whiteball", // name + ac_whiteball, // id + 19.0/64, // radius + 1.0 // mass +}; + +ActorTraits WhiteBall_Small::traits = { + "ac-whiteball-small", // name + ac_meditation, // id + 13.0f/64, // radius + 0.7f // mass +}; + +ActorTraits Killerball::traits = { + "ac-killerball", // name + ac_killerball, // id + 13.0f/64, // radius + 0.7f // mass +}; + + +/* -------------------- Functions -------------------- */ + +void world::InitActors () +{ + RegisterActor (new Bug); + RegisterActor (new Horse); + RegisterActor (new Rotor); + RegisterActor (new Top); + RegisterActor (new BlackBall); + RegisterActor (new WhiteBall); + RegisterActor (new WhiteBall_Small); + RegisterActor (new Killerball); + RegisterActor (new CannonBall); +} diff --git a/project/jni/application/enigma/src/actors.hh b/project/jni/application/enigma/src/actors.hh new file mode 100644 index 000000000..755f5b67f --- /dev/null +++ b/project/jni/application/enigma/src/actors.hh @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ACTORS_HH_INCLUDED +#define ACTORS_HH_INCLUDED + +#include "objects_decl.hh" + +namespace world +{ + enum ActorID { + ac_INVALID = -1, + ac_FIRST = 0, + ac_blackball = 0, + ac_whiteball = 1, + ac_meditation = 2, + ac_killerball = 3, + ac_rotor = 4, + ac_top = 5, + ac_horse = 6, + ac_bug = 7, + ac_cannonball = 8, + ac_spermbird = 9, + ac_LAST = 9, + ac_COUNT + }; + + struct ActorTraits { + const char *name; + ActorID id; + float radius; + float default_mass; + }; + +/* -------------------- ActorInfo -------------------- */ + + struct Contact { + ecl::V2 pos; + ecl::V2 normal; + + // Constructor + Contact (const ecl::V2 &pos_, const ecl::V2 &normal_) + : pos(pos_), normal (normal_) {} + }; + + typedef std::vector ContactList; + + /*! + * This class contains the information the physics engine + * maintains about dynamic objects ("actors"). + */ + struct ActorInfo { + // ---------- Variables ---------- + + ecl::V2 pos; // Absolute position + ecl::V2 vel; // Velocity + ecl::V2 forceacc; // Force accumulator + double charge; // Electric charge + double mass; // Mass + double radius; // Radius of the ball + bool grabbed; // Actor not controlled by the physics engine + bool ignore_contacts; // Do not perform collision handling + + // Variables used internally by the physics engine + + ecl::V2 last_pos; // Position before current tick + ecl::V2 oldpos; // Backup position for enter/leave notification + ecl::V2 force; // Force used during tick + ecl::V2 collforce; + ContactList contacts; + ContactList new_contacts; + + // Constructor + ActorInfo(); + }; + + + class Actor : public Object, public display::ModelCallback { + public: + // ModelCallback interface + void animcb (); + + /* ---------- Object interface ---------- */ + Actor *clone() = 0; + virtual Value message (const string &m, const Value &); + void set_attrib (const string& key, const Value &val); + + /* ---------- Actor interface ---------- */ + virtual const ActorTraits &get_traits() const = 0; + + virtual void think (double dtime); + + virtual void on_collision(Actor *a); + virtual void on_creation(const ecl::V2 &pos); + virtual void on_respawn (const ecl::V2 &pos); + + virtual bool is_dead() const = 0; + virtual bool is_movable() const { return true; } + virtual bool is_flying() const { return false; } + virtual bool is_on_floor() const { return true; } + virtual bool is_drunken() const { return false; } + virtual bool is_invisible() const { return false; } + + virtual bool can_drop_items() const { return false; } + virtual bool can_move() const; + virtual bool can_be_warped() const { return false; } + virtual bool has_shield() const { return false; } + + virtual void init(); + + /* ---------- Methods ---------- */ + void move (); + virtual void move_screen (); + void warp (const ecl::V2 &newpos); + bool sound_event (const char *name, double vol = 1.0); + + void respawn(); + void set_respawnpos(const ecl::V2& p); + void remove_respawnpos(); + void find_respawnpos(); + const ecl::V2 &get_respawnpos() const; + const ecl::V2 &get_startpos() const; + + virtual void hide(); + void show(); + + void add_force (const ecl::V2 &f); + + /* ---------- Accessors ---------- */ + ActorInfo *get_actorinfo(); + const ActorInfo &get_actorinfo() const; + const ecl::V2 &get_pos() const; + + const ecl::V2 &get_vel() const { + return m_actorinfo.vel; + } + + bool has_spikes() const { return spikes; } + void set_spikes(bool has) { spikes = has; } + + static double get_max_radius(); // max. radius of all actors + + int get_controllers () const { return controllers; } + double get_mouseforce () const { return mouseforce; } + + bool controlled_by(int player) const { + return (get_controllers() & (1+player)) != 0; + } + + const GridPos &get_gridpos() const { return gridpos; } + + protected: + Actor(const ActorTraits &tr); + void set_model (const string &modelname); + void set_anim (const string &modelname); + + display::SpriteHandle &get_sprite() { return m_sprite; } + + private: + /* ---------- Variables ---------- */ + ActorInfo m_actorinfo; + display::SpriteHandle m_sprite; + ecl::V2 startingpos; + ecl::V2 respawnpos; + bool use_respawnpos; + bool spikes; // set by "it-pin" + int controllers; + double mouseforce; + GridPos gridpos; + }; + + inline ActorID get_id (Actor *a) { + return a->get_traits().id; + } + + inline double get_radius (const Actor *a) { + return a->get_actorinfo().radius; + } + + inline double get_mass (const Actor *a) { + return a->get_actorinfo().mass; + } + + inline double get_charge (const Actor *a) { + return a->get_actorinfo().charge; + } + +/* -------------------- Global Functions -------------------- */ + + void InitActors(); +} + +#endif diff --git a/project/jni/application/enigma/src/actors_internal.hh b/project/jni/application/enigma/src/actors_internal.hh new file mode 100644 index 000000000..dc12dfdad --- /dev/null +++ b/project/jni/application/enigma/src/actors_internal.hh @@ -0,0 +1,216 @@ +namespace +{ + // helper class to find good respawn positions + + struct ExaminedLocation : public GridPos { + public: + ExaminedLocation(GridPos p) : GridPos(p) {} + + bool operator<(const ExaminedLocation& other) const { + return (x == other.x) ? y ExaminedLocations; + + class FreeRespawnLocationFinder + { + ExaminedLocations checked; + ExaminedLocations blocked; + ExaminedLocations candidates; + + const Actor &actor_to_set; + bool actor_is_marble; + V2 preferred_position; + + double max_enemy_gap; + V2 max_gap_pos; + + static const double MAX_DISTANCE_WANTED; + + static bool is_marble(const string& k) { + // true if kind 'k' is a marble + return k == "ac-blackball" || k == "ac-whiteball" || k == "ac-whiteball-small"; + } + + static bool is_respawn_floor(const string& k) { + // true if marble may appear on floors of kind 'k' + return + k != "fl-abyss" && + k != "fl-water" && + k != "fl-space"; // player cannot be moved on fl-space + } + + static bool is_respawn_item(const string& k) { + // true if marble may appear on items of kind 'k' + return + k != "it-laserbeam" && + k != "it-burnable-ignited"; + } + + static bool search_through_stone(const Stone& st) { + if (st.is_movable() || st.is_floating()) return true; + + const string& k = st.get_kind(); + return k == "st-puzzle"; + } + + static double wanted_distance_to(const string& k) { + // returns the size of the gap wanted between a marble and an actor of kind 'k' + + if (k == "ac-rotor") return MAX_DISTANCE_WANTED; + if (k == "ac-top") return 3.0; + if (k == "ac-killerball" || k == "ac-bug") return 1.5; + return 0.3; + } + + double distance_wanted_to(const Actor& a) { + double dist = 0.3; + + if (actor_is_marble) dist = wanted_distance_to(a.get_kind()); + else if (is_marble(a.get_kind())) dist = wanted_distance_to(actor_to_set.get_kind()); + + ASSERT(dist <= MAX_DISTANCE_WANTED, XLevelRuntime, "FreeRespawnLocationFinder: distance_wanted_to too large "); + return dist; + } + + bool enemyActorAt(const V2& p) { + vector found_actors; + double range = get_radius (&actor_to_set) + MAX_DISTANCE_WANTED + Actor::get_max_radius(); + + if (GetActorsInRange(p, range, found_actors)) { + bool found_near_enemy = false; + double min_enemy_gap = 1000.0; + + for (vector::const_iterator ai = found_actors.begin(); + ai != found_actors.end(); + ++ai) + { + Actor *a = *ai; + + if (a != &actor_to_set) { + double distance = length(p - a->get_pos()); + double gap_between = distance - get_radius (&actor_to_set) - get_radius(a); + double wanted_gap = distance_wanted_to(*a); + + if (gap_between < wanted_gap) + found_near_enemy = true; + + if (gap_between < min_enemy_gap) + min_enemy_gap = gap_between; + } + } + + if (found_near_enemy) { + if (min_enemy_gap<999.0) { + if (min_enemy_gap > max_enemy_gap) { + max_enemy_gap = min_enemy_gap; + max_gap_pos = p; + } + } + } + + return found_near_enemy; + } + + return false; + } + + void examine(GridPos p) { + if (checked.find(p) != checked.end()) return; // already examined + checked.insert(p); // never check again + + Floor *fl = GetFloor(p); + if (!fl || !is_respawn_floor(fl->get_kind())) return; // bad floor + + bool may_respawn = true; + bool continue_search = true; + + Item *it = GetItem(p); + if (it && !is_respawn_item(it->get_kind())) may_respawn = false; // bad item + + Stone *st = GetStone(p); + if (st) { + if (!search_through_stone(*st)) continue_search = false; + may_respawn = false; + } + + if (may_respawn) { // may be a candidate -> check for enemy actors + if (enemyActorAt(p.center())) may_respawn = false; + } + + if (continue_search) blocked.insert(p); + if (may_respawn) candidates.insert(p); + } + + public: + + FreeRespawnLocationFinder(V2 p, const Actor& actor) + : actor_to_set(actor) + , preferred_position(p) + , max_enemy_gap(-1000.0) + { + actor_is_marble = is_marble(actor_to_set.get_kind()); + ExaminedLocations affected; // all locations affected by current respawn position + { + double radius = get_radius (&actor_to_set); + int xmin = int(p[0]-radius); + int xmax = int(p[0]+radius); + int ymin = int(p[1]-radius); + int ymax = int(p[1]+radius); + + for (int x = xmin; x <= xmax; ++x) { + for (int y = ymin; y <= ymax; ++y) { + affected.insert(GridPos(x, y)); + } + } + } + + for (ExaminedLocations::const_iterator ai = affected.begin(); ai != affected.end(); ++ai) { + examine(*ai); + } + + if (candidates.size() != affected.size()) { // if any affected location may not be used for respawning + // choose alternate respawn location + + blocked = affected; // start with all affected positions + + while (candidates.empty()) { + ExaminedLocations curr_blocked; + swap(curr_blocked, blocked); + + if (curr_blocked.empty()) { + break; // no chance to find a candidate + } + + for (ExaminedLocations::const_iterator bl = curr_blocked.begin(); bl != curr_blocked.end(); ++bl) { + examine(move(*bl, NORTH)); + examine(move(*bl, SOUTH)); + examine(move(*bl, EAST)); + examine(move(*bl, WEST)); + } + } + + if (candidates.empty()) { // no better location -> take least worse tested location + if (max_enemy_gap > 0.0) { + preferred_position = max_gap_pos; + } + } + else { // a better location has been found + ExaminedLocations::const_iterator c = candidates.begin(); + advance(c, IntegerRand(0, int (candidates.size()-1))); + + ASSERT(c != candidates.end(), XLevelRuntime, "FreeRespawnLocationFinder: list of candidates corrupt"); + preferred_position = c->center(); + } + } + } + + V2 get_position() const { return preferred_position; } + }; +} + +namespace +{ + const double FreeRespawnLocationFinder::MAX_DISTANCE_WANTED = 5.0; +} diff --git a/project/jni/application/enigma/src/client.cpp b/project/jni/application/enigma/src/client.cpp new file mode 100644 index 000000000..09fb39cad --- /dev/null +++ b/project/jni/application/enigma/src/client.cpp @@ -0,0 +1,970 @@ +/* + * Copyright (C) 2004 Daniel Heck + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "client.hh" +#include "game.hh" +#include "display.hh" +#include "options.hh" +#include "server.hh" +#include "gui/HelpMenu.hh" +#include "main.hh" +#include "gui/GameMenu.hh" +#include "sound.hh" +#include "player.hh" +#include "world.hh" +#include "nls.hh" +#include "StateManager.hh" +#include "lev/Index.hh" +#include "lev/PersistentIndex.hh" +#include "lev/Proxy.hh" +#include "lev/RatingManager.hh" +#include "lev/ScoreManager.hh" + +#include "ecl_sdl.hh" + +#include "enet/enet.h" + +#include +#include +#include +#include +#include + +using namespace enigma::client; +using namespace ecl; +using namespace std; + +#include "client_internal.hh" + +/* -------------------- Auxiliary functions -------------------- */ + +namespace +{ + /*! Display a message and change the current mouse speed. */ + void set_mousespeed (double speed) + { + int s = round_nearest(speed); + options::SetMouseSpeed (s); + s = round_nearest (options::GetMouseSpeed ()); + Msg_ShowText(strf(_("Mouse speed: %d"), s), false, 2.0); + } + + /*! Generate the message that is displayed when the level starts. */ + string displayedLevelInfo (lev::Proxy *level) + { + std::string text; + std::string tmp; + + tmp = level->getLocalizedString("title"); + if (tmp.empty()) + tmp = _("Another nameless level"); + text = string("\"")+ tmp +"\""; + tmp = level->getAuthor(); + if (!tmp.empty()) + text += _(" by ") + tmp; + tmp = level->getLocalizedString("subtitle"); + if (!tmp.empty() && tmp != "subtitle") + text += string(" - \"")+ tmp + "\""; + tmp = level->getCredits(false); + if (!tmp.empty()) + text += string(" - Credits: ")+ tmp; + tmp = level->getDedication(false); + if (!tmp.empty()) + text += string(" - Dedication: ")+ tmp; + return text; + } +} + + +/* -------------------- Variables -------------------- */ + +namespace +{ + Client client_instance; + const char HSEP = '^'; // history separator (use character that user cannot use) +} + +#define CLIENT client_instance + + +/* -------------------- Client class -------------------- */ + +Client::Client() +: m_state (cls_idle), + m_levelname(), + m_hunt_against_time(0), + m_cheater(false), + m_user_input() +{ + m_network_host = 0; +} + +Client::~Client() +{ + network_stop(); +} + +bool Client::network_start() +{ + if (m_network_host) + return true; + + m_network_host = enet_host_create (NULL, + 1 /* only allow 1 outgoing connection */, + 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, + 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); + + if (m_network_host == NULL) { + fprintf (stderr, + "An error occurred while trying to create an ENet client host.\n"); + return false; + } + + + // ----- Connect to server + + ENetAddress sv_address; + ENetPeer *m_server; + + /* Connect to some.server.net:1234. */ + enet_address_set_host (&sv_address, "localhost"); + sv_address.port = 12345; + + /* Initiate the connection, allocating the two channels 0 and 1. */ + m_server = enet_host_connect (m_network_host, &sv_address, 2); + + if (m_server == NULL) { + fprintf (stderr, + "No available peers for initiating an ENet connection.\n"); + return false; + } + + // Wait up to 5 seconds for the connection attempt to succeed. + ENetEvent event; + if (enet_host_service (m_network_host, &event, 5000) > 0 && + event.type == ENET_EVENT_TYPE_CONNECT) + { + fprintf (stderr, "Connection to some.server.net:1234 succeeded."); + return true; + } + else + { + /* Either the 5 seconds are up or a disconnect event was */ + /* received. Reset the peer in the event the 5 seconds */ + /* had run out without any significant event. */ + enet_peer_reset (m_server); + m_server = 0; + + fprintf (stderr, "Connection to localhost:12345 failed."); + return false; + } +} + +void Client::network_stop () +{ + if (m_network_host) + enet_host_destroy (m_network_host); + if (m_server) + enet_peer_reset (m_server); +} + + +/* ---------- Event handling ---------- */ + +void Client::handle_events() +{ + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_KEYDOWN: + on_keydown(e); + break; + case SDL_MOUSEMOTION: + if (abs(e.motion.xrel) > 300 || abs(e.motion.yrel) > 300) { + fprintf(stderr, "mouse event with %i, %i\n", e.motion.xrel, e.motion.yrel); + } + else + server::Msg_MouseForce (options::GetDouble("MouseSpeed") * + V2 (e.motion.xrel, e.motion.yrel)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + on_mousebutton(e); + break; + case SDL_ACTIVEEVENT: { + update_mouse_button_state(); + if (e.active.gain == 0 && !video::IsFullScreen()) + show_menu(); + break; + } + + case SDL_VIDEOEXPOSE: { + display::RedrawAll(video::GetScreen()); + break; + } + + case SDL_QUIT: + client::Msg_Command("abort"); + break; + } + } +} + +void Client::update_mouse_button_state() +{ + int b = SDL_GetMouseState(0, 0); + player::InhibitPickup((b & SDL_BUTTON(1)) || (b & SDL_BUTTON(3))); +} + +void Client::on_mousebutton(SDL_Event &e) +{ + if (e.button.state == SDL_PRESSED) { + if (e.button.button == 1) { + // left mousebutton -> activate first item in inventory + server::Msg_ActivateItem (); + } + else if (e.button.button == 3|| e.button.button == 4) { + // right mousebutton, wheel down -> rotate inventory + rotate_inventory(+1); + } + else if (e.button.button == 5) { + // wheel down -> inverse rotate inventory + rotate_inventory(-1); + } + } + update_mouse_button_state(); +} + +void Client::rotate_inventory (int direction) +{ + m_user_input = ""; + STATUSBAR->hide_text(); + player::RotateInventory(direction); +} + + +/* -------------------- Console related -------------------- */ + +class HistoryProxy { + static int instances; +public: + static string content; + + HistoryProxy(); + ~HistoryProxy() { + if (!--instances) app.state->setProperty("CommandHistory", content); + } +}; + +string HistoryProxy::content; +int HistoryProxy::instances = 0; + +HistoryProxy::HistoryProxy() { + if (!instances++) { + content = app.state->getString("CommandHistory"); + if (content.find(HSEP) == string::npos) content = string(1, HSEP); + } +} + +static void user_input_history_append(const string& text, bool at_end = true) { + HistoryProxy history; + size_t old_pos = history.content.find(string(1, HSEP)+text+HSEP); + + if (old_pos != string::npos) + history.content.erase(old_pos, text.length()+1); + + if (at_end) + history.content += text+HSEP; + else + history.content = string(1, HSEP)+text+history.content; +} + +void Client::process_userinput() +{ + if (m_user_input != "") { + STATUSBAR->hide_text(); + string commands = m_user_input; + + user_input_history_append(m_user_input); + m_user_input = ""; + + size_t sep_pos; + while ((sep_pos = commands.find_first_of(';')) != string::npos) { + string first_command = commands.substr(0, sep_pos); + commands.erase(0, sep_pos+1); + server::Msg_Command (first_command); + } + server::Msg_Command (commands); // last command + } +} + +void Client::user_input_append (char c) { + m_user_input += c; + Msg_ShowText (m_user_input, false); +} + +void Client::user_input_backspace () +{ + if (!m_user_input.empty()) { + m_user_input.erase (m_user_input.size()-1, 1); + if (!m_user_input.empty()) { + // still not empty + Msg_ShowText (m_user_input, false); + } else { + // empty + STATUSBAR->hide_text(); + } + } +} +void Client::user_input_previous () +{ + HistoryProxy history; + size_t last_start = history.content.find_last_of(HSEP, history.content.length()-2); + + if (last_start != string::npos) { + string prev_input = history.content.substr(last_start+1, history.content.length()-last_start-2); + history.content.erase(last_start+1); + user_input_history_append(m_user_input, false); + m_user_input = prev_input; + + if (m_user_input.empty()) + STATUSBAR->hide_text(); + else + Msg_ShowText (m_user_input, false); + } +} +void Client::user_input_next () +{ + HistoryProxy history; + size_t first_end = history.content.find_first_of(HSEP, 1); + + if (first_end != string::npos) { + string next_input = history.content.substr(1, first_end-1); + history.content.erase(0, first_end); + user_input_history_append(m_user_input); + m_user_input = next_input; + + if (m_user_input.empty()) + STATUSBAR->hide_text(); + else + Msg_ShowText (m_user_input, false); + } +} + +void Client::on_keydown(SDL_Event &e) +{ + SDLKey keysym = e.key.keysym.sym; + SDLMod keymod = e.key.keysym.mod; + + if (keymod & KMOD_CTRL) { + switch (keysym) { + case SDLK_a: + server::Msg_Command ("restart"); + break; + case SDLK_F3: + if (keymod & KMOD_SHIFT) { + // force a reload from file + lev::Proxy * curProxy = lev::Proxy::loadedLevel(); + if (curProxy != NULL) + curProxy->release(); + server::Msg_Command ("restart"); + } + default: + break; + }; + } + else if (keymod & KMOD_ALT) { + switch (keysym) { + case SDLK_x: abort(); break; + case SDLK_t: + if (enigma::WizardMode) { + Screen *scr = video::GetScreen(); + ecl::TintRect(scr->get_surface (), display::GetGameArea(), + 100, 100, 100, 0); + scr->update_all(); + } + break; + case SDLK_s: + if (enigma::WizardMode) { + server::Msg_Command ("god"); + } + break; + case SDLK_RETURN: + { + video::TempInputGrab (false); + video::ToggleFullscreen (); + sdl::FlushEvents(); + } + break; + default: + break; + }; + } + else { + switch (keysym) { + case SDLK_ESCAPE: show_menu(); break; + case SDLK_LEFT: set_mousespeed(options::GetMouseSpeed() - 1); break; + case SDLK_RIGHT: set_mousespeed(options::GetMouseSpeed() + 1); break; + case SDLK_TAB: rotate_inventory(+1); break; + case SDLK_F1: show_help(); break; + case SDLK_F2: + // display hint + break; + case SDLK_F3: + if (keymod & KMOD_SHIFT) + server::Msg_Command ("restart"); + else + server::Msg_Command ("suicide"); + break; + + case SDLK_F4: Msg_AdvanceLevel(lev::ADVANCE_STRICTLY); break; + case SDLK_F5: Msg_AdvanceLevel(lev::ADVANCE_UNSOLVED); break; + case SDLK_F6: Msg_JumpBack(); break; + + case SDLK_F10: { + lev::Proxy *level = lev::Proxy::loadedLevel(); + std::string basename = std::string("screenshots/") + + level->getLocalSubstitutionLevelPath(); + std::string fname = basename + ".png"; + std::string fullPath; + int i = 1; + while (app.resourceFS->findFile(fname, fullPath)) { + fname = basename + ecl::strf("#%d", i++) + ".png"; + } + std::string savePath = app.userImagePath + "/" + fname; + video::Screenshot(savePath); + break; + } + case SDLK_RETURN: process_userinput(); break; + case SDLK_BACKSPACE: user_input_backspace(); break; + case SDLK_UP: user_input_previous(); break; + case SDLK_DOWN: user_input_next(); break; + default: + if (e.key.keysym.unicode && (e.key.keysym.unicode & 0xff80) == 0) { + char ascii = static_cast(e.key.keysym.unicode & 0x7f); + if (isalnum (ascii) || + strchr(" .-!\"$%&/()=?{[]}\\#'+*~_,;.:<>|", ascii)) // don't add '^' or change history code + { + user_input_append(ascii); + } + } + + break; + } + } +} + +static const char *helptext_ingame[] = { + N_("Left mouse button:"), N_("Activate/drop leftmost inventory item"), + N_("Right mouse button:"), N_("Rotate inventory items"), + N_("Escape:"), N_("Show game menu"), + N_("F1:"), N_("Show this help"), + N_("F3:"), N_("Kill current marble"), + N_("Shift+F3:"), N_("Restart the current level"), + N_("F4:"), N_("Skip to next level"), + N_("F5:"), 0, // see below + N_("F6:"), N_("Jump back to last level"), + N_("F10:"), N_("Make screenshot"), + N_("Left/right arrow:"), N_("Change mouse speed"), + N_("Alt+x:"), N_("Return to level menu"), +// N_("Alt+Return:"), N_("Switch between fullscreen and window"), + 0 +}; + +void Client::show_help() +{ + server::Msg_Pause (true); + video::TempInputGrab grab(false); + + helptext_ingame[15] = app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST + ? _("Skip to next level for best score hunt") + : _("Skip to next unsolved level"); + + video::ShowMouse(); + gui::displayHelp(helptext_ingame, 200); + video::HideMouse(); + + update_mouse_button_state(); + if (m_state == cls_game) + display::RedrawAll(video::GetScreen()); + + server::Msg_Pause (false); + game::ResetGameTimer(); + + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) + server::Msg_Command ("restart"); // inhibit cheating + +} + + +void Client::show_menu() +{ + server::Msg_Pause (true); + + ecl::Screen *screen = video::GetScreen(); + + video::TempInputGrab grab (false); + + video::ShowMouse(); + { + int x, y; + display::GetReferencePointCoordinates(&x, &y); + enigma::gui::GameMenu(x, y).manage(); + } + video::HideMouse(); + update_mouse_button_state(); + if (m_state == cls_game) + display::RedrawAll(screen); + + server::Msg_Pause (false); + game::ResetGameTimer(); + +} + +void Client::draw_screen() +{ + switch (m_state) { + case cls_error: { + Screen *scr = video::GetScreen(); + GC gc (scr->get_surface()); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + Font *f = enigma::GetFont("menufont"); + + vector lines; + + ecl::split_copy (m_error_message, '\n', back_inserter(lines)); + int x = 60; + int y = 60; + int yskip = 25; + const video::VMInfo *vminfo = video::GetInfo(); + int width = vminfo->width - 120; + for (unsigned i=0; irender(gc, x, y, lines[i].substr(0,breakPos).c_str()); + y += yskip; + if (breakPos != lines[i].size()) { + // process rest of line + lines[i] = lines[i].substr(breakPos); + } else { + // process next line + i++; + } + } + scr->update_all(); + scr->flush_updates(); + break; + } + default: + break; + } +} + + +std::string Client::init_hunted_time() +{ + std::string hunted; + m_hunt_against_time = 0; + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) { + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::Proxy *curProxy = ind->getCurrent(); + lev::RatingManager *ratingMgr = lev::RatingManager::instance(); + + int difficulty = app.state->getInt("Difficulty"); + int wr_time = ratingMgr->getBestScore(curProxy, difficulty); + int best_user_time = scm->getBestUserScore(curProxy, difficulty); + + if (best_user_time>0 && (wr_time == -1 || best_user_time0) { + m_hunt_against_time = wr_time; + hunted = ratingMgr->getBestScoreHolder(curProxy, difficulty); + } + + // STATUSBAR->set_timerstart(-m_hunt_against_time); + } + return hunted; +} + +void Client::tick (double dtime) +{ + const double timestep = 0.01; // 10ms + + switch (m_state) { + case cls_idle: + break; + + case cls_preparing_game: { + video::TransitionEffect *fx = m_effect.get(); + if (fx && !fx->finished()) { + fx->tick (dtime); + } + else { + m_effect.reset(); + server::Msg_StartGame(); + + m_state = cls_game; + m_timeaccu = 0; + m_total_game_time = 0; + sdl::FlushEvents(); + } + break; + } + + case cls_game: + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) { + int old_second = round_nearest (m_total_game_time); + int second = round_nearest (m_total_game_time + dtime); + + if (m_hunt_against_time && old_second <= m_hunt_against_time) { + if (second > m_hunt_against_time) { // happens exactly once when par has passed by + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::Proxy *curProxy = ind->getCurrent(); + lev::RatingManager *ratingMgr = lev::RatingManager::instance(); + int difficulty = app.state->getInt("Difficulty"); + int wr_time = ratingMgr->getBestScore(curProxy, difficulty); + int best_user_time = scm->getBestUserScore(curProxy, difficulty); + string message; + + if (wr_time>0 && (best_user_time<0 || best_user_time>wr_time)) { + message = string(_("Too slow for ")) + + ratingMgr->getBestScoreHolder(curProxy, difficulty) + + ".. [Ctrl-A]"; + } + else { + message = string(_("You are slow today.. [Ctrl-A]")); + } + + client::Msg_PlaySound("shatter", 1.0); + Msg_ShowText(message, true, 2.0); + } + else { + if (old_second= (m_hunt_against_time-5) || // at least 5 seconds + second >= round_nearest (m_hunt_against_time*.8))) // or the last 20% before par + { + client::Msg_PlaySound("pickup", 1.0); + } + } + } + } + + m_total_game_time += dtime; + STATUSBAR->set_time (m_total_game_time); + // fall through + case cls_finished: { + m_timeaccu += dtime; + for (;m_timeaccu >= timestep; m_timeaccu -= timestep) { + display::Tick (timestep); + } + display::Redraw(video::GetScreen()); + handle_events(); + break; + } + + case cls_gamemenu: + break; + case cls_gamehelp: + break; + case cls_abort: + break; + case cls_error: + { + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_KEYDOWN: + case SDL_QUIT: + client::Msg_Command("abort"); + break; + } + } + } + break; + } +} + +void Client::level_finished() +{ + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::Proxy *curProxy = ind->getCurrent(); + lev::RatingManager *ratingMgr = lev::RatingManager::instance(); + int difficulty = app.state->getInt("Difficulty"); + int wr_time = ratingMgr->getBestScore(curProxy, difficulty); + int best_user_time = scm->getBestUserScore(curProxy, difficulty); + string par_name = ratingMgr->getBestScoreHolder(curProxy, difficulty); + int par_time = ratingMgr->getParScore(curProxy, difficulty); + + int level_time = round_nearest (m_total_game_time); + + string text; + bool timehunt_restart = false; + + if (wr_time > 0) { + if (best_user_time<0 || best_user_time>wr_time) { + if (level_time == wr_time) + text = string(_("Exactly the world record of "))+par_name+"!"; + else if (level_time0) { + if (level_time == best_user_time) { + text = _("Again your personal record..."); + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) + timehunt_restart = true; // when hunting yourself: Equal is too slow + } + else if (level_time= 0 && level_time <= par_time) + text = _("New personal record - better than par!"); + else if (par_time >= 0) + text = _("New personal record, but over par!"); + else + text = _("New personal record!"); + } + + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST && + (wr_time>0 || best_user_time>0)) + { + bool with_par = best_user_time == -1 || (wr_time >0 && wr_time0) { + if (best_user_time>0 && level_time (behind/60)%100, behind%60); + if (with_par) + text += _("behind world record."); + else + text += _("behind your record."); + + timehunt_restart = true; // time hunt failed -> repeat level + } + } + + if (text.length() == 0) { + if (par_time >= 0 && level_time <= par_time) + text = _("Level finished - better than par!"); + else if (par_time >= 0) + text = _("Level finished, but over par!"); + else + text = _("Level finished!"); + } + if (m_cheater) + text += _(" Cheater!"); + + Msg_ShowText (text, false); + + if (!m_cheater) { + scm->updateUserScore(curProxy, difficulty, level_time); + + // save score (just in case Enigma crashes when loading next level) + lev::ScoreManager::instance()->save(); + + } + + if (timehunt_restart) + server::Msg_Command("restart"); + else + m_state = cls_finished; +} + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +void Client::level_loaded(bool isRestart) +{ + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::Proxy *curProxy = ind->getCurrent(); + + // update window title + video::SetCaption(ecl::strf(_("Enigma pack %s - level #%d: %s"), ind->getName().c_str(), + ind->getCurrentLevel(), curProxy->getTitle().c_str()).c_str()); + + string hunted = init_hunted_time(); // sets m_hunt_against_time (used below) + + // show level information (name, author, etc.) + { + string displayed_info = ""; + if (m_hunt_against_time>0) { + if (hunted == "you") + displayed_info = _("Your record: "); + else + displayed_info = _("World record to beat: "); + displayed_info += ecl::strf("%d:%02d", (m_hunt_against_time/60)%100, + m_hunt_against_time%60); +//+ _(" by ") +hunted; +// makes the string too long in many levels + Msg_ShowText (displayed_info, true, 4.0); + } + else { + displayed_info = displayedLevelInfo(curProxy); + Msg_ShowText (displayed_info, true, 2.0); + } + } + + sound::FadeoutMusic(); + if (options::GetBool("InGameMusic")) { + sound::PlayMusic (options::GetString("LevelMusicFile")); + } else { + sound::StopMusic(); + } + + // start screen transition + GC gc(video::BackBuffer()); + display::DrawAll(gc); + + m_effect.reset (video::MakeEffect ((isRestart ? video::TM_SQUARES : + video::TM_PUSH_RANDOM), video::BackBuffer())); + m_cheater = false; + m_state = cls_preparing_game; +} + + +void Client::handle_message (Message *m) { // @@@ unused + switch (m->type) { + case CLMSG_LEVEL_LOADED: + + break; + default: + fprintf (stderr, "Unhandled client event: %d\n", m->type); + break; + } +} + +void Client::error (const string &text) +{ + m_error_message = text; + m_state = cls_error; + draw_screen(); +} + + +/* -------------------- Functions -------------------- */ + + +bool client::NetworkStart() +{ + return CLIENT.network_start(); +} + +void client::Msg_LevelLoaded(bool isRestart) +{ + CLIENT.level_loaded(isRestart); +} + +void client::Tick (double dtime) { + CLIENT.tick (dtime); + sound::Tick (dtime); +} + +void client::Stop() { + CLIENT.stop (); +} + +void client::Msg_AdvanceLevel (lev::LevelAdvanceMode mode) { + + lev::Index *ind = lev::Index::getCurrentIndex(); + // log last played level + lev::PersistentIndex::addCurrentToHistory(); + + if (ind->advanceLevel(mode)) { + // now we may advance + server::Msg_LoadLevel(ind->getCurrent(), false); + } + else + client::Msg_Command("abort"); +} + +void client::Msg_JumpBack() { + // log last played level + lev::PersistentIndex::addCurrentToHistory(); + server::Msg_JumpBack(); +} + +bool client::AbortGameP() { + return CLIENT.abort_p(); +} + +void client::Msg_Command(const string& cmd) { + if (cmd == "abort") { + CLIENT.abort(); + } + else if (cmd == "level_finished") { + client::Msg_PlaySound("finished", 1.0); + CLIENT.level_finished(); + } + else if (cmd == "cheater") { + CLIENT.mark_cheater(); + } + else if (cmd == "easy_going") { + CLIENT.easy_going(); + } + else { + enigma::Log << "Warning: Client received unknown command '" << cmd << "'\n"; + } +} + +void client::Msg_PlayerPosition (unsigned iplayer, const V2 &pos) +{ + if (iplayer == (unsigned)player::CurrentPlayer()) { + sound::SetListenerPosition (pos); + display::SetReferencePoint (pos); + } +} + +void client::Msg_PlaySound (const std::string &wavfile, + const ecl::V2 &pos, + double relative_volume) +{ + sound::EmitSoundEvent (wavfile.c_str(), pos, relative_volume); +} + +void client::Msg_PlaySound (const std::string &wavfile, double relative_volume) +{ + sound::EmitSoundEvent (wavfile.c_str(), V2(), relative_volume); +} + +void client::Msg_Sparkle (const ecl::V2 &pos) { + display::AddEffect (pos, "ring-anim"); +} + + +void client::Msg_ShowText +(const std::string &text, bool scrolling, double duration) +{ + STATUSBAR->show_text (text, scrolling, duration); +} + +void client::Msg_Error (const std::string &text) +{ + CLIENT.error (text); +} diff --git a/project/jni/application/enigma/src/client.hh b/project/jni/application/enigma/src/client.hh new file mode 100644 index 000000000..e0237a3e5 --- /dev/null +++ b/project/jni/application/enigma/src/client.hh @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef CLIENT_HH_INCLUDED +#define CLIENT_HH_INCLUDED + +#include "lev/Index.hh" + +namespace enigma_client +{ + using namespace enigma; + +/* -------------------- Functions -------------------- */ + + bool NetworkStart(); + + + void Tick (double dtime); + + bool AbortGameP(); + + void Stop(); + +/* -------------------- Server->Client messages -------------------- */ + + void Msg_Command(const std::string& cmd); + + void Msg_AdvanceLevel (lev::LevelAdvanceMode mode); + void Msg_JumpBack(); + + void Msg_LevelLoaded(bool isRestart); + + void Msg_PlayerPosition (unsigned iplayer, const ecl::V2 &pos); + + void Msg_Sparkle (const ecl::V2 &pos); + + void Msg_ShowText (const std::string &text, bool scrolling, double duration=-1); + + //! Play sound emanating from an absolute position in the world + void Msg_PlaySound (const std::string &soundname, + const ecl::V2 &pos, + double relative_volume); + + //! Play a sound emanating from the player's current position + void Msg_PlaySound (const std::string &soundname, double relative_volume); + + //! Stop the game and display an error message instead + void Msg_Error (const std::string &text); +} + +namespace enigma +{ + namespace client = enigma_client; +} + +#endif diff --git a/project/jni/application/enigma/src/client_internal.hh b/project/jni/application/enigma/src/client_internal.hh new file mode 100644 index 000000000..c7eccc6f8 --- /dev/null +++ b/project/jni/application/enigma/src/client_internal.hh @@ -0,0 +1,172 @@ +#include "gui/Menu.hh" +#include "video.hh" +#include "ecl_buffer.hh" + +namespace +{ + using ecl::Buffer; + using std::string; + +/* -------------------- Server -> Client messages -------------------- */ + + enum ClientCommand { + CLMSG_NOOP, + CLMSG_NEW_WORLD, + CLMSG_LEVEL_LOADED, + CLMSG_CHANGE_FIELD, + CLMSG_ADD_ACTOR, + CLMSG_MOVE_ACTOR, + CLMSG_FOCUS_ACTOR, + CLMSG_CHANGE_LINE, + CLMSG_PLAY_SOUND, + CLMSG_SHOW_TEXT, + CLMSG_ERROR // error occurred + }; + + struct Message { + Message (ClientCommand type_ = CLMSG_NOOP) : type (type_) { + } + + ClientCommand type; + }; + + struct Cl_NewWorld { + std::string levelname; + int width; + int height; + }; + + struct Cl_LevelLoaded : public Message { + Cl_LevelLoaded() : Message (CLMSG_LEVEL_LOADED) { + } + }; + Buffer &operator << (Buffer &b, const Cl_LevelLoaded &m) { + return b << Uint8 (CLMSG_LEVEL_LOADED); + } + + struct Cl_ChangeField { + + + }; + + struct Cl_AddActor { + + }; + + struct Cl_MoveActor { + + }; + + struct Cl_FocusActor { + + }; + + struct Cl_ShowText : public Message { + Cl_ShowText() : Message(CLMSG_SHOW_TEXT) { + } + + string text; + float duration; + bool scrolling; + bool interruptible; + }; + + struct Cl_AddEffect { + + float x, y; + }; + + struct Cl_PlaySound { + string soundname; + float x, y; + int priority; + }; + +/* -------------------- Client class -------------------- */ + + enum ClientState { + cls_idle, + cls_preparing_game, // level loaded, currently updating the screen + cls_game, + cls_finished, // level finished, waiting for next one + cls_gamehelp, + cls_gamemenu, + cls_abort, + cls_error + }; + + + class Client { + public: + Client(); + ~Client(); + + void tick (double dtime); + void stop() { m_state = cls_idle; } + bool network_start(); + void network_stop(); + + + void handle_message(Message *msg); + + void level_loaded(bool isRestart); + void level_finished(); + + void error (const std::string &text); + + void abort() { m_state = cls_abort; } + bool abort_p() const { return m_state == cls_abort; } + + void mark_cheater() { m_cheater = true; } + void easy_going() { m_hunt_against_time = false; } + private: + + std::string init_hunted_time(); + + + /* ---------- Private methods ---------- */ + + void show_menu(); + void show_help(); + + // Screen update (state dependant) + void draw_screen(); + + // Event handling + void handle_events(); + void on_keydown(SDL_Event &e); + void on_mousebutton(SDL_Event &e); + void update_mouse_button_state(); + + // Inventory & command line + void rotate_inventory(int direction); + + void process_userinput(); + void user_input_append (char c); + void user_input_backspace (); + void user_input_previous (); + void user_input_next (); + + + // Variables + ClientState m_state; + string m_levelname; + double m_timeaccu; + + double m_total_game_time; + int m_hunt_against_time; + bool m_cheater; + + string m_user_input; + string m_error_message; + + std::auto_ptr m_effect; + ENetHost *m_network_host; + ENetPeer *m_server; + + private: + Client (const Client&); + Client &operator = (const Client &); + }; +} + diff --git a/project/jni/application/enigma/src/config.h b/project/jni/application/enigma/src/config.h new file mode 100644 index 000000000..7b9cec211 --- /dev/null +++ b/project/jni/application/enigma/src/config.h @@ -0,0 +1,323 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* android paths */ +#define SYSTEM_DATA_DIR "." +#define LOCALEDIR "." + + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Include experimental features */ +/* #undef ENABLE_EXPERIMENTAL */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#define ENABLE_NLS 1 + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARGZ_H */ + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +/* #undef HAVE_DCGETTEXT */ + +/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_FEOF_UNLOCKED 0 + +/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if + you don't. */ +#define HAVE_DECL_FGETS_UNLOCKED 0 + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#define HAVE_DECL_GETC_UNLOCKED 1 + +/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you + don't. */ +#define HAVE_DECL__SNPRINTF 0 + +/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you + don't. */ +#define HAVE_DECL__SNWPRINTF 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the `fwprintf' function. */ +#define HAVE_FWPRINTF 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getegid' function. */ +#define HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define HAVE_GETEUID 1 + +/* Define to 1 if you have the `getgid' function. */ +#define HAVE_GETGID 1 + +/* Define to 1 if you have the `getpagesize' function. */ +/* #undef HAVE_GETPAGESIZE */ + +/* Define if the GNU gettext() function is already present or preinstalled. */ +/* #undef HAVE_GETTEXT */ + +/* Define to 1 if you have the `getuid' function. */ +#define HAVE_GETUID 1 + +/* Define if you have the iconv() function. */ +#define HAVE_ICONV 1 + +/* Define if you have the 'intmax_t' type in or . */ +#define HAVE_INTMAX_T 1 + +/* Define if exists and doesn't clash with . */ +#define HAVE_INTTYPES_H 1 + +/* Define if exists, doesn't clash with , and + declares uintmax_t. */ +#define HAVE_INTTYPES_H_WITH_UINTMAX 1 + +/* Define if you have and nl_langinfo(CODESET). */ +/* #undef HAVE_LANGINFO_CODESET */ + +/* Define if your file defines LC_MESSAGES. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if you have the `png' library (-lpng). */ +#define HAVE_LIBPNG 1 + +/* Define to 1 if you have the `png12' library (-lpng12). */ +/* #undef HAVE_LIBPNG12 */ + +/* Define to 1 if you have the `SDL_image' library (-lSDL_image). */ +#define HAVE_LIBSDL_IMAGE 1 + +/* Define to 1 if you have the `SDL_mixer' library (-lSDL_mixer). */ +#define HAVE_LIBSDL_MIXER 1 + +/* Define to 1 if you have the `SDL_ttf' library (-lSDL_ttf). */ +#define HAVE_LIBSDL_TTF 1 + +/* Define to 1 if you have the `winmm' library (-lwinmm). */ +/* #undef HAVE_LIBWINMM */ + +/* Define to 1 if you have the `xerces-c' library (-lxerces-c). */ +#define HAVE_LIBXERCES_C 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the 'long double' type. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define if you have the 'long long' type. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +/* #undef HAVE_MEMPCPY */ + +/* Define to 1 if you have a working `mmap' system call. */ +/* #undef HAVE_MMAP */ + +/* Define to 1 if you have the `munmap' function. */ +#define HAVE_MUNMAP 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NL_TYPES_H */ + +/* Define if your printf() function supports format strings with positions. */ +#define HAVE_POSIX_PRINTF 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define if exists, doesn't clash with , and declares + uintmax_t. */ +#define HAVE_STDINT_H_WITH_UINTMAX 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +/* #undef HAVE_STPCPY */ + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `tsearch' function. */ +/* #undef HAVE_TSEARCH */ + +/* Define if you have the 'uintmax_t' type in or . */ +#define HAVE_UINTMAX_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the 'unsigned long long' type. */ +#define HAVE_UNSIGNED_LONG_LONG 1 + +/* Define if you have the 'wchar_t' type. */ +#define HAVE_WCHAR_T 1 + +/* Define to 1 if you have the `wcslen' function. */ +#define HAVE_WCSLEN 1 + +/* Define if you have the 'wint_t' type. */ +#define HAVE_WINT_T 1 + +/* Define to 1 if you have the `__argz_count' function. */ +/* #undef HAVE___ARGZ_COUNT */ + +/* Define to 1 if you have the `__argz_next' function. */ +/* #undef HAVE___ARGZ_NEXT */ + +/* Define to 1 if you have the `__argz_stringify' function. */ +/* #undef HAVE___ARGZ_STRINGIFY */ + +/* Define to 1 if you have the `__fsetlocking' function. */ +/* #undef HAVE___FSETLOCKING */ + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST const + +/* Define if integer division by zero raises signal SIGFPE. */ +#define INTDIV0_RAISES_SIGFPE 0 + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "enigma" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "enigma 1.01" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "enigma" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.01" + +/* Define if exists and defines unusable PRI* macros. */ +/* #undef PRI_MACROS_BROKEN */ + +/* Whether SDL_image library provides init */ +#define SDL_IMG_INIT + +/* Whether SDL_mixer library provides init */ +#define SDL_MIX_INIT + +/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +/* #undef SIZE_MAX */ + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `long int' if does not define. */ +/* #undef off_t */ + +/* Define as the type of the result of subtracting two pointers, if the system + doesn't define it. */ +/* #undef ptrdiff_t */ + +/* Define to empty if the C compiler doesn't support this keyword. */ +/* #undef signed */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to unsigned long or unsigned long long if and + don't define. */ +/* #undef uintmax_t */ diff --git a/project/jni/application/enigma/src/config.h.in b/project/jni/application/enigma/src/config.h.in new file mode 100644 index 000000000..a17cf7042 --- /dev/null +++ b/project/jni/application/enigma/src/config.h.in @@ -0,0 +1,317 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#undef CRAY_STACKSEG_END + +/* Define to 1 if using `alloca.c'. */ +#undef C_ALLOCA + +/* Include experimental features */ +#undef ENABLE_EXPERIMENTAL + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#undef ENABLE_NLS + +/* Define to 1 if you have `alloca', as a function or macro. */ +#undef HAVE_ALLOCA + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#undef HAVE_ALLOCA_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARGZ_H + +/* Define to 1 if you have the `asprintf' function. */ +#undef HAVE_ASPRINTF + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#undef HAVE_DCGETTEXT + +/* Define to 1 if you have the declaration of `feof_unlocked', and to 0 if you + don't. */ +#undef HAVE_DECL_FEOF_UNLOCKED + +/* Define to 1 if you have the declaration of `fgets_unlocked', and to 0 if + you don't. */ +#undef HAVE_DECL_FGETS_UNLOCKED + +/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you + don't. */ +#undef HAVE_DECL_GETC_UNLOCKED + +/* Define to 1 if you have the declaration of `_snprintf', and to 0 if you + don't. */ +#undef HAVE_DECL__SNPRINTF + +/* Define to 1 if you have the declaration of `_snwprintf', and to 0 if you + don't. */ +#undef HAVE_DECL__SNWPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRENT_H + +/* Define to 1 if you have the `fwprintf' function. */ +#undef HAVE_FWPRINTF + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define to 1 if you have the `getegid' function. */ +#undef HAVE_GETEGID + +/* Define to 1 if you have the `geteuid' function. */ +#undef HAVE_GETEUID + +/* Define to 1 if you have the `getgid' function. */ +#undef HAVE_GETGID + +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#undef HAVE_GETTEXT + +/* Define to 1 if you have the `getuid' function. */ +#undef HAVE_GETUID + +/* Define if you have the iconv() function. */ +#undef HAVE_ICONV + +/* Define if you have the 'intmax_t' type in or . */ +#undef HAVE_INTMAX_T + +/* Define if exists and doesn't clash with . */ +#undef HAVE_INTTYPES_H + +/* Define if exists, doesn't clash with , and + declares uintmax_t. */ +#undef HAVE_INTTYPES_H_WITH_UINTMAX + +/* Define if you have and nl_langinfo(CODESET). */ +#undef HAVE_LANGINFO_CODESET + +/* Define if your file defines LC_MESSAGES. */ +#undef HAVE_LC_MESSAGES + +/* Define to 1 if you have the `png' library (-lpng). */ +#undef HAVE_LIBPNG + +/* Define to 1 if you have the `png12' library (-lpng12). */ +#undef HAVE_LIBPNG12 + +/* Define to 1 if you have the `SDL_image' library (-lSDL_image). */ +#undef HAVE_LIBSDL_IMAGE + +/* Define to 1 if you have the `SDL_mixer' library (-lSDL_mixer). */ +#undef HAVE_LIBSDL_MIXER + +/* Define to 1 if you have the `SDL_ttf' library (-lSDL_ttf). */ +#undef HAVE_LIBSDL_TTF + +/* Define to 1 if you have the `winmm' library (-lwinmm). */ +#undef HAVE_LIBWINMM + +/* Define to 1 if you have the `xerces-c' library (-lxerces-c). */ +#undef HAVE_LIBXERCES_C + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define if you have the 'long double' type. */ +#undef HAVE_LONG_DOUBLE + +/* Define if you have the 'long long' type. */ +#undef HAVE_LONG_LONG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `mempcpy' function. */ +#undef HAVE_MEMPCPY + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the `munmap' function. */ +#undef HAVE_MUNMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NL_TYPES_H + +/* Define if your printf() function supports format strings with positions. */ +#undef HAVE_POSIX_PRINTF + +/* Define to 1 if you have the `putenv' function. */ +#undef HAVE_PUTENV + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if exists, doesn't clash with , and declares + uintmax_t. */ +#undef HAVE_STDINT_H_WITH_UINTMAX + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the `tsearch' function. */ +#undef HAVE_TSEARCH + +/* Define if you have the 'uintmax_t' type in or . */ +#undef HAVE_UINTMAX_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the 'unsigned long long' type. */ +#undef HAVE_UNSIGNED_LONG_LONG + +/* Define if you have the 'wchar_t' type. */ +#undef HAVE_WCHAR_T + +/* Define to 1 if you have the `wcslen' function. */ +#undef HAVE_WCSLEN + +/* Define if you have the 'wint_t' type. */ +#undef HAVE_WINT_T + +/* Define to 1 if you have the `__argz_count' function. */ +#undef HAVE___ARGZ_COUNT + +/* Define to 1 if you have the `__argz_next' function. */ +#undef HAVE___ARGZ_NEXT + +/* Define to 1 if you have the `__argz_stringify' function. */ +#undef HAVE___ARGZ_STRINGIFY + +/* Define to 1 if you have the `__fsetlocking' function. */ +#undef HAVE___FSETLOCKING + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* Define if integer division by zero raises signal SIGFPE. */ +#undef INTDIV0_RAISES_SIGFPE + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define if exists and defines unusable PRI* macros. */ +#undef PRI_MACROS_BROKEN + +/* Whether SDL_image library provides init */ +#undef SDL_IMG_INIT + +/* Whether SDL_mixer library provides init */ +#undef SDL_MIX_INIT + +/* Define as the maximum value of type 'size_t', if the system doesn't define + it. */ +#undef SIZE_MAX + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +#undef STACK_DIRECTION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `long int' if does not define. */ +#undef off_t + +/* Define as the type of the result of subtracting two pointers, if the system + doesn't define it. */ +#undef ptrdiff_t + +/* Define to empty if the C compiler doesn't support this keyword. */ +#undef signed + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to unsigned long or unsigned long long if and + don't define. */ +#undef uintmax_t diff --git a/project/jni/application/enigma/src/d_engine.hh b/project/jni/application/enigma/src/d_engine.hh new file mode 100644 index 000000000..8be4d5062 --- /dev/null +++ b/project/jni/application/enigma/src/d_engine.hh @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef D_ENGINE_HH +#define D_ENGINE_HH + +#include "ecl_geom.hh" +#include "ecl_array2.hh" +#include "ecl_alist.hh" +#include "ecl_fwd.hh" + +namespace display +{ + +/* -------------------- DisplayEngine -------------------- */ + + class DisplayEngine { + public: + DisplayEngine (int tilew=32, int tileh=32); + ~DisplayEngine(); + + /* ---------- Class configuration ---------- */ + void add_layer (DisplayLayer *l); + void set_screen_area (const ecl::Rect & r); + void set_tilesize (int w, int h); + + int get_tilew () const { return m_tilew; } + int get_tileh () const { return m_tileh; } + int get_width() const { return m_width; } + int get_height() const { return m_height; } + const ecl::Rect &get_area() const { return m_area; } + + /* ---------- Scrolling / page flipping ---------- */ + void set_offset (const ecl::V2 &off); + void move_offset (const ecl::V2 &off); + ecl::V2 get_offset () const { return m_offset; } + + /* ---------- Game-related stuff ---------- */ + void new_world (int w, int h); + void tick (double dtime); + + /* ---------- Coordinate conversion ---------- */ + void world_to_screen (const ecl::V2 & pos, int *x, int *y); + WorldArea screen_to_world (const ScreenArea &a); + ScreenArea world_to_screen (const WorldArea &a); + + /* "Video" coordinates are like screen coordinates, except the + origin coincides with the world origin, not the current + scrolling position. */ + void world_to_video (const ecl::V2 &pos, int *x, int *y); + void video_to_screen (int x, int y, int *xx, int *yy); + void video_to_world (const ecl::Rect &r, ecl::Rect &s); + + V2 to_world (const V2 &pos); + + /* ---------- Screen upates ---------- */ + + void mark_redraw_screen(); + void mark_redraw_area (const WorldArea &wa, int delay=0); + + void redraw_screen_area (const ScreenArea &a); + void redraw_world_area (const WorldArea &a); + + void update_screen(); + void draw_all (ecl::GC &gc); + void update_offset(); + + private: + void update_layer (DisplayLayer *l, WorldArea wa); + + /* ---------- Variables ---------- */ + + std::vector m_layers; + int m_tilew, m_tileh; + + // Offset of screen + ecl::V2 m_offset; // Offset in world units + ecl::V2 m_new_offset; // New offset in world units + int m_screenoffset[2]; // Offset in screen units + + + // Screen area occupied by level display + ecl::Rect m_area; + + // Width and height of the world in tiles + int m_width, m_height; + + ecl::Array2 m_redrawp; + }; + + +/* -------------------- DisplayLayer -------------------- */ + + class DisplayLayer { + public: + DisplayLayer() {} + virtual ~DisplayLayer() {} + + /* ---------- Class configuration ---------- */ + void set_engine (DisplayEngine *e) { m_engine = e; } + DisplayEngine *get_engine() const { return m_engine; } + + /* ---------- DisplayLayer interface ---------- */ + virtual void prepare_draw (const WorldArea &) {} + virtual void draw (ecl::GC &gc, const WorldArea &a, int x, int y) = 0; + virtual void draw_onepass (ecl::GC &/*gc*/) {} + virtual void tick (double /*dtime*/) {} + virtual void new_world (int /*w*/, int /*h*/) {} + + // Functions. + void mark_redraw_area (const ecl::Rect &r) + { + get_engine()->mark_redraw_area(r); + } + private: + DisplayEngine *m_engine; + }; + + +/* -------------------- ModelLayer -------------------- */ + + /*! The base class for all layers that contains Models. */ + class ModelLayer : public DisplayLayer { + public: + ModelLayer() {} + + // DisplayLayer interface + void tick (double dtime); + void new_world (int, int); + + // Member functions + void activate (Model *m); + void deactivate (Model *m); + void maybe_redraw_model(Model *m, bool immediately=false); + + virtual int redraw_size () const { return 2; } + private: + + // Variables + ModelList m_active_models; + ModelList m_active_models_new; + }; + + +/* -------------------- DL_Grid -------------------- */ + + /*! Layer for grid-aligned models (stones, floor tiles, items). */ + + class DL_Grid : public ModelLayer { + public: + DL_Grid(int redrawsize = 1); + ~DL_Grid(); + + void set_model (int x, int y, Model *m); + Model *get_model (int x, int y); + Model *yield_model (int x, int y); + + private: + // DL_Grid interface. + void mark_redraw (int x, int y); + + // DisplayLayer interface. + void new_world (int w, int h); + void draw (ecl::GC &gc, const WorldArea &a, int x, int y); + + // ModelLayer interface + virtual int redraw_size () const { return m_redrawsize; } + + // Variables. + typedef ecl::Array2 ModelArray; + ModelArray m_models; + int m_redrawsize; + }; + + +/* -------------------- Sprites -------------------- */ + + class Sprite : public ecl::Nocopy { + public: + Model *model; + V2 pos; + int screenpos[2]; + SpriteLayer layer; + bool visible; + bool mayNeedRedraw; + + Sprite (const V2 & p, SpriteLayer l, Model *m) + : model(m), pos(p), layer(l), visible(true), mayNeedRedraw(false) + { + screenpos[0] = screenpos[1] = 0; + } + ~Sprite() { delete model; } + }; + + typedef std::vector SpriteList; + + class DL_Sprites : public ModelLayer { + public: + DL_Sprites(); + ~DL_Sprites(); + + /* ---------- DisplayLayer interface ---------- */ + void draw (ecl::GC &gc, const WorldArea &a, int x, int y); + void draw_onepass (ecl::GC &gc); + void new_world (int, int); + + /* ---------- Member functions ---------- */ + SpriteId add_sprite (Sprite *sprite); + void kill_sprite (SpriteId id); + void move_sprite (SpriteId, const ecl::V2& newpos); + void replace_sprite (SpriteId id, Model *m); + + void redraw_sprite_region (SpriteId id); + void draw_sprites (bool shades, ecl::GC &gc); + + Model *get_model (SpriteId id) { return sprites[id]->model; } + + void set_maxsprites (unsigned m) { maxsprites = m; } + + Sprite *get_sprite(SpriteId id); + + static const SpriteId MAGIC_SPRITEID = 1000000; + SpriteList sprites; + + private: + // ModelLayer interface + virtual void tick (double /*dtime*/); + + // Variables. + unsigned numsprites; // Current number of sprites + unsigned maxsprites; // Maximum number of sprites + }; + + +/* -------------------- Shadows -------------------- */ + + struct StoneShadowCache; + + class DL_Shadows : public DisplayLayer { + public: + DL_Shadows(DL_Grid *grid, DL_Sprites *sprites); + ~DL_Shadows(); + + void new_world(int w, int h); + void draw (ecl::GC &gc, int xpos, int ypos, int x, int y); + + void draw (ecl::GC &gc, const WorldArea &a, int x, int y); + private: + /* ---------- Private functions ---------- */ + void shadow_blit (ecl::Surface *scr, int x, int y, + ecl::Surface *shadows, ecl::Rect r); + + bool has_actor (int x, int y); + virtual void prepare_draw (const WorldArea &); + + Model * get_shadow_model(int x, int y); + + /* ---------- Variables ---------- */ + DL_Grid *m_grid; // Stone models + DL_Sprites *m_sprites; // Sprite models + + StoneShadowCache *m_cache; + + Uint32 shadow_ckey; // Color key + ecl::Surface *buffer; + + ecl::Array2 m_hasactor; + }; + + +/* -------------------- Lines -------------------- */ + + struct Line { + V2 start,end; + V2 oldstart, oldend; + + Line(const V2 &s, const V2 &e) :start(s), end(e) {} + Line() {} + }; + + + typedef ecl::AssocList LineMap; + + class DL_Lines : public DisplayLayer { + public: + DL_Lines() : m_id(1) + { + } + + void draw (ecl::GC &/*gc*/, const WorldArea &/*a*/, int /*x*/, int /*y*/) + {} + void draw_onepass (ecl::GC &gc); + + RubberHandle add_line (const V2 &p1, const V2 &p2); + void set_startpoint (unsigned id, const V2 &p1); + void set_endpoint (unsigned id, const V2 &p2); + void kill_line (unsigned id); + + private: + // Private methods. + void mark_redraw_line (const Line &r); + + // Variables. + unsigned m_id; + LineMap m_rubbers; + }; + + +/* -------------------- CommonDisplay -------------------- */ + + /*! Parts of the display engine that are common to the game and + the editor. */ + class CommonDisplay { + public: + CommonDisplay (const ScreenArea &a = ScreenArea (0, 0, 10, 10)); + ~CommonDisplay(); + + Model *set_model (const GridLoc &l, Model *m); + Model *get_model (const GridLoc &l); + Model *yield_model (const GridLoc &l); + + void set_floor (int x, int y, Model *m); + void set_item (int x, int y, Model *m); + void set_stone (int x, int y, Model *m); + + DisplayEngine *get_engine() const { return m_engine; } + + SpriteHandle add_effect (const V2& pos, Model *m); + SpriteHandle add_sprite (const V2 &pos, Model *m); + + RubberHandle add_line (V2 p1, V2 p2); + + void new_world (int w, int h); + void redraw(); + + protected: + DL_Grid *floor_layer; + DL_Grid *item_layer; + DL_Grid *stone_layer; + + DL_Sprites *effects_layer; + + DL_Lines *line_layer; + DL_Sprites *sprite_layer; + DL_Shadows *shadow_layer; + + private: + + DisplayEngine *m_engine; + }; + + +/* -------------------- Scrolling -------------------- */ + + + class Follower { + public: + Follower (DisplayEngine *e); + virtual ~Follower() {} + virtual void tick(double dtime, const ecl::V2 &point) = 0; + virtual void center(const ecl::V2 &point); + + void set_boundary (double b) { m_boundary = b; } + + protected: + DisplayEngine *get_engine() const { return m_engine; } + bool set_offset (V2 offs); + double get_hoff() const; + double get_voff() const; + ecl::V2 get_scrollpos(const ecl::V2 &point); + + double m_boundary; + + private: + DisplayEngine *m_engine; + }; + + /*! Follows a sprite by flipping to the next screen as soon as the + sprite reaches the border of the current screen. */ + class Follower_Screen : public Follower { + public: + Follower_Screen(DisplayEngine *e); + void tick(double dtime, const ecl::V2 &point); + }; + + /*! Follows a sprite by softly scrolling the visible area of the + screen as soon as the sprite reaches the border of the current + screen. */ + class Follower_Scrolling : public Follower { + public: + Follower_Scrolling (DisplayEngine *e, bool screenwise_); + void tick(double dtime, const ecl::V2 &point); + void center(const ecl::V2 &point); + private: + bool currently_scrolling; + V2 curpos, destpos; + V2 dir; + double scrollspeed; + double resttime; + bool screenwise; + }; + + class Follower_Smooth : public Follower { + public: + Follower_Smooth (DisplayEngine *e); + void tick (double time, const ecl::V2 &point); + void center (const ecl::V2 &point); + virtual void set_boundary (double b) {} + + ecl::V2 calc_offset (const ecl::V2 &point); + }; + + +/* -------------------- GameDisplay -------------------- */ + + class GameDisplay : public CommonDisplay { + public: + GameDisplay (const ScreenArea &gamearea, + const ScreenArea &inventoryarea); + ~GameDisplay(); + + StatusBar * get_status_bar() const; + + void tick(double dtime); + void new_world (int w, int h); + + void resize_game_area (int w, int h); + + /* ---------- Scrolling ---------- */ + void set_follow_mode (FollowMode m); + void follow_center(); + void set_follow_sprite(SpriteId id); + void set_reference_point (const ecl::V2 &point); + void set_scroll_boundary (double d); + + // current screen coordinates of reference point + void get_reference_point_coordinates(int *x, int *y); + + /* ---------- Screen updates ---------- */ + void redraw (ecl::Screen *scr); + void redraw_all (ecl::Screen *scr); + void draw_all (ecl::GC &gc); + + private: + void set_follower (Follower *f); + void draw_borders (ecl::GC &gc); + + /* ---------- Variables ---------- */ + Uint32 last_frame_time; + bool redraw_everything; + StatusBarImpl *status_bar; + + V2 m_reference_point; + Follower *m_follower; + + ScreenArea inventoryarea; + }; + + class ModelHandle { + public: + ModelHandle (); + }; +} + +#endif diff --git a/project/jni/application/enigma/src/d_models.cpp b/project/jni/application/enigma/src/d_models.cpp new file mode 100644 index 000000000..d7ccf5683 --- /dev/null +++ b/project/jni/application/enigma/src/d_models.cpp @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2002,2003,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "display_internal.hh" +#include "d_models.hh" + +#include "lua.hh" +#include "options.hh" +#include "d_engine.hh" +#include "video.hh" +#include "main.hh" +#include "nls.hh" +#include "gui/ErrorMenu.hh" + +#include "SDL_image.h" + +#include +#include + +using namespace enigma; +using namespace display; +using namespace std; +using namespace ecl; + +#ifndef CXXLUA +extern "C" { +#include "lualib.h" +#include "tolua++.h" +} +#else +#include "lualib.h" +#include "tolua++.h" +#endif + + +#include "lua-global.hh" +#include "lua-display.hh" +#include "lua-enigma.hh" +#include "lua-ecl.hh" + +/* -------------------- Types -------------------- */ + +namespace +{ + class SurfaceCache_Alpha : public PtrCache{ + public: + Surface *acquire(const std::string &name); + }; + + class SurfaceCache : public PtrCache{ + public: + Surface *acquire(const std::string &name); + }; + + class ModelManager { + public: + ModelManager(); + ~ModelManager(); + + void define (const std::string name, Model *m); + + /* Create new model of type `name'. Returns 0 if no such + model exists. */ + Model *create (const std::string &name); + + /* Remove model definition for `name'. */ + void remove (const std::string &name); + + bool has_model (const std::string &name) const; + + size_t num_templates() const; + + private: + // Variables + typedef ecl::Dict ModelMap; + ModelMap m_templates; + }; +} + + +/* -------------------- SurfaceCache -------------------- */ + +Surface *SurfaceCache_Alpha::acquire(const std::string &name) +{ + string filename; + if (app.resourceFS->findImageFile (name + ".png", filename)) + return ecl::LoadImage(filename.c_str()); + else + return 0; +} + + +Surface *SurfaceCache::acquire(const std::string &name) +{ + string filename; + if (app.resourceFS->findImageFile (name + ".png", filename)) { + SDL_Surface *s = IMG_Load(filename.c_str()); + if (s) { + SDL_Surface *img = 0; + if (s->flags & SDL_SRCALPHA) { + img = SDL_DisplayFormatAlpha(s); + } else { + SDL_SetColorKey(s, SDL_SRCCOLORKEY, //|SDL_RLEACCEL, + SDL_MapRGB(s->format, 255,0,255)); + img = SDL_DisplayFormat(s); + } + if (img) { + SDL_FreeSurface(s); + return Surface::make_surface(img); + } + return Surface::make_surface(s); + } + } + return 0; +} + + +/* -------------------- ModelManager -------------------- */ + +ModelManager::ModelManager() +: m_templates (1069) +{} + +ModelManager::~ModelManager() { + delete_map (m_templates.begin(), m_templates.end()); +} + +void ModelManager::define (const std::string name, Model *m) { + m_templates.insert (name, m); +} + +Model * ModelManager::create (const std::string &name) { + ModelMap::iterator i = m_templates.find(name); + + if (i != m_templates.end()) + return i->second->clone(); + else + return 0; +} + +void ModelManager::remove (const std::string &name) +{ + ModelMap::iterator i = m_templates.find(name); + if (i != m_templates.end()) { + delete i->second; + m_templates.remove (name); + } +} + +bool ModelManager::has_model (const std::string &name) const { + return m_templates.has_key (name); +} + +size_t ModelManager::num_templates() const { + return m_templates.size(); +} + + + +/* -------------------- Variables -------------------- */ + +namespace +{ + SurfaceCache surface_cache; + SurfaceCache_Alpha surface_cache_alpha; + ModelManager *modelmgr = 0; + vector image_pile; + string anim_templ_name; + Anim2d *anim_templ = 0; +} + + +/* -------------------- Functions -------------------- */ + +void display::InitModels() +{ + modelmgr = new ModelManager; + + lua_State *L = lua_open(); + luaL_openlibs(L); + lua_register (L, "FindDataFile", lua::FindDataFile); + tolua_open(L); + tolua_global_open(L); + tolua_enigma_open(L); + tolua_display_open(L); + tolua_px_open(L); + + if (lua::DoSysFile(L, "compat.lua") != lua::NO_LUAERROR) { + std::string message = ecl::strf("Error loading 'compat.lua'\nError: '%s'\n", + lua::LastError(L).c_str()); + fprintf(stderr, message.c_str()); + gui::ErrorMenu m(message + + _("\n\nThis error may cause the application to behave strange!") + , N_("Continue")); + m.manage(); + } + + string fname; + + const video::VMInfo *vminfo = video::GetInfo(); + fname = app.systemFS->findFile (vminfo->initscript); + if (lua::DoSysFile(L, vminfo->initscript) != lua::NO_LUAERROR) { + std::string message = ecl::strf("Error loading '%s'\nError: '%s'\n", + fname.c_str(), lua::LastError(L).c_str()); + fprintf(stderr, message.c_str()); + gui::ErrorMenu m(message + + _("\n\nThis error may cause the application to behave strange!") + , N_("Continue")); + m.manage(); + } + enigma::Log << "# models: " << modelmgr->num_templates() << endl; + + surface_cache_alpha.clear(); + lua_close(L); +} + +void display::ShutdownModels() +{ + delete modelmgr; + surface_cache.clear(); + delete_sequence (image_pile.begin(), image_pile.end()); + image_pile.clear(); + anim_templ_name = ""; + anim_templ = 0; +} + +Surface *display::CropSurface (const Surface *s, Rect r) { + return ecl::Grab(s, r); +} + +/* Register a new model template `m' under the name `name'. */ +void display::DefineModel (const char *name, Model *m) +{ + if (modelmgr->has_model (name)) { + enigma::Log << "Redefining model '" << name << "'\n"; + modelmgr->remove (name); + } + modelmgr->define (name, m); +} + +Model * display::MakeModel (const string &name) +{ + if (Model *m = modelmgr->create (name)) + return m; + else { + enigma::Log << "Unknown model " << name << endl; + return modelmgr->create ("dummy"); + } +} + +int display::DefineImage(const char *name, const char *fname, + int xoff, int yoff, int padding) +{ + ecl::Surface *sfc = surface_cache.get(fname); + if (!sfc) + return 1; + + ecl::Rect r = sfc->size(); + r.x += padding; r.y += padding; + r.w -= 2*padding; r.h -= 2*padding; + DefineModel(name, new ImageModel(sfc, r, xoff+padding, yoff+padding)); + return 0; +} + +void display::DefineImageModel (const char *name, ecl::Surface *s) +{ + DefineModel (name, new ImageModel(s, 0, 0)); +} + +int display::DefineSubImage(const char *name, const char *fname, + int xoff, int yoff, ecl::Rect subrect) +{ + ecl::Surface *sfc = surface_cache.get(fname); + if (!sfc) + return 1; + + DefineModel(name, new ImageModel(sfc, subrect, xoff, yoff)); + return 0; +} + +void display::DefineRandModel(const char *name, int n, char **names) +{ + RandomModel *m = new RandomModel(); + for (int i=0; iadd_model(names[i]); + DefineModel(name, m); +} + +void display::DefineShadedModel (const char *name, const char *model, const char *shadow) +{ + DefineModel(name, new ShadowModel(MakeModel(model), MakeModel(shadow))); +} + + +/* Create an image by overlaying several other images. The first entry in + `images' is the name of the background image, the following images are + drawn on top of it. */ +void display::DefineOverlayImage (const char *name, int n, + char **images) +{ + Surface *sfc = Duplicate(surface_cache.get(images[0])); + if (sfc) { + GC gc(sfc); + for (int i=1; iadd_frame(MakeModel(model), time / 1000.0); +} + +void display::DefineAlias (const char *name, const char *othername) +{ + DefineModel(name, new AliasModel(othername)); +} + + + +/* -------------------- Model -------------------- */ +void Model::get_extension (ecl::Rect &r) +{} + + + +/* -------------------- Image -------------------- */ + +Image::Image(ecl::Surface *sfc) +: surface(sfc), rect(surface->size()), refcount(1) +{} + +Image::Image(ecl::Surface *sfc, const ecl::Rect &r) +: surface(sfc), rect(r), refcount(1) +{} + + +void display::incref(Image *i) { + ++i->refcount; +} + +void display::decref (Image *i) { + if (-- i->refcount == 0) { + delete i; + } +} + + +void display::draw_image (Image *i, ecl::GC &gc, int x, int y) +{ + blit(gc, x, y, i->surface, i->rect); +} + +/* -------------------- ImageModel -------------------- */ + +ImageModel::ImageModel (Image *i, int xo, int yo) +: image(i), xoff(xo), yoff(yo) +{ + assert(image); + incref(image); +} + +ImageModel::ImageModel(Surface *s, int xo, int yo) +: image(new Image(s)), xoff(xo), yoff(yo) +{} + +ImageModel::ImageModel(Surface *s, const ecl::Rect &r, int xo, int yo) +: image(new Image(s, r)), xoff(xo), yoff(yo) +{} + +ImageModel::~ImageModel() { + decref(image); +} + +void ImageModel::draw(ecl::GC &gc, int x, int y) { + draw_image (image, gc, x+xoff, y+yoff); +} + +Model *ImageModel::clone() { + return new ImageModel(image, xoff, yoff); +} + +void ImageModel::get_extension (ecl::Rect &r) { + r.x = xoff; + r.y = yoff; + r.w = image->rect.w; + r.h = image->rect.h; +} + +/* -------------------- ShadowModel -------------------- */ + +ShadowModel::ShadowModel (Model *m, Model *sh) { + model=m; shade=sh; +} + +ShadowModel::~ShadowModel() { + delete model; delete shade; +} + +void ShadowModel::expose (ModelLayer *ml, int vx, int vy) { + model->expose(ml, vx, vy); + shade->expose(ml, vx, vy); +} +void ShadowModel::remove (ModelLayer *ml) { + shade->remove(ml); + model->remove(ml); +} + +void ShadowModel::set_callback(ModelCallback *cb) { + model->set_callback(cb); +} + +void ShadowModel::reverse() { + model->reverse(); + shade->reverse(); +} + +void ShadowModel::restart() { + model->restart(); + shade->restart(); +} + +void ShadowModel::draw(ecl::GC &gc, int x, int y) { + model->draw(gc,x,y); +} + +void ShadowModel::draw_shadow(ecl::GC &gc, int x, int y) { + shade->draw(gc,x,y); +} + +Model *ShadowModel::get_shadow() const { + return shade; +} + +Model *ShadowModel::clone() { + return new ShadowModel(model->clone(), shade->clone()); +} + +void ShadowModel::get_extension (ecl::Rect &r) { + ecl::Rect r1, r2; + model->get_extension (r1); + shade->get_extension (r2); + r = boundingbox (r1, r2); +} + +/* -------------------- RandomModel -------------------- */ + +Model * RandomModel::clone() { + if (!modelnames.empty()) { + int r = enigma::IntegerRand(0, modelnames.size()-1); + return MakeModel(modelnames[r]); + } else { + fprintf(stderr, "display_2d.cc: empty RandomModel\n"); + return 0; + } +} + +/* -------------------- AliasModel -------------------- */ + +Model * AliasModel::clone() { + return MakeModel(name); +} + +/* -------------------- Anim2d -------------------- */ + +Anim2d::Anim2d(bool loop) +: rep(new AnimRep(loop)) +{} + +Anim2d::Anim2d (AnimRep *r) +: rep(r), curframe(0), frametime(0), + finishedp (false), + changedp (false), + reversep (false), + videox (0), videoy (0), + callback(0) +{ + rep->refcount++; + frametime = 0; // enigma::DoubleRand (0, 0.02); +} + +Anim2d::~Anim2d() { + if (--rep->refcount == 0) + delete rep; +} + +void Anim2d::restart () { + finishedp = false; frametime = 0; curframe = 0; changedp = true; +} + +void Anim2d::add_frame(Model *m, double duration) { + rep->frames.push_back(new AnimFrame(m, duration)); +} + +void Anim2d::draw(ecl::GC &gc, int x, int y) +{ + if (!finishedp) { + AnimFrame *f =rep->frames[curframe]; + f->model->draw(gc,x,y); + changedp = false; + } +} + +void Anim2d::draw_shadow (ecl::GC &gc, int x, int y) +{ + if (!finishedp) { + AnimFrame *f =rep->frames[curframe]; + f->model->draw_shadow(gc,x,y); + } +} + +void Anim2d::expose (ModelLayer *ml, int vx, int vy) { + ml->activate (this); + videox = vx; + videoy = vy; +} + +void Anim2d::remove (ModelLayer *ml) { + ml->deactivate (this); +} + +bool Anim2d::has_changed (Rect &r) { + bool retval = changedp; + if (changedp) { + get_extension (r); + r.x += videox; + r.y += videoy; + } + return retval; +} + +void Anim2d::move (int newx, int newy) { + videox = newx; + videoy = newy; +} + +void Anim2d::get_extension (ecl::Rect &r) { + AnimFrame *f =rep->frames[curframe]; + f->model->get_extension (r); +} + +void Anim2d::tick (double dtime) +{ + assert(curframe < rep->frames.size()); + frametime += dtime; + double framedur = rep->frames[curframe]->duration; + + if (frametime >= framedur) { + frametime -= framedur; + changedp = true; + + if (reversep) { + if (curframe >= 1) + curframe--; + else if (rep->loop) + curframe = rep->frames.size()-1; + else + finishedp = true; + } + else { + if (curframe+1 < rep->frames.size()) + curframe++; + else if (rep->loop) + curframe = 0; + else + finishedp = true; + } + if (finishedp && callback!=0) + callback->animcb(); + } +} + +/* -------------------- Functions -------------------- */ + +namespace display +{ + Surface *GetSurface (const std::string &filename) + { + return surface_cache.get(filename); + } + + void RegisterImage (const std::string &name, Image *img) + { + } + + Image *LookupImage (const std::string &name) + { + return 0; + } + +} diff --git a/project/jni/application/enigma/src/d_models.hh b/project/jni/application/enigma/src/d_models.hh new file mode 100644 index 000000000..9f62c82af --- /dev/null +++ b/project/jni/application/enigma/src/d_models.hh @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2002,2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef D_MODELS_HH +#define D_MODELS_HH + +#include "ecl_fwd.hh" +#include +#include +#include + +namespace display +{ + using std::string; + using std::vector; + using ecl::Surface; + +/* -------------------- Image -------------------- */ + + struct Image { + // Variables. + ecl::Surface *surface; + ecl::Rect rect; // location of image inside surface + int refcount; // reference count, initialized to 1 + + // Constructors. + Image(ecl::Surface *sfc); + Image(ecl::Surface *sfc, const ecl::Rect &r); + }; + + void incref (Image *i); + void decref (Image *i); + void draw_image (Image *i, ecl::GC &gc, int x, int y); + +/* -------------------- ImageModel -------------------- */ + + class ImageModel : public Model { + Image* image; + int xoff, yoff; // relative origin of the image + public: + // Constructors + ImageModel (Image *i, int xo, int yo); + ImageModel (Surface *s, int xo, int yo); + ImageModel (Surface *s, const ecl::Rect &r, int xo, int yo); + ~ImageModel(); + + // Model interface + void draw(ecl::GC &gc, int x, int y); + Model *clone(); + void get_extension (ecl::Rect &r); + Image *get_image() { return image; } + }; + +/* -------------------- ShadowModel -------------------- */ + + class ShadowModel : public Model { + public: + ShadowModel(Model *m, Model *sh); + ~ShadowModel(); + + // Model interface + void expose (ModelLayer *ml, int vx, int vy); + void remove (ModelLayer *ml); + + void set_callback(ModelCallback *cb); + void reverse(); + void restart(); + void draw (ecl::GC &gc, int x, int y); + void draw_shadow (ecl::GC &gc, int x, int y); + Model *get_shadow() const; + Model *clone(); + + void get_extension (ecl::Rect &r); + + private: + Model *model, *shade; + }; + +/* -------------------- CompositeModel -------------------- */ + + class CompositeModel : public Model { + Model *bg, *fg; + public: + CompositeModel(Model *b, Model *f) : bg(b), fg(f) {} + ~CompositeModel() { + delete bg; delete fg; + } + + // Animation interface + void set_callback(ModelCallback *cb) { +// bg->set_callback(cb); + fg->set_callback(cb); + } + void reverse() { +// bg->reverse(); + fg->reverse(); + } + void restart() { + fg->restart(); + } + + // Model interface + Model *get_shadow() const { return bg->get_shadow(); } + virtual void expose (ModelLayer *ml, int vx, int vy) { + fg->expose (ml, vx, vy); +// bg->expose (ml, vx, vy); + } + virtual void remove (ModelLayer *ml) { + fg->remove (ml); +// bg->remove (ml); + } + void draw(ecl::GC &gc, int x, int y) { + bg->draw(gc,x,y); + fg->draw(gc,x,y); + } + void draw_shadow(ecl::GC &gc, int x, int y) { + bg->draw_shadow(gc,x,y); + } + Model *clone() { + return new CompositeModel(bg->clone(), fg->clone()); + } + + void get_extension (ecl::Rect &r) { + fg->get_extension (r); +// ecl::Rect r1, r2; +// bg->get_extension (r1); +// fg->get_extension (r2); +// r = boundingbox (r1, r2); + } + }; + +/* -------------------- RandomModel -------------------- */ + + /* Creates new models randomly from a set of template models. */ + class RandomModel : public Model { + std::vector modelnames; + public: + void add_model(const std::string &name) {modelnames.push_back(name);} + Model *clone(); + }; + +/* -------------------- AliasModel -------------------- */ + + class AliasModel : public Model { + string name; + public: + AliasModel(const string &modelname) : name(modelname) {} + Model *clone(); + }; + +/* -------------------- Animations -------------------- */ + + struct AnimFrame : public ecl::Nocopy { + // Variables + Model *model; + double duration; + + // Constructor and Destructor + AnimFrame(Model *m, double dur) + : model(m), duration(dur) + {} + + ~AnimFrame() { delete model; } + }; + + struct AnimRep { + // Variables + vector frames; + bool loop; + int refcount; + + // Constructor and Destructor + AnimRep(bool l) : loop(l), refcount(1) {} + + ~AnimRep() { + delete_sequence(frames.begin(), frames.end()); + } + }; + + class Anim2d : public Model, public ecl::Nocopy { + public: + Anim2d (bool loop); + ~Anim2d(); + void set_callback(ModelCallback *cb) { callback = cb; } + + void add_frame(Model *m, double duration); + + /* ---------- Model interface ---------- */ + void draw(ecl::GC &gc, int x, int y); + void draw_shadow(ecl::GC &gc, int x, int y); + Model *clone() { return new Anim2d(rep); } + void reverse() { reversep = !reversep; } + void restart (); + + void expose (ModelLayer *ml, int vx, int vy); + void remove (ModelLayer *ml); + + void tick(double dtime); + bool has_changed(ecl::Rect &changed_region); + bool is_garbage() const { return finishedp; } + + void move (int newx, int newy); + void get_extension (ecl::Rect &r); + + private: + Anim2d(AnimRep *r); + + /* ---------- Variables ---------- */ + AnimRep *rep; + unsigned curframe; // Current frame number + double frametime; // Elapsed time since frame was activated + bool finishedp; // Animation has finished + bool changedp; // Model state has changed since last redraw + bool reversep; // Play the animation in reverse direction + + int videox, videoy; // Video coordinates of sprite + ModelCallback *callback; + }; + + ecl::Surface *GetSurface (const std::string &filename); + ecl::Surface *CropSurface (const ecl::Surface *s, ecl::Rect r); + void DefineModel (const char *name, Model *m); + void DefineImageModel (const char *name, ecl::Surface *s); +} + +#endif diff --git a/project/jni/application/enigma/src/display.cpp b/project/jni/application/enigma/src/display.cpp new file mode 100644 index 000000000..ce6f7eea6 --- /dev/null +++ b/project/jni/application/enigma/src/display.cpp @@ -0,0 +1,2132 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This file contains the code renders the graphics during the game + * and in the editor. This includes displaying the current landscape + * with all its objects and the inventory at the bottom of the screen. + */ + +#include "display_internal.hh" +#include "video.hh" +#include "main.hh" + +#include "ecl_sdl.hh" +#include "ecl.hh" + +#include +#include +#include +#include + +using namespace std; +using namespace ecl; +using namespace display; +using namespace enigma; + +#include "d_engine.hh" +#include "d_models.hh" + +class dRect { +public: + dRect (double x_, double y_, double w_, double h_) { + x = x_; y = y_; w = w_; h = h_; + } + double x, y, w, h; +}; + +Rect round_grid (const dRect &r, double w, double h) { + double x = r.x / w ; + double y = r.y / h; + double x2 = (r.x + r.w-1) / w; + double y2 = (r.y + r.h-1) / h; + + Rect s (round_down (x), round_down (y), + round_down (x2), round_down (y2)); + s.w -= s.x-1; + s.h -= s.y-1; + return s; +} + + +/* -------------------- Local variables -------------------- */ + +namespace +{ + const int NTILESH = 20; // Default game screen width in tiles + const int NTILESV = 13; // Default game screen height in tiles + + + DisplayFlags display_flags = SHOW_ALL; + GameDisplay *gamedpy = 0; + bool ShowFPS = false; + +} + + +//====================================================================== +// STATUS BAR +//====================================================================== + +StatusBarImpl::StatusBarImpl (const ScreenArea &area) +: Window(area), + m_itemarea (), + m_models(), + m_changedp(false), + m_textview (*enigma::GetFont("statusbarfont")), + m_leveltime (0), + m_showtime_p (true), + m_counter (0), + m_showcounter_p(false), + m_showodometer_p(false), + m_interruptible(true), + m_text_active(false) +{ + const video::VMInfo *vminfo = video::GetInfo(); + m_itemarea = vminfo->sb_itemarea; +} + +StatusBarImpl::~StatusBarImpl() { + ecl::delete_sequence(m_models.begin(), m_models.end()); + m_models.clear(); +} + +void StatusBarImpl::set_time (double time) { + double oldtime=m_leveltime; + m_leveltime = time; + if (m_showtime_p && floor(m_leveltime)-floor(oldtime) >= 1) + m_changedp = true; // update clock +} + +void StatusBarImpl::hide_text() +{ + if (m_text_active) { + m_text_active = false; + m_changedp = true; + } +} + +void StatusBarImpl::set_speed (double /*speed*/) +{ +} + +void StatusBarImpl::set_travelled_distance (double /*distance*/) +{ +} + +void StatusBarImpl::set_counter (int new_counter) +{ + if (m_showcounter_p && new_counter != m_counter) { + m_changedp = true; + m_counter = new_counter; + } +} + +void StatusBarImpl::show_move_counter (bool active) +{ + if (active != m_showcounter_p) { + m_showcounter_p = active; + m_changedp = true; + } +} + +void StatusBarImpl::show_odometer (bool active) +{ + if (active != m_showodometer_p) { + m_showodometer_p = active; + m_changedp = true; + } +} + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + +void StatusBarImpl::redraw (ecl::GC &gc, const ScreenArea &r) { + const video::VMInfo *vminfo = video::GetInfo(); + ScreenArea a = get_area(); + clip(gc, intersect(a, r)); + + blit(gc, a.x, a.y, enigma::GetImage ("inventory", ".jpg")); + + +// set_color (gc, 255, 0, 0); +// frame (gc, vminfo->sb_timearea); +// frame (gc, vminfo->sb_textarea); +// set_color (gc, 0, 255, 0); +// frame (gc, vminfo->sb_movesarea); +// frame (gc, vminfo->sb_itemarea); + + if (m_showtime_p || m_showcounter_p) { + const int BUFSIZE = 8; + char buf[BUFSIZE]; + int xsize_time = 0; + int xsize_moves = 0; + Surface *s_time = 0; + Surface *s_moves = 0; + Font *timefont = enigma::GetFont ("timefont"); + Font *movesfont = enigma::GetFont ("smallfont"); + ScreenArea timearea = vminfo->sb_timearea; + ScreenArea movesarea = vminfo->sb_movesarea; + + if (m_showtime_p) { + double abstime = m_leveltime >= 0 ? m_leveltime : fabs(floor(m_leveltime)); + int minutes = static_cast(abstime/60); + int seconds = static_cast(abstime) % 60; + + if (minutes >= 100) { + minutes = 99; + seconds = 59; + } + snprintf(buf, BUFSIZE, + m_leveltime >= 0 ? "%d:%02d" : "-%d:%02d", + minutes, seconds); + s_time = timefont->render(buf); + xsize_time = s_time->width(); + } + + if (m_showcounter_p) { + int len = snprintf(buf, BUFSIZE, "%d", m_counter); + s_moves = movesfont->render(buf); + xsize_moves = s_moves->width(); + } + + + if (m_showtime_p) { + if (m_showcounter_p) { // time + moves + int x = timearea.x + (movesarea.x - timearea.x - xsize_time)/2; + int y = timearea.y + (timearea.h - timefont->get_lineskip())/2; + blit(gc, x, y, s_time); + + x = movesarea.x + (movesarea.w - xsize_moves)/2; + y = movesarea.y + (movesarea.h + timefont->get_lineskip())/2 - movesfont->get_lineskip() - 4; + blit(gc, x, y, s_moves); + } + else { // only time + int x = timearea.x + (timearea.w - xsize_time)/2; + int y = timearea.y + (timearea.h - timefont->get_lineskip())/2; + blit(gc, x, y, s_time); + } + } + else { // only moves + int x = timearea.x + (timearea.w - xsize_moves)/2; + int y = timearea.y + (timearea.h - movesfont->get_lineskip())/2; + blit(gc, x, y, s_moves); + } + + delete s_moves; + delete s_time; + } + + if (m_text_active) { + m_textview.draw (gc, r); + } + else { + int itemsize = static_cast(vminfo->tile_size * 1.25); + int x = m_itemarea.x; + for (unsigned i=0; idraw(gc, x, m_itemarea.y); + x += itemsize; + } + } + m_changedp = false; +} + + +void StatusBarImpl::set_inventory (const std::vector &modelnames) +{ + if (m_text_active && m_interruptible) { + hide_text(); + } + + ecl::delete_sequence(m_models.begin(), m_models.end()); + m_models.clear(); + + for (size_t i=0; isb_textarea; + + time = maxtime = 0; +} + +void TextDisplay::set_text (const string &t, bool scrolling, double duration) +{ + text = t; + textsurface.reset(font.render(text.c_str())); + + + time = 0; + + if (scrolling) { + if (duration <= 0) { + xoff = -area.w; + scrollspeed = 160; + } else { + // Showscroll mode: first show string then scoll it out + showscroll = true; + scrollspeed = 0; + if (area.w < textsurface->width()) { + // start left adjusted for long strings + xoff = 0; + } else { + // start centered for short strings + xoff = -(area.w - textsurface->width())/2; + } + } + } + + if (duration > 0) + maxtime = duration; + else + maxtime = 1e20; // "infinite" for all practical purposes + + if (!scrolling) {// centered text string + if (area.w < textsurface->width()) { + pingpong = true; + scrollspeed = 4 * (textsurface->width() - area.w) / duration; + xoff = 0; + } + else { + pingpong = false; + xoff = -(area.w - textsurface->width())/2; + scrollspeed = 0; + } + } + + finishedp = false; + changedp = true; +} + +void TextDisplay::tick (double dtime) +{ + time += dtime; + if (time > maxtime) { + if (showscroll) { + showscroll = false; + scrollspeed = 160; + maxtime = 1e20; // "infinite" for all practical purposes + } else { + finishedp = true; + changedp = true; + } + } + else { + int oldxoff = round_nearest(xoff); + xoff += dtime * scrollspeed; + int newxoff = round_nearest (xoff); + changedp = newxoff != oldxoff; + if (pingpong) { + if (scrollspeed > 0 && area.w + newxoff >= textsurface->width() ) { + scrollspeed = -scrollspeed; + } + else if (scrollspeed < 0 && newxoff <= 0) { + scrollspeed = -scrollspeed; + } + } + else if (xoff >= textsurface->width()) { + finishedp = true; + changedp = true; + } + } +} + +void TextDisplay::draw (ecl::GC &gc, const ScreenArea &r) { + clip(gc, intersect(area, r)); + set_color(gc, 0,0,0); + box(gc, area); + if (Surface *s = textsurface.get()) + blit(gc, area.x-round_nearest(xoff), area.y, s); +} + + + + +//====================================================================== +// DISPLAY ENGINE +//====================================================================== + +DisplayEngine::DisplayEngine (int tilew, int tileh) +: m_tilew(tilew), m_tileh(tileh), + m_offset(), + m_new_offset(), + m_area (), + m_width(0), m_height(0), + m_redrawp(0,0) +{ + m_area = video::GetScreen()->size(); + m_screenoffset[0] = m_screenoffset[1] = 0; +} + +DisplayEngine::~DisplayEngine() +{ + delete_sequence (m_layers.begin(), m_layers.end()); +} + +void DisplayEngine::set_tilesize (int w, int h) +{ + m_tilew=w; + m_tileh=h; +} + + +void DisplayEngine::add_layer (DisplayLayer *l) { + l->set_engine (this); + m_layers.push_back(l); +} + +void DisplayEngine::set_offset (const V2 &off) { + m_offset = m_new_offset = off; + world_to_video (off, &m_screenoffset[0], &m_screenoffset[1]); +} + +void DisplayEngine::move_offset (const ecl::V2 &off) { + m_new_offset = off; +} + +/*! Scroll the screen contents and mark the newly exposed regions for + redraw. This method assumes that the screen contents were not + modified externally since the last call to update_offset(). */ +void DisplayEngine::update_offset () +{ + ecl::Screen *screen = video::GetScreen(); + + int oldx = m_screenoffset[0]; + int oldy = m_screenoffset[1]; + int newx, newy; + world_to_video (m_new_offset, &newx, &newy); + + if (newx != oldx || newy != oldy) { + const Rect &a = get_area(); + Rect oldarea (a.x+oldx, a.y+oldy, a.w, a.h); + Rect newarea (a.x+newx, a.y+newy, a.w, a.h); + Rect common = intersect(newarea, oldarea); + + // Blit overlapping screen area from old to new position + GC screengc (screen->get_surface()); + Rect blitrect (common.x-oldx, common.y-oldy, common.w, common.h); + blit (screengc, common.x-newx, common.y-newy, screen->get_surface(), blitrect); + blitrect.x = common.x-newx; + blitrect.y = common.y-newy; + screen->update_rect(blitrect); + + // Update offset + set_offset (V2(newx/double(m_tilew), newy/double(m_tileh))); + + // Mark areas that could not be copied from old screen for redraw + RectList rl; + rl.push_back (get_area()); + rl.sub (blitrect); + for (RectList::iterator i=rl.begin(); i!=rl.end(); ++i) { + Rect r = screen_to_world(*i); + mark_redraw_area (r); + } + } +} + +void DisplayEngine::set_screen_area (const ecl::Rect & r) { + m_area = r; +} + +void DisplayEngine::new_world (int w, int h) +{ + m_width = w; + m_height = h; + m_offset = m_new_offset = V2(); + m_screenoffset[0] = m_screenoffset[1] = 0; + m_redrawp.resize(w, h, 1); + + for (unsigned i=0; inew_world(w,h); +} + + +void DisplayEngine::tick (double dtime) { + for_each (m_layers.begin(), m_layers.end(), + bind2nd (mem_fun(&DisplayLayer::tick), dtime)); +} + +void DisplayEngine::world_to_screen (const V2 & pos, int *x, int *y) +{ + *x = round_nearest(pos[0]*m_tilew) - m_screenoffset[0] + get_area().x; + *y = round_nearest(pos[1]*m_tileh) - m_screenoffset[1] + get_area().y; +} + + +void DisplayEngine::world_to_video (const ecl::V2 &pos, int *x, int *y) +{ + *x = round_nearest(pos[0]*m_tilew); + *y = round_nearest(pos[1]*m_tileh); +} + +void DisplayEngine::video_to_screen (int x, int y, int *xx, int *yy) +{ + *xx = x - m_screenoffset[0] + get_area().x; + *yy = y - m_screenoffset[1] + get_area().y; +} + +/* Calculate the smallest rectangle `s' in world space aligned to + tiles that contains a certain rectangle `r' in video space. This + function is used for calculating the region that needs to be + updated when a sprite with extension `r' is moved on the screen. */ +void DisplayEngine::video_to_world (const ecl::Rect &r, Rect &s) { + dRect dr (r.x, r.y, r.w, r.h); + s = round_grid (dr, get_tilew(), get_tileh()); +} + + +ScreenArea DisplayEngine::world_to_screen (const WorldArea &a) +{ + int x, y; + world_to_screen (V2(a.x, a.y), &x, &y); + return ScreenArea (x, y, a.w*m_tilew, a.h*m_tileh); +} + + +WorldArea DisplayEngine::screen_to_world (const ScreenArea &a) +{ + int sx = m_screenoffset[0] + a.x - get_area().x; + int sy = m_screenoffset[1] + a.y - get_area().y; + + int x1 = Max(0, sx / m_tilew); + int y1 = Max(0, sy / m_tileh); + int x2 = Min(m_width, (sx + a.w+m_tilew-1) / m_tilew); + int y2 = Min(m_height, (sy + +a.h+m_tileh-1) / m_tileh); + + return WorldArea (x1, y1, x2-x1, y2-y1); +} + + +V2 DisplayEngine::to_world (const V2 &pos) { + return m_offset + + V2((pos[0]-get_area().x)/m_tilew, (pos[1]-get_area().y)/m_tileh); +} + +void DisplayEngine::mark_redraw_area (const WorldArea &wa, int delay) +{ + int x2 = Min(m_width, wa.x+wa.w); + int y2 = Min(m_height, wa.y+wa.h); + for (int x=Max(0,wa.x); x prepare_draw (wa); + m_layers[i]->draw (gc, wa, xpos, ypos); + m_layers[i]->draw_onepass(gc); + } +} + +void DisplayEngine::update_layer (DisplayLayer *l, WorldArea wa) +{ + GC gc(video::GetScreen()->get_surface()); + + int x2 = wa.x+wa.w; + int y2 = wa.y+wa.h; + + clip(gc, get_area()); + int xpos0, ypos; + world_to_screen (V2(wa.x, wa.y), &xpos0, &ypos); + + l->prepare_draw (wa); + for (int y=wa.y; ydraw (gc, WorldArea(x,y,1,1), xpos, ypos); + } + } + l->draw_onepass (gc); +} + +void DisplayEngine::update_screen() +{ + ecl::Screen *screen = video::GetScreen(); + GC gc(screen->get_surface()); + + if (m_new_offset != m_offset) { + update_offset (); + m_new_offset = m_offset; + } + + Rect area=get_area(); + clip(gc, area); + + WorldArea wa = screen_to_world (area); + for (unsigned i=0; i= 1) { + if ((m_redrawp(x,y) -= 1) == 0) + screen->update_rect (world_to_screen (WorldArea (x, y, 1, 1))); + } +} + + +/* -------------------- ModelLayer -------------------- */ + +void ModelLayer::maybe_redraw_model(Model *m, bool immediately) +{ + Rect videoarea; + if (m->has_changed(videoarea)) { + int delay = immediately ? 0 : enigma::IntegerRand (0, 2); + WorldArea wa; + get_engine()->video_to_world (videoarea, wa); + get_engine()->mark_redraw_area(wa, delay); + } +} + +void ModelLayer::activate (Model *m) +{ + list &am = m_active_models_new; + am.push_back(m); +} + +void ModelLayer::deactivate (Model *m) +{ + list &am = m_active_models; + list::iterator i = find(am.begin(), am.end(), m); + if (i == am.end()) { + m_active_models_new.remove(m); + } else { + *i = 0; + } +} + +void ModelLayer::new_world (int, int) { + m_active_models.clear(); + m_active_models_new.clear(); +} + +void ModelLayer::tick (double dtime) +{ + ModelList &am = m_active_models; + + am.remove(static_cast (0)); + am.remove_if(mem_fun(&Model::is_garbage)); + + // Append new active models to list + am.splice (am.end(), m_active_models_new); + + /* for_each does not work; animation may remove itself during a + tick. This may happen for example when a model callback + decides to replace the old model by another one. */ + for (ModelList::iterator i=am.begin(); i!=am.end(); ++i) { + if (Model *m = *i) { + m->tick(dtime); + + // We have to check (*i) again because the list of active + // models can change during a tick! + if ((m = *i)) + maybe_redraw_model (m); + } + } +} + + +/* -------------------- GridLayer -------------------- */ + +DL_Grid::DL_Grid(int redrawsize) +: m_models (0, 0), m_redrawsize (redrawsize) +{ +} + +DL_Grid::~DL_Grid() { + delete_sequence (m_models.begin(), m_models.end()); +} + +void DL_Grid::new_world (int w, int h) { + ModelLayer::new_world (w, h); + delete_sequence (m_models.begin(), m_models.end()); + m_models.resize (w, h, 0); +} + +void DL_Grid::mark_redraw (int x, int y) { + get_engine()->mark_redraw_area (WorldArea (x, y, m_redrawsize, m_redrawsize)); +} + +void DL_Grid::set_model (int x, int y, Model *m) { + if (!(x >= 0 && y >= 0 && + (unsigned)xremove(this); + delete oldm; + } + mark_redraw (x, y); + m_models(x,y) = m; + if (m) { + int vx, vy; + get_engine()->world_to_video (V2 (x, y), &vx, &vy); + m->expose (this, vx, vy); + } + } +} + +Model * DL_Grid::get_model (int x, int y) { + return m_models(x,y); +} + +Model *DL_Grid::yield_model (int x, int y) { + Model *m = get_model (x, y); + if (m) + m->remove (this); + m_models(x,y) = 0; + mark_redraw (x,y); + return m; +} + + +void DL_Grid::draw (ecl::GC &gc, const WorldArea &a, int destx, int desty) { + int x2 = a.x+a.w; + int y2 = a.y+a.h; + int tilew = get_engine()->get_tilew(); + int tileh = get_engine()->get_tileh(); + int xpos = destx; + for (int x=a.x; xdraw(gc, xpos, ypos); + ypos += tileh; + } + xpos += tilew; + } +} + + +/* -------------------- Sprites -------------------- */ + +SpriteHandle::SpriteHandle (DL_Sprites *l, unsigned spriteid) +: layer(l), id(spriteid) +{} + +SpriteHandle::SpriteHandle() +: layer(0) +{ + id = DL_Sprites::MAGIC_SPRITEID; +} + +void SpriteHandle::kill() { + if (layer) { + layer->kill_sprite (id); + layer = 0; + id = DL_Sprites::MAGIC_SPRITEID; + } +} + +void SpriteHandle::move (const ecl::V2 &newpos) const { + if (layer) + layer->move_sprite (id, newpos); +} + +void SpriteHandle::replace_model (Model *m) const { + if (layer) + layer->replace_sprite (id, m); + else + delete m; +} + +Model *SpriteHandle::get_model () const { + return layer ? layer->get_model (id) : 0; +} + +void SpriteHandle::set_callback (ModelCallback *cb) const { + if (Model *m = get_model()) + m->set_callback(cb); +} + +void SpriteHandle::hide() const { + if (layer) { + Sprite * s = layer->get_sprite(id); + if(s->visible) { + s->visible = false; + layer->redraw_sprite_region(id); + } + } +} + +void SpriteHandle::show() const { + if (layer) { + Sprite * s = layer->get_sprite(id); + if(!s->visible) { + s->visible = true; + layer->redraw_sprite_region(id); + } + } +} + + +/* -------------------- Sprite layer -------------------- */ + +DL_Sprites::DL_Sprites() +: numsprites(0), maxsprites(1000) +{} + +DL_Sprites::~DL_Sprites() { + delete_sequence(sprites.begin(), sprites.end()); +} + +Sprite *DL_Sprites::get_sprite(SpriteId id) { + return (id == MAGIC_SPRITEID) ? 0 : sprites[id]; +} + +void DL_Sprites::new_world (int w, int h) { + ModelLayer::new_world (w,h); + delete_sequence (sprites.begin(), sprites.end()); + sprites.clear(); + numsprites = 0; +} + +void DL_Sprites::move_sprite (SpriteId id, const ecl::V2& newpos) +{ + Sprite *sprite = sprites[id]; + + int newx, newy; + get_engine()->world_to_video (newpos, &newx, &newy); + + if (newx != sprite->screenpos[0] || newy != sprite->screenpos[1] || + sprite->mayNeedRedraw ) { + redraw_sprite_region(id); // make sure old sprite is removed + sprite->pos = newpos; + sprite->screenpos[0] = newx; + sprite->screenpos[1] = newy; + if (Anim2d* anim = dynamic_cast(sprite->model)) + anim->move (newx, newy); + redraw_sprite_region(id); // draw new sprite + } +} + +SpriteId DL_Sprites::add_sprite (Sprite *sprite) +{ + if (numsprites >= maxsprites) { + delete sprite; + return MAGIC_SPRITEID; + } + + SpriteList &sl = sprites; + SpriteId id = 0; + + // Find the first empty slot + SpriteList::iterator i = find(sl.begin(), sl.end(), static_cast(0)); + if (i == sl.end()) { + id = sl.size(); + sl.push_back(sprite); + } + else { + id = distance(sl.begin(), i); + *i = sprite; + } + get_engine()->world_to_video (sprite->pos, &sprite->screenpos[0], &sprite->screenpos[1]); + if (Model *m = sprite->model) + m->expose (this, sprite->screenpos[0], sprite->screenpos[1]); + redraw_sprite_region(id); + numsprites += 1; + return id; +} + +void DL_Sprites::replace_sprite (SpriteId id, Model *m) { + Sprite *sprite = sprites[id]; + if (Model *old = sprite->model) { + redraw_sprite_region(id); + old->remove (this); + delete old; + } + sprite->model = m; + if (m) { + m->expose (this, sprite->screenpos[0], sprite->screenpos[1]); + redraw_sprite_region(id); + } +} + +void DL_Sprites::kill_sprite (SpriteId id) { + if (Sprite *sprite = sprites[id]) { + redraw_sprite_region(id); + if (Model *m = sprite->model) { + m->remove (this); + } + sprites[id] = 0; + numsprites -= 1; + delete sprite; + } +} + +void DL_Sprites::draw (ecl::GC &gc, const WorldArea &a, int /*x*/, int /*y*/) +{ + DisplayEngine *engine = get_engine(); + clip (gc, intersect (engine->get_area(), engine->world_to_screen(a))); + draw_sprites (false, gc); +} + + +void DL_Sprites::draw_sprites (bool drawshadowp, GC &gc) { + SpriteList &sl = sprites; + + for (unsigned i=0; imodel && s->visible) { + int sx, sy; + get_engine()->world_to_screen(s->pos, &sx, &sy); + if (drawshadowp) + s->model->draw_shadow(gc, sx, sy); + else + s->model->draw(gc, sx, sy); + } + } +} + +void DL_Sprites::draw_onepass (ecl::GC &gc) +{ +// draw_sprites (false, gc); +} + +void DL_Sprites::redraw_sprite_region (SpriteId id) +{ + Sprite *s = sprites[id]; + if (s && s->model) { + Rect r, redrawr; + s->model->get_extension (r); + r.x += s->screenpos[0]; + r.y += s->screenpos[1]; + DisplayEngine *e = get_engine(); + e->video_to_world (r, redrawr); + e->mark_redraw_area (redrawr); + } +} + +void DL_Sprites::tick (double dtime) +{ + SpriteList &sl = sprites; + for (unsigned i=0; imodel) + continue; + + if (s->model->is_garbage() && s->layer==SPRITE_EFFECT) { + // Only remove effect sprites -- actor sprites remain in + // the world all the time + kill_sprite (i); + } + } + ModelLayer::tick (dtime); +} + + + +//---------------------------------------------------------------------- +// RUBBER BANDS +//---------------------------------------------------------------------- + +void DL_Lines::draw_onepass (ecl::GC &gc) +{ + DisplayEngine *engine = get_engine(); + + set_color (gc, 240, 140, 20, 255); + set_flags (gc.flags, GS_ANTIALIAS); + + for (LineMap::iterator i=m_rubbers.begin(); i!= m_rubbers.end(); ++i) + { + int x1, y1, x2, y2; + engine->world_to_screen (i->second.start, &x1, &y1); + engine->world_to_screen (i->second.end, &x2, &y2); + + line (gc, x1, y1, x2, y2); + } +} + +/* Mark the screen region occupied by a rubber band for redraw. + Problem is: what region is that exactly? What pixels on the screen + will the line rasterizer touch? Hard to tell, especially when + anti-aliasing is used. + + This function constructs a list of rectangles that completely + enclose the line by subdividing the line into n segments and + constructing the bounding box for each of these segments. To + account for the (effective) finite width of the line, these boxes + need to be enlarged by a small amount to make them overlap a bit. + + The number n of subdivision depends on the length of the line. n=1 + would of course do, but we want to redraw as little of the screen + as possible. `n' is therefore chosen in such a way that the line + is covered with boxes of size not larger than `maxboxsize'. +*/ +void DL_Lines::mark_redraw_line (const Line &r) { + const double maxboxsize = 0.5; + + double w0 = r.start[0]-r.end[0]; + double h0 = r.start[1]-r.end[1]; + int n = int (max(abs(w0),abs(h0)) / maxboxsize)+1; + + double w = w0/n; + double h = h0/n; + + double overlap = 0.1; + + double x = r.end[0]; + double y = r.end[1]; + + double xoverlap = w<0 ? -overlap : overlap; + double yoverlap = h<0 ? -overlap : overlap; + + for (int i=0; imark_redraw_area (wa); + + x += w; + y += h; + } +} + +RubberHandle +DL_Lines::add_line (const V2 &p1, const V2 &p2) +{ + m_rubbers[m_id] = Line(p1, p2); + mark_redraw_line (m_rubbers[m_id]); + return RubberHandle(this, m_id++); +} + +void DL_Lines::set_startpoint (unsigned id, const V2 &p1) +{ + mark_redraw_line (m_rubbers[id]); + m_rubbers[id].start = p1; + mark_redraw_line (m_rubbers[id]); +} + +void DL_Lines::set_endpoint (unsigned id, const V2 &p2) +{ + mark_redraw_line (m_rubbers[id]); + m_rubbers[id].end = p2; + mark_redraw_line (m_rubbers[id]); +} + +void DL_Lines::kill_line (unsigned id) { + mark_redraw_line (m_rubbers[id]); + LineMap::iterator i=m_rubbers.find(id); + if (i != m_rubbers.end()) + m_rubbers.erase(i); +} + +RubberHandle::RubberHandle(DL_Lines *ll, unsigned id_) +: line_layer (ll), id(id_) +{ +} + +void RubberHandle::update_first(const V2 &p1) +{ + line_layer->set_startpoint (id, p1); +} + +void RubberHandle::update_second(const V2 &p2) +{ + line_layer->set_endpoint (id, p2); +} + +void RubberHandle::kill() +{ + line_layer->kill_line(id); +} + + +//---------------------------------------------------------------------- +// SHADOWS +//---------------------------------------------------------------------- + +/* +** Drawing the shadows is a lot more difficult than drawing any of the +** other layers. There are a couple of reasons for this: +** +** 1. Both Stones and actors cast a shadow. Not a real problem, but +** it makes the implementation more complex. +** +** 2. Shadows can overlap. Not only can the shadows of stones and +** actors overlap, but also the shadows of two adjacent stones can. +** Since we are using alpha blending for the shadows, this means +** that we cannot blit the invidual shadows to the screen, but we +** have to use an intermediate buffer. +** +** 3. Performance is critical. Drawing the shadows is time-consuming, +** firstly because alpha blending is costly and secondly because of +** the intermediate buffer. So we should try to cache shadows *and* +** avoid the buffer if possible. +** +** So, how do we approach these problems? We handle stone and actor +** shadows separately: The stone shadows do not change very often so +** it's easy to cache them, one tile at a time. If there is no actor +** on this tile, we can blit the cached image directly to the screen, +** otherwise we have no choice but to use the buffer. +** +** The remaining problem is the shadow cache. The easiest solution +** would be to use one huge image for the whole level and keep it in +** memory all the time. This would consume roughly 20mb for a 100x100 +** landscape, which is of course excessive, considering that there are +** rarely more than 40 different shadow tiles in each landscape. +** +** Instead, Enigma caches the most recently calculated shadow tiles in +** a linked list. (If this should one day turn out to be too slow, +** it's still possible to resort to a hash table or something +** similar.) +*/ + +namespace display +{ + struct ImageQuad { + Image *images[4]; + + ImageQuad() { /* do not initialize fields. */ } + + ImageQuad (Image *i1, Image *i2, Image *i3, Image *i4) { + images[0] = i1; + images[1] = i2; + images[2] = i3; + images[3] = i4; + } + bool operator == (const ImageQuad &q) { + return (images[0]==q.images[0] && + images[1]==q.images[1] && + images[2]==q.images[2] && + images[3]==q.images[3]); + } + Image *operator[] (int idx) { return images[idx]; } + }; + + bool only_static_shadows (Model *models[4], ImageQuad &q) { + int nimages=4; + + for (int i=0; i<4; ++i) { + if (models[i] == 0) { + // No model at all? -> static + q.images[i] = 0; + } + else if (Model *shadow = models[i]->get_shadow()) { + if (ImageModel *im = dynamic_cast(shadow)) + // We have a model with a static image shadow + q.images[i] = im->get_image(); + else + q.images[i] = 0, nimages--; + } + else + q.images[i] = 0; + } + return nimages==4; + } + + + struct StoneShadow { + ImageQuad images; + Surface *image; + bool in_cache; + + StoneShadow (ImageQuad iq, bool cached) + : images(iq), image(0), in_cache(cached) + {} + }; + + class StoneShadowCache : public ecl::Nocopy { + public: + StoneShadowCache(int tilew, int tileh); + ~StoneShadowCache(); + + StoneShadow *retrieve (Model *models[4]); + void release (StoneShadow *s); + void clear(); + private: + typedef std::list CacheList; + + // Variables + size_t m_max_size; // Max. number of different shadow tiles to cache + CacheList m_cache; + int m_tilew, m_tileh; + vector m_surface_avail; + + // Private methods. + Surface *new_surface (); + StoneShadow *find_in_cache (const ImageQuad &images); + + void fill_image (StoneShadow *s); + void fill_image (StoneShadow *sh, Model *models[4]); + }; +} + +StoneShadowCache::StoneShadowCache(int tilew, int tileh) +: m_max_size(50), m_cache() +{ + m_tilew=tilew; m_tileh=tileh; +} + +StoneShadowCache::~StoneShadowCache() +{ + clear(); +} + +void StoneShadowCache::clear() { + for (CacheList::iterator i = m_cache.begin(); i!=m_cache.end(); ++i) + delete (*i)->image; + delete_sequence (m_cache.begin(), m_cache.end()); + m_cache.clear(); + delete_sequence (m_surface_avail.begin(), m_surface_avail.end()); + m_surface_avail.clear(); +} + +void StoneShadowCache::fill_image (StoneShadow *sh) { + // Special case: no shadows at all: + if (sh->images[0] == 0 && sh->images[1] == 0 && + sh->images[2] == 0 && sh->images[3] == 0) + { + sh->image = 0; + return; + } + + Surface *s = new_surface(); + GC gc(s); + set_color (gc, 255,255,255); + box(gc, s->size()); + + if (Image *i = sh->images[0]) + draw_image (i, gc, -m_tilew, -m_tileh); + if (Image *i = sh->images[1]) + draw_image (i, gc, 0, -m_tileh); + if (Image *i = sh->images[2]) + draw_image (i, gc, -m_tilew, 0); + if (Image *i = sh->images[3]) + draw_image (i, gc, 0, 0); + + SDL_Surface *ss = s->get_surface(); + SDL_SetColorKey(ss, SDL_SRCCOLORKEY | SDL_RLEACCEL, + SDL_MapRGB(ss->format, 255,255,255)); + SDL_SetAlpha (ss, SDL_SRCALPHA | SDL_RLEACCEL, 128); + + sh->image = s; +} + +void StoneShadowCache::fill_image (StoneShadow *sh, Model *models[4]) { + Surface *s = new_surface(); + GC gc(s); + set_color (gc, 255,255,255); + box(gc, s->size()); + if (models[0]) models[0]->draw_shadow (gc, -m_tilew, -m_tileh); + if (models[1]) models[1]->draw_shadow (gc, 0, -m_tileh); + if (models[2]) models[2]->draw_shadow (gc, -m_tilew, 0); + if (models[3]) models[3]->draw_shadow (gc, 0,0); + SDL_Surface *ss = s->get_surface(); + SDL_SetColorKey(ss, SDL_SRCCOLORKEY | SDL_RLEACCEL, + SDL_MapRGB(ss->format, 255,255,255)); + SDL_SetAlpha (ss, SDL_SRCALPHA | SDL_RLEACCEL, 128); + sh->image = s; +} + + +StoneShadow * +StoneShadowCache::find_in_cache (const ImageQuad &images) +{ + CacheList::iterator i=m_cache.begin(); + for (; i!=m_cache.end(); ++i) { + if ((*i)->images == images) { + StoneShadow *sh = *i; + // Move entry to front of list + m_cache.splice (m_cache.begin(), m_cache, i); + return sh; + } + } + return 0; +} + +/* Try to lookup the shadow created by the four models in `models[]' + in the shadow cache. */ +StoneShadow * +StoneShadowCache::retrieve (Model *models[4]) +{ + StoneShadow *shadow = 0; + + ImageQuad images; + + // Only cache static stone shadows, i.e., those consisting + // only of Image models. + if (only_static_shadows (models, images)) { + shadow = find_in_cache(images); + if (!shadow) { + shadow = new StoneShadow (images, true); + fill_image (shadow); + m_cache.push_front (shadow); + } + } + else { + shadow = new StoneShadow (images, false); + fill_image (shadow, models); + } + return shadow; +} + +void StoneShadowCache::release (StoneShadow *s) { + if (s->in_cache) { + // Image is in cache, no need to free anything + } + else { + m_surface_avail.push_back(s->image); + delete s; + } +} + +Surface *StoneShadowCache::new_surface () { + Surface *s = 0; + if (m_surface_avail.empty()) { + // WARNING: Always make sure the surface format here matches + // the format of `buffer' in class DL_Shadows!!! + SDL_Surface *ss = SDL_CreateRGBSurface(SDL_SWSURFACE, + m_tilew, m_tileh, 32, + 0,0,0,0); + s = Surface::make_surface(ss); + } else { + s = m_surface_avail.back(); + m_surface_avail.pop_back(); + } + return s; +} + + +/* -------------------- Shadow layer -------------------- */ + +DL_Shadows::DL_Shadows (DL_Grid *grid, DL_Sprites *sprites) +: m_grid(grid), m_sprites(sprites), m_cache(0), buffer(0), + m_hasactor (0,0) +{ +} + +DL_Shadows::~DL_Shadows() { + delete m_cache; + delete buffer; +} + +void DL_Shadows::new_world(int w, int h) +{ + m_hasactor.resize (w, h, false); + + DisplayEngine *e = get_engine(); + int tilew=e->get_tilew(); + int tileh=e->get_tileh(); + + delete m_cache; + m_cache = new StoneShadowCache(tilew, tileh); + + delete buffer; + // WARNING: Always make sure the surface format here matches + // the format in `StoneShadowCache::new_surface' !!! + SDL_Surface *ss = SDL_CreateRGBSurface(SDL_SWSURFACE, + tilew, tileh, 32, + 0,0,0,0); + SDL_SetAlpha(ss, SDL_SRCALPHA, 128); + SDL_SetColorKey(ss, SDL_SRCCOLORKEY , + SDL_MapRGB(ss->format, 255,255,255)); + + buffer = Surface::make_surface(ss); +} + +void DL_Shadows::draw (ecl::GC &gc, const WorldArea &a, int destx, int desty) { + int x2 = a.x+a.w; + int y2 = a.y+a.h; + int tilew = get_engine()->get_tilew(); + int tileh = get_engine()->get_tileh(); + int xpos = destx; + for (int x=a.x; xsprites.size(); ++k) { + Sprite *s = m_sprites->sprites[k]; + if (s && s->layer == SPRITE_ACTOR && s->model) { + Rect r, redrawr; + s->model->get_extension (r); + r.x += s->screenpos[0]; + r.y += s->screenpos[1]; + DisplayEngine *e = get_engine(); + e->video_to_world (r, redrawr); + redrawr.intersect (wa); + + for (int i=0; i= 0 && y >= 0) { + if (Model *m = m_grid->get_model(x,y)) + return m; //return m->get_shadow(); + } + return 0; +} + +void DL_Shadows::draw(GC &gc, int xpos, int ypos, int x, int y) { + Model *models[4]; + models[0] = get_shadow_model (x-1, y-1); + models[1] = get_shadow_model (x, y-1); + models[2] = get_shadow_model (x-1, y); + models[3] = get_shadow_model (x, y); + + StoneShadow *sh = m_cache->retrieve (models); + + int tilew = get_engine()->get_tilew(); + int tileh = get_engine()->get_tileh(); + + bool hasActor = this->has_actor (x, y); + if (hasActor || sh->image) { + Surface *s = sh->image; + if (hasActor) { + GC gc2(buffer); + if (s) { + s->lock(); + buffer->lock(); + SDL_Surface *ss = s->get_surface(); + SDL_Surface *bs = buffer->get_surface(); + memcpy (bs->pixels, ss->pixels, ss->w * ss->h * ss->format->BytesPerPixel); + buffer->unlock(); + s->unlock(); + } + else { + set_color (gc2, 255, 255, 255); + box (gc2, buffer->size()); + } + for (unsigned i=0; isprites.size(); ++i) { + if (Sprite *sp = m_sprites->sprites[i]) { + if (sp->visible && sp->model) { + int sx = round_nearest(sp->pos[0]*tilew) - x*tilew; + int sy = round_nearest(sp->pos[1]*tileh) - y*tileh; + sp->model->draw_shadow(gc2, sx, sy); + } + } + } + blit(gc, xpos, ypos, buffer); + } + else { + blit (gc, xpos,ypos,s); + } + } + + m_cache->release (sh); +} + + +//---------------------------------------------------------------------- +// Sprite following code +//---------------------------------------------------------------------- + +Follower::Follower (DisplayEngine *e) +: m_boundary (0.5), + m_engine(e) +{} + +double Follower::get_hoff() const +{ + ScreenArea gamearea = m_engine->get_area(); + return gamearea.w / m_engine->get_tilew() -m_boundary*2; +} + +double Follower::get_voff() const +{ + ScreenArea gamearea = m_engine->get_area(); + return gamearea.h / m_engine->get_tileh() -m_boundary*2; +} + +void Follower::center(const ecl::V2 &point) +{ + double borderh = m_boundary; + double borderv = m_boundary; + double hoff = get_hoff(); + double voff = get_voff(); + + V2 off = point; + off[0] = floor((off[0] - borderh) / hoff) * hoff; + off[1] = floor((off[1] - borderv) / voff) * voff; + + set_offset(off); +} + +bool Follower::set_offset (V2 offs) +{ + DisplayEngine *e = get_engine(); + offs[0] = max (offs[0], 0.0); + offs[1] = max (offs[1], 0.0); + offs[0] = min (offs[0], double(e->get_width()-get_hoff()-1)); + offs[1] = min (offs[1], double(e->get_height()-get_voff()-1)); + if (offs != e->get_offset()) { + e->set_offset(offs); + return true; + } + return false; +} + +/* -------------------- Follower_Screen -------------------- */ + +Follower_Screen::Follower_Screen(DisplayEngine *e) +: Follower(e) +{} + + +/*! Determine whether the screen must be scrolled or not, and change + the coordinate origin of the screen accordingly. */ +void Follower_Screen::tick(double, const ecl::V2 &point) { + DisplayEngine *engine = get_engine(); + V2 oldoff = engine->get_offset(); + Follower::center(point); + if (oldoff != engine->get_offset()) + engine->mark_redraw_screen(); +} + + + +/* -------------------- Follower_Scrolling -------------------- */ + +Follower_Scrolling::Follower_Scrolling(DisplayEngine *e, bool screenwise_) +: Follower (e), + currently_scrolling(false), + scrollspeed(0), resttime(0), + screenwise (screenwise_) +{} + +void Follower_Scrolling::center(const ecl::V2 &point) +{ + Follower::center(point); + curpos = destpos = get_engine()->get_offset(); +} + +void Follower_Scrolling::tick(double dtime, const ecl::V2 &point) +{ + DisplayEngine *engine = get_engine(); + + if (!currently_scrolling) { + ScreenArea gamearea = engine->get_area(); + int tilew = engine->get_tilew(); + int tileh = engine->get_tileh(); + int borderx = tilew/2; + int bordery = tileh/2; + + int sx, sy; + engine->world_to_screen(point, &sx, &sy); + + bool scrollx_p = (sx < gamearea.x + borderx) + || (sx >= gamearea.x + gamearea.w - borderx); + + bool scrolly_p = (sy < gamearea.y + bordery) + || (sy >= gamearea.y + gamearea.h - bordery); + + if (scrollx_p || scrolly_p) { + V2 olddest = destpos; + V2 scrollpos = engine->get_offset(); + + currently_scrolling = true; + + // Move `point' to center of the screen + curpos = scrollpos; + + if (screenwise) { + double hoff = get_hoff(); + double voff = get_voff(); + destpos[0] = floor((point[0]-m_boundary) / hoff) * hoff; + destpos[1] = floor((point[1]-m_boundary) / voff) * voff; + } else { + destpos = point - V2(gamearea.w/tilew, gamearea.h/tileh)/2; + } + + // Round to integer pixel offset + destpos[0] = round_nearest(destpos[0]*tilew)/tilew; + destpos[1] = round_nearest(destpos[1]*tileh)/tileh; + + // Don't scroll off the game area + destpos[0] = Clamp (destpos[0], 0.0, + (double)engine->get_width()-gamearea.w/tilew); + destpos[1] = Clamp (destpos[1], 0.0, + (double)engine->get_height()-gamearea.h/tileh); + if (!scrollx_p) + destpos[0] = olddest[0]; + if (!scrolly_p) + destpos[1] = olddest[1]; + } + } + + if (currently_scrolling) { + scrollspeed = 45.0; + resttime = length(destpos - curpos)/scrollspeed; + + resttime -= dtime; + if (resttime <= 0) { + engine->move_offset (destpos); + currently_scrolling = false; + } else { + dir = normalize(destpos - curpos); + curpos += dir * scrollspeed*dtime; + engine->move_offset (curpos); + } + } +} + +/* -------------------- Follower_Smooth -------------------- */ + +Follower_Smooth::Follower_Smooth (DisplayEngine *e) +: Follower (e) +{ +} + +ecl::V2 Follower_Smooth::calc_offset (const ecl::V2 &point) +{ + DisplayEngine *engine = get_engine(); + ScreenArea gamearea = engine->get_area(); + int tilew = engine->get_tilew(); + int tileh = engine->get_tileh(); + + V2 destpos = point - V2(double(gamearea.w)/tilew, double(gamearea.h)/tileh)/2; + // Round to integer pixel offset + destpos[0] = round_nearest(destpos[0]*tilew)/double(tilew); + destpos[1] = round_nearest(destpos[1]*tileh)/double(tileh); + destpos[0] = Clamp (destpos[0], 0.0, + (double)engine->get_width()-gamearea.w/tilew); + destpos[1] = Clamp (destpos[1], 0.0, + (double)engine->get_height()-gamearea.h/tileh); + return destpos; +} + +void Follower_Smooth::tick (double /*time*/, const ecl::V2 &point) +{ + DisplayEngine *engine = get_engine(); + engine->move_offset (calc_offset (point)); +} + +void Follower_Smooth::center (const ecl::V2 &point) +{ + set_offset(calc_offset (point)); +} + + +//---------------------------------------------------------------------- +// Editor / game display engine +//---------------------------------------------------------------------- + +CommonDisplay::CommonDisplay (const ScreenArea &a) +{ + m_engine = new DisplayEngine; + m_engine->set_screen_area (a); + + const video::VMInfo *vminfo = video::GetInfo(); + m_engine->set_tilesize (vminfo->tile_size, vminfo->tile_size); + + // Create and configure display layers + floor_layer = new DL_Grid; + item_layer = new DL_Grid; + sprite_layer = new DL_Sprites; + stone_layer = new DL_Grid (2); + shadow_layer = new DL_Shadows(stone_layer, sprite_layer); + line_layer = new DL_Lines; + effects_layer = new DL_Sprites; + effects_layer->set_maxsprites(50); + + // Register display layers + m_engine->add_layer (floor_layer); + m_engine->add_layer (item_layer); + m_engine->add_layer (shadow_layer); + m_engine->add_layer (sprite_layer); + m_engine->add_layer (stone_layer); + m_engine->add_layer (line_layer); + m_engine->add_layer (effects_layer); +} + +CommonDisplay::~CommonDisplay() +{ + delete m_engine; +} + +Model * CommonDisplay::set_model (const GridLoc &l, Model *m) +{ + int x = l.pos.x, y=l.pos.y; + + switch (l.layer) { + case GRID_FLOOR: floor_layer->set_model (x, y, m); break; + case GRID_ITEMS: item_layer->set_model (x, y, m); break; + case GRID_STONES: + stone_layer->set_model (x, y, m); +// shadow_layer->set_model (x, y, m); +// shadow_layer->update (x, y); + break; + case GRID_COUNT: break; + } + return m; +} + +Model * +CommonDisplay::get_model (const GridLoc &l) +{ + int x = l.pos.x, y=l.pos.y; + switch (l.layer) { + case GRID_FLOOR: return floor_layer->get_model (x, y); + case GRID_ITEMS: return item_layer->get_model (x, y); + case GRID_STONES: return stone_layer->get_model (x, y); + case GRID_COUNT: return 0; + } + return 0; +} + +Model * +CommonDisplay::yield_model (const GridLoc &l) +{ + int x = l.pos.x, y=l.pos.y; + switch (l.layer) { + case GRID_FLOOR: return floor_layer->yield_model (x, y); + case GRID_ITEMS: return item_layer->yield_model (x, y); + case GRID_STONES: return stone_layer->yield_model (x, y); + case GRID_COUNT: return 0; + } + return 0; +} + + +RubberHandle +CommonDisplay::add_line (V2 p1, V2 p2) +{ + return line_layer->add_line (p1, p2); +} + +SpriteHandle +CommonDisplay::add_effect (const V2& pos, Model *m) +{ + Sprite *spr = new Sprite (pos, SPRITE_EFFECT, m); + return SpriteHandle (effects_layer, effects_layer->add_sprite(spr)); +} + +SpriteHandle +CommonDisplay::add_sprite (const V2 &pos, Model *m) +{ + Sprite *spr = new Sprite (pos, SPRITE_ACTOR, m); + return SpriteHandle (sprite_layer, sprite_layer->add_sprite(spr)); +} + +void CommonDisplay::new_world (int w, int h) { + get_engine()->new_world (w, h); +} + +void CommonDisplay::redraw() { + get_engine()->update_screen(); +} + +void CommonDisplay::set_floor (int x, int y, Model *m) { + floor_layer->set_model (x, y, m); +} + +void CommonDisplay::set_item (int x, int y, Model *m) { + item_layer->set_model (x,y , m); +} + +void CommonDisplay::set_stone (int x, int y, Model *m) { + stone_layer->set_model (x,y , m); +} + + + +//---------------------------------------------------------------------- +// Game Display Engine +//---------------------------------------------------------------------- + +GameDisplay::GameDisplay (const ScreenArea &gamearea, + const ScreenArea &inventoryarea_) +: CommonDisplay(gamearea), + last_frame_time (0), + redraw_everything(false), + m_reference_point (), + m_follower (0), + inventoryarea (inventoryarea_) +{ + status_bar = new StatusBarImpl (inventoryarea); +} + +GameDisplay::~GameDisplay() +{ + delete m_follower; + delete status_bar; +} + +void GameDisplay::tick(double dtime) { + get_engine()->tick (dtime); + status_bar->tick (dtime); + + if (m_follower) + m_follower->tick (dtime, m_reference_point); +} + +void GameDisplay::new_world (int w, int h) { + CommonDisplay::new_world (w, h); + status_bar->new_world(); + resize_game_area (NTILESH, NTILESV); + set_follow_mode (FOLLOW_SCREEN); + m_reference_point = V2(); + +// shadow_layer->new_world(w,h); +} + +StatusBar * +GameDisplay::get_status_bar() const +{ + return status_bar; +} + + +/* -------------------- Scrolling -------------------- */ + +void GameDisplay::set_follow_mode (FollowMode m) { + switch (m) { + case FOLLOW_NONE: + set_follower(0); + break; + case FOLLOW_SCROLLING: + set_follower (new Follower_Scrolling(get_engine(), false)); + break; + case FOLLOW_SCREEN: + set_follower (new Follower_Screen(get_engine())); + break; + case FOLLOW_SCREENSCROLLING: + set_follower (new Follower_Scrolling(get_engine(), true)); + break; + case FOLLOW_SMOOTH: + set_follower (new Follower_Smooth(get_engine())); + }; +} + +void GameDisplay::set_follower (Follower *f) { + delete m_follower; + if ((m_follower = f)) + follow_center(); +} + +void GameDisplay::follow_center() { + if (m_follower) + m_follower->center (m_reference_point); +} + +void GameDisplay::set_reference_point (const V2 &point) { + m_reference_point = point; +} + +void GameDisplay::get_reference_point_coordinates(int *x, int *y) { + get_engine()->world_to_screen(m_reference_point, x, y); +} + +void GameDisplay::set_scroll_boundary (double boundary) +{ + if (m_follower) + m_follower->set_boundary (boundary); +} + +/* ---------- Screen updates ---------- */ + +void GameDisplay::redraw_all (Screen *scr) { + get_engine()->mark_redraw_screen(); + redraw_everything = true; + scr->update_all(); + redraw (scr); +} + +void GameDisplay::redraw (ecl::Screen *screen) { + GC gc(screen->get_surface()); + if (SDL_GetTicks() - last_frame_time > 10) { + CommonDisplay::redraw(); + + if (ShowFPS) { + char fps[20]; + sprintf (fps,"fps: %d\n", int(1000.0/(SDL_GetTicks()-last_frame_time))); + Font *f = enigma::GetFont("levelmenu"); + + clip(gc); + Rect area (0,0,80,20); + set_color (gc, 0,0,0); + box (gc, area); + f->render (gc, 0,0, fps); + + screen->update_rect(area); + } + last_frame_time = SDL_GetTicks(); + } + if (status_bar->has_changed() || redraw_everything) { + status_bar->redraw (gc, inventoryarea); + screen->update_rect(inventoryarea); + } + if (redraw_everything) + draw_borders(gc); + screen->flush_updates(); + redraw_everything = false; +} + +void GameDisplay::draw_all (GC &gc) { + get_engine()->draw_all(gc); + status_bar->redraw (gc, inventoryarea); + draw_borders(gc); +} + +void GameDisplay::draw_borders (GC &gc) { + RectList rl; + rl.push_back (gc.drawable->size()); + rl.sub (get_engine()->get_area()); + rl.sub (inventoryarea); + clip(gc); + set_color (gc, 0, 0, 0); + for (RectList::iterator i=rl.begin(); i!=rl.end(); ++i) { + box (gc, *i); + } +} + +void GameDisplay::resize_game_area (int w, int h) +{ + DisplayEngine *e = get_engine(); + int neww = w * e->get_tilew(); + int newh = h * e->get_tileh(); + + const video::VMInfo *vidinfo = video::GetInfo(); + + int screenw = vidinfo->width; + int screenh = NTILESV * vidinfo->tile_size; + if (neww > screenw || newh > screenh) { + enigma::Log << "Illegal screen size ("<< neww << "," << newh + << "): larger than physical display\n"; + return; + } + Rect r ((screenw-neww)/2, (screenh-newh)/2, neww, newh); + e->set_screen_area (r); + follow_center(); +} + + + +/* -------------------- Global functions -------------------- */ + +void display::Init() { + InitModels(); + + const video::VMInfo *vminfo = video::GetInfo(); + gamedpy = new GameDisplay (vminfo->gamearea, + vminfo->statusbararea); +} + +void display::Shutdown() { + delete gamedpy; + ShutdownModels(); +} + +void display::Tick (double dtime) { + gamedpy->tick(dtime); +} + +StatusBar * display::GetStatusBar() { + return gamedpy->get_status_bar(); +} + +void display::NewWorld(int w, int h) { + gamedpy->new_world (w, h); +} + +void display::FocusReferencePoint() { + gamedpy->follow_center(); +} + +void display::SetReferencePoint (const ecl::V2 &point) { + gamedpy->set_reference_point (point); +} + +void display::SetFollowMode(FollowMode m) { + gamedpy->set_follow_mode(m); +} + +void display::SetScrollBoundary (double boundary) { + gamedpy->set_scroll_boundary (boundary); +} + + +void display::GetReferencePointCoordinates(int *x, int *y) { + gamedpy->get_reference_point_coordinates(x, y); +} + +Model *display::SetModel (const GridLoc &l, Model *m) { + return gamedpy->set_model (l, m); +} + +Model *display::SetModel (const GridLoc &l, const string &modelname) { + return SetModel(l, MakeModel(modelname)); +} + +void display::KillModel(const GridLoc & l) { + delete YieldModel(l); +} + +Model *display::GetModel(const GridLoc &l) { + return gamedpy->get_model (l); +} + +Model *display::YieldModel(const GridLoc &l) { + return gamedpy->yield_model (l); +} + +SpriteHandle display::AddEffect (const V2& pos, const char *modelname) { + return gamedpy->add_effect (pos, MakeModel(modelname)); +} + +SpriteHandle +display::AddSprite (const V2& pos, const char *modelname) +{ + Model *m = modelname ? MakeModel(modelname) : 0; + return gamedpy->add_sprite (pos, m); +} + +void display::ToggleFlag(DisplayFlags flag) +{ + toggle_flags (display_flags, flag); +} + +void display::DrawAll (GC &gc) { + gamedpy->draw_all(gc); +} + +void display::RedrawAll(Screen *screen) { + gamedpy->redraw_all(screen); +} + +void display::Redraw (Screen *screen) { + gamedpy->redraw (screen); +} + +void display::ResizeGameArea (int w, int h) { + gamedpy->resize_game_area (w, h); +} +const Rect& display::GetGameArea () { + return gamedpy->get_engine()->get_area(); +} + +RubberHandle +display::AddRubber (const V2 &p1, const V2 &p2) +{ + return gamedpy->add_line (p1, p2); +} diff --git a/project/jni/application/enigma/src/display.hh b/project/jni/application/enigma/src/display.hh new file mode 100644 index 000000000..6f6447db7 --- /dev/null +++ b/project/jni/application/enigma/src/display.hh @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef DISPLAY_HH_INCLUDED +#define DISPLAY_HH_INCLUDED + +#include "enigma.hh" +#include "ecl.hh" + +//---------------------------------------- +// Definition of models +//---------------------------------------- +namespace display +{ + class DisplayLayer; + class ModelLayer; + + /*! Animations can invoke a callback of this type on completion. + Note that you may not delete or replace models other than the + one that induced the callback from inside the callback--use a + timer or a flag to do this. */ + class ModelCallback { + public: + virtual ~ModelCallback() {} + virtual void animcb() = 0; + }; + + class Animation { + public: + virtual ~Animation() {} + virtual void set_callback (ModelCallback *) {} + virtual void reverse() {} + virtual void restart() {} + + virtual bool is_garbage() const { return false; } + virtual void tick(double /*dtime*/) {} + virtual bool has_changed(ecl::Rect &/*changed_region*/) { return false; } + }; + + class Model : public Animation { + public: + + virtual void draw(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {} + virtual void draw_shadow(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {} + + virtual Model *get_shadow() const { return 0; } + + virtual void expose (ModelLayer * /*ml*/, int videox, int videoy) {} + virtual void remove (ModelLayer * /*ml*/) {} + + virtual Model *clone() = 0; + virtual void get_extension (ecl::Rect &r); + }; + +/* -------------------- Functions -------------------- */ + + void InitModels(); + void ShutdownModels(); + + Model * MakeModel (const std::string &name); + + + int DefineImage (const char *name, const char *fname, + int xoff, int yoff, int padding); + int DefineSubImage (const char *name, const char *fname, + int xoff, int yoff, ecl::Rect r); + void DefineRandModel (const char *name, int n, char **names); + void DefineShadedModel (const char *name, const char *model, const char *shade); + void DefineOverlayImage (const char *name, int n, char **images); + void DefineComposite (const char *name, const char *bgname, const char *fgname); + void DefineAnim (const char *name, bool loop_p); + void AddFrame (const char *name, const char *model, double time); + void DefineAlias (const char *name, const char *othername); +} + +//---------------------------------------- +// Models on the grid +//---------------------------------------- +namespace display +{ + using enigma::GridPos; + using enigma::GridLayer; + using enigma::GridLoc; + + Model* SetModel (const GridLoc & l, const std::string &modelname); + Model* SetModel (const GridLoc & l, Model *m); + void KillModel (const GridLoc & l); + Model* GetModel (const GridLoc & l); + Model* YieldModel (const GridLoc & l); +} + +/* -------------------- Scrolling -------------------- */ +namespace display +{ + enum FollowMode { + FOLLOW_NONE = 0, // Don't follow any sprite + FOLLOW_SCROLLING = 1, // Scroll the screen + FOLLOW_SCREEN = 2, // Flip the screen region + FOLLOW_SCREENSCROLLING = 3, // Scroll to the next screen + FOLLOW_SMOOTH = 4, // Follow pixel by pixel + }; + + + void SetFollowMode (FollowMode m); + void SetScrollBoundary (double boundary); + + void SetReferencePoint (const ecl::V2 &point); + void GetReferencePointCoordinates(int *x, int *y); + void FocusReferencePoint(); +} + +/* -------------------- Sprites -------------------- */ +namespace display +{ + enum SpriteLayer { + SPRITE_ACTOR, SPRITE_EFFECT, SPRITE_DEBRIS + }; + + typedef unsigned int SpriteId; + + class DL_Sprites; + + class SpriteHandle { + DL_Sprites *layer; + unsigned id; + public: + SpriteHandle (DL_Sprites *l, unsigned spriteid); + SpriteHandle(); + + void kill(); + void move (const ecl::V2 &newpos) const; + void replace_model (Model *m) const; + Model *get_model () const; + void set_callback (ModelCallback *cb) const; + void hide() const; + void show() const; + }; + + /*! Add a new effect sprite. Sprites of this type are + automatically deleted once the animation has finished. */ + SpriteHandle AddEffect (const ecl::V2 &pos, const char *modelname); + + /*! Create a new sprite. If modelname==0, the sprite is + considered invisible. Sprites of this type are _never_ + automatically deleted. */ + SpriteHandle AddSprite (const ecl::V2 &pos, const char *modelname=0); + +} + +/* -------------------- Rubber bands -------------------- */ +namespace display +{ + class DL_Lines; + + class RubberHandle { + public: + RubberHandle (DL_Lines *layer=0, unsigned id=0); + operator unsigned() { return id; } + + void update_first (const ecl::V2 &p1); + void update_second (const ecl::V2 &p2); + void kill(); + + DL_Lines *line_layer; + unsigned id; + }; + + RubberHandle AddRubber (const ecl::V2 &p1, const ecl::V2 &p2); +} + + +/* -------------------- Status bar -------------------- */ +namespace display +{ + using enigma_player::Inventory; + + class StatusBar { + public: + virtual ~StatusBar() {} + virtual void set_inventory (const std::vector &modelnames) = 0; + + virtual void show_text (const std::string &str, + bool scrolling, + double duration = -1) = 0; + virtual void hide_text() = 0; + + virtual void show_move_counter (bool active) = 0; + virtual void show_odometer (bool active) = 0; + + virtual void set_time (double time) = 0; + virtual void set_speed (double speed) = 0; + virtual void set_travelled_distance (double distance) = 0; + virtual void set_counter (int nummoves) = 0; + }; + + StatusBar *GetStatusBar(); + +#define STATUSBAR display::GetStatusBar() + + +/* -------------------- Interface to display engine -------------------- */ + + enum DisplayFlags + { + SHOW_FLOOR = 0x01, + SHOW_STONES = 0x02, + SHOW_ITEMS = 0x04, + SHOW_SHADES = 0x08, + SHOW_SPRITES = 0x10, + SHOW_TIME = 0x20, + SHOW_INVENTORY = 0x40, + SHOW_ALL = 0x7f + }; + + void ToggleFlag(DisplayFlags flag); + + + void Init(); + void Shutdown(); + + void NewWorld (int w, int h); + void ResizeGameArea (int w, int h); + const ecl::Rect& GetGameArea (); + + void DrawAll (ecl::GC &gc); + void RedrawAll (ecl::Screen *sfc); + void Redraw (ecl::Screen *sfc); + void Tick (double dtime); +} + +#endif diff --git a/project/jni/application/enigma/src/display_internal.hh b/project/jni/application/enigma/src/display_internal.hh new file mode 100644 index 000000000..f422eadf4 --- /dev/null +++ b/project/jni/application/enigma/src/display_internal.hh @@ -0,0 +1,96 @@ +#ifndef DISPLAY_INTERNAL_HH +#define DISPLAY_INTERNAL_HH + +#include "display.hh" + +namespace display +{ + using ecl::V2; + + class DisplayLayer; + class StatusBarImpl; + class Model; + + typedef ecl::Rect ScreenArea; + typedef ecl::Rect WorldArea; + typedef std::list ModelList; + + + class Window { + public: + Window() {} + Window (const ScreenArea &area) : m_area(area) + {} + + const ScreenArea &get_area() const { return m_area; } + private: + ScreenArea m_area; + }; + + + class TextDisplay { + public: + TextDisplay(ecl::Font &f); + + void set_text(const std::string &t, bool scrolling, double duration = -1); + + void tick(double dtime); + bool has_changed() const { return changedp; } + bool has_finished() const { return finishedp; } + + void draw(ecl::GC &gc, const ecl::Rect &r); + private: + ecl::Rect area; + std::string text; + bool changedp, finishedp; + bool pingpong; + bool showscroll; + double xoff; + double scrollspeed; // pixels per second + std::auto_ptr textsurface; + ecl::Font &font; + double time, maxtime; + }; + + class StatusBarImpl : public StatusBar, public Window { + public: + StatusBarImpl (const ScreenArea &area); + ~StatusBarImpl(); + + bool has_changed() const { return m_changedp; } + void redraw (ecl::GC &gc, const ScreenArea &r); + void tick (double dtime); + void new_world(); + + // StatusBar interface. + void set_time (double time); + void set_inventory (const std::vector &modelnames); + void show_text (const std::string &str, bool scrolling, double duration); + void hide_text(); + + void show_move_counter (bool active); + void show_odometer (bool active); + + void set_speed (double speed); + void set_travelled_distance (double distance); + void set_counter (int new_counter); + + private: + ScreenArea m_itemarea; + std::vector m_models; + bool m_changedp; + TextDisplay m_textview; + + double m_leveltime; + bool m_showtime_p; + int m_counter; + bool m_showcounter_p; + bool m_showodometer_p; + bool m_interruptible; // Current text message may be interrupted + bool m_text_active; + }; + + +} + +#endif diff --git a/project/jni/application/enigma/src/editor.cpp b/project/jni/application/enigma/src/editor.cpp new file mode 100644 index 000000000..be58ade90 --- /dev/null +++ b/project/jni/application/enigma/src/editor.cpp @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "editor.hh" + +#include "ecl_sdl.hh" +#include "ecl_video.hh" // set_color, line +#include "ecl_util.hh" // set_flags + +#include "main.hh" +#include "world.hh" +#include "lua.hh" +#include "video.hh" +#include "gui/widgets.hh" +#include "display_internal.hh" // WorldArea + +#include +#include + +#include "display_internal.hh" +#include "d_engine.hh" + +#ifndef CXXLUA +extern "C" { +#include "lualib.h" +#include "tolua++.h" +} +#else +#include "lualib.h" +#include "tolua++.h" +#endif + +#include "lua-editor.hh" + +using namespace std; +using namespace enigma; +using namespace editor; +using namespace ecl; + +using display::ScreenArea; +using display::DisplayEngine; +using display::Model; +//using world::ObjectTraits; + +using world::ItemID; + +#include "editor_impl.hh" + + +/* -------------------- Editor state -------------------- */ + +EditorState::EditorState() +{ +} + + +EditorState::~EditorState() +{ +} + + +void EditorState::save( std::ostream & /*os*/ ) +{ +} + +void EditorState::load( std::istream & /*is*/ ) +{ +} + + + +/* -------------------- Editor display engine -------------------- */ + +EditorDisplay::EditorDisplay() +{ + DisplayEngine *engine = get_engine(); + engine->set_screen_area( video::GetInfo()->gamearea ); + engine->add_layer( new DL_Editor ); +} + +void +EditorDisplay::redraw() +{ + display::CommonDisplay::redraw(); +} + + +/* -------------------- Editor tools -------------------- */ + +void +GridObjectTool::advance_in_group( int offset ) +{ + int newidx = m_objectidx + offset; + if( newidx >= 0 && newidx < (int)m_groups[ m_groupidx ].entries.size() ) { + m_objectidx = newidx; + } +} + +void +GridObjectTool::advance_group( int offset ) +{ + int newidx = m_groupidx + offset; + if( newidx >= 0 && newidx < (int)m_groups.size() ) { + m_groupidx = newidx; + m_objectidx = 0; + } +} + +void +GridObjectTool::on_mousebutton( const SDL_Event &e, V2 worldpos ) +{ + Editor *ed = Editor::get_instance(); + string obj = current_object(); + + round_coordinates( &worldpos ); + int x = (int)worldpos[ 0 ]; + int y = (int)worldpos[ 1 ]; + + if( e.button.type == SDL_MOUSEBUTTONDOWN ) { + if( e.button.button == 1 ) { + // left mb -> set object + switch( m_layer ) { + case GRID_FLOOR: ed->set_floor( x, y, obj ); break; + case GRID_ITEMS: ed->set_item( x, y, obj ); break; + case GRID_STONES: ed->set_stone( x, y, obj ); break; + default: break; + } + } + else if( e.button.button == 3 ) { + // right mb + } + } +} + +void +GridObjectTool::object_menu() +{ +} + +void +ActorTool::on_mousebutton( const SDL_Event &e, V2 worldpos ) +{ + Editor *ed = Editor::get_instance(); + string obj = current_object(); + + if( e.button.type == SDL_MOUSEBUTTONDOWN ) { + if( e.button.button == 1 ) { + // left mb -> set object + ed->set_actor( worldpos[0], worldpos[1], obj ); + } + else if( e.button.button == 3 ) { + // right mb + } + } +} + + +/* -------------------- The Editor -------------------- */ + +Editor *Editor::m_instance = 0; + +Editor::Editor() + : m_editarea( 0,0,640,13*32 ) + , m_iconarea( 0,13*32,640,64 ) + , m_display() + , m_iconbar( m_iconarea, 2, 640/32 ) + , m_quit_editor( false ) + , m_cursor() + , m_lua( luaL_newstate() ) + , m_editmode( MODE_FLOOR ) + , m_tools() +{ + luaL_openlibs(m_lua); + tolua_open( m_lua ); + tolua_editor_open( m_lua ); +} + +Editor::~Editor() +{ + lua_close( m_lua ); +} + +void +Editor::init() +{ + delete_sequence( m_tools.begin(), m_tools.end() ); + m_tools.clear(); + m_tools.resize( MODE_COUNT, NULL ); + m_tools[ MODE_FLOOR ] = m_floortool = new FloorTool; + m_tools[ MODE_ITEMS ] = m_itemtool = new ItemTool; + m_tools[ MODE_STONES ] = m_stonetool = new StoneTool; + m_tools[ MODE_ACTORS ] = m_actortool = new ActorTool; + + new_world( 20, 13 ); + + // TODO - just printing a message is not enough - the app will crash on missing editor.lua + if (lua::DoSysFile(m_lua, "compat.lua") != lua::NO_LUAERROR) { + std::string message = "While processing 'compat.lua':\n" +lua::LastError(m_lua); + fprintf( stderr, message.c_str() ); + } + if (lua::DoSysFile(m_lua, "editor.lua") != lua::NO_LUAERROR) { + std::string message = "Error loading 'editor.lua'\n" +lua::LastError(m_lua); + fprintf( stderr, message.c_str() ); + } +} + +void +Editor::run() +{ + m_quit_editor = false; + while( !m_quit_editor ) { + video::HideMouse(); + m_display.redraw(); + video::ShowMouse(); + video::GetScreen()->flush_updates(); + + SDL_Event e; + if( SDL_PollEvent( &e ) ) { + dispatch_event (e); + } + + SDL_Delay( 10 ); + } +} + +void Editor::set_mode( EditMode m ) +{ + m_editmode = m; + update_cursor(); +} + +void Editor::update_cursor() +{ + // set proper position + int mx; + int my; + SDL_GetMouseState( &mx, &my ); + DisplayEngine *engine = m_display.get_engine(); + V2 worldpos = engine->to_world( V2( mx, my ) ); + current_tool()->round_coordinates( &worldpos ); + m_cursor.move( worldpos ); + + // set proper face + set_cursor( current_tool()->current_object() ); +} + +Tool *Editor::current_tool() +{ + if( Tool *tool = m_tools[ m_editmode ] ) { + return tool; + } + else { + fprintf( stderr, "undefined tool\n" ); + assert( m_stonetool != NULL ); + return m_stonetool; + } +} + + +void +Editor::set_floor( int x, int y, std::string const& name ) +{ + m_display.set_floor( x, y, display::MakeModel( name ) ); +} + +void +Editor::set_item( int x, int y, std::string const& name ) +{ + m_display.set_item( x, y, display::MakeModel( name ) ); +} + +void +Editor::set_stone( int x, int y, std::string const& name ) +{ + m_display.set_stone( x, y, display::MakeModel( name ) ); +} + +void +Editor::set_actor( double x, double y, std::string const& name ) +{ + m_display.add_sprite( V2( x, y ), display::MakeModel( name ) ); +} + + +void +Editor::new_world( int w, int h ) +{ + m_display.new_world( w, h ); + for( int x=0; xget_offset() + V2( xoff, yoff ); + video::HideMouse(); + engine->move_offset( newoffset ); + video::ShowMouse(); +} + +void +Editor::scroll_abs( double x, double y ) +{ + DisplayEngine *engine = m_display.get_engine(); + video::HideMouse(); + engine->move_offset( V2( x, y ) ); + video::ShowMouse(); +} + +bool +Editor::on_mousemotion( SDL_Event &e ) +{ + DisplayEngine *engine = m_display.get_engine(); + V2 worldpos = engine->to_world( V2( e.motion.x, e.motion.y ) ); + current_tool()->round_coordinates( &worldpos ); + m_cursor.move( worldpos ); + return true; +} + +bool +Editor::on_mousebutton( SDL_Event &e ) +{ + DisplayEngine *engine = m_display.get_engine(); + V2 worldpos = engine->to_world( V2( e.motion.x, e.motion.y ) ); + + if( Tool *tool = current_tool() ) { + tool->on_mousebutton( e, worldpos ); + } + return true; +} + +bool +Editor::on_keydown( SDL_Event &e ) +{ + bool ctrl_pressed = e.key.keysym.mod & KMOD_CTRL; + bool shift_pressed = e.key.keysym.mod & KMOD_SHIFT; + int hoff = 19; + int voff = 12; + + Tool *tool = current_tool(); + + switch( e.key.keysym.sym ) { + case SDLK_ESCAPE: + m_quit_editor = true; + break; + case SDLK_LEFT: scroll( ctrl_pressed ? -hoff : -1, 0 ); break; + case SDLK_RIGHT: scroll( ctrl_pressed ? +hoff : +1, 0 ); break; + case SDLK_DOWN: scroll( 0, ctrl_pressed ? +voff : +1 ); break; + case SDLK_UP: scroll( 0, ctrl_pressed ? -voff : -1 ); break; + case SDLK_HOME: scroll_abs( 0, 0 ); break; + + case SDLK_PAGEUP: + tool->advance_group( -1 ); + update_cursor(); + break; + case SDLK_PAGEDOWN: + tool->advance_group( +1 ); + update_cursor(); + break; + + case SDLK_PLUS: + case SDLK_KP_PLUS: + tool->advance_in_group( +1 ); + update_cursor(); + break; + + case SDLK_MINUS: + case SDLK_KP_MINUS: + tool->advance_in_group( -1 ); + update_cursor(); + break; + + case SDLK_a: + if( shift_pressed ) + ; + set_mode( MODE_ACTORS ); + break; + + case SDLK_f: + if( shift_pressed ) + ; + set_mode( MODE_FLOOR ); + break; + + case SDLK_i: + if( shift_pressed ) + ; + set_mode( MODE_ITEMS ); + break; + + case SDLK_s: + set_mode( MODE_STONES ); + if( shift_pressed ) { + tool = current_tool(); + tool->object_menu(); + } + break; + + default: + return false; + } + return true; +} + +void editor::DefineFloorGroup( char const* name, char const* descr, + int nentries, char ** entries ) +{ + Editor *ed = Editor::get_instance(); + + vector entryvec(entries, entries+nentries); + ObjectGroup group(name, descr, entryvec); + ed->add_floor_group (group); +} + +void editor::DefineItemGroup( const char *name, const char *descr, + int nentries, char **entries ) +{ + Editor *ed = Editor::get_instance(); + + vector entryvec( entries, entries + nentries ); + ObjectGroup group( name, descr, entryvec ); + ed->add_item_group( group ); +} + +void editor::DefineStoneGroup( const char *name, const char *descr, + int nentries, char **entries ) +{ + Editor *ed = Editor::get_instance(); + + vector entryvec( entries, entries+nentries ); + ObjectGroup group( name, descr, entryvec ); + ed->add_stone_group( group ); +} + + +void editor::Run() +{ + Editor *ed = Editor::get_instance(); + ed->init(); + ed->run(); +} diff --git a/project/jni/application/enigma/src/editor.hh b/project/jni/application/enigma/src/editor.hh new file mode 100644 index 000000000..9bc48723e --- /dev/null +++ b/project/jni/application/enigma/src/editor.hh @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef EDITOR_HH +#define EDITOR_HH + +#include + +namespace editor +{ + void Run(); + void DefineFloorGroup (const char *name, const char *descr, + int nentries, char **entries); + + void DefineItemGroup (const char *name, const char *descr, + int nentries, char **entries); + + void DefineStoneGroup (const char *name, const char *descr, + int nentries, char **entries); +} + +#endif diff --git a/project/jni/application/enigma/src/editor_impl.hh b/project/jni/application/enigma/src/editor_impl.hh new file mode 100644 index 000000000..97b231584 --- /dev/null +++ b/project/jni/application/enigma/src/editor_impl.hh @@ -0,0 +1,335 @@ +namespace +{ + class EditorState { + public: + EditorState(); + ~EditorState(); + + void save (std::ostream &os); + void load (std::istream &is); + + private: + + }; + + class DL_Editor + : public display::DisplayLayer + { + public: + DL_Editor() + { + } + + ~DL_Editor() + { + } + + void prepare_draw( const display::WorldArea & ) + { + } + + void draw( ecl::GC &gc, const display::WorldArea &a, int x, int y ) + { + } + + void draw_onepass( ecl::GC & gc ) + { + //just a test... + //ecl::set_color( gc, 240, 140, 20, 255 ); + //ecl::set_flags( gc.flags, GS_ANTIALIAS ); + //ecl::line( gc, 0, 0, 320, 240 ); + } + + void tick( double dtime ) + { + } + + void new_world( int w, int h ) + { + } + }; + + class EditorDisplay + : public display::CommonDisplay + { + public: + EditorDisplay(); + ~EditorDisplay() {} + + void redraw(); + + void tick(double /*dtime*/ ) {} + + Model *make_model( const std::string &name ); + private: + + }; + + class IconBar + : public gui::Container + { + public: + IconBar( const ScreenArea &a, int rows, int cols ) + : m_area( a ) + , m_rows( rows ) + , m_cols( cols ) + , m_bgcolor( 150, 150, 150 ) + { + } + + ~IconBar() {} + + ScreenArea get_area() const + { + return m_area; + } + + // Widget interface. + void draw( ecl::GC &gc, const ecl::Rect &area_ ) + { + set_color( gc, m_bgcolor ); + box( gc, area_ ); + } + + private: + // Variables. + ScreenArea m_area; + int m_rows, m_cols; + ecl::RGB m_bgcolor; + }; + + + enum EditMode { + MODE_FLOOR, + MODE_ITEMS, + MODE_STONES, + MODE_ACTORS, + MODE_COUNT + }; + + struct ObjectGroup { + string name; + string descr; + vector entries; + + ObjectGroup( const string &name_, + const string &descr_, const vector entries_ + ) + : name( name_ ) + , descr( descr_ ) + , entries( entries_ ) + { + } + }; + + + class Tool { + public: + virtual ~Tool() {} + + virtual void object_menu() = 0; + + virtual void advance_in_group( int offset ) = 0; + virtual void advance_group( int offset ) = 0; + virtual string current_object() = 0; + + virtual void on_mousebutton( const SDL_Event &e, V2 worldpos ) = 0; + + /// Rounds coordinates for item display. Actor-like tools + /// will want to leave the coordinates intact, grid-like tools + /// could want to round to integers. + virtual void round_coordinates( V2 * worldpos ) = 0; + }; + + class GridObjectTool + : public Tool + { + public: + + // Tool interface + void object_menu(); + + void advance_in_group( int offset ); + void advance_group( int offset ); + + string current_object() + { + return m_groups[ m_groupidx ].entries[ m_objectidx ]; + } + + void add_group( const ObjectGroup &g ) + { + m_groups.push_back( g ); + } + + // Constructors + GridObjectTool( GridLayer layer ) + { + m_groupidx = m_objectidx = 0; + m_layer = layer; + } + + void round_coordinates( V2 * worldpos ) + { + (*worldpos)[ 0 ] = round_down( (*worldpos)[ 0 ] ); + (*worldpos)[ 1 ] = round_down( (*worldpos)[ 1 ] ); + } + + void on_mousebutton( const SDL_Event &e, V2 worldpos ); + + protected: + + // Variables + vector m_groups; + size_t m_groupidx; + size_t m_objectidx; + GridLayer m_layer; + }; + + class FloorTool + : public GridObjectTool + { + public: + FloorTool() + : GridObjectTool(GRID_FLOOR) + {} + }; + + class ItemTool + : public GridObjectTool + { + public: + ItemTool() + : GridObjectTool( GRID_ITEMS ) + { + } + }; + + class StoneTool + : public GridObjectTool + { + public: + StoneTool () + : GridObjectTool( GRID_STONES ) + { + } + }; + + class ActorTool + : public Tool + { + public: + // Tool interface. + void object_menu() {} + void advance_in_group( int /*offset*/ ) {} + void advance_group( int /*offset*/ ) {} + string current_object() { return "ac-blackball"; } + void on_mousebutton( const SDL_Event & e, V2 worldpos ); + void round_coordinates( V2 * /*worldpos*/ ) {} + private: + }; + + + class Level { + public: + Level( EditorDisplay *display ) + : m_display( display ) + { + } + + private: + // Variables. + EditorDisplay *m_display; + }; + + + + class Editor + : private sdl::EventHandler + { + public: + static Editor * get_instance() + { + if( m_instance == NULL ) { + m_instance = new Editor; + } + return m_instance; + } + ~Editor(); + + void init(); + void run(); + + Level *get_level() const + { + return m_level.get(); + } + + void add_floor_group( const ObjectGroup &g ) + { + m_floortool->add_group( g ); + } + + void add_item_group( const ObjectGroup &g ) + { + m_itemtool->add_group( g ); + } + + void add_stone_group( const ObjectGroup &g ) + { + m_stonetool->add_group( g ); + } + + void set_floor( int x, int y, string const& name ); + void set_item( int x, int y, string const& name ); + void set_stone( int x, int y, string const& name ); + void set_actor( double x, double y, string const& name ); + + private: + Editor(); + + + /* ---------- Private methods ---------- */ + void set_cursor( const string &name ); + + /// Change cursor face and position based on active tool. + void update_cursor(); + + void set_mode( EditMode m ); + Tool *current_tool(); + + void new_world( int w, int h ); + + void scroll( double xoff, double yoff ); + void scroll_abs( double x, double y ); + + /* + ** EventHandler interface. + */ + bool on_mousemotion( SDL_Event &e ); + bool on_mousebutton( SDL_Event &e ); + bool on_keydown( SDL_Event &e ); + + /* + ** Variables. + */ + ScreenArea m_editarea; + ScreenArea m_iconarea; + EditorDisplay m_display; + IconBar m_iconbar; + bool m_quit_editor; + display::SpriteHandle m_cursor; + + lua_State *m_lua; + + FloorTool *m_floortool; + ItemTool *m_itemtool; + StoneTool *m_stonetool; + ActorTool *m_actortool; + EditMode m_editmode; + vector m_tools; + auto_ptr m_level; + + static Editor *m_instance; + }; +} diff --git a/project/jni/application/enigma/src/enigma.cpp b/project/jni/application/enigma/src/enigma.cpp new file mode 100644 index 000000000..a661999a7 --- /dev/null +++ b/project/jni/application/enigma/src/enigma.cpp @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "enigma.hh" +#include "ecl.hh" +#include "main.hh" + +#include +#include +#include + +using namespace std; +using namespace ecl; +using namespace enigma; + + +/* -------------------- Game Type -------------------- */ + +static const char *versionName[GAMET_COUNT+1] = { + "enigma", // same indices as enum GameType + "oxyd1", + "per.oxyd", + "oxyd.extra", + "oxyd.magnum", + 0 +}; + +GameType enigma::GetGameType(std::string name) { + GameType type = GAMET_UNKNOWN; + for (int v = 0; v= GAMET_FIRST && type <= GAMET_LAST) + return versionName[type]; + else + return "unknown"; +} + +/* -------------------- Direction -------------------- */ + +Direction enigma::reverse(Direction d) { + static Direction rdir[] = { NODIR, EAST, NORTH, WEST, SOUTH }; + return rdir[d+1]; +} + +Direction enigma::rotate_cw (Direction d) +{ + static Direction rdir[] = { NODIR, NORTH, WEST, SOUTH, EAST }; + return rdir[d+1]; +} + +Direction enigma::rotate_ccw (Direction d) +{ + static Direction rdir[] = { NODIR, SOUTH, EAST, NORTH, WEST }; + return rdir[d+1]; +} + +Direction +direction_fromto(GridPos source, GridPos target) +{ + // source and target have to be adjacent + + int dx = target.x-source.x; + int dy = target.y-source.y; + Direction d = NODIR; + + if (dx == 0) { + if (dy == -1) d = NORTH; + else if (dy == 1) d = SOUTH; + } + else if (dy == 0) { + if (dx == -1) d = WEST; + else if (dx == 1) d = EAST; + } + + ASSERT(d != NODIR, XLevelRuntime, + "direction_fromto: source and target not adjacent"); + return d; +} + +string enigma::to_suffix(Direction d) { + static const char *sfx[] = { "", "-w", "-s", "-e", "-n" }; + return sfx[d+1]; +} + + +/* -------------------- DirectionBits -------------------- */ + +DirectionBits +enigma::rotate(DirectionBits d, bool clockwise) +{ + if (clockwise) { + d = DirectionBits(((d>>1) | (d<<3)) & ALL_DIRECTIONS); + } else { + d = DirectionBits(((d>>3) | (d<<1)) & ALL_DIRECTIONS); + } + return d; +} + + +/* -------------------- Value implementation -------------------- */ + +Value::Value(const char* str) +: type(STRING) +{ + val.str = new char[strlen(str)+1]; + strcpy(val.str, str); +} + +Value::~Value() +{ + clear(); +} + + +Value::Value(const string& str) +: type(STRING) +{ + val.str = new char[str.length()+1]; + strcpy(val.str, str.c_str()); +} + +Value::Value (const Value& other) : type(NIL) { + this->operator=(other); +} + +Value& Value::operator= (const Value& other) { + if (this != &other) { + if (other.type == STRING) { + assign(other.val.str); + } else { + clear(); + type = other.type; + val = other.val; + } + } + return *this; +} + + +void Value::assign(const char* s) { + clear(); + type = STRING; + val.str = new char[strlen(s)+1]; + strcpy(val.str, s); +} + +void Value::assign(double d) +{ + clear(); type=DOUBLE; val.dval=d; +} + + +void Value::clear() { + if (type == STRING) + delete[] val.str; + type = NIL; +} + +double Value::get_double() const throw() +{ + ASSERT(type == DOUBLE, XLevelRuntime, "get_double: type not double"); + return val.dval; +} + +const char* Value::get_string() const throw() +{ + ASSERT(type == STRING, XLevelRuntime, "get_string: type not string"); + return val.str; +} + +Buffer& enigma::operator<<(Buffer& buf, const Value& val) +{ + buf << Uint8(val.get_type()); + + switch (val.get_type()) { + case Value::NIL: + break; + case Value::DOUBLE: + buf << val.get_double(); + break; + case Value::STRING: + { + const char* str = val.get_string(); + buf << (Uint16)strlen(str); + buf.write(str, strlen(str)); + } break; + } + return buf; +} + +// Buffer& enigma::operator>>(Buffer& buf, Value& val) +// { +// Uint8 type = Value::NIL; +// buf >> type; + +// switch (type) { +// case Value::NIL: +// // ## fixme +// break; +// case Value::DOUBLE: +// { +// double tmp; +// if (buf >> tmp) +// val = Value(tmp); +// } break; +// case Value::STRING: +// { +// Uint16 len; +// if (buf >> len) { +// char* tmp = new char[len+1]; +// tmp[len] = 0; +// if (buf.read(tmp, len)) +// val.assign(tmp); +// delete[] tmp; +// } +// } break; +// } +// return buf; +// } + +int enigma::to_int(const Value &v) { + switch (v.get_type()) { + case Value::DOUBLE: return round_nearest(v.get_double()); + case Value::STRING: return atoi(v.get_string()); + default: return 0; + } +} + +bool enigma::to_bool(const Value &v) { + return (v.get_type() != Value::NIL); +} + +double enigma::to_double(const Value &v) { + switch (v.get_type()) { + case Value::DOUBLE: return v.get_double(); + case Value::STRING: return atof(v.get_string()); + default: return 0; + } +} + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +const char * enigma::to_string(const Value &v) { + static char buf[30]; + switch (v.get_type()) { + case Value::NIL: return ""; + case Value::DOUBLE: + snprintf(buf, sizeof(buf), "%f", v.get_double()); + return buf; + case Value::STRING: return v.get_string(); + default: return 0; + } +} + +Direction enigma::to_direction (const Value &v) { + int val = Clamp(to_int(v), 0, 3); + return static_cast(val); +} + +ostream& enigma::operator<<(ostream& os, const Value& val) +{ + switch (val.get_type()) { + case Value::NIL: os << "nil"; break; + case Value::DOUBLE: os << val.get_double(); break; + case Value::STRING: os << val.get_string(); break; + } + return os; +} + +/* -------------------- GridPos -------------------- */ + +GridPos::GridPos(const ecl::V2& p) +: x (round_down(p[0])), + y (round_down(p[1])) +{} + + +std::ostream& enigma::operator<<(std::ostream& os, const GridPos& val) +{ + return os << '(' << val.x << ',' << val.y << ')'; +} + +/* + 516 + 203 + 748 +*/ +GridPos enigma::get_neighbour (GridPos p, int i) +{ + ASSERT (i >= 0 && i <= 9, XLevelRuntime, "get_neighbour: index out of bounds"); + static int xoff[9] = { 0,0,-1,1,0,-1,1,-1,1 }; + static int yoff[9] = { 0,-1,0,0,1,-1,-1,1,1 }; + return GridPos(p.x + xoff[i], p.y + yoff[i]); +} + +/* -------------------- GridLoc -------------------- */ + +bool enigma::to_gridloc (const char *str, GridLoc &l) { + GridLoc loc; + const char *numstr = str + 3; + + if (strncmp (str, "fl(", 3) == 0) + loc.layer = GRID_FLOOR; + else if (strncmp (str, "it(", 3) == 0) + loc.layer = GRID_ITEMS; + else if (strncmp (str, "st(", 3) == 0) + loc.layer = GRID_STONES; + else + numstr = str; + + if (2 != sscanf (numstr, "%d %d", &loc.pos.x, &loc.pos.y)) + return false; + l = loc; + return true; +} + + + + +/* -------------------- Random numbers -------------------- */ + +void enigma::Randomize () +{ + srand (time(NULL)); +} + +void enigma::Randomize (unsigned seed) +{ + srand (seed); +} + +int enigma::IntegerRand (int min, int max) +{ + int r = round_down((max-min+1) * (rand()/(RAND_MAX+1.0))); + return r+min; +} + +double enigma::DoubleRand (double min, double max) +{ + return min + double(rand())/RAND_MAX * (max-min); +} + + +/* -------------------- Time & Date -------------------- */ + +#define MAX_DATE_LENGTH 256 +const char *enigma::date(const char *format) { // format see 'man strftime' + static char *result = 0; + char buffer[MAX_DATE_LENGTH]; + + time_t t; + time(&t); + + struct tm *tm = localtime(&t); + strftime(buffer, MAX_DATE_LENGTH, format, tm); + + if (result) free(result); + result = strdup(buffer); + + return result; +} + + +/* -------------------- Resource management -------------------- */ + +namespace +{ + struct FontDescr { + // Variables + string name; + string ttf_name; + int ttf_size; + string bitmap_name; + int r, g, b; + + // Constructor + FontDescr (const string &name_, + const string &ttf_name_, + int ttf_size_, + const string &bitmap_name_, + int r_, int g_, int b_) + : name (name_), + ttf_name (ttf_name_), + ttf_size (ttf_size_), + bitmap_name (bitmap_name_), + r (r_), g(g_), b(b_) + {} + }; + + class FontCache : public PtrCache { + public: + Font *acquire (const std::string &name) { + Font *f = 0; + if (m_fonts.has_key (name)) { + const FontDescr &fd = m_fonts[name]; + f = load_ttf (fd.ttf_name, fd.ttf_size, fd.r, fd.g, fd.b); + if (f == 0) { + std::cerr << "Could not load .ttf file " << fd.ttf_name << "\n"; + f = load_bmf (fd.bitmap_name); + } + } + else { + f = load_bmf (name); + } + return f; + } + + void define_font (const FontDescr &descr) { + remove (descr.name); // remove entry in cache (if any) + if (m_fonts.has_key (descr.name)) + m_fonts[descr.name]= descr; + else + m_fonts.insert (descr.name, descr); + } + + private: + + Font *load_bmf (const string &name) { + string png, bmf; + if (app.resourceFS->findFile(string("fonts/")+name+".png", png) && + app.resourceFS->findFile(string("fonts/")+name+".bmf", bmf)) + { + return ecl::LoadBitmapFont(png.c_str(), bmf.c_str()); + } + return 0; + } + + Font *load_ttf (const string &name, int ptsize, int r, int g, int b) { + string ttf; + if (app.resourceFS->findFile(string("fonts/") + name, ttf)) + return ecl::LoadTTF (ttf.c_str(), ptsize, r, g, b); + return 0; + } + + // Variables + ecl::Dict m_fonts; + }; + + // ---------- Variables ---------- + + FontCache font_cache; + ImageCache image_cache; +} + +ecl::Surface *ImageCache::acquire (const std::string &name) +{ + return ecl::LoadImage(name.c_str()); +} + +void enigma::DefineFont (const char *name, + const char *ttf_name, + int ttf_size, + const char *bmf_name, + int r, int g, int b) +{ + font_cache.define_font (FontDescr (name, ttf_name, ttf_size, bmf_name, r, g, b)); +} + +ecl::Font *enigma::GetFont (const char *name) +{ + return font_cache.get(name); +} + +void enigma::ClearFontCache() { + font_cache.clear(); +} + +ecl::Surface *enigma::LoadImage(const char *name) +{ + string filename; + if (app.resourceFS->findImageFile (string(name) + ".png", filename)) + return ecl::LoadImage(filename.c_str()); + return 0; +} + +ecl::Surface *enigma::GetImage(const char *name, const char *ext) +{ + string filename; + if (app.resourceFS->findImageFile (string(name) + ext, filename)) + return image_cache.get(filename); + return 0; +} + +ecl::Surface *enigma::RegisterImage (const char *name, ecl::Surface *s) +{ + image_cache.store(name, s); + return s; +} + +void enigma::ClearImageCache() { + image_cache.clear(); +} diff --git a/project/jni/application/enigma/src/enigma.doxygen b/project/jni/application/enigma/src/enigma.doxygen new file mode 100644 index 000000000..45eac2548 --- /dev/null +++ b/project/jni/application/enigma/src/enigma.doxygen @@ -0,0 +1,946 @@ +# Doxyfile 1.2.16 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Korean, +# Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovene, +# Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = enigma.cc world.hh objects.cc actors.cc laser.cc items.cc + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = gfx + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line and do not end with a semicolon. Such function macros are typically +# used for boiler-plate code, and will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/project/jni/application/enigma/src/enigma.hh b/project/jni/application/enigma/src/enigma.hh new file mode 100644 index 000000000..a44bc3174 --- /dev/null +++ b/project/jni/application/enigma/src/enigma.hh @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2002,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ENIGMA_HH +#define ENIGMA_HH + +/* + * This file contains declarations for facilities used by several + * different parts of the program, like common types and constants, + * and routines for resource management. + */ + +#include "fwd.hh" +#include "file.hh" +#include "ecl_fwd.hh" +#include "ecl_math.hh" +#include "ecl_util.hh" +#include "ecl_cache.hh" + +#define NUMENTRIES(array) (sizeof(array)/sizeof(*array)) + +namespace enigma +{ + +/* -------------------- Various types -------------------- */ + + enum Difficulty { + DIFFICULTY_EASY = 1, + DIFFICULTY_HARD = 2, + DIFFICULTY_ANY = 3 + }; + + + /*! Enigma can run its own levels but also emulate various + versions of Oxyd. All these games behave similarly, but there + are a few differences in object behaviour, visual appearance, + etc. */ + enum GameType { + GAMET_FIRST, + + GAMET_ENIGMA = GAMET_FIRST, + GAMET_OXYD1, + GAMET_PEROXYD, + GAMET_OXYDEXTRA, + GAMET_OXYDMAGNUM, + + GAMET_LAST = GAMET_OXYDMAGNUM, + GAMET_COUNT, + GAMET_UNKNOWN + }; + + GameType GetGameType(std::string name); + std::string GetGameTypeName(GameType type); + +/* -------------------- Resource Management -------------------- */ + + class ImageCache : public ecl::PtrCache { + public: + using ecl::PtrCache::store; + + ecl::Surface *acquire(const std::string &name); + }; + + +/* The `Get...' functions return a pointer to a cached copy of the + font or image. The `Load...' functions load a new copy which + you must deallocate yourself. */ + + + void DefineFont (const char *name, + const char *ttf_name, + int ttf_size, + const char *bmf_name, + int r, int g, int b); + ecl::Font *GetFont (const char *name); + void ClearFontCache(); + + ecl::Surface *LoadImage (const char *name); + ecl::Surface *GetImage (const char *name, const char *ext = ".png"); + ecl::Surface *RegisterImage (const char *name, ecl::Surface *s); + void ClearImageCache(); + +/* -------------------- Direction, DirectionBits -------------------- */ + + enum Direction { + NODIR = -1, + WEST = 0, + SOUTH = 1, + EAST = 2, + NORTH = 3, + }; + + Direction reverse (Direction d); + Direction rotate_cw (Direction d); + Direction rotate_ccw (Direction d); + + std::string to_suffix(Direction d); + + enum DirectionBits { + NODIRBIT = 0, + WESTBIT = 1 << WEST, + SOUTHBIT = 1 << SOUTH, + EASTBIT = 1 << EAST, + NORTHBIT = 1 << NORTH, + ALL_DIRECTIONS = 15 + }; + + DirectionBits rotate (DirectionBits d, bool clockwise); + DirectionBits to_bits (Direction d); + bool has_dir (DirectionBits db, Direction dir); + + +/* -------------------- Value -------------------- */ + + class Value { + public: + enum Type { NIL, DOUBLE, STRING }; + + Value() : type(NIL) {} + Value(double d) : type(DOUBLE) { val.dval = d; } + Value(const char* str); + Value(const std::string& str); + ~Value(); + + Value(const Value& v); + Value& operator=(const Value& v); + + void assign(double d); + void assign(const char* s); + + Type get_type() const { return type; } + double get_double() const throw(); + const char* get_string() const throw(); + private: + void clear(); + + // Variables + Type type; + union { + double dval; + char* str; + } val; + }; + + ecl::Buffer& operator<<(ecl::Buffer& buf, const Value& val); +// ecl::Buffer& operator>>(ecl::Buffer& buf, Value& val); + + std::ostream& operator<<(std::ostream& os, const Value& val); + + bool to_bool(const Value &v); + int to_int(const Value &v); + double to_double(const Value &v); + const char *to_string(const Value &v); + Direction to_direction (const Value &v); + +/* -------------------- Timers -------------------- */ + + /* Interface for time event handlers. */ + class TimeHandler { + public: + virtual ~TimeHandler() {} + virtual void tick (double /*dtime*/) {} + virtual void alarm() {} + }; + +/* -------------------- GridPos -------------------- */ + + struct GridPos { + // Variables + int x, y; + + // Methods + explicit GridPos(int xx=0, int yy=0); + explicit GridPos(const ecl::V2& p); + void move(Direction dir); + ecl::V2 center() const; + }; + + GridPos move(GridPos p, Direction dir); + GridPos move(GridPos p, Direction dir, Direction dir2); + bool operator== (GridPos a, GridPos b); + bool operator != (GridPos a, GridPos b); + bool operator< (GridPos a, GridPos b); + + /* 516 + 203 + 748 */ + GridPos get_neighbour (GridPos p, int i); + + // source and target have to be adjacent + Direction direction_fromto(GridPos source, GridPos target); + + std::ostream& operator<<(std::ostream& os, const GridPos& val); + + + // ---------- GridLayer ---------- + + enum GridLayer { + GRID_FLOOR, + GRID_ITEMS, + GRID_STONES, + GRID_COUNT + }; + + enum GridLayerBits { + GRID_FLOOR_BIT = 1, + GRID_ITEMS_BIT = 2, + GRID_STONES_BIT = 4 + }; + + // ---------- GridLoc ---------- + + struct GridLoc { + // Variables + GridPos pos; + GridLayer layer; + + // Constructor + GridLoc(GridLayer l = GRID_FLOOR, GridPos p = GridPos()); + }; + + /*! Converts strings like "it(10 10)", "st(5 2)" to GridLoc + structures. */ + bool to_gridloc (const char *str, GridLoc &loc); + + +/* -------------------- Random Numbers -------------------- */ + + void Randomize (); + void Randomize (unsigned seed); + int IntegerRand (int min, int max); + double DoubleRand (double min, double max); + +/* -------------------- Time & Date -------------------- */ + + const char *date(const char *format); // format see 'man strftime' + + + /* ==================== Inline definitions ==================== */ + + inline DirectionBits to_bits(Direction d) { + if (d==NODIR) + return NODIRBIT; + return DirectionBits(1 << d); + } + + inline bool has_dir(DirectionBits db, Direction dir) { + return (db & to_bits(dir)) != 0; + } + + + // ---------- GridPos ---------- + + inline GridPos::GridPos(int xx, int yy) + : x(xx), y(yy) + {} + + + inline void GridPos::move(Direction dir) { + switch(dir) { + case NORTH: y--; break; + case SOUTH: y++; break; + case EAST: x++; break; + case WEST: x--; break; + case NODIR: break; + } + } + + inline ecl::V2 GridPos::center() const { + return ecl::V2(x+.5, y+.5); + } + + + inline GridPos move(GridPos p, Direction dir) { + GridPos tmp = p; + tmp.move(dir); + return tmp; + } + + inline GridPos move(GridPos p, Direction dir, Direction dir2) { + GridPos tmp = p; + tmp.move(dir); + tmp.move(dir2); + return tmp; + } + + inline bool operator == (GridPos a, GridPos b) { + return (a.x==b.x && a.y==b.y); + } + + inline bool operator != (GridPos a, GridPos b) { + return (a.x!=b.x || a.y!=b.y); + } + + inline bool operator< (GridPos a, GridPos b) { + return ((a.y<<16) + a.x) < ((b.y<<16) + b.x); + } + + + // ---------- GridLoc ---------- + + inline GridLoc::GridLoc(GridLayer l, GridPos p) + : pos(p), layer(l) + {} + +} + +#endif diff --git a/project/jni/application/enigma/src/enigma.ico b/project/jni/application/enigma/src/enigma.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a579ddb6949c63bac0dcb2f4a4e2cd3ee48a8fe GIT binary patch literal 100022 zcmZQzU}WH85D);-91Iz(3=GQ{7#I{3Ap8{^3=Dj13=9SaP(B|6!!|w!hJ*wVUx8tb z76U_^ECWMB1BAcAgMop|g8>W}859_fY-C{AG=TwXz5+uA3j;%$6C;C!1VnuYCj-Mh zH%6#^3Jfa*7#Pw57@_tlFsx8zVBkw+WYAz>U=RSgivgsM18lwnBbY)VxLKGOIto%5 zG$n-?nzLdVjMNkvuIGyM7Uhhf|FW`=LyzA;?5aDk!k%72FJWB(aEcKl~hUG<-V0fZ;rVPIll zVDNUfXNV5=XYlg!Vz__zF2nlOD;d_TS;J6UTg%|@@6RAFFVDab`k#S`iHSi^PMpEn zNRvTFL7G8SRFvWE`TY#bmMvpgJE4goIXRiZ#KeR_O+t`?g@J*AhnbN9fPBNv$iQHvB+X#0tHz)vCCVTt$j$KK z!U2X;tL8AASv#NMSoVL09UlJ~R;m7HSjF(4VG_d~1`Y;BhBz-r20t59215e_hQELQ zF#O;6pW%Pse}@0r{~7*!{Ac*D3JR(J40jmrFzjU5$soYW%utt~#gOLb%<%o;d4}(g zE;4MK+{W-f`xwK2j~xvERY9h&Vz|RFiGhuYiJ_}Fiyax-X23Ny&?vNH&Aurdg8vN1e={D|Svf%OdMS4?ME+EU8U zSDMA}KU|gJe<%Y(go`zUo3S>7v!NzKypIcm04F=cuP=`nKHomhaBS%`hNH`7F#O-) z!SH{TD#QO(3=DTBF)+-oFJ`Dni)AQ@^kr}}(q#De?=QovlRFvy|LDHQ1W50P*-3u zQw1R8>_m_<4IX7;308aBy%iC<^m3Nbzzp z$ntYC2ywA9urV<*+`V&~;pow$41NDsGi3koXYlx6%%J)|oBnsSm17cX99*!ceuL*M@^4B7vWF?jsn!JrBX91sQt5F-PFo{Bs}U3x4-n4<;5 zn*Au-UC;oraC3{M|EV7PbZ zHpA^(w;1-$?q%4%VJ*Y94Qm;Wub#oMeR>N+Q*szXS9&-@d1ewraeNpw3{tM znwK4ejhZZjt+q0Qp^`L%lB76;prD}WftM*VcMhql>{ogqC@6U1pnZPi8Xt3W_45Y$ zR|FOA@>ChJ@(CE3n;%j>DIz3W?NGU0<1ml0iMja&Awk8XH3b!0+BJ8W9x*XD*Ao#lm>c4{3nH`f+)?rd@06YF-*Z|XzXNdv1%*ET{QKwE z_fJ9)E-DEwBq$(2<*Wh&1A_xnIokj#j0l(sDyKjgR33s#Dpm#t1~vu;26jdU1`b9> z22Mss1}-K>25x31Fy>=nVh~_uVi00uVi00yW)R_EVG!kHWf0|JV-V$LXAtAzV36SD zWRT$JVvyqJW{?)(W{?%&2A65_f;Q7auN*s@)8V⁣Wm(nwjB!B9nx!330_)D##j zG!z*uHI*1FwUikwv{f0*wAC0)wbdC+v^5yabu<{PbTt@k^)(pm4Kx|-4Ye5TjdU37 zjrADpP4pS;Obr?A%#0aqEle5gEX^70tt=QEtt}Xwtt}YbZOj?GZOj<_Y)lveY)u#f z?MxYh?adfM9L&LaCfv!AA=25BA@q#SHVCOBoimmNP7Ft7KTx zUd6DqvzlRfS1rSe?plVGJ#`GLdK(zl^ffZ9?QddOH>s6jYrEDk+}yc};nvQT40m>| zWVpX)4a4ID>lvOM+05|r_*RBjC$}@aIlY77?b+Q7A1>}=_;~ps!>6l<7(QJ;#_;9l zafUCqPBMJCeTw1Bozo0o?w(=z`rsVH*GCr_zC6Cn@b%d>hA%H}F?@b?pW)-{#|$6d zJY#tG<|V_sx33xAy?w**{{1_K4!N@E{xQ%S|NsA=;Xf4IJ-xcIB;Lc?+S=M9 zzPNtj+EZ5^K&3#`g+>oGes(ry5Mbrtmr^(K$ZcA886*Y9tJS&L+1a@{*+B}J*`?If zjjY|`8}~!aSgFCw$;rdZ%g4jb0ahUemakfP7Nq-bjko}hfRKoYh_C=J4@7~uwY7VE z&B_b^|Np;REG;M`CabKhC@CVq$HNOzAmyFYxE3V+zgAgPLQz{+S4&A!M39G*1L6ke z+zZ!1hW}rsDJiREXlh~zQXnQI0JVUbc?DSdvW={)ilM!ugPonJfsUqE5u_e88mn@w>xgtHx z$KKZ5+(?BRY(w?`|M9{idclS5^EdC>y>)S4NvyA(iLsHP3O`uhH?1JwWjR}$6s&F`7B`M`;jhqukCOb>Q6H_}m+gvj&%|F0x( z7+E%P-sb%$P8`}cw<{ye#m+=mQ$_$Bi2VQm+v%7mRZN<@Y}cV9dpFJL%8djX@7%p(*__VOw7BqK9~WC)F^~g2{{L_C4NB>nFn#voWm~r{ zTQIewqNFe9gi7+ja23-W{8lEt);Kqc}j3i&mQeb*{?~Hj%w;#KD?exi`d$ui`-JWYF&d#g`j^F?5f!f0`2T-KXmW1%?4^6Je)#|Y;oXz_HqR_|RuSN+ z0w4^1xWp1$Si?S~&8-afHwURjWdl4#CdnEd~{&3;j7CEfG3?K^s8=h8_< z5%&68H3;ed|NpNEbnx|y%qZ>Z?kY_Wvo~;91=kE=T%O^esGy{0<`)|3Yv$YxaXCZ^ zO5R*s8*8Dh@0-=M?j}@-7>Z$l0jQl~z`($efZR?2b>6VJ3)C)wVNm;om64Hwosp4& z4Md~0Q9$h!ZYD+s9wtTxP=ADvnUMjGSr{4kS(q63S(zCG*q9jvIoKG4Iar`=7ZwI_ zP8J3UE>;E!ZdL|K9ySIk9(D#PUUmj)J`M(HK28Q1K2C63TwZ_++;>qB;$e^%;b)K) z5nzxJ5d_onqJj)cVuB3H;(`on5<(0blEMrcpf(H$ON%gQ%ZP#7F?#Zn47v(Z40?)E z4El=F42H@w48|(53??da;QoQ3nj(X~h6;m`hBAYxrZR(tmI{NVwhDu_wkm_2t}26r zo*ILbzB+@8krud5e zfwra$L3U;gp$_H@;f@vz5#aWVHAA$kHA9S>4MQBf{o=ro=F{H#qGNi>uG33TYfZOti^$U;@LTg_9T#FPX}4Wa)H<B;tqd=ZZ)13QVmrgDQ#%=6 zpW4On>hvClmuL1eygYY+;nju14DT)cvxrcW<6De0uko;p>M-4BtOJX87^#Im6HIuNZ#*e8ceT=X-`CzF^R@xAzN7 z$Znpz5$d3Irb?nBqLMPo>KYpAY6{X~B7D3Y2pbs8{Q`rso7Y@=2yxCvOI1lRX$5s% z17jn7U3E}X69xl2D+;9(vpG#yj*Mu z2Qg?}d;&50|NsAw6AV-oR5XmOY;A3yIoRMHU|{frhs^(e12q*jT@xD@ zH&+)YTd)pwRYh4zK|Ul47@A?`Uo}-zQPVK8a`gfM4>uPl8%tATeGN4QNfBPiKqNRC zu0l1Gt0}6f>zX)ug@%PkL`H;#dU?1yS%bAGNeLn}WJAjZPEAco zN{9&$@pg4`w6!!d)>RUPYhb?y*3hRUr>3TE;u;#0oRMEtRFt2amJ}80R#ewHYvJ-0 zOXqgCROY5chkCm^J3Bc!+8Rp1eK6_&|NoV$qLM0Rp(zD59dlM}+p}l;s`=gZ#p&^( z-XQne*;yLOf`$!1?g5P-{ZCR5k=A#K%`I=8wsiZ^Q>TyaUOBt1GB+vQ%gxc&+S1%a zR|;P12mb&6-&s~f!OSbUsIGJFssm@vojY@M+rsXef|MvPcPCp*GebRfF}Qoo|NsAQ zC@ZP%5}r}fGIim$qvvkjI(KT<(y4VtX)z(*F1D7YhPp}!_2U2k|JP8IH}Z(juWg^U zWZN;2`qR6XPHiYoj|=s7akMry)>RVYhQ$C#y@9&AsaHx#L+A8`TaTVOckbNDolB=S zlw>4?dwDoJTACVY$nwJ+1XAx}U}O`TUe?$-ZNaL&C(oQceRS*MsSRbhNim^b9&V1d z=EfQlywDf`xj)6q#w9Gbvaw_8>}A{bA3bq&@2Ume4duCMi80|JUOw*5cBYyVPzyle zQ|apA5s_Ej)X_bC{)%n8_wC)jYW~!gnv&e~l%)9Rh>#E;XLBWJh=Nk+eD|>Mn7r!N z_O7Wj7c5`3ZQIsW3#WHBR+Saz8f4(_wL-fYU%vh zQ@T4_8*9o7QbP2^z#$4wpZ_O^#ir#~HFk7%bx)fyck!wNCr=$avTysU6^rLh@9t=- zDar7Hs)wZi8xfI7`Q;65on772XU<)^?bw-fXHK6yvKwT_^zPQWvUDFkagh1Y{Pn*% zGAga8y0N`$+RQl%SL{B0?$)_;XHOs9w{6wZc~d&;OH*8Q!0rKs4k*W5kB^AXE2(bl zoHl#Gvh7FD-M(|@_N{Ygj_=_sbgoxXkV z{)78>Zl61QeBaikvpeewA}wS=SrX!UkOhxRA|sOWiW<6SFW$cI?46e&aR1J&vqyKX zoYPsI;jItKVMyiM|7$5xajAJ_Z8I0G+;{HY%m4rXe|q`g&bd>2R?ccIiLz0b;9${$ zy9Q*##<-ZI^up@4nTvLuxdT%F|No;qw@&R`(p8?|ZmKTEEx!Sz41qUBMJ1*cR5x@j z+IjZQqnDpPy}W<>?7n4PWl3I6rs~q_YY|Eztc_7&ajAJl)on9Z?mKht_U&6|_pO}W zP>>wvZe^ltvk{^QnY^0e85Wxa($YS2(UN70=X5ug=Ossa*qE6lptv2T>G1>)S9e!; z&#?H^y!@h~(xUvd#4tA-GwXgt$q!Qtp|4finCPqPn^?KJc}6BC#z%O$SXouTigAb< zRPuUXh^nHBy1Krpjk~9(t4(x2WT*gD6$&?@$HaSW-L^R~9KN9B6u{neY_znnSm z_r9{SG=IJF>E=6^xJ;Zf&HL2?S5LvzTYe%^+hmL#G;U7fVeI6w=1a2vFj0d;!Fofl zN`s1S# zyGYTc12$;^T$U6E_;!di^+)+ z_XJxWrirw22TJt12j1ygyhBhuQ`z%UnCjZT@Jpwhf>!1ndTjk*>+wYvzY=8J?QgzY zVt?z^nY;Hh&igEVdv5CM>HB{h{h3!g`TY0N+p`@VF3h-P6S~&_-`*W&-_AD~{Mf0$ zq1PzWKK<6USt~TSk^^-fi$17N%s6CV6eu%W#n(}5SwRS-fdm=1$X!5 zmJ~g`b9`C$OY_4TKXo*o?`5dzxv;3oT9*ASpX2VwEGCi+>l#8Os?;m*{z3gH}Wr|5lhgCHDr0FU<`W@e~{(O^S^z-b;n``^G-afr}yHxw# zZPD%w8Go*P+POboetiwI$v^Q&7Lg1e4qkRyap{TQ1537D*Gx(oHr$jlUHffDW`E!8 zvh+(mlYC}w{HS?Z^7-Ez7B=mdW*RL1uy5i;z7sPt`+7JPRV91bCb8Y`O+xfpSI+&|A~}DKq44D5w4|zYKIhKNetA>X*miqL+Vkm|{YIv{Gt`zp zf4Fz^hn<-}{;*6i%ZpN8oWlE6?G%S#RK8hfhgB4tKw#^A*76jEh~zl}oy-c!Y)_^i zUwkh7_%d_3+}{7?tERqszWVF0LIx{`@__ZL+qOIIwq9~l znBm)#D>jXmrkfcO^k>bCWUJ0gysD$KZg%Ta<+RMy!#aBTGnQUmyyf`wXJXeHdQy!U z=Q}H12y%0q$g+WjMc#2e^NX!&6MG+AHteWg6|y>|RwCSq>zzn46UT$&j&}BD#b3&$ zdbh4Td?aoA+Y1~Kc4ZHf)<*q}+8XwoVNQmNdQYB1gFNGPBQ}R#|1$yqP9(KGsMXCD=1s4cy$vp#p`)UU~DPfu`e-c@*L%|e;h{rxiS&$pj35@LEke{Q_t zW#jI&q^xyYI7-}o%j~7NLpml{*@Z9O(Gcd~vLHI;I!9;Nr^Y2yYu1`>Ec>|O!_8c4 z7N&Em*~{hsnXv3SfA#fO)(aaJxIO4zV9&O{M~~^R=`NWo5nG#9W~wl3sVnR?tt;hr zuyae0ziE@%m$z_^fpqloGsm8ZolyI;H}zWoq2Fd^jXl0fxpjq8J5s)zI2$gx`A$Gk zWQw9h@*Yp^Ij*4|t5i7?SOpe1e3+vU)T(%F&H1gz!tHqdRoIVbK7Z@Lq9e0^_vX^F zN8eXxeto<(OL?s&Z*Xt&cV|Yy28Ztpt{GUd)ydyEuCim|%%7^8H*8^hkk7Edy|JNm zb6E;!ij;#K^9J{Z9Fqj{ZY&-w}0NkU9YjYY=m9D_U>?e9rM|El$PevFUxC1#S8?bMh(eUl@KCTDb9~KuYto|UIo#xl^qT%-8 zGpksiB-<#Q6@QTIQ|st-o~uARC$UTEc$2{VT_N1F;*G9uJnHNmw{5mk!skDeX9ouT zHk$EC@LcGJ_ZvmhJ_FA%hu>Iu58+p)Sn6Oq||Z=FgkE z{D(2u{@#^ZQQFto>kRmsCNb#naci(`Uc*0$)olBA^}wg6)nxD21an%jKDe%ZQrz)# zf5;`ThN)_zmKm3Bh&jCduDjq^q=)leein}&*2OdGm^hQ?3L3K|l&dKj8!|pHO^`Rf zVLE|@fwS4+k1@AhpW`Y8Q;`DMuO=-u_AKS>mv|m-Px>I``{Q4sv7Mb}qr7lKq(Wws z2&3qdCReGQ3nWenv&CBJo#d!1N%uQ|P2xlpF=;*zzC^SKz+no%S^dzG+vlnh}H1k7hZWF z`Gkt&V#i4gUF#V7SUp;DmNRl)$l3bB-2J%KlwL*7+!f9jj4X9qZ!vT^Z(=yD5@2$m zU_$z`7v;VeUo)})7}wG(*czB_(m`uDCmrJ&}6irgaIFSB)y z^ZhW~62l_d&wN)^=wgo9LDrs{9cubpjXs`-mK`pJX7F1?y^{(TqYjFx>`S*vKb zWv?RRb3L0{!9Rbv8S)wqJf9#M;c@Xpi_*)L3vNDQOy2CxY>+N-tC;ijr1^h#A8u`E z%Hd})RTFmD%4^H8OLME-oQ9W&=CSNsFkgHb`-AV*s+^WW6O}oqcuk7b=n=7ARM5b& z@0Q&w9yeB3d2>15HCWjTV1g0>Uv_I+Vm@xnL!%*Wn1o7(fcmn*!gXFFqlbdE%@ z5sTu-Egc>Vy?!T;7x6Zz3+%pfZpY*YLhZU7cU>4)Jhxlu=9F)pdYo5M<(`B; zI5E#K|Bd5!w*xz?@6{S|EdKt`KfUCG zFpBxvbVmI(J5LgS^G4G|sR>h8?0b20L39##!fD0TCwbm?GN@b>5PZ`1OY(o)gofk& zTps|5DP&- zvN#?ncUbGN_^;}O|16(wHF9vg+Pa^cy_lz--;~{)MY{30%)C3ktGFNfFo~v0O*ndg z_nTvl=UK((CsUi|AyW1XE?^CK8ulYlBjB}ih@{p6U)PN zCZ(nV#%&yP_xPPz9ZxKn zds)A=UFF*M!*IfVrpl+wI3zr0pFh~|FI+Rj^Zn>ijDnr z?X$`UF^LsL^BNk1)pJj63eaIZ@IL&6l2H#M)2?N!4zoOIP4jnhi2L=sF3c}>7TyQW*~ z?GLnuP7U^cIekLF)99>yzv`UZGcE5P?pbzW)y!s%G>a9zKmIB4Z@tZN-qEdPn&Pxz z4MrjToBz9|g}zTd%zX9RK9;hxH6N@+|LBAZ6>%POaL@}|V4tu?oPWuo>APB^gxs`} zvRhkX4Ua1S=Qy!`w(XH7#_C&EpFeEATJ?GJf{%>pY!kOkeeLA5^~`teJ5dhbon1O+ zKH3zgp#Jboly7v$<5dxt_pQpjsN&pQBBZSOB{m@{=F4vHTCqfsp0$b_x->0{mkn1 z`<%{~Zr|=Je=oUMhk3KC(ZWXFmp=t3)cB^2IxD{Mli@x`*d zZA-~Xx$M%z9-9k0E}uMT-gVTU@9XQ6DO+btyoyMC&^1pZY`bXS_DRo5T0Bfj zeXpK&h@PTMc-WRDC?gOmP>yYq2im)y`JH}RHm_?5kNcVl*!U5%;yx@qU1Q>ULjogQx#zHU#}wue90xX%s9 za(uJ#!QaXW{_k2FUYt0|{(sB8jCl)gddF^H`%t=*?{6t@7DJ5BLD!y;YayIZG^V@? zZ<@yW#PZ*oYaA0({C1@)DMg<0dHUPp!o^uRaU3sZUcM%@=hAWQ4V4qEKTNgw%g4L# zOI%j#lxYv{NKDwU_w2?8udarlT|GVCaQ&V?wH?gU&wkBbKXZ?JY=B~d|LuBK%jCQS zBbNi8j>R6ol=b(#!i{4qebx*9nAEl~`#_d>_*5CjAG^Ms+bS#DmiY3uYH#v`gNs5w zmx}6Yi61`sH=ujgZhH>9?3($^+1HBy&6b~DH`iE$b?(;KgL+)sH@v&#uiAO7`e;=Q z8>`9!Q3cNHal5?^EEZJ|^zhr43-b-#R$ z9f6_ZZ}Yd_UKW-+;o7T!;3qq08J#$`Zp-vYpO0VXuzqCn)0nRt&Qi1Xy?5yC&&_&v z>Nk5X*POqbvAN&vR6TJUE>|%kL;{n4$ioOl25{N$Ir z-UrQJpPBJLixT_~ZYKmW9mRb!%E@+H{qSWtW%wv^NAh z2&%A4ysws9!BP|6AZ{?T<9ht>QOYulAy_TIu!M5aTZzt#7~Xa!$#ae!e>8*@xO3M(alvi`Vis zWgoZx*0$){iz|B?#&y2vDoEw}0NK_i@o1c3$zcA9j3C>O$Xe6u-;3Ex6$5 zZM&=n#vaFi$|wHc_;_^5wMwP~)jWR<)?XCU>vc}ZSi0g+rqa?CMUBo&xW&%BJ+dik zTEN=_4>mu$927s*^FhRc+zg#Vf8>P1;&(3Vni;n2ui{xz^DFPCF--b4-C^3ypq=`c zZ~k+zdad=Ov2K#T#{T2aybkQsW!S#H`On8bmLpTI88bYRy^>(GEhpmP)uND}%Ti+> zFlny)nf2SjqvP~Zp0(2?YvwtneQ#agYSSk4GQ{O(=9TWx2H#Trylr{kdg}<}hCSHw z{5?Z`Y?(@bY_XqwHSg{-zt1fUV>^0iHur&DfBh%e-vB;&7@Phyt?>$z#ro+7R=;y!7xyIaL(8(O1zEh$8x2k`UGZ2fpDA-o;fM1@S5M7* z9#)kk8e1rNspWIGai;C`1J7A1rcM9a?(eNtI4%OBAb`)|H% zmEbFo;@3Ol6x?F7)?uRsPmEx4YUHGDnKI$^@fR-aJbNM1Wzk25&vlZLM{IfLOa9x@ zcmIKsD1T*Utw52f?2p%{H*HwY{U@x?y>E3vZpS8t)7?1>6pFVteYFecnKZfbYWdCo z8=t8iJf2&%U0i{4J$0g%Sc9T`FFFv)bDEPMNW^Y3=*2J&rjSbWEj5d@>ukZ3bxmH8cbIp<|x+%Pe z{>UjTdc>N*bxd)$yrgr_;sdkiaGVO&&$2gWtet*$%G`6G-p5H6Y?l4AcbnQ3sU?3G zh%FEc+@|ef^NDMt$hY55HBS5H-B#GYN0~8!(VqE%>4xcxD=q%$8zMWIK!Scu>`BF$FiB8{@A%~ z;#}4YHj!%98QbJ8ZQ(f7VHN<+aM`l0^>7M5>!jPuS*MFH=?UnsuJ}gl^4)rger!zB1MewiXl> z9#mK8nJbzxzdh>AjCGTKbFo%<+B(Yy>l{38wcNisY-Y%kRMV)igRzlbGH1TBGrf0U z-?AVfJ9p*ZYgLn_<^}wYzTbTC#M3|QW%fG~MfYsx?z$xA=h$;uXx9uy-GI|QJ@20< zpZuD9{%K3%&HtSx%Z`br2w87<&{YvP{~-UIZ=Y9*3pcEek68L`tKc*9qkE3cTsH6N zo&H}n_K#0`J8(V_s(5T`X7nZX?jND(W=W1c?E6k~En8^K{&0)gC!UONOyY@>=l@+~ zSbaUG*!K60ZuX_GC1XBnHTFNgvUR@mMux{fYqy`%ZVs!JTvH@_=ItioS22QH<+m&i zNOOE@)v#pwt2`U=BkPV#5f`w?c)Cw?TMxV2KKJ1J%y|(Ve^<}tT(NDV=*E+a>y3W8 zL@PLZsb28g+}d*DYs;g?gf*@+C$+54wma`@o`2Td+$bWIBO*sJ-~0NcYbm?$Ix=m^ zeY0u7KZkF@9tU*y{M(=P&rdnDaW~&dKB0uGTzqfp3J!-I>1&M$$?dUaP`H0k>ZPE9 zZj*tI?!lc3u`bsGk2GFgzsYuCa&5^+H-V2^CiKndxPN`)p*sCyDgWC4Z(lndD^*O` zonxE*Jb3y1mA7=64N?uSeg3X`DQ}&7@Ph-rg+Ib3@b6s6Ec`)rbF}uNYZ7;x6n@Q! zYUuuEb@hSXx(hSq{2W7HH#c52s298!zlJ|`gXR)P-RHkkw{5m7k8j_7 z-N#YtT4(me{8rKZ|GNu4qT@2tPi+Z5C_m?so~{G8nc9PZcU9l=&+47E;OKugyYY-0 zo0Xmp`x(*2C90F_b~>>tsBkivUFhkK_2&p${Yi>3Cf0aO{rg`CEa6Z@Vs9gT<3yt2+W;x*S zwrBUeX_s#mJc*gQdzDLZ&dZG|dqeadTzI`!XQuV~ny(o)1=YJ>Un{FzS#WzTdxro1 z51(t#U*GNP_8{}jTt4M5cP8u!KgS!t_~+FF*MB*BTRr$DJK_IkZAJy#3ybU<=f@XK z&V6@DgspQL$LBL=gVpYC*|V!ALF?R(z3~qeGDI)L?Oo=>d1;4pm_jbwnV&Nm?tM@3 z-M&6^`M0W)Pl+pBW8y-)WJCR|*X|0{m%H$sYb%FsoUdc$h946+eh429+wj;eSkQB+ zDeHmQ5_Y2kHKj&Pb%ifc4E3g`XEg3Ny`Ls@N>E@`$Q=E01!A@6WntddS34H{ADC=ocB| zik6F~gfga0pPZGtu75KBZ4(7=m7M(DUnXx}V8kr7qG9jOdZ~`d)xMdIyV+oRloB;Y+yI>^sIsem0F#v$9pw|E>wxHT2=r4oR;mn zQ@YtZl(oWxs~l&uF*u!T;*y+l$!v>#!%}4p+sKahzh57y-TB7j)`@iuMn?m_o^A{& zuE{<Xee5MiUB) z_K7ye1V^u}ny<NwdB467!Iy+gef_8eg+dO%SPkW)da-xb#TNGKG@NAS1Q#pI#TA z?wYa8$8+uOkCUdZ%XnzDaMzDZ)3>tE<$v_^yq#bH$Gi0Nhj*%F`pjLm#{<6jg_w9M-a5aQ+b{60${e#pduIC9OKjiL@i4MJ z&++rB@S9#{alvcfl!!SwJiIg6zVcx9={?^jFkZC!yQlx>`MYL+nl>x^PxWVcaR2MK zKj92_s*5AjRc(7yGQy69SxB^R`Vx_4y|(FOt90jW^*qhV+y=hU%;gIenbX*BaWH!~ z|4g`V|5+?9=KAC<*7|q5b&L!S{%p9NcrqsI?K0hh4_dD~zrC;g^T+MBeB+({>-AS{ zOPePv8Lt;LH#J&%=Ju=C=B(W+o|~L=blb7(b9#JaHZBf!fBWQ*r}h4_bL-@GpU<0H zx!Sh$`0ckhtLJYikE=L!@Arq_Z(5JrrQHAZqyL8c{hx`yzb&u3WB2dg{4M@_-Y!b%KB@oyzYTYHy>+bqa)r6<@srxJzdvlt-rl%dR{B|Q zchMKV-Fphp>B(AVi(fx~2KWF%`xh(N*&)jIs+;qV&ONAMJO`AKzHp}`lPtEGh9M4U& zFCSad%ksy-^~eCf(i`%of6Bg9+E$hS z43!>!tJb@DyOD!M_@qOEr{0P^$LG0kcwes`$-7V9zJ1*rmZmx}$9nD^Guht?{&;7~ zP{+FEE_chZ4EhC3JjaBgZk`t|dYr7Jdi9O^9Y7ZBWG zzwr0wet`==ei^bzo6ccy*R5x;tC!!o`RU>nFUtj96t8CZbxM-`7fXX3-+pVh1^s@C zFJ6d+KiuJR`}XGrZ#SE*RQ@oTr>5!MGS{f{dg``09|Hf~xwGcI{Jp4T?|%O?awpZE zMQrw1+`-V#!f|2-Z}0g|mCTi~r6Le{;`qJ;oQ^ z?(Y_y;c|2jT7Gbsnz-MD)y|uaFO&Q-vD37uo#&TOFRAF>T3};bkh<6hR z-~NuVcVB;p1=qWl7%sMek57MC?V6U#IQP!!SCJRLTwe21?nQ1vzul3I*X>H%Ys(D8$MaVlu zdVzu9n?ASY<~M{GELa2?UHA9iJatR#*^BQyO?vNA8Iz9R;mZC!*k$k_ z`CIG$;3hl$1?LT3T=is-+cfLQ^&iui_mx=k)CtIF{m{^Fx-;wc-F{!4_&KxeZyj|? z6Ef-%^zmn`StGc@Z-G}Ne-{#>Hu%iGXv-z;QV?2794y<^Ii(E4HMQE`Li!HtJ&_n!TG z0;Ztwy>O8JzG}ilET{Tvy-N{@a$B%@Jy0H+|g}=Velaxw*T{_ndCh$AMkq6 zzn=StH%pz@gVnPcd)3XGU#fUq5qog;?iJ71ks>u$|2eBN#PZ)$UClO0DlXnK##|-( z(#)uz-#reQh*%^CXHR()cz`*epg)p*Qqxgsrt8a0L_2RR3K4WXro*P|+`37MCH@Yd zP{Whn3H#$!Pdg=P3&nVTo_npIH=|bM=Y#7kHCO*dCoyvTt7ofZdEz%=vcl@N^{g4q zA-wA??oF5=eQ(>P+rKKeS8Nk6**`_)<=a3R_KmZ*Kc8op=O{Qs>Bwxo!fJ~n&!QJz zZ)}ZI_Itlyxwh*1{^RUVEkEt(d)ofX{J&!U#>2-gx4c{wWccBl^Q@8s^%8vIx9>9+ zi9QKEY`oz9_a~y2yz51e{rmR%8tXT&hMhG(_2bvI6~A8<`TMDbU*CfnR%K$HMe5{Mnk{*WN8l zU@N%yzrJyeB-=La^NKg7JH{E+Oc$B*pZk;T;g;B`->sCs`7r!_b^3mtVdmLbqp6R& z11GI{u(o64@q34_>GK*HR+#X5XvZl|7G$}1c8-^~M*2hv;nka8ExOAx`P*0747pEM zcFB9%|0r%Y=Da4u^y9|rV`3Y1)JmZQl}COMQE#2d(AarxmWe z-tSaiy!_JTDep28g8X%o7~+%56c^TPR5=Pzwf^MO75py zyF8T6FFgI~{cHxSF75lj-zS#7S?js>AG^b$x14dsDapI08pMaI8gjAEFX>Ns-Z*<3 zhtpPuUe4y*4|aI$JP;Rk?A&p)7pp`*IPW~c6nlI-Z+6~>ZVkl@*-nn8zG+5DGj{A# zzv(O3aQoZ0)B1V&;`!ML7JDa(IQ1#2+=yXj+CTH0^#sqY4sDN0gnT_G8ct#~ zSE?2-xN7v}=b}Hi8*}#_KgzfM?v*x2ky=arY4;(^yz0OSmb?w#?teVxR)w3_4MJb!cB=H2gP`D-na7PKI3B#+ zy8qm+XUBaaem>UlvzKhSD57c7yCRzN99K%EYF{^NpMdT3hq*U|+V-Gc14t|hYP-}dS{I=bh&`yOA!vwZP>)=&F+Cl`d) zKMynM+hB0hC}z>)m@`U8W?L#G@;q{m`Iqw1s{GThdfUu&ZHYf~=4ZXD4zBn4&(r%+ zpGQvQ|D>OHg!i^~)v;%p?vmiSwc=%jagF!4A3rwjIWGHVtpdY-VeJE-6~rC<{n% z%GA^+)qho=sXo@ux?cZ2I0X}ybv?0FkmW(Zramgr!uYB((J zcVXGhv-|G!?z_4FXS2LT8>2j1ETg-5YeE(?pQHQsCWqg;>^1+oDw(&Jt>!qu`~$&pZ7AppY65r^y5U`^Db4(t`xAw zWHWtv&GD<;^@HKJiU|TGjvT+{J(JE?W&HIw;%}m)U82OFO2M2UU#V?eHL{*7y01Jb zj@po6_1*3GYvawr5A1GmZTXe|Hbd#gEUh>($6U9D_(}WYr#+uIA^VeMuk!Ct?b~ND z{4W>qxl+U;W!;t`d&AHr$x7*m%%p|a0;iu>kX$LbMrrDd3%_~g|0MQ)i#@STvvzya zT0PgNo8GLiSRX(A{C6Ko?z~<5Kc?k!h%~h?P`Z%8>fQGLuPDzQeyO*5 z@jKWte(LA>e_EvCad$lPPu~gkjGy#f?UH{?QrM%<{LfmiqFv~Z_{Ms^CyU$OCw2cn z#{I82_`Ttrl@H%P?7F6TI8W&RL_Z&?07gTe~DWM6=TGa*y28IUkTGhjLiKiJDJo2GyRrhjkNX-lr`}O|gneTThPgNJo ztuorImHlGYu3ft`7OY;Fn|qtlx?>q*=7q2W?#v&ZIK|vi#@wl&3$WKfn92IMVFbv^B>6rmc~w-J&7>V&~KQw_mQUNzLE$?e^}) zz2*JW%;vr4FI%Uhvvy72G3%YxduRI0y1wko&wq*k4@{2wzuL$lvd`?ic=Olt@^ZS=eNciA3|{Yw6qzN=JzOPV&dUp=x)(f@L==d88QQuobRcGGFvbneJN zJuz3$^HZ01?b#C`_VjUO*uID{r|K@WBP-eZ})2F}w z-OGMF``q8EzZ?7y6t4Y}D1Tt?`iC1f7kqnq?$FEr@?+`sX1?tImHfN?(>#Cj>UqYi z9Lh_LyyV&W$j*DRiLdak*(o*qOCrN8?mTn7F8DC^=Gz^cE|=c*J6~G8x8Mf`1ym&5Zb|-az>z=FhpRMZMX-LcwdabzOxJ(k7hYe!et+FobE)@tcUk`JDW3fDuw8QbZaaBN1xY>Is>B_8{(jy2 z@zejw=_kzvUd~h6AXRyP$vsuhyK3_~dU%tnmFgZl*6ez}vGbKD(=O{n48Nvw3LBp< zR?C|w-ovEwWZeq^sR!Ex+D?3GiVqi9yky(wDR$jvcEbOS%^T)k5;S}w>^ZBxVa9{< zvNrD1sh0ni{*B)MQJVj^%DtDrYJwO~e!4X|KhpN^lkZ=T?VYb_byQw)QLN&trxs6n zIlT2hsXW}r_R-ec(wM=+vE|&0r9KB1DNo`#G_6(ncEba?3HOWV>Xpq;h%Z}jT@d@` zp2NJwzh$dp8?R65(%_Wzs)=KtA8PdY$tjf|>y*w5SNWJ9ovI}NXW}C3KR2hI)xK{Y zvEz%_@f)9i?zNMbP>8&>=hvNI?=IWV;{W6?9{F*hj^?x&Yd+)^8YG-Hh0mmS0z% zy&(TggD#KrH%4QAW5F4Xl8Wjj3T_$)Y9w#YJ;IZ^Z_3oW{9naR`LEBh|FwAU*6#B= z&nLT8tl%+_vaia0`bF6O!b`q7CH?O2lO?`#774G24Z5N7pn;p8>o_qx zFSYjc+Ps|ITT5?AFVD!|{q=0{pF3K%+jlwEFTZY>tf9qV>(<3`hUEyu+a07k&iRpv}`#s-MaE)?rHCCd(((}wd;S| z1akBDFitC~b4$M$_0|?R>gbH*JyYuQQ4QiHS2Wq)rU=&R00jT+QGj?lgIAW3p~*V)Yk^4hNqzW=uS; zH&h)M4H#bhpN1Z|?Jz zUjNEI;qkiZL#n$@-kcPPgyuT`FIJKn4OIs|S7dg6{h{=_wDpH*{yq0zehu;n+^&4hbRpy2vL8og zKDBF^%E#8w>ZjG9I_b+7CBwr?f@~TaoC@}D+T~iX{)l=f`$fk)Z|8RBJ^!M*z51Ea zTkUrydFSKGtkXZtvF$(Jzhmy*?jsW7FZVuwXprOa+E3vYx!7HD~A zAT>dKLb=7X1Md_(>)K~5I=n7@yDQUH&KaNN{CFP;r^sK}sV@Dz>X!DuV+k+MxW7yh zRrl_0zTRng;Mk8}pMGZFp0DxgU%60ApY>e3!?pZY`>rcCUG98UCK7aXMUr?PgR{3z z{*K&+B(r~Rr+e589!z8StFrYq%irkb9*_JRKZxIX_p;mNPI2CE6T`#rwpo`}&3xDY zvdm?-|894d_dDMjZz+HA@!XBv+{@Wz=9%*oQ*PX_@}6Hk>uu}Q&=s>y-R8A!T*aQ& zvT#@XgddY0Gt1bz$T%^34`*n}){4aM-Qn4yrBG|ElQ_xm*nMX$YyDvA- zyiIt2Y1wnLmu1=Js%(pXN3-7j*1;*(>FGD|mcZ`B+{qKtd?gMFFvfLM^3>dEsQnhF za%#pwg|-02?v%kBovLJ)4e({d;Bgjz2?OW$nGS%^$XG-yXbu@3fs~pPtF>H`;#j^j$ag!t5u@*FP`0 z?Ri(qcDtF^-B&@6u7w+Y;(Jln`_HX9b#9({bxOdxUAhvJtha>P>KH1mF?8`S=JAF|*e$Tt~cU96K!#P6CBDRH_Z-}SAY`?$% zK;3_(-3{3;96x?LC$T9#k`gR7R%B67(qEpJJY#og)4fuqLtW?ZD9w!L-qOCLl>JL_ zyXU!naS!qSllk!rjvuxu>l?PDXOfgteyCcuuU<=2o-`U<^3_GQyzK@UVj|vr=9B zq0P+;ceHYUxO!4LB1+|OL4=TVMN^7@T;t;B9+RwRM*DHAy_!%L_OSn4iQ?YiOYbhe zuKKdV`OGE7%(sG8CDmJg&+BXa=eGO4dYT093kk88)-NT(Iuttu!^5uMP<4GGnD@G& zZ~9|q8JSls$_5q8UuqX_VyS6pQC-=%Yu%kCvvz129^A5OvQA<83!8r%yv6Sq#(c5- zVdQ&}O2yZ`cAjnL`5bGQ?jEI__4vyynZ2by9pRjgIVZ*7lr`W6LVP#IVCx)BZK~gK4-iBe!5@#+TTeH+5Z2RZEp7a&fc9@C{~y+Q5jZoaD)4ILFIPd z12c_}{CHVu#`ZZ#V1crFP!iLo$R@A56+S(I0w&@UJQh`4^z|rJ(fR1=^QStZaO3@& zxzqjk2ka z;cn(Ewv-0n2QjYY%f9??nX$m>>A{;S`gIp}p1WE7Z=(B}>a>RJ>HA6w4Pd$OOAdXJQ^m#-CKY z_S=?OrBY#!p_8uv$*hpj!wJSOb6@-xJ{{7vkp1elw_UP_z} z*2XGU1x`}OMXe%uO6RbgbcvcE!?;mMwJy%+P5I5guRol8%q7bbU?t{pvNf}4kGgAM zf7HbHl@+!XmyS+6mzk`VVpcy=nGg*TJqsEK7t1v@6s#_q;E= z5ORFSyH>_Y%kM0fKN+~g(SqZj>)Y5?ueok@e9N7KJ^Pyy=G8CwQ!p{F@$4kk{Q{*7 zDJNPTjlRlRY-*d_x??66r|?2S-LCw@xjF~ScP%ImJP@w=u=R&qNtMmg-zvszW81%V-PhkzisE(h#!Zj!Gc$)zSTB4< z=FO}+|I=r?Y>sd|S}dY_;TA{Lku`iWo3owId3|m_$z^4B{=qtlgy*+*hyPCfUhPq@ zZ#exLN0LDKq1HmSi9xg5wdT3}jn~`MB7R|_M8nkJgTW8HV)vMzW;$4Cw0D=sqJ~)Q zRs}ZcGcF2NzoZ2GW-q<5lUY*n;&aCrwN-ET-TRcB#_>zW;Gj#H>vO4tJEiUiUfg(P zldEos%%QLA0vDg{?tIZ2*y*t7)s1VOKWa7z^2~O7E$3tzIcwWH#mln)Q{I&^eoqpv z_;aY`TEI6}RkSwteMY4m?ZhwJyc8A9`$9+jlR&f#tH4o20YR%ojPI4TLWg zF6)#i?yReyG;89FqLmRVr=Bnn`QCMXX@!gPF;UO$8e)^{Zht;wbM(EF=O^Kk&xXH# z%Ue8fRX@zozyC+{K8w@V@_wguj{aP;inHB7QB0vGtmCTQowYp^BHzR>*l;*AsI$-V zY0Y{zv5rZrK0fZ8DYRbnz?Rjko(Z3fXJ?uy&cMPTyrk73eX@rD$2ONoYa2p(bPSk< z?FCg5OeaYk;l3#OD?3qdU;IP6ke(H@HNVUHyf6*cb3ATnC}i>}HtGKfwqO;hj#F1( zKTMFAzC)CeQNVbmkto{(@yoFbTh5b*Y)z2}!zCK@m@{@XT z*&6Xn$2RnDb}jJOuXDiq)UuWvhwdi5?dCfZ`RVqL9iD58^&$@C_*8cG99-uS@oHh( zx6E{vUdhA7zAozGH&a=zODaLUy-FDqgqn(926aA+FL>AR~1t z;^4zH-qmtP4(#jxcK*V@JGO!M?zy<&yg!`mVe!uRVCbvD=#}Tv53grBWuo&_v|Ps5!i#-TxB*l4 z$Epa%Dn&C*w|`9MvrcYQsrF@(6!KuqlzYW)94g!y=D2%F*dh64Y&jdJmNb8Rzx=QI zyJ;V`-J8Iu7gu>*wdnS~i5zDGr=P4lsy?}1B>mf|2`48j__;gn&-x*lp>aGrNX656 zLx*yrQ&m&GRisaWOwij;R*5ScM8ZVxStlM|b-bDp|> zaNz`o?7c=z#a~uyi*DwqOk48&!qmgR1Lv{13Ml^2eb;Hr zDo0g=w>I_bxHtKfaS8Cdw0qC#+AgBi?tHai)z)UIk5)?S47Y#iws-h_{?fmlk5d|s zd>7w0lf&u=h*t!R@x?5J(8^2v&Qr^kbZgk+O`@G#@Y5)FuYME(1n9wdYXGc%x`L`_Q+{>#1dV)8eC-6%*IBlPD z(anz~#>{}>PTcxD@g?h-Hf9BxD##_VZS{)2rnEfVGbLMIX#GE{+v1^zGa5J+tlukD zKGmaMbwb{R`zyYDF82Dv_3`J*3Z@5IE9Bfdk40Tk4SADm!C1Fdv0CXxnjC|0?Eh;m zO4A}fz3*V$z_w+g`j21MwanKVLxa=hYYUeLm)Xaa{dO+Up0C@I@mYzb)MCNGsrIWb zPx!8X9*$$)xFC zI&y23*w&*vj$W%%(tVn=o7LHW%^ywopL-;$dkvcel@M) zT<0A!2~M0A)#GU(?6ptIdFsn~x0k=4=ksl+#*~@8p%V>u?l744X_s=M{BLXvb;#7j@=xE2ur5)wIui?tD(Ic774R?{Rw@ zmplx}Kjr%%K-M>bsdA@^U#dpSkNXxIQzvf{T&`wu{9$Zz{>LnzuG)u5%mPQ0m6{q1 za-1hD4_xFg%KBl>&p&bh!!)H7#4a_gI3*J|qv`HpudB1B&tU6Q&D_({EzB#>IMMm{ zjE(!3UQV8Lf2voz`-jvm|G&)L_ppr3!t~7&*H4Z`cOw#&uP`2pnlo0 z-+K!j-pw+O7Q5njwQpDDn!}%bk{LA~I~AwIpW44Q^w!3O%JGL?m1b@*Gj994TtHuJ z)0By+nPL)pr*+TIV`q6JSKDWwpVA6XLO{tZV0?I*4#o6v(SrF5uw@TqH@9WQA z%C#rh9_`l3|Dd-{-ktsD-Y$VGd4rGHnlBH%4SCz)mCIr%q-%A9C6Qy3#=4f>{VMXW zOct=c>tIY1Ot5@E_kPv4``>;)-mG!$j9~lu?tp!JiD;-#OEPP>c>hzwotF?r=tW}8ut5na`={~5uX!uORkHD(z|bmlg=I310$O7j)SIe9_s#xnsjbFm&Sr;PufFsZp_qm5g`SS(Z5Ma4IN$+_^GF;&SeL(RjSi)lqvp&*xx z%$twJ+spTsW!k@e$H1DlulD()&Gj_~68{eRt)K4Db=pVRdQ0$AQO!5LmT$Cm@70=1 zxJ+F?WBTH}cq``}CN6s?Dg2$et4K`lth3-SyNZ?DeY!pWMq@71?&c{^>LI8%1I{Mvc!Wt;>< z`?tAU_W#Y3P_VE4c(dL_qHmsahxpH$a~~&jO(@b8Pml@f{G|5ofh5yE@f{n5nS~E9 zO#9=(JW=6%a&v_c!^xKWw{>Zk3^(-DQmduI>h-z|>F(}>WB@n8-ks-0+ zWs1@<1%W81nZfmAC!@V(TRex40%fB~Ko%uWmCh;A;kui1R zkKcYX9RpTmhI*)ea}40*U~A-LO6Iy1@pRH0+#++5w*0r`1>6mMb!jV{@^o~Pr4Zd>lk0Zy%MxAv*XFqWv`V_ zo?rAzVzv4i@0ANrZ#ibL`O3e`a;wxX!DPcvO1 zk8A6aUe7+fH+b@{GGnF&hN%WFqJn29B(Lg8zMy+#_7)e@wg=z+qnfmfJ$Myo_^HnS zVx1(IFr%~XUdVm(oA>#%JP#fCe(9~jW(C#%Gr!&6FU;n_c3S#>`b^R1OS|0{t$wrr z`lI~EPc|hVXG=K8%O|^|O*Zq3LvMZ3rMXXJz8G%T|H2i;o}wJ%pECxw)aGg32Tn{}m&O_$lwdBw(4wqoDUGelHxv9Dz33E*oxJhlI+tJyctb&LzH zL`v-lYgRLS`CM#8&}*h8m!`jFm~0a9EK}&^&lOS{!H>9~h$pV`O8jo<>@Zui@}9o< z^clPpGR`-oo>h4-7qfL&-sYlEVU@jGIa_{MeB64A@iK?1)>`Hji*HX5TNmB0q|&wO z=5vb#k$?BLn6(?STsd&$g3FbO3p;B2pFe3`+3-7!;YP*%*D8s17R)+p=G;`({Bunv z{kC=Q#mI-vM>oeB+*;G`zp2U6+x{L`#erH?=|10o9&wWnb-4D8o#vf&ueK1LbqP~w3ShE^0la?#aU)fE;SQB?R1)u`Md6; zmeT*YZs*%;5|=(dweS3eWnW#Au5heUa+=`A8~D1@qAm2&zq(uVZ6`5KdiO(f(yR5) z^zMgTHe7LJ>&@T2E=5QFZ~OJl$bjkhJGSRm0-D}u$CmlAu47W^g{a35w>}efX%SW4(Pei( zhnM?Li0-c&)mhSs`sa_ks_9B?J)ZL3QjOVQ+57v;{>t-LH|}V>{K`hQ;?f4jM;2~( zRqnJs`NC!9r z_;BCzAJhNXWr}O>@SSsfZQ>mV&d%?DBr8{$7O#BvS9PMo@|8wYiVn`_50ZcMsdUNt z#v>5mAK=xmWkQoY)l59VZuEpRjP} z?+UB1Y1=;jRZ?&}IA1aOYSGSB?B7My&ah;Ba`-m!YrWFdeLYNm+0Ui)O;63)H`T>& z>C>Cjwx!D7UsTuo?VIoNb6T}Y&F3CiOiRi4TySea{MEL6{qV#8HIE#Bb?8Dfx4Txd zj^kB{RHl!$K0lH#{@?b``mMapB*vOcKdY3E>2-j z_43AamMn$yTmIQ{n%1mwGTM2FZTi$!Ii{JCC$1hnW>(<0C|0~{~(s1SbK21m71c{_2_EE2IPhKPYmMKHO z(qod#vldPfNrk6#_e`zO<#Mf*h;Q}2>LFRlutMS0ly5gSK5WlXn$ZyI_{wC>n=1@g zs*+0o2LzrGI9<%8GTVIas{Q)DkV9&J^>Q1qinb-l&Y?6nu?3V1s_YJbdmCMGSX>q~Lu!mIoZ zfx@Q)s`OM$U+)iFoMXlKP%Y-2>D6ER9cnKBEsIyK-6)^WmY}_J;_T-H~eyRwM~PL(05~+}r!D8-?GBWnJEv`#NHsN7KQHs>Y$`K7XH6CSv)OVas#}@AG@Z zzIz?bKjbSHXI-hy`}&%&?-_?n>XQ||2J`MZVBBDp(ZnlY*s!z0uBldeoly&K(}oC- z42F$!TH2QHoENn;_Cmru#+rnNi1)KE$Ud&RDjTSFu2Erraq{kn8~4~}OitqVh!9IS z&!&7P;=+ZUu7`JRKk?+qGZ~i5m|G@{R=S0U_ZDAMKA>*G`}NPJppBn`GdDh)`r>k6 zi%9<3FK@$stiM~K7h`&5=QfeMryDrZpYq(B@bC%kDMaveHXp^kYCbU=f@t0JJkfVu1?l+RlOG69^sIy`t{ZC&kCJxJx&wVIQgsRT0Z$& z%300j`6Btj_unhN9D1?Lv^zJ`P37f1_a~j5_xF8zk(!!)QM>kcr23w3eZLkTVeGmX zF3gk9xUM#3!;OQj%Z{zER0;Wf=0*9{pJ6PE?&_~hht|kMBRxkZ@qQ*UA-TXIeK7_~!KJdn(h5tbSDBtyyjsRI zdzxa?V$J&oTLce^X8v?K-v6Rr`%=TZ`{IlJFRu$a$jqM*|LOCsBNro>G&V6y@gBd{ zYgYT~gmh5y`E#BbHiZvYUYq)P;uiL;7gsstH*@6~Gx(?cZJONkpwU}mfprbP-~*}h zhriTH_TKsw_HgpCi+i`Yw#IlJVPRECK5^69z(w!+*RyZ;MlLk|(iQM->Wsvb>o+!b zK0GaXj``!xskhU9S#W;wX<_`9_{Fzi_jysXg;DIOu~x5+o;tirf^m`8BDeZ_**iP0 zUC3UeVRG&Ny3~ekFKq4peKRfmw8FJPIK5&1(!Yz_|4x)#@Ir6TT%8}!cbHnctb4>f zNy)$YZ@~WiOY%+!XNIsvHoIpzPrZDb%U0ENPWFoHRSa8lY^J!)@>O`gx^G$a$2ARJ zJpSu0&VIA=5cAST0E2iDwvi9_2r*pNdx8DwT zxp6tk+qF4s(bu1oS6J;RDim13rx-79c<`;>to-vnC0&;iZXCPL9>lCDv4fG-iQ~tO z!}~urefdB6@V^s07bN+%ZQAo^ZssKQY4Y<;J!)pePd>J-;TB^q_jI%D0x^S&6IZ_d z8*?npvvGx+_*w>2UaR@(M?RcfrF-Z07U4aHN`4*r7xtgOv_Jp*cb0#OAAi^EK7Vw6 z*v{zo2N$-KCg;A3=V07EaqqIP;>!99VhdY~gzQ&0%g^zun-o!?VYKOe9ovDsdNmq6 z2jVT>unKW#h3`(CS8t*2b)a|knx!|-uWSssJm2L)-p;zcO=U`!G3#`ny;ShyIiZr@ z?dDj*CuZsQYRSs0KaO6|58L+Q--#Eq&9ctC)&3%!_}%jsC(4#8OyBhCu#C`FeWTb$~p7j z;tlH=hP!K4e2)8A+H&6Z-Q?Mw!MC~g>xtB;O*A}VTHWz5Elnrud*8Ietpb|K7merg z^i5d*^Ia#4T*xa^OK1LNlhs0A`Y^u!U9Iyprry8*yTgsY(JFsczUtgP!V#0jd+2>( zQif6R+}mQ^EqC?^?hjk6@37zX`y%6sTegSIpAq#wdDhFdHqXwKD%UA-g<4)*_V!uW zpUS3R^1?2LE}y<0jyuq}VD|d|uhOj_xBj1Ga#o1n$~UKP{l7~$mR^i8SJ6CO@`Y27 zDJaZ`l~pbBiYyOX;Yz8AVKTEeElj)q{9&-_;l`IKPi1aT?|l3z%9(Bb)2Pc)QVx!1 z{&q^#Ixt@RSoKv`qUq|sFHe0_pDmEBUZJ?>a-IMTI*7dRqSzD?|12p_wUY^^(CybQ3745YZF6_*Z!NTeMwqO`SDW89oJ@M zzJ9f7f_MH*2CHf7C#;-nqsP;x;BnvkP{2x^+v1tqqwI4ZK3jKh(c@*6x_cRR8Z^$G zALYJLQfk9Ib(R(1Tn?!RS1_gUzu^`(nZvx|=cJrFtJhzvTdaI|4{!a4jrMZ7CmjsK zGPO?J)VUz3_CV4r@QljDo36e}mqRVS$liMHbIaf4PxkBho#roidFDDs*6;9b|GQL! zeQM7%*%glai_3OyPFZ(yGcSW`)z%3v_0yLY&fUEH&E9*w?FSU!_1#G*5M9&cAGt0 zpMH>avi`(s&s%D%RHCG|uD!|H{$SH9jhm~ooWwUSY3|?pFTd`g^Z%yF76%q;8#v7S zZY%g@wb1;uNqpBDUDGbknwYlh?$N9J%4%aS{+|JZf&zE0#e{biuqLm-nl?7fUG_85CPpaYfB*q@A# z{@ySMzJBl7tYwZID#a%kzb&-)WezYiTXMrSH#=(mfu0k^FPHv)&hpiMmgBz_lP7I( z4j0)d(a&~z@qGPDpLmWf^vK&guR1b&?P=AAw<-_H?tZ3!o@>$X8KMWm|7v|T+9;c= za`fn;C$k)Fa&~E77o4CFq`vE#gJ9b0EWL;sZL{t7dKjL6rL0uUd1D_-lf#RXYXzid z_}zMTu5)SEVF!;+u49K~cl@4h+n^;zU^i zR;VoyX$rAB!(cT{Y(ukH5ktSFC%gQvG^en?AD{6*jr_Sgpfq^(d7buYS@YJoGVL~r zSt#C^HPbW8rQG|zF}R-HtiB)Qowp32$h96BpBXuIf<&=qd8vJ$wqOL)ALp3P9Eq{ZO!JL^{c z)804>=`HW{-tbs0j#tQ(`Q$pS@nS z`hP2PaTDLQ^a)u8nwPV=Q_>7>t>-$br6gLc#xl3~L z_O>iNC#vB!wdO64-qM3Ao=bD?v`VsGRE#P)_wozxTE8S_?(ddf7KJwR{eLsvP`L7p z-;>qcaqrf-cEwZo-OFCs<;QZ-XF+T6T(Wm*KS(WSzhioyVV!Zz&s%vn|9`eSDU>~PQn*^o=l%mdA+LW-FyJX>`MfQAhU%Ux zxBg42W#lj2xg}Tcq1?eex)vLsbn#B)t^BsaZ2u;q>~EiMr}3-(IAT=idt2(+QlC95 zbw38XPu#yk#O~r3-P?{)kxP6Um>u0F>E|wHTO@ctjk&W{iKpQwmw{U)!xEwYcF+II zZ2hosN5zr&9h1fHA7fZ&d{3@~-QN93_I|tD{A;#F3EG!__p;Sk zh$fzy=%SI(s9f1Ev3%=+`lq4av-6p5t53ebsS_T<_|X5#kKd;1*M+Pu{oPz@&3xe% z@3sB*@4xP6nsi*OcnzDy%*m}{xzbD;i|;RD3u^woh=(U(`XuhdiblEmr>~&dlVrAdV1+7Yf(5 z6*Sk+^Kzlh`b(aRJ~(XIbs~+Q<70OD{LTO0r6l@&n7%`KSL5^R2YDR2 z7TDkWb#l{-{h|>TGrgEwE1GAnV)B)!+$}c2h~o|8)E72R3_DtDruJm~*zC*8*{hr> z_KMf%|Ls68-`3Qpx9(K+u;t3_e|z%#Dz!6VD|A^_UwB{f?*E$e`aB9>IXtImZ+P{f zjIH6E3&+|=xzh_IjN>Ig9#m83`WvC~=Z8~y`{Y9#`H~r~%j(r{c+>aK$TKxfIdnLq%mZYUEST-@&WX+Mgy3Zb5aTS)$4cs;JSZ{~m zt(bTW*-tB7tW9?4OUvuW% zZg1uPul1eHdEv9`3wa!}Eh6sxdl|R9eCwlkvoo`~UraU7HAt~+H1IU(sXJS8#d{&| z!WW7tSbN2!BurnYcsBM zUAph$^I+-Y8?MLXjx%M={-*nt>)YR4aUsmwR!2~efxL3{+)bh-m>HAe+qu;ye+QVQgl&&P288qz20@1kGtaU zRb5#>ch|e^=j)Sm(Z~G~%{^dvg58?BFCDp%uIsI?)`F+RlSNt>osUBakzW&qN z{WtBuKFa@ie$U(OzqVhfd-nGI_w#!m|NVEf{EykLzq$WzPS4o?xctN4?HBIf`}FPp ztNC>wYya5o`Oo=lj`aDx$K>dH?ykeb4Rq+F#v&7yH*ttbcOM`tQ@@ai7H3@4Xwp z@ANwU zuOt5Eu1DeK`oE|4cGngDW%_IPYwqdge+xLDKbZ2qZ(EJ!jSsH%XP?haoiEyLch=3$ zmwik16W#KR=W+Te6_cdfj<)yrez+*rH?92N(^uD2tt6-aJ=A5`n3vQLc!njrWYV=4 zDY-FfY&%|sZ(o1Ov-Zw>tM60nebxUxy1LysaGmX6z5h3i-+wdjNJwas*5A|KDPcB4 z>COE!{gw9jg?@ib-#>-3?yauAaPZ#K_P;~~|8(kq3D2u>RQOe?^y{bl{a5GhKH1d% z-|X@wz2k-d<&QR=J((``J{@&j$A4!i{1W=x=K9MvS;=Zf{fYh6$G?jy9s1t!;jZZX zsoi#`#XEjDE7i;|o6Zq$r|~HHzqk7IzTK}&r*J*EDxXug!1zsF{k*BJOPwVSl~zoi zpcpRgu)0p@rN*}GM>CG*Z)Ax0Cluv(gZaa?J>>@!BlF_&Zawqn+`p}PR*b=eSvT+0 z89Y#F%J})SIpO}gZ%2O;7qc%d?EUV58!wsfY=0UX_NsP~)qSx|UIoAFUM$(S zz&g(IZ~66Ya+%k*t)Ea5ud*Xth2yu+YQC^@=NB<}UirYnM?`^h_`3lRo#r}1I`5tBy9&;(oy>dd;jV=4n&R5p7 z|9*n|-x<*Z)}Fsyo4$YcH~!SI+1Pj@~Y+j)L%zP*$B1diQx0>WF&7~eF0oOR&EEzK8KUh`~Uer~eXj!!zmuP<%g zE*QAKp~O^hdPmLFyp0DXeBK;qJ=J?de8vr?*U#l@Cnn7O@^h+q#MU=L7t1?d{P^>B zGuQhiRoBYpAHQ$c5?T~5m=VAKgp2&zdCQYU7dcPmo$T#r#&?nqJy)nHznU(q zRI_nOi@fl`=-BzoWLy8Rd)4HK)Y_FVI&yug&YE|BR(=hh{`zaI(Yp@81d-lp=g$}& zd}Jd$!O_F2w3lOppU(!@Nhf>?URqu|Yj8%-{#%*aH)Zp(%{Lqb2f^T_!eC*-?(JHkSDv;!w9jk#1onqUt0W}aop<* ztJ1Fw@1?cPOJrN(l@IB=x5O{VZFzrQ&_4Z<`blPGyDz8b7nLtK)}Q|M^3$hZi|zOC z{GGA6_L0kN-0V}(xVK4k(w@+!`(N+Ac3C6Mb9T2lE2rLs)Vhfm z7+Urf$XY8^HGF$MGxLG(8-qReN9P~B#COR3(!N7&cS66p9DD8b<>IMIyLpUe9R9~| z$ZB6~FLrs~_(J95Q%52D9*+B=kHq<#e>D7?zkKhXgpT#u7WXQzzq-~MzK!vDy8QPI zoC&J@QyCRJ_Zn_9JQQj7GkKFV!(QzI-cCWY^LFQgxVvRqY~2m&<}HzS*Ti`o&4e}s8-m!PU72Jw|mR;hM$u<=AZYj z@u^U*vvmLS#nFg?&3^A|#)-9QOTD$1?@La8UbL0j)9$x~WYzSeat4b#m#jLyJ%PoU zc}LLn85R@HnHKGK*rV&loX8@Pw|n~hMESXYzjU8JQ7k?`t^V(opX#iy&fiP8K1sE1 z`PKK{Ek@l6Hj)dJC;d`9Xd^L!)1T+2;-Tp(Z&_cLDqH1F`nD(Ja30?)J z33YfF9J{uymD9}ce!|ubzL)l%?)fk!>Z8k_&rUyNS+dXBIu`t2^6RZv&5NIF-KYGR z-ugpZoaLS3*TbAj`LrItS{dK-mcnNF>}g8r>8z0Z*QBj)UoK81CI6dF#Y2VM^&;Fl3!no$Dw$wS1U;k=;O#4<@d1>qOx^Ms97XN+p+&|@E z^Z&(@z2?W;tmC(R^Xa4ey4lt9e;$3fc5eTho!9?IZND9T&fwKrO(*ZIJ^_;_pMLs3 z&aWzigVFKQ9Ph;?-&Qw&SMe3A`dKlbt1#%L+JkJ~X2&f?$IqIqzja~hw!Pj=ZfEbj zexm=d?Yj;Fy-B!qYY~fVNmEe`iH8|QdA^nyOm(p^pO?~xow_5(} zQJs6}efpA)gKOUUa7}vohi4;m^NaZO{Kr#W&PyM(U3~M;QF(ulUmq8Ce5*ZlQq=Lh zh3EHM^ImOJI=nlnqdJYD=A`!iZ}I;hWn^X>d(O_+?^<-kw9YB|>y@;J8jM>GHn0EE z{lUUKgNr%gTD{k*W=SQfbq}_R>^+qFs#H#3@313wCcYfA?gBCz*IAp3^T>I;8=Z2W(@^I*EmnXlZ16BG*i%kG~wYBM}KX`lRw#-9uhYzvqs zBrl8ovqOU6^|jaQYo0C6OPsrJ#ZTtS$g+UX`s&Lk=C3`g)^SlGaQ0a#|Fdx!l8aXM z8os))C@pY;fz&n2y*;bNzY5J^ST8QNaw+TG+tydA{LgmuJgkj7zg;8kFH>6nT!D{{ zJL3EJ(yraI`C-eJzd+L}yMZCryqoQZ&Wg4I$(@f=7}or~9{+P{{`cmu=fg@rzfBF} z`(%@zFfAx+>%O&%kG!^by!$Fd=slD1iQ<(;%~@4ETX^y_t3}t=D@t^BM*eDJN>bSN znWN`N+^u_uUu?@T^03(ccER##a>xB@YAiecXXi~@vGLue-$uUPW*mG`Wpg}sOqDy% zE-7sk7hn8_ry=xi)!XoYDc9HkdA=;eE-vM$RJ^^Gwo85S{>0l+X3CW{JAVC6G6_)D zTi}wU;=d%yEmJz~yeLm5p^YQdr` zo!XOqs=Ma~WHV%IN^<=)wo^>ueIet&M$r|rMbh~Ih5 zyz;E+3FXHgUt~Xenn~o%vY`*3=B}?(|gZHyqe~3vY-PoVcXAsdSUw?V)9drK=6=x6s z3#^{U_$>C|f^vBV<`1)e{0;x7v+;-j)2A=|mq&gSQgi?0P?5wGBXKA+>kXU94E|kb zxSfOzTkdpCys_gp>$1OVZgW~ZpSt6AXI)RrcK$ZKI@f&u=+7+u^JmxJ-J>-lT}Am< z$bRE5%WXB-Hhks2`sC_*?@gcndu)E!e#u6Dv5AH24+-1G(p9p@H(s>%-g52Le0zJl z3dYU*e=msMEiYa3P4eDZ?lXqoe_!w7pY*HprTv?_KX)9zIs9Q}SS$S3uccqt>V*9Z zM`MPbiTl>8+lODDy#I2@Q*o8ba${d3NuhHO6XXpW|F)G$8YX>Tue#uF|M|7u@8q8d z-b|PJeB`{W^uvt;$5;j5ndMeYxL5wJjI(2U$zHCF3zKwW{&dMXbFkTFur++`Tyd4_ z?IrWc@t-E2T&t#eZSocgwVIU%e`fgXVJN%)ieu7k)nJ=P&+nfon5Uj0BzRL|(Y)sW zzoWh}THF+C+wZbb z`&aKfH{GM3zv5O2Q@&#WuX0%IC9hK9XQD^GzkQr+GDFp9 z%Fj2~H9y!tXZew{NA7>U_s5i%@5-O8^?dL7$FWy_Y_m#?0$>&=VE#yR= zZ)5Q=`+f0Z#6(Hv^Rfli?%Ds2elC2t_?ykaH|v!?xjl>ypJY7<)E6ZDDL+k@k}~D3CZ$@=-yW%=^CiyQQ^%ttb_EE|cOR z7YZ*mu<}t6l^4tV)Mge zqQ5eI7i1OKAbNl&VS&vpw?8HC{@FP12`pgEwo6LcUS{vsSm`9Mc5~VOwQEi@8MD21 z`*Pm(dvela+vT}u1FRZ=R`78S3eeO0h<{yn5&;RqEJeilt5Vhn@ zJphtw>{F*w_s@r|8Yz$`83WOU1T*vwPmR|7Gvp^;aj~dG+mr z?rPPoEvkvWHzw(FPDssBx{>1L0K)qA0J2k|gE{d5#3dcFV?RPDs_|s*oPo#=tyKIVe+^6fUHB&b~^y*dd z<4PsT#)YZhyt^L2l=5J$S<+qcCrN3Sl)_!|g^Je+y zfBsLu`s;;>9?*eV`--(~nOjPvksXP)SzTW?e%Ho6_36v??TXA! zt`9$0peQ4EsYsFGoxkLhPsYnnA6>e9`||7eH(Krbck%1TA14jg{c~XZ{{F1O-xRhd z`O_utO+WZ+8qbCF;#!mD-zIGv&iFN@)5s1BZ1RP*>$+TRnO z5C6K{M6hof>No;rCpHlc6!1igjvBUgR8vEuj?mcxo*|FqaxMN(Frs$i6 zjMnubj`8-FlLP)r*stScXWe_vkh^K&wS&_Q^^dDbIEm`~yuC5*#0>G(HTOV@O`L;Vt9&da+S-LqVzvid+{N0mS582uHs6}@%O>NO&#Mwxt0ULn*K9C%GqCv4z3lvHM^pQV0|`0}S~m_pxX*mrA=hcy zG}nJ;CnqI6agIABUuVGcadEfnj|bmMejGddQaSzb$*KCaFDpK>{=D;iUrl`6aaYR- zJ(dHqy8i3Mr?0R3n^pUD_pz@ZX7==-Pgzluf9%QbqO%SC%XjLn3KK7U+dC=9dE1uM zQ@1&P?|h;7SC4gH1ET|n$>#+Pr3^3FMOj$>FseD0lqIA&m0VVGI^YoDs`%`p*{54d z|Ia@2`e=FL_UCWy$1UoPtL5C^YFYi|RaNm%?)V?e-T%8hk)MD6?DoRIxScT^4Yzf# zub0mk@3-G?v-4Z$)v78YRwqyZ1#6S*I@FRUFICNtjGW^(<@5n7?CY_LrzLgEVd z>TAxQBTr1-^XY5l&xJMT?(ejXtxKpa`upkAlV9Tdc1$~e^11#0rMv%c(mS;$hNB@` z>%9K->+5VcTkia~vDD_*-Hk=wKX|!&T)KJ=$b8faBCTR-Ll_-x&Hz)|3#SoGni0mu!BM3%)%SnoO_<7bEGvL z$l+95aHn2ymd_+Dfo-Z%6T|Hco;}|t*>`WJ-L74-*O$-#ed&4qiV zaCLe?)vrHO7i;&|eST$9mu7j)e%`_fFKd6a{kiD*ai?~CzQO!P`_Q#(9ggt@>c(Ht z4-Wou;g6Pv)yKwSsRet!`5ma;y|=O6q}RSr*^t44adGBKe4#xYj^jKfA#x+9IgMDkg z3x1xq`)X=(VROF1jby_Em;Xw5UpPNY-tbAf95zXvCp!dpS$}&hWGxy!W1>bvP3s=xl;xLjt^ci{nRpg&2jUL#p(SM=YM^a z{<-w;tEtlWKTKY3zVF-Hr*rQ#i~kP|)nhpjS6Nm2_oR94=da8C>h;VY#GgF-k^gzy z&)!JmwRfk5p0*Y|y8fI^^y_byH(vgJmQZ|3_N*HF1(}wsYS&dAY;PGci!;RO$X?j_ z?er#j(c|l1ZJLuF*%!S^AY#jBzHYWUak*c+4>z$~Tyfp9q$x3Aq2TdFfC4XP?U6JuUVzN>7}L;pxWX+rmN9CjEA$ zUpFi2)#;ia^MC#;ccb?5h7i@xFK&3)Fj_w;i&TNCxD1r{}|9;|1Y z9O4_cms|)~NtKLL!J zaPy{e?%L1MswBWBETAIO*2=g~&it2c?AHZ*H%}ze-JbL?=Y{T|FAhP`|cW(xAz!sWj|uN!{x&Iw&-fZ-k8j_5AshlJ-M2- z_Ovf^*-C94^U~U_F~2u1UH7tP^;FIV*8l7D0*|Y*oIUhvQL?a>Mu@_Ldv2mkz7`Wa z4@I^Y1ULzZ9N=Hbb#m6`1y^NzLko_5%4@TzOOk1e-}PzN=K80Xwm!dCn<#as>Zg_% z!+HJg-uk~UFV8>!_EmV>jvw>x>{<7`O-z7!yNi_fnnJ|`h53w2&DCQzUT42rvo6$o z?UbsEE6Ok5S}k{g|SL%6x^XK)y@8{$! z+}NYUF|9jEXw9zd_#SW89kUp_1E(9ffBMV&##nVi~rr*AyfbKQu5>T^Y8tbwcY!zWo6*LNuRE2Ftn}i-}~{kd}Y<|;GKd0 zrrwdTpZZ!kaz8nwrI9C=lxr-Ur zWeJAuuX=gx`rj)buP)yor?)72{jQo&&W6X*`TPF82+sdq+x_*Mc=6-k6YpruU-3+} z`s(6aFT&if+RX73zuP8%N3G0ie-PKXEw>MDWAcqRJ+S^dL&dt=Y`;qH-U~CBev7eo z_Vw+)5?7*F4{dvQ`<9)!*osgFhaYRqcInu)Z2c)Qt@v`n!zlTXdWqN@g|Px}xBq4K zVfxN{p!3GG6ZRjbJmo5Q7}K0^d+EWfEeC>EdiyU;tSIXF=svCctbSb8m+bocA3tus z{=U4rTH;iA=aQPF;=f)IuE8WgR3%^Stpndg5s;dytUdD%BU=PA++c3d3uo30h#W0=(TO8!*wGT*zC(t25c zI~4PFJ$`nvX5aVL)QZfVJHOP|fBfwJ-{Hypm@k$)JS*37Hx#`6Xl`Hr^43y&tDeLC z=@<6Rkp5(3{w|@@@M7lv*2lZvY|xA@=H|{)4zx$Lm(g z{anh$5K{5ddKTa1g-!CaYMvfyoKx-VvE{F}wR%f)R(VYP&U*_pGaB~a`R!55vO`_* zyB?46h2xSdnDc}0v6M#@F$h09+IY`M`*%$D1zG#k$1^-#PuJ+?pV>=_u~FVrfyd)kTRbv25X)z|4K=QhZrl1>YWKtm*vB#g;C)?AKh+9nbQ84k+mx zZeq~tJLJA8a>D(;Kf0G+zxUx|_n*eUpQ1zJ&3ECu z^)Zb*&obyHtXr_|t4m{D+51cAYi~bR(m6Nl!ilXMt!JO9#DCxTQ@rc3z`5gnl`|9?gbOZilqvtRQX9_W%3yadQ0cb?@W)AL~D7`@y_(q3rWxB2kZ}JfaUWng%7i?8pn*wXfi} zq>1!{?H6u`*}lu&*?M_xPX6rRzSaX#TmOE0v7)K&cILOqJRz5!RPGIBFJ=AN*)*#m zzU8o|AX|E6eB`{iX_sUC{iKyGf({0@9NT8pJ~>MJbO7U)cM*;5Leo~SZ5PnCj*!w$ z_^|upN*M`}mcJrE+r+ujvOt6}gvMA@3?DPBo z8+Szh|8#Myc};nq?(y%N*V<`3HC1Ma``)(QcIVEbuj-FC{4n16aPJK10Evz|Dc;mo z{ZFP9PE*?bA;-$%?wRU+EN|nZGji8{{H}WR(RHuy(G_{wiMy0kxIP@ESULv zsLvENo~p#~mV-{koDcjzY)HsAbL4P2+qoe{<44T5U&01I*wcAMwBG#Xvf*P?P41j= z{-Aa6RuRDu&tm_VqUZrjMPVC*x?K*q>xDWk{&(66!({bg|Ywc!h6Potcmt1-CQTf8Z z&C4cc=gyKnuqKo-&ZL@kbJnlRdupp*7P0<*d2{Nv*scGjZnMcge&zks^Zr`WJMMg& z`gOARF}}Wj|F=?o5Ay@c670IxZjwqh*Z#rxK)<25aO0PB&rXNKJ6_c2DVN*I?Royt zWA5yTgvISjbGH8FaCs+SqMo!!@rr>}W4-$3#){mYhx~nof7i>$SN*O2p__&Il%=7C;ehzl5i>% z(DRfb|oS$iPlS4FmI9^WO^SAXk=Y)N`J&tUTZ#o<0)tBB0&qE%NmX!A!g z9bZtU;uLXlP7_bn`&m!-F)CgQ_<6wi=NDch6I( ztAFIMI%>b=t6AIZPR-9PihcI@7|*^L2Q8j_onbFu)GN8gkh^`M-tDS&(--AV?|Phd z_5IYUm@K<+%l8)xE$hX2j=%o4J5pmxw&DCEA3J|>``rm&@Q=}g*F0O4B~5I_;R%0P zHrsH2=8woS{m%bkV&9I#kV_FK#;UCanO6`4oxo~B2QF<` z#MLV17Lu5LDB<#??koNbY60vDkzW*@`G0>+Fn4sQHCprE@rp>wt7W=rY#pvFLjP2y z+UB-pdD{tO7xLOnH9d5Gji20a!-=QUzi)hb|Ldm%+zsjX|Gd2(9)8T;u;-)kPQ{p9 z~;8Tr2b{8u+v!1iCn=k?d5xLouO zOx(y|%=~H-M@MR0|E9AEx~I>YF~46Cvh*3lbIt>+Pcyv!rJ5b~?OB4|q6n z`e}nV$Cxg;)HTg=iMVwueVY1gd&T4Cahq+qHYmlrkPaNGQy z{nE4MjSM}J`IqC5YP7o@F1=m8#ymFI=xCZ057kjm${pbh^e`&TM~R z+odPDzVdM?4oCh}o-l2GwW9IO3lGzGo$KURehWJw-^=@g_s-Aioq44vU#CX4Fnn9@ zt$Tatw5L(u*BUMGx|`CtA?{gncx7~})82ihH}kf}y!K*sjnr>S*c}@>?eZU~7rP5` zDp}(Cdw8$vU67b^q{?nrWP%X`@O?{cHWNn1&$6e{2m( z3T-)jM^?p}UsyNw6Z_`xjyL7{$$2!Ka!nybaF9wbN<-T=gKg}GvZW>PNcwWtt0-5rnP zCtOuNe@ETo&el%~Tn!(-=Knq+9X@-p{cOhH3wiFj@_u&ZS!6l;Sl1!vdrPlBJ|^e? zO<=)KF0tkkyXHnKu9cn-b~5<=yv~4WZQq6% z@!n;#-P+Zzt1|4-h%IxL*3;hSBfDiOAbU3FlRv`SzgtgUknMZP zm`V2yzedCSWwCMZQo>(dd3B}5;b&N#&d0+)EB{1B#?&p|({`wZe-6Wm2la`TJ@r=2 zADNwtzj8m@EYDU^zqh%=y0v0U)%ZOCqpR;Pp9vhq?l<}P!;d`#w8x4;ddd zyz0L0xu)@~o(TIb`the{v>vedW_rI!lxJyL+_de|$q_vp?>+b0?Ni<~q2=MRh>Oe0 z#qze9-Oyz7mh=>GIV~;ma;`EzgH-%CA4YC=H5<-{H*c=Gw*K z^NG8>-3E!{_H$bgaF=hnc>nk(1US==@?dGXSkyWT<$})7Ki2T zEaTdHEo$5KWX2liTGriJzl;t&b#l~Cl)Jj`X#1M2o0v0|5Aj6UZ#kp8grn(C$E{6V zOo5FHPAf5dIx3cE(6=%3qiTQA7L&$?eZtzQZu6pQQUaO16&~d{@qPdFKxG>D14~AY zCs$sx7wIM1&zgI5#(^bQ_Fs!lX3gSyu_W(k-yhw`8-IUZ=Mt_+U~ypk&z-pO&t+$s z`=RYmwk_%ZbBLL(=g$21=iC-={P@dju|)2h+W89>x$@*F_#3G{aLKW~GEGkENu$1) zpKq;)e$Si7$&A|sOHY5h@uKMB1XdaEO>3uGt)F&QkbU;uRVKbUQNHhLyqg6wCKkub zZ}4w*kX~;8UjIyj-)7%#RQ321fNVkb7upp`Gos_0^uyY4Ec#HY&A>klru z^4v)8gy)Hy&9&x<3+AX7yliQ>YtyGKEi3zBm-nuHs}CJW%b6CUayI{*uZeNv;`Vu* zHUdl!r0;*5nBMmERr+V|+V~Y+_0!%ovP|3?v7u>x`i+Gpf#(e##}v(<)@6Jlr$FGX zb%W1=Rx75xrwxpmcz!-j{%cx)aK)^^Eo`f&>WL=?XcU+HzMWGb*qeP{`m4~5ISNv} zwSvEIJDfbVOX|mJ0|^(_mI($Ee$Clgy`glwocPuDq{IEvv$KjN*X)|p)yo&ZFKF@t zdE1W7EV(xfi}$S&J!p0LiQ_w6EKkE;_BI9>!C>a_c8`1VeU#6yKA zDGL%!-WS{dxq9eL;5&gM$qc_7=BElw+!&a&b+R~jlwM7>&Xp*sqwRNNP1f-i6!xD| zl$Si9Z_IY-nMZ)*1-mfmm^0S=4=>zcWcjG`%W2{?B^^Z;gC|nQ>~u6c zjt~2|8qD|q+`ayI;aBy|+kR}b@>qZKo1L4@^6DnWEpjp!YZ)a@7+WMiV6@F`I(u;U zoK3|OsyWO~y|3lou~2Wt$1=skc?vvB4@U^ut~2|r6k^5u{*S|h{_l)`ma5zoXZrm0 zXY0Z1WgE1j517Q;U09`_TKZU5%Q+(Kexp@bW|&I(Iy3yNz zF2z3r^(wBPRGwiz{qeqqRx4`iEcyStW|jQV*|KJnK&^|^7l+M^>J?4g>C9Kf8*~n` zs*3XJ*a-Obh`v!~tdmF-Umc)PVrlZ0KmGREjF)%yrv0&s{`7CJe!#}S3XU9`WFeL> z2kfM{Zys{wR)3RFDS0;JMXk=WjH@y|s?W5R7$}~%yuoXJFLrGN`-0k)*ZtqmyShK@ z`kz+L6YG9*UfDn4Y55Nqi9`NRzP9V-{@BOSa6InU+S{jRd(S_q|F_*JBTRL#c$r=K z%P$9Is}vI||Jqple{lHu|2l&AQ(l`DsFbOz^q8 z4gAqN@{S*4aOht6_3XKQ-F^GQgH3+_T{gdTM!KscpV6zCClxMj+`htT;j+y3y#=}~ zEA8XuW@cs$aLRSG?Hp@11(oyzFlYA9+`7Hdy*7 zE9U9@{{bBC^Cz~iVZOLmoI!u@mWsP&pKX7({w=5rylwS*9p9S(F8*5q+>f>|3tYi( zame9r#Fu$uue@hXuKU2@U>^5iMKVVZUxoa3zt-Nb+An@D-Tw59<+UgMw@$v(akfr= zTD)J@aK_aN|JTYsZT2gVpLg7NaABG4jTQ&-&EMbcV?2}aOkd){TIpnSy>PR$QRYs< zRXKf&Tbpd7mh>DEIrYYG?M_7<1M#D0Smy92Wd-x&56K}59@}+&bLZ$~Wt=tL^41fzlHYpbm-AKs*+y?)YBAw#r`wO4 z{TnRLn$dU*=zP{yQdLEpS`-vCxEZ=W_v@h1qL38n?bKUUulLy4&v`%d{RY^V_~V;KnN7 zC4X-|N_`uD@2JVe-J16U6&vins!5bYyWDKFi}&$S*#~< zpT(i@SMK)X_OnzUZ=a%L-}CU;+Ij408(z6Bx%4xG&o-T+~~YGgy~7e!5_3Y!UtbJ=Zfy?jwA2eyvLtvvnxbXepE4b@q79mz}avM~*FWnx=L} zxKl>x_!1AR_dWKu`SW+L+oiDLdz8tY?Hz0CLgWgTKe!9Yh&8)^9{eL@H??u=n@eUt z8?T*{SP=Z{?zy%nF%0q3#BWaNj(uC_9N@0F(URv$)AwC()XO7#5}5KXnI1aX zb8-DiU#4%&U;GaK-IA2f_<{dHX8Wu=l6I;_{}$LB@H@C$kKx{?+Fx^jw*K{)cH?-g zzwR}o%jeuv9JPb0b!R+F{jmBOyRd+$K;Vh8d_@ccmDM$7xh^bJx~ zH=Avl-Wy%SV4xc|p+W1sss0L!%O9-+`F^a=&2$jIS^n>8whvc@Ib+oMot7^y-zy7J zyg1{~6F!qFi*Ns!TB|)9x_$=Rthqk3~#M%fSNpJ=FE)^p$;{~h<#hYM%C zieH>jwPNp@I+Gc@;`RyL-2F!LpxtGiz`sfohY~nl9NVWC*&jI*>wa#}DPA9@FW+}A zmv|;6_+Y*Qi&4guoeWESH<&Dx=()P*SJ4BZDy#n5U1!%YGcBxL*R{5zf929pjKn9m9SFn_hf zjAfs1o!>8K?mCle_3UN;WWxjP>bne>mxb5-^5uANQu?$muiEy%A0GcK;}-t$dws#8 z>bQ4O%oYEBewSNscG`Z5?!WIX7A<-_mmBMYbT2K>GCQ#Hn0&^IlozENYS$lan=bY-TWyM=Dw{S7w*cSni`OH(XV>1F z|7{uT{F{~zU(-e8zxAS;0`t#2hf+p~#V;Ey1qo$~&FX1J85! zS0x8#C@4;R9{1;N#P#@vU)~=yvf2HI&-dAkue>Y2;VykC@}TF&#FUD& zn!EW0oN9j?Ht`rUSTwav?wgahO|~m4L(zmOA!fQ?ipO0}^FI&1Sge=+s=q)gD(`?u z4EJ^6WhuATW!2y9TX4K6g2SNa%#ukn8x+^_izP4}J2Tpx}?D6^DZY1*X#o_YYQ%HUq7Wy{$ihF-nyxiJ zqvHNV#Qod8^WWU?%0;{5-tAzh__g$3HpOUwr%To6_vEmc#3Lcek+L-Sg_z z+tAsvhil9YzI!UiJ{R(Saqx-!JEu(~a_Ng~nq!~cEfh>yZa-sISB8!P3v=z?x01{pB{OSIzHv{ykSw2F z^IPWLpC;?AKN;VAy6I{kNQ6WcG-6K@>e{j$ce zqI{VK)6@Uk%WWf+#Bcd$%kQ2mx45pMFSO%#>?>pby#}xJ-i7YWyj{ySIgXX<;n6iL z|04dqY5&}(zo%>Rov%g9SsiwKdb(P^_w>Wv+CP*tZ(M0GK6r2bvURSrx8G-b?XXR@ zvar2!`hAbfwSk*BmNqY%ze8fely{vTFCHju=%2XcKu|=eH)rO&oo#zJ26QZGKq;@JM@7sW`g`HwsW@5xmrcB z^Os%gTO}~#fZVcUk9IBZx}R)+z}HQ3*U8(NP7LRc6ixbZ_owfluY3M^|K-0szsQ~S z!0i1mmCy4%{;>GxL%E+P*2+EKo+W2kyJE>gcAq&pzsAK2{nlx*PN z#CgD9%kl%y@+VCWJb%8-`FdYR@A5p?1Vz155_dYg^$uQitDj})ZY+PRsA)g@)@D`a z|6u_Wzg$(irtDt%b8+SW`svsIZ;!ToJXfdOhr#CK5o@!=jenSbKHR4nwP4;0j9$$a)+*S~&wxFCVg&rrCdao?9bv6;&RvljFC z3NL=xwn{T3`HI`T=^HP`ZeZBTHS1eX)a$BkAJxCIK3IOi*yC-zvRm=n{`4;P)v4za zpPAK7l3SCr-ul7p>-S}=-fq4dV<3=iF3h0(dfBuySJ^Hd31YuIrfqRcYWtm!xgtJ7h$Ecr9UUVYp-S(6{ow{0aF# z7i0e43@=z@9rtbq!;Vi+7Qa8q|5y0u!?5-HZ?E!WJS!N|W%=14SG@7pfj0(ryZEiT zGs-U}zS#GAO20JA%_)rGs&X$G@0#~1lznYIbc|(#dhUshray{mZiE)J&#ihR{F7;! zT1iln@qyPV*QdPNxk@HTF@Q_y^t<^t@=J<)<4;X_VJv(gmp%BO%Y*sP=AYgC@067i zV-3Hi;043e=gyt6+5Gz;!xXE6N8#sVf3)r8{kV7bg?Yc(m@?~I+B$i~f{W4>9po~# zOeU{2u>4nRGOK0ob}fc4dP3`hkL~%_x#zR}iLds*xcp1pPs<82r2gB-bHRR!{5R!! zZ-wXFlVlaCo{?K3_x^>U&Vb)VCVH@>eUdKK8ce+K+h?RMH-w|NPf_L3zmhKkQ$3TQC1()0Fp|qh$H| zFS^uG6_5 z%CO+$s16aCkgA7h)V`0h8e&lkxP)qy7z_o%IKJ!|`( z^VU)U_V3)TNxZSsAAUM`eBl^B zk8cMzOQX*#c9X7!Z!7bd?sQ3R2>0?ocz^Qi`bn?v@4GwsuGdam#t(l!pSCzAe}?~4 z>x!5A8V~eO=ZWOXy|ZVt$qY5$v!;{O>jQ)v+#enM5U99G;X}%VbFcXuF7Ixcdp34b z_G6*tix-;wim3RwApWvU+YN^MO{Hfw&V^4FJr>d!@5QS>Gk{~Nt;pGU_eov65v`xX zKO9d;bIg3>oLP5m#$C~)ObfWgjHX9Emj2sbCeHq=FX>ysx0g7W_88^-*>C zJyz?$Kbu-CCuqoIG%UALjgLCDK}?uu?}l~zL<+Axna{bA`SJB%YXlfMWR(-t7nYk8 znLSTyn8DCy_$ci_xDekR{a<&BUK#B?!T;>naM2yhpO%^z1)7~+sj=w3j8InmrCf@9?Lv5??LjN`|K|c9FXBubIea> z`|#n`wmhz8CJ~?^?>p%^N%mS~J8|sUHJzh3Td&;M}a+l|eZ!ulVcW5e!s%QJO^X>aU z#>^=j;@GnPI4Aww=Jir5=9r+4=Ar**=UTm7t-D=^VP5UGo1rpd^UpZc$R59)y-?hH z-g&+scQVuM*y3&PUpW4Mx4{Dgi>0sk{Ox^L&C=i{RQ2$N_>o`V*y=a>NIlq_A+qb} zuWS|N3?ctiF5Ua~AqNG{Yz+5lT$kT)>HjRXHUAj<*)kS;o!rW=$1Q(;+J#?>q@pUK zYxe)$)}eB1-j8CL%a$CrhtJQc$hvO)!S*FPlhRkGgreMd=Oye;O?-7P^L5TjIGt~I zX_?gY%l6|mz&bx%q3`TGJA8GD8!%$#) zj%}9Phu8Nf-}g*=zwJ#xd_!5#D&C!0e^tC0zHsDD4&1R_IXqUiz@}809jG2rMr`4~$TDkmZ=wan$^Y5tKczb8(@y#!` zy? zs>2@K=HzSJJAYp?&w?pMJLOM*)qnmg{?19^{Xsib89&@e*3UI=sLx;a;QAufn;DCP z)~`E|6#VwoVY}N0c&(iH%b%`oE{b}kpZ?|OzGi_JF)sFh?cT*JGd79eDNg^cr|ulN zsI847*5&wu6zOXL4~;YY4yC>FnR(efI4NhrTa(y~S@sqF2T$>+ZO>Kz(=(Ybcgf1^ z|FR!8&bIkuF-`N|oPWk|{aI?vRK3FDFR?VZoZFXKWPjkutbLomhg!@yd%y4gL7sqU zW}btrGw!uy*6+1n^sni;(wCW+>b=)(GHX2PpJ~H6E2nMdYC|T$eC6uC{ky}~J^huL zIk(lW-n_o!v@U1Eh6%sypid-BXzm&8!>R+1Hiwf*svhY}n!gs|jU#%Z;9nQ2X6$uh+>-N91E$F;(jFZ+3 z!?49HpZOaZcg(!d_x|PnlGC$;T@9C(w|h+tzCZ1C{;99|cQ$U`=k&9S^}uES{pGgD zK3-nCT7QZ9i~SYe+N|3mzC`SQ`s4M4*aSA+{qvno4j$;8!^m^vx_D~a#!|Oy;;3iZkD`5t5c`sdy|%EC8pH9Sw9bPCEe4K^*3?k=-Vok zn!#%{^Spyn9ryOfd>&$9p8W?!jSsw5yZNO;eeN~$-t|leZZIk_)E=M9cSTe7&bwn} z4V4q_gb3W?Z%jCp$h2emO~YR^J1jYw?{6_TG@fA9KJOp%-RFiXd*^c*H(Z$8EZeNB zd7>^*QgUDMbCm`Q?jQP3<-Usj>0&+bD(B-iiA(HVuiQ^%X-+t{vfA>V^lItm`-iFm zkH1*G-fMpK{6(4-g(hkb_U%=hyG>m2pzJMER`(3C#5~C01U1m-3JM@;mcDeYNC`bN)my zTiAt9DEDDF=~%UnbHVHqhW?i^6Halr-*1^9=Rd)-Y~Rs8)@%K5)i(vt2x{Y@+s`Oy|L_r2|V!+yW|NA8_K#Q?u=jWPG9y}rLE zSiN@VCsEFZM?d9{|ES$LG5+9TQ`6n*Gbg^eoc>nw#qN7m3%=IM+>ehl$$UF6cJjx$ zTaMo@UX=Ey<=YvlsG!G&en(zkSsk-+@!r2mKHN*cxSkH*DHO7rH8$U2<>P-(t4p8X z2sqprI>Xm@nUAhs4)!*5p5*s}o-IcVEo^X?L1C4uq>PJoE-OqXWoDketg)2lWJvY^gL zSmA%#rDLJ**E(&!xnYr84e!IP1+l%4)NR*g{RmKIo@gTL#<-5T^8lm9qlE{H=FZy6 zt}fcpcJB#IB*ZO{({wHwmnz8mQ-t!OE>vMi) zn)T(~O~&KDTUn0WxmOICf6-L&%HsTF)sS6;E&5D={IG5>`=3we((Aet2GfG>o5OeiQ0UB`OItc z+rMs^em~F2sQ>oQSc~&M%~x*zsyP$W@Zix8>t{c_&t`U%vCVH-5%4+gj>s-yS^t1J zya(2&)G>xBRLo1d(?9!y?`hdGmPLE?uD?I?wSLO(IkG=iu^!m`@S^%>X6;+`1&gCQ zBAvhPetUl7AE`r&C)P51Cw%;DS}d=xdal7Q?$%!O#p}&HkMkeFz@^r1z^-WP) z4bjIFtQ$LS&U?X|CK6(DM?KB*g&lwGPY#b8%bA-e9d&d$B@(sZdDgAD5*m|g9TslP z{h@FmxRQ&kp_r8YQ?H|11pG zW^h_|PyVFBzgri&Y7Zo2u}^v?FL3(%8=C;56a4=-^>=AUL}@L5@nrtKyQ)Pq6b`PO zw3#8Fd)fb~cenq&)p2K5;MvD3EibWni!khdXm8SG{iArre8z&S=@)Jv2$r2)XRtQr zIOB{x6Sv-XkJ?!Bc(O{pPo6RNS8fJxv)~4keKVP#ENtJa|88Tx>?}TpYx^EQ+;7G+ zgIk#Y>(Y5)AZWv-_4W%s)?r`{3} zz7_cDw#vEUf)0l|{qk*7w0~MS6m@_fxBd82oMi_nrPZvo?L&tn9_1?0v<% z=e9JoG2SdZem`x}J8R9YGmhn%TivTX_Uv|z){CZ}F4CMWch@VJp0e=m^kcW0_$A^i zV}aMgcZXtCm^X2=ZaKqtK}e2g)e9TJ6*vDqEen1<*DSk$Co5<1TBGa@KlV4g+U=0F z_-oOnzNU)jAOD?Gobkds*2>`KpQZqL>t9vv^GXF?O{)LJ;5$D@nYQ2 zaeBJZg8O&QKI7)kY`2wNKKEFh@h<+`u_AQ~t|+ z3O{`N^4(eb-t4=qK8Gjzgf#YVSa@<)%60WDvl9l{mgn}zu8#a;qxIwHFXoV)ixiG! z?DKI@A1UWBi<3!AW99AZ zwdntC4KVEKNHbM484{GK;9ez8pbzU{G}eOCIvg@%WI1)bua`6TspER+4I*~PV* z*Z=>>`+H!u0K<9xc7s2C0*_xR^5zNf{e7{9y?FK8jGC*OVJ6kHZGST){pY{WSbO;N zyP5Kb-BoM9o?W=U_V)XP!{!R2KXOV|eNtD;*USN&y0R<(UTa(C z=OE@%Q+Id9irn4ti_$qJXQeQtu?HB=m|EA}SbJ@@KjTTc-<_X+EIfO=GUmWc2Z8(V z6`g;*?d$Z(Ex7utS|IP_{8(c)o*x|sGcG6}h~6?!Zr;`#Dwi~sPb+=>E`LI&rt(p3 z`NZ{4L!`KWoPVNc$s8ZZVDpdn`n+=$b@c_)v)rdY5HX&0&oV~jjA*v;Dz!hKye@wJ zV0q+vWYg=5Z?l&Q-Y`9xc>9R{_U)?=zv15eUhVGjKG%B&VM+J-?|x@72@_x|iQb(i z%+|%0VP)`ZLGikvIUP9p2ix%P8KFtrB^)1tcyg>Df4~Kdhf*Aq%~RPULV7^jQ0j__MdL< zeIaVM|MSwAi@%F&quyQOIJ91aVcMj7ljZMRUUPJ7?< zz>zHyZcZruy6`mM{h^VTOZ*BhF`g66#~>$R8eos#V0&vJL(PkONRXmvWDV9Ni(EeCV{?ku}rTqYA+|9V;bC%#3^ zjjX)$ub<&(s(gOE;PyqMA5HEYzmG*Q2;Tlvzj7(x^g0(;4X1BgzMbUdaX8Mb`k`yH z#_}gzNtaeEI=w9PXye_+QuQ|qZ&(;OB^u%Z1-+7A2iuf7bjjZCGeJm)k?m`vrPy0rJKyY^68A3@pPx} z;VnD&Ns7!B@%Za`B$ex(CYPb$DVsHS)E92{d;k2v&RvZj&HO$uj|g4a8^Zek%9g;E z-c{c>1UDRvjxw4J*N|cjt=8ubU zd^h(TJeVw_d`5LMSFZxww6YbqkLXrj)%~$3#!d~YV{+UH$FVk z(x#9c*1+g_E7W^-z?Pl=E-tv8FFyHzFsDJl3VY|J8;sb+=hXhbb#rds6KM;F@3J3e z73Wv}(upX$c3a^127U*gCwc}wTMfEjZ29CeOX~ZE&#w(9CyC8c*}Ulev^9Y&`|5Y8 z*?3=n5@$1sCCqzPd`!oi$A9MC_^;9G^RF!PaQpp>d56qJ8T@&!8~>f2|7LFdl6*#| ziF*C_JGV7$-D7e$em9%?Tejs#Gwg1jPh0%(y4CMJ)35AHJyy5f{*1k!$rCf(p10)* zVnRg+kEx{OGH!0&>Tx_{g@wnGHlb1#!;Ov_-Zu@umbYJ9vStCZ#_fYLo0NC&e7$}B zygzsOw%N_(@bz8o;BX>;*-BTdhYC`q9*4?oGUNWNTk@`Y-kKxHTr1`uT*rN@CjI{Q z0=0Lo#nLzG7xbjc$!7FV6=9dq*uJpr;g)ngi+7SQo5q8lT=%uP@%gaxEz`5BzM0IaUy!)&rEI6H=;rdLe}DQ$tDCX^$@!5q|N7hG z8;U0;A9`ynXE3Qo@LKFlWm(=IcS3j0Q$ANCvBQJ8eud_t)CPfxC+9cvv9__XTVHeJ z5AeMu>HbpR+mJ2cX7}z>O3NgLUlj3P_!hJLtQ1FgQ%bIXPGx-Sfz(Xl&O5b?0muHF z=KUG1)cfGEOXWlQo|2VFjXA?b&K-^{W9eGSJf(bl?s0(=z6@tF?4Fu-)E;{vY901MZcVO%K(M}z z%$iGWao#oOt<$ZzD(d)T_?}do8z0y(`_Dd>7Wwz9m;afzOX(%Uv-dj%G_)Q?7ymVX z%GW1Z5x{Wd?2igD27RIFq8F;P8SFbRT@zSxF~{Uh6-UvsgA9LUChU3V|Goc!*0YWO zQ+gcg!$TfK@Y@yfbF3{9xw~>tx?We)o#PW;t4&&Pv3>VH-qHfjUZESC7e!8L$~t)A z*7XoO=P)JP@&@^`FAD?aWLJG}df)m{jpxq9T?_ZNWK_zrR%`>ZC z^KalU+8|*pd1jAdJ=<^gH@^i`{JyJOZSN1~P`p0FLHg(nyV(=gzBt6>P`*q{_U-rY z2W1rQzS&S6>J?lWcuU|>xsLjS4GbKguKmgGw^F&Oc4_s@nU8rFtGv~IB+9q(?5@*a z;$PUD-M#OBilW7w%q3-h2H$7iz3ulb3%TFn6b+_4X%Rk`z8}L|-r=m)ey3+uj{O7YaciuGhTY7myJ6CpH zb7{8Qj^gLv{hTJW20d4}^!$A8$!#xo1-_rGP+@X?`>DI44CnQeJ>H+c(H--1PzGm~AtuyKP zg!06L%u}o6S-DumuB-_o)cV->@&HYbkuf-m3T~33;zIsd#W_<49`pVEgpXt6zc~f!2jSeQ8 z4L=_?HRS&Ey4CJ>JDELAeoIRJl%$UvxpSu^Nj+fcb137Ob^oEjQI)Ekc_nOmrV;FB zr8n=+o^_jX@8iV1{1)fuTzmh@^h1K|qGt}z8RlHjUAO(`?OK7Y;jRy6$*(Wol>1-j z=XahL9~?dO8IHW?z5ax$i0#w6xbj$cb>sDyd=8ws=WT4Pc7hI zIk@Y+al_k3FYhdx-`SwL_b{7_dv^DVDeZgHxjQm%t@viNx8$R9O!Dk+*M%pDWtdo= zh>JS#CgPlE-t*>XOFyzMIJ!74nZqvlO^#UP3|2elK5uIA&%ZVla=AXNlcx zQ4vy6Hbs`H!Ne+pfw^v#*?Yek-xfU#_u4aCXUQ?c&w6*wDkTJ>M5OBG?YQj4{dZ=8 z00+aJ>nY`&jMv)&UpHRkwrBl%#_anB#TzGWv>yKBx%TSm&aH*~W#4pc)_8nhzgW^;T zNST~*{!Ym4{jQFa?iT81oN@ZcV6l6}u4(i4T)fU!@0aHE%I4V|tJ_N-OzOO7k}^N& zj^&#(-FBB>Y&$>qVA-! zAou1?_h5^K`4cvqRmH11Nt*7kV19qXWl=GsL5s-!S@H+CtUpX(Dt(@M@{Zf(sqb9F zzRzLOQ#_!{HDhMVM$Zk*TRLUmb;T~Ycu?+z&NBg*{dxLXo!ysCubHX zn~KT4oZIMoVP&(3ozUyFId7!i{5o5I(-M~on6wG?Cibt^MYP{-Lp3@ zUR~Y4OG5m9OxnjztLgGvqV1=Khb#|Qc-1!j?Yz%cCOgh?Y>Ch*iruy==9H2C@u!>K z$>&~M;5ygl$g+~K523SdyBfv+3S=$XbnDBSi8DXB_pq!M?v3EFx4OX2RDb!4)iOoi z!e@p5UrcPuD@*!RbtL2bkqGX2{zbfZczdTVldO;~Sp963RNU4Xm3vR#T>3j&WI^Ws zSt?65|5P)Q7s_;c{k}i9Q1Njshg785E8FkOCT}a79XBURT`r70VOj3t2Y$y}9ZyQt zv;DEla{6xgm8qZgabCIJ^QV$?>m&F5dAcWVdirb;;{zD88?Z) zH|Exu-1g^M%>R#X=dDO)IQ(o!i{!lnKJD(CG<(Ii)o@?+Fh2P9=nM0!8?Ut;;AfYc zaXNg~w99j=Ilt`@uJt*7Wb^-T7dukU?T=dgm-Ftk%Vt|;~tllmE;j7gREox^L)kcyE)z{p~>Wui3%|EOXho zVyjB~nk1^(7o1HNSmhw0l>LkCN8*ditrrzf)G*yW`tz>J9q*iPshcOpCa7#JwMkjQ zWYEEww2ARmm4Q^nM&Eyp=d9Q19Qyo9P-;n^#Ss^qzzn~s8*Nvv$-926%*4c;;cPC8 z8vpHI$5hg8CkWR*X*lTrdfJEP^BPJ_@dw`RG3R|@xk0H|vE=^2x)oD24ixYFw|;xI zk{H9z%z5_nQ+7Ju{>*;S>Vrz}#c7#q)hUvB$Tf7@@}+Ee^= z$%5k-5AqwGnDEOa_jSmg<@Ty5qUnLO5I1W<4uL|4?wRg+MrT-Cv{hpc$4Klnc)@bxip2SIPEEgb%Iq$7 zwmNzqDAE0PP>4-c{=v_f|FiZy+OSkV-h;tr_lf#@5g~RC@|OY*hb*1z*2tEnWw}5* zx`_9xSN6`_oZlX|V$;GHWbLaTq?)#+^c_4QXnOg>W&+KJ{oiUKWn^ zU$ge&i^;8=-JA2>qvo65&&xBrP+n+fIQhJ3@@HNH4|P!=y^RbhE@CoI1o-yee87Lq zvHxP*GoQ=r8n6A9YtLgVI8`EC^-f|Am+}RV4*L(4v)@Xsvtd}eWu5u6D1++g&+M}p zENuTCnz-bwbd{!5_T;t6l}kLGt0b)NKQ8Qy%VTs4+Sbs*wT%5ieryMN7F&#&U1Su}Z@*~5Sa_b=PM4fP(pf5vo2 z^2ANWnHknAjW=*Co*Oh#C82J?y=xk~?$qwt?;LgaU89D5U;BQ>#}ZCAzAW>2^=^GR z%csqak{f~#zWB*F*Z#n|_l=i+eGdA=;9j(x<-nti-yU^*{gFFX@`dibe7k?@`DBHq zUWuk}uf``>`*3-J8qn`;X)E{=H6r_ow*z0?vk4$qa9ouU_wymbUy|(ri(SmnDxj@(NyK34g70 zM=tQiyxC5N9WM3ety%CfvPybK>~GcV7xOHxw4dxce*3`Wst-Goedpws+=>oa?O)xq z;okC@{QvfC{b=#jY;wartIIW}3%ynv>&!l&$acw<@z)Ek*;chrQm$`S5cYENdC(}r z)c^EX$DLALEP&)&}4bG|-6TA+Sa9^cy;0n3kcDLLtf z+V0uw)*P!Io9xu=b?d^#&*!(;7XO#woTv?)bLy{!1ICXMdP~$hOFoJqps#ihmj|sBC#by* zJ$}ydLiUXlD}}i{)6czZ5xn^P-`;AD^ZHXW?d#3NHRk+_mR5;9e@1ggRlURekLqO( z&n_&i6!~-O+0({}45wrEyWLu%xV>b-W}jESQK>Gi7o+OHBPGiqo5z25 zJ`3Yo?wcFkKDXJH_f#HNVwlIqF*kR6-VTv>#>W?2E)86EIk;Sir|hgn+t%tu&)F3Z zJ!`vA_1`u)N_IwdFvHx%%g#x1HBLCO#qsdFt^b)>{snb0fB5S3>PHwC%K;_thUK4{ zJ5*mTw~F&(zG`r_&t&fMZ6B&G`<)eEw)21HCh>eT*(H0=EUj5@y63}Q!MEnVH)o5? zV|G~7xMi#AHwFF#2_5+Z#(y`kSU>1FU}kJ(#hS>V*z)JcrPZAMGF=*KCTd^jgo}4& zUGX@y;a5Oe?uHH1v~EkO{Nahc&~Bo`vV3D^qe6zi%&dLq=N13H=hMuhp0w$_g?)nE z9P`N}Wd*~IJ?FOn-}Q&VDdc_q!e65Q3;eF0*_SYZXX=S<;$1r1e!a}O zXS2A$xGTN4e9_;xhmWz`5>U!Ymlr5v++ulYm-@dOB~L^cKU}%L?fk)hn}&-rjm(A& zx62tbI9jt_uqYmBm>PHDEGq+>L+RxMt@CGxyq(ptE~s{aS0>B)$!6Uja=987 zAGNZdBYoK*Xyy3LoTg+Pz?r>gr@%pCSH;h?69<#|5k{QAY&Mz_Dx$WSH~Qd`D$N!Gf>cFU~N91Riv}$o`jCZ^lFR+)Agz zntoSbDd!$q(ZZH^?Mg!ST(O#vW3w!pUuCcDdmIi?L6^7Osw;C5dus z&6*Dz*#6FMza_LcI3rV$xA(_?IpqmI3t8+I|6sqN=+o|(p}N~|;r*w5J~Cpux96~C z{^J*(^5?>{1@#YF915Al7z<8o9`t3HvpiYyvgn?aZJFxDRTK9aR$bY~9i7n|VQL;1 zQK`wy82jhwrpN@|mem!>o{yXk%uPA9|Erb5*WY&<_BBuV$=mdu?Tzt&wk{4HHIZ8% zXQdx;UNBohVDEIVhBbeec$&Pn4|OzDJrFy!=bYM}!#ln1w}`Bspy3&^o1sm$M4qd7 z15aYZ;t7!|ml{3fbuyjUFPbdA@Jw5w;PZu;V~;u({kB{7@5rx~gR6BJ_VwPhytQs- zzr*+Hdv*6ycfQ}uAa*g`+E3?)5ciIG6FsiS@!LNC9p`oZ!Q+_!>!WQQMH!Os#GchJ z6At}!eM9uprMi##dF8I`U%YeCxvjs>X#C%Q*|%cjTkE-3-o-siIj-zy7o1qdyJ(Aj zr3%CKplMH5n$Mma)L^N;AeH4%;)~yH2hXyqNNjs=tT6fSI#E+*wU`V+rlTw~OcpH? z?7VhRD@=X$1Iy)c9y41v7w_k8QQ(%yCbxt>ueL;@{zIaOC9eHtzPg%!*}6`aBF}o`1KcTgZJ%UQm^;UVn&xiDW=f z)B0z>%JaXngfc9N-DAajc~#o)sqHNhyVF*O`Yyd3G4qrE$*T#OCp2#!m~R$v!splb zTMPYl-|jhRIn%9}CW@Hw?0YN7@^NY7{FVca z?OX?+@Td3$-7lvmGd5ZD5aWJ5ZXd zH8*X}2}O&=kJ-L?TALZRUi%(p-J&8c_pA1YTMJ8@%!2Q+zb~{McVb;o-smhfcP7t$ zVa@rK{~5omZ3wdZks!Y&=os6MDr@x>hrhHQ{q}a}!rE{SNhZTtZ(s8A_BJ2L`F(Uw zL2Q_;`o$Ttwo!BTF1{m`b=Xe6MmeJ5T;^_x1=jrbRnG!n^v|s+jcH0WsovihJGZuj zqeqv8Yr{E>kzsB-2_fw!V1uM(ry-YPfOT=6Sn zoAr)4*LGd|_GJ3H=APE?-!9jkRV-^bwRvy;gZEZXc3gY6((aAY%d>0?a~W=C>?mns zxpn`O*okM~U(fT1sJz1T<_KGe=jZ2BSM;7YwVTbvAEUYUmHm|4Gjx{xWC+?dC%^EE z&iWPn`euu_$zA@prF_Me?Ui>;+!J1`oFaC)>7*&I%HE?(*B<2btlzu;%6^fQ`wM68 z+;-e_+j~W+hDPTJyK*EOU!2e9bCQ3@?OY@MWAOAaE0;f@zmYdon>booZ-BCyUVHh*I&k}=I^QfJvaEvlUcX77|biMzQ=au zO-rLOZ(9Dcg$G4G#OQ2N(EBjKH)H?J<-*_hv9m9lDZpUOyuX`egF&gW+nYO1uWf(G z2V5xD{IR#jT0Mg&;YN){_Q~(lKfb+KxJ>L&s8HR-1wFSs7*}~1mVMf$yix5&gH*w$ z!fCPhJ=x7FGv7KhsIoWwo^of|)aTDXzI}B@n$7zcXTgWe73ZDmcJ?j3{a)b8YyGNq zf4}}b;Vf3xqI{`k%9oqx#auJ}x3SMQ`^IVZO2;fWD*L(So(T1|_Ah5NbZ(6^|HPl^ zyREl!9s_Itfo1PnT={?0t*H=g;nAeR=Nw|7G{z+en^r znqgD4$u53teA<=!_dBzvvIj{_ar+Yg^w<3RuQ*pRbQFHQc<0sqlarIb)ZY2I(81|) zXp6(P(#4nmI!HG!zaFG2_E!P13l9=syxbq(%rtNF z-v?j&dH)=2xVPee0L%FYbqyK&81MF(td@xD{>rDKp7`PXg=@Ymx|+}B=Wp3JP0>93|JT~OoxE4&ugjl*P{DmE z#j!hEu{}8GNwdVY?M}019L{nUcUHHSJ89~=IeP}L&w^yWRiV4L+~xb=Bw1bTE}!~0 zQ%(L&&Fc@w6+80RFgaE;@-5^OiE^~HxS-4K;LFVTqq*7s!lq(Awz>&P%a(2OZ=Cx+ zHoBZ?xyG_RMPVDPIg(!A?%&BgDf`Raz7|F{0n_LxQ=Q7=)$WoPbAFx4$?~-0;7I>g zy00a3*-GP`_Q$Xu z5D2?Jzwt8@f8WJhIr7;>z6yEzl7CD2cc))F>vqlX)cpo7_C)bXuP2qVZ9ZGuw@*Pf z_~3K1h~pQw*nj;f&;I`G@ny#sG|%Hxp2@M{XZy0h(Z>`Fn>?cIgYp<`zI>he;Npwo zC1p#xE=}>*^;u}BrLSRk^HQzU>vLPm?;p7N(A-#A-Nsf+>x;HzMaR<^wM{KW^)?Nj9q?4JF4P1i(J>I@}j>_qyKLD#c<7j z!rMpBqi(%%P+We&JZ^DD4cGSn%-MJSerr#4zaGu@bB>}*$`j2KzK?ge-_OY_biDBV z)9cCycck5fG^{v0BKuPQ-!5fi{^d|2&$NJ_m5upl&2BsWT01p;Va_YJ-WfFaHLqU& z{$0)EEmwoet(Se++-RSu=c8w0k#(W}@AC!5_6>|(hg#G8CtsVxJo%3VpNWr>65pP4 z$HaE~dv(!SmyP=rHu`UoFhk?Vvsr-{)H>3YZRrjxfhD6i{}3D%ly*3cY~S3$pu@CtwL31uj5sU`kd?eXuI~4 zKI59V7HSKU_zpS8)rj2q*=bV$>HLA9uNzl>m~vQs@ix&Zwl}t_{nE9%k;lzz+0gn@ z+=_SF8!IbbO`b(6ol-TQm}_dwHr{=I`Ky10{DmcD9rqfwAKlz~VcWgn{(ScRZ)X|` z{c+IGsR}%DlvTjE-SS5KyUpyY-uTY^Jyk?Xtfv2c^lT>9MQ-8!bRA~x%V)hxBooMykHXPnLSxFV!w zz44VALo~lb3QJeTGPzWyLqEgUyGpO$`tqE#ghJmDw(x`HZ)c>eXS&F|TUlm3`;0rB z|L&IQU%2!|j<<&=rYBH9=7UPa?)%$jD;&IS>+e(^B)Sn%8Y zpXa7GJj{LPH%hcZGU1bXXo%Ho#ySh&H zgRA5GNebLsxtLfLCRLVg_+Re1`F91IbPQ4P*An=r6Z>$1S&Fh`$fsr8>5^;894m>q67{Hr|8!KVGq z-aH?BepS)lG>jwY&S308x~Zx&!2nyf7bittn7x`_LliqHaDek zI#^%+p=P62GOzH%Zw|{7-;x71e_Oug!4+;k59@Z7%u1$@c2D@UC#$erlZ@V87I}W{ zX6^Qbujeq04UhgswOrxu2nAAe_bTI-lJSwG=8;ut*n?<|kkljRSb7q2>YK&@jY>)#ZG7ff$+6mR@Z zUJ_qk_Uf&c_KeN{9C9zuyLrUn`d3aVv40GU-lm=QFMZ@?%-kV(X8HG9b2q#TgoPN{O#{Y2Kz1t=dngkZ#Vtl!{Xt- zf9|yMZ~GZ{m@`(Ly%Ss^E@QFmuhIeCM%M1%62CUGw(B0z8)^j%n$C>zxv9a`dn|{-pmck%M5P*DXZpmjNv_UM_#Vs{oX%^9=vb6 zQxs)fs@7ofV{NR8cWIgKxrSM3UuwVcxa04e-;*9ZH>)vb`w%c~zvA}W zvKD&&`!s`}eNSGI^NrEqsPW1i{vbzVl{~ z`xk$9`p0E{vo~bOQEu9ByELla$n3!^nR8BMW>H*5-Ckz5jez&d_1i_~aX(n7RqvZu?J%M6x6`(p_H!Hs zqWBy%4{q7de^24=>1&5Qq}{L2U>Euks5zn!fX;xH$y2N|;=4 zSj2E}Ipgb3A{~r3HZmJ>e$K8Camd&izau>UcJm!h8zT<33;)$vCNWBu)kfN{cd#pu zKk&qo!Hl7UlkH4__0{(!%NR;O-F#d1xxxMK(}g!Jlo%%GzkBie(f?a!J)fil3#4oI z@8Mc-wnj&fBVpb-2~CaFvG-#beEt=Ed-Hdnuf5Bc{V^}rKl`;k?<-5LO~ciXn-2@v z&tkNQNItGFQ}FG}(MQSE=Pdc|Z{$qyZjf$}k(jd3^3$0d36T~#)dz-5E_^e@pVaKh zR9F2oZ_b$KIcDh?rHY zn{nY?59A|C+Bt?lXbYhiwv5QeknRQ}tpT@2Y%knq6I_}c5d#t$sd2`PP z4adU|7W24s=n0D$WZwDao^0xtrSSZ@$})zDbzMv3e=uwPy{{_D*p=_kawx;0`mq3qOdapD%P|rh+RPhkHEJc^ zX|vq8v%2cR=4c{vCVgTbVuBa%WEUbK9@%D^>@r_#%I(p@k*; z@2!&H&9H20?rWbczL@PtB-nv&%LRSo>T+;kU5H6c46@tlWF*+p6{+ zO>jEkaiHhd?qvVB)rAj~g$35EYI!Gdr{KZd`Pv80m)D9c;9@KPV(Mnl&o@VUL3s|> znPdDPE<9Lrw|(-B74}ct1k6P0-~X;-2stmCA>Uy?rSC~~bSC$KsZQF)AKvRv){<6q zd~=__Ch46LD%>Z|mPKNLr8C}+vq-|1%+8@2wa-~nZMQ$_d4hA+>*ZFmr1e>i_f+WdBj zW@EIhcA5SGJA3=>evZBp8rlKI z5B}7I_d9Z22yQ#STjPM=aS@yF;-ptjjh%1m4TTrRZf{e!)M_Zd|Fh-9#xOJINpTEc z&Yxv?SH0x${tnyfH!dPMv>3>XI^1G%LFm7gl zc8*1HwwztpieAlxtU{~yfZ)x?IHK>%Hi(&cNj(x~+Q;z6>6}CV&+xbX5%sq$FPuFW z-W%6)aC)Lp#ik3EKRG5iGqf4Z^kj6G_hGv6)5K{)>%WrOc}Y$j6E3&jb}k5RztgyE zV)~3()}NRvXPn(Fl(GM0*UoS1_Lq778pl8AS^2{BRfkhzKkJ2~b~Q^=82?!pNndU@ zXH)YXm^2?Sx|E@o0(fzeNWZs)P zm2Yppon>A#|Gmr`kAIDv2@hQs|Ckr>q?YBBwd<-4?(8xab2wK-$@e=)I(^O5XM}4edSurg#6Hz4Kr7 z&UblVRha#@-I=#HRpQX@?0s|X9yVLvH|gfA>pT<3 zg}_!HmFIKM%f8sJIPvnCGk~f=Fh2H^0NHH zm(9=EzUqtIU-5t4EKcFgEU5y$_vQKQ-N#5`>G)v^g}c0-FSBgVezh|VpPK$< zt^2?7&atD;3@5URUw)1JEoPjsr95k!;?{zi4dHXn%?x`suXfdci_Ow4J z*)VtWzmWG|jMWrs*2rJBd#rPOn~TY=pF%Bjyc}KH-|}2Cb?-hN@99=alnd=!V7yc9dBE_WRX*Wy7#Gk3?!}m%?IYx^M{tKjARA&CoZ;BT&DeFJ$ zW3X_+gKsYmEL=3Jo`1`q9Ut$N*t6`AyEwayM(nbG zYOgqKt#{E*%=I^!w?Dn4=srs!7uPYT>o2z(?|!(_xZUAxHRA$KyJ`FV`SzYn{%OAa z-`>Ogx9lU=Ip~{wn^PyillkgfHPZz@wKQfmfoBT}KOgX6;NaBu)%;>sS<$L$7P;a) zqg}nb?Tdl?tXT0$p}RJhqv5=FH^ZO3w=Fw48;*XvRMqh?S<=Do_6nnhKBudH z+s`nFe@-)=Z4xeU*k0kzYrQg)`;*Ff{urOty=SnfF0OvA5C3ho-*^8oX<586-sJdp z*~jTh^1jSn&1#j~Z*?-Vt*>Ev!1*Ta^8Dz!|CX8W@4h=-b!X@C-*0ae+lxgu|Ewrh zQ<}!V(4hZg@G*J+37Jd_?mRO&{nvkGy^^u-|Nq;+t6rF;8y>m)+|U2j`Tg&oK7Go| zY1618z$naeC@8$cv5jGo5qF@&p&1fw5=#znlJBzjVE?zR5qwt#;tBVR$~HInD9*_Wi5g$10SU zUNBo@>woqX^He?$&o6#SL676!O8p7ZYUgWL3oN;}?pUWq*nBWF`oEuN$HfUwcZTPP4T<5lGqolO>VfqT!^LQXO*53 zOTp7q%!kkIJZ_`h_#u<0t#Rh$cnx`*M=lw=)&4puMKa%d!B8bvvDo=!zZ9cK&5O_R zeX2X!!~dVIJ@%PXOSbRjaxXhU!-b&}o2D}CIW57ZE7~$I?BE5MB!sa!y^K^zyI`msWe%2nD zA9B1)13E*^wcayzeqK01@Xv~Grje9Er~%_5_y13MUB%(RzSee;~ihlhK%{oj9#NkpMpP2qdtD?6{cHuG@3 zJ_#MynJd;Tyt3fe7nQBj+J6ihd~K}0R}}QF@>;Llb0J&#L&RV6hr4y&{wxpO^W(;o z?NMu&bWX7SqkZ{v|5E!KKc>2D`7S@j`2hb%hO5`~7qd@&uB*ln=<+rGibAb#Rw=dym9~u$%e$cqK6TcqU{A=@rPay_x>P zikts4Q_uUi-wzwa_%YaMPjCHj`sCan$!^;@Pg$<&j?6tHuX?4^&dJ^8RPG6eS6mTJ z+n4EuCh>`#ni|14Pj9>4>DOLWuM7QJwyyddvs7#4nh-|Sw1$l<3Us^#*16Q`W#8{_ zI8n%1_~N+Hvr0~jH8ak3XLd0ueO>o>PR+(2Y!XitK3bmgfAf3QqxIKr$yTt~AN$rE zX?U;wN@h0Of0+YQ9@jJT{55vvC9zRq@~RE7`_$N0g(%eNe%;HzA@=yU z*&L#F%cnI)$46Y`5In}hqRMBwKkCpi^IN~BT>Y2Bw52`7%E^hhq3n0DqRuaWzx8jM zJeX_3fL@lbM&u+h;T9Xj#-BtnmErcv0km(8^M`RXe865D<71yp>6Y zZ;mh1<(ZW@+}G?JF|&R3F-+km!2%j4`7sgPP|qKg$j6R!_L*Y-wWoqv&}j=Hkup0ERtb z4eK{}i2isTpyGdb-?XR|LC&m!9&47r$~}HIrdD;AOK0s?+0Q0vx8fIuZ{60McO>HA zYTi?I&5Tl&`KzuW)na%YquQ>c~E zXIEw|?*0`@Gp_z$ao?_^FW~#2P{B|D4XEwF~^*V(z_I+wTxj8e6Z+ zI78#uTkrZQpQQUAJGn70aF>gGJ7?#QeJ^|W$7^)O-joZKlHyGW+r8f`fhu&7*=tv685h@xRd)f=RR8@#%tOa8M@}GO3kwRb0fa_)`V?0 zGPoOV{f`!7nj`-}J?LMc6&u^@yH!3c1^J8K@GrjOJ>!E|Sdj-8!9-Hd5Rs5VqGk@EUq||8d zYXR}OtJj}qs1g1>zkmJQ=CC_V8xFlYpk1nRE!T%(+wb*f7pFHgI&cVmm^0byJj3ay zqL#J`QrY?*$#^i^@$awhe8`pRcKU@UqmS$Fi4!;ugmhYjDQ?ekS74vQ_todL#K`rzf z&%e@{9$P#%2h6Fv&=XPeJ5;l5#aj8=o&Of`OW$^WRFN@}rS{3h{7dhX0%hteqvsc? zExxEC(WSiUb?F(VYi=_Vn!@t_Tls|FTIKsfqTxbhduY|f7Z;ootbQJNAb;ufdNW%$ z1=Y;HH7%}QVHZn3IsR``toqaR_2#1#{p-9ob}QO6URB>cvd!)`YlYzvoB499sxR!l z&hga6p23YNN|^2H-^_b@5AB!PupP+mf1M=Ew8Q4Y>+@d^zZSY6FE1;e9w}q|q05jx zy)iI-+fEkwj@%~(BER>Y7TtbYblz_Uk@dedcq46P^n-r=OyBZY_S)BJFGZr(-+jCA zr^lNk69uzWCEotucjM3F2P!*rWM)h`VDXFLTGFD9BTA(Wn{FOBf9_HTxZ~t8=+QV%qM>vzV`_vDE9XoN{||QDVG%s@;KP&wDHi zyMA5B+FAW@eMxMERqw1>QKuh0Fw*ZmsA^Y!qwikq>PIsL62E=pUU0)tXCL!CzMjVR zl;qc;6TDP!xnIaIX-%I|Qx@2I)APtHLmst*j;d)Ex*zx4Ub#17Z9nt5d#k_gH~Y(L z)N-AxhVfSJj#+|*x(D2E$N&2K`O^lrm;23*7|&$BV10DXW&iu_mB9(IowcFn54NlS z_!cGqM7neT0n3y-Yu}j9@+#{K5xvxHBbQ|2vZ8r<#rywZf)08$^H`qjzfj7?P&g}t zK{M8O>RZ!9j`O~ge%M4YDsGrB{<11bfQfSoZ_T__?cbEUEH8+DHO*#y&GM-4_TSTvgGzw11xToKOd!9lXJbYOWfKr(aXM(SBcdPx8#WE7%x0H$G%<`kfPb z>h1sa3oi+OhzN4_SHHEh;Q3_nyXRJa=5Vw*#DDAE8oybmH?Wkw{wH?n+U)kfd{)l+ za@+>}*LHiDRnGsEv{C1DZS4BrzpHX)K3VMdb?)--aZ}ztzS9(I7vF#O`Sa>2*)Kk= zcz1g5>k~%|GFS9PocYLUWfQsX)1xa)%3*WPm|85`a`g3wC>5cuRo@tN886lCU&UwF zp>2?O_-KtrT>V>Xg=a?=#MWDW5}2)g{m);w3u{s?3FS&|xSRF)a+b=9TW6PPu4VFR zxOYkB&MJX!<=Kl$BZ~t+N6mAcpa1sWMBd(It8@d`>`&d|R(Vd>Yv<4R%0d6`A1^(q z#<-;9+nb}+`^_1jBpU^Fv-AgL?)<&tMq>_xMDqF{m1}RU`(60reWBN;ILDZV-AkUH z3-VCB5?SjW^g_(2{eTC<&pCVQH%>U|`ngJRLXqxFL5ur~wN)-$&n}zF<0dr0=-`y3 zJB=s51o3>@@o1shffb+Fa`NBS$6pPbcVqH$)@C~vt6ThAe)jMEf1$pimaW0z(W*H1 zWd0nRrc6EI&Fp_zT(-I=R0^pu_$_|+Z0YV2NjLd}A=ML-8#KC($g6$ZJ>kNFsA$Po zHm`OUy=Pjiwo)kRnv^bM&Q!Ju+`m^%v2XPXe~|21%;K>7`TNRyNf&FgCw1+8wZmej z>g}Lc%mLPSB-+mIF7k1(+a~ULhULI5hpS93yTXMe7;lGW-t{+Ooc5?yM~AWJci^YZ zY({z7Q4Rt@O$$$PG4EjXI$Lk%`geWo#6qpY9PxCALhTlh|0^CSbK9`3=AS3%$s6Th$evci{wF+V+rc-TId+oL z@~4+2URC3%>TOZ2SAETUt3mA;IlXdgP&ENmtY7wO=2e*?cqqR7=1wwk>;j z92;UqShjs$TGaSge(7za#&0UMa%@+7O0rDl99OFTjeL4P>Z^VG|2l!#<2nv&O}rU2 z-_G4xaNb|-LX80bDaAE5Wf_supLv(oMZP;A%8=(AD&>-`ak=@cQXzN2YT5X|jJ2l1 zEFVHoiJx41;GN6b^)3t!=Tk1VW*Kq3{IZJCV4;USi#S^k%K?=Do5TcO(O}MKpEbPP zg+DIL5^%liZYutmbw|+gS6`ZcGRz22vS4Rt;VqZ$EBhAT!!7YK;W5jx^P+*FYzgkQ zZg-E%->|-w^&qUpie(>DZtsRVb~V3wZ!>OPPgFJ5Ni123uX6NpG8>038m6|bosn7a!ki9U)RiUA1=OWG(v(7|s)a^TeNbTUh zbN6mDUJy?b({VQvSfD5Jf$NT6NAuhAjXxjm_-J+4si8NGMgR1V7MqVAy&*EP^|Cz< zOZf8|0{%6z&pI$~t#6RdQIj}%?TXlAer^r|FD$kQ$vM|weKWz3@#nuI%NRl@H!d}= zkC*+;f6)Jr%uipJNzYepPV#Lla{g%Gsftj&UDHd-dP_ps)YCgX&%u##bma>YTTGS1We<=<45jh5X(>BDDFeSL!ftNM3V&Tik_- z7ezC;1e-qYD1Y!#--W?!&-8bTL--f{>aSz?_GEMIc{P^F$6j1>=ymSD+UZ=)XlMQZ z*rrbw?GJuzNiG z6Y5&#qqQJ&DobRsXwG6< zJ=M+IFSou<&5U`Q)?Z7}_y0?I8E$z=b2hvveE$91_xsa59xQfxd-U*|GqV0IFL@a^ zT;G$$Qna06YA5#sg-e=yt2SPbd{sPoLB-FTec}(wANicQEv|6Zn$b3!bI#o(OAOo@ z&zYQId3v@%<0-$Z(ya90-8Wet2;8{&MxJ5)o}QQIoBJn(Rdgy!EUnl1t(?L0z+Paj z;``GH3RkQmtRHGPY_{G{~n)?i>>|xkAqWL#5|a8ZR_92yfD~$zfQTS+kz~n#_PMi_`F0G zdrt6VO;>8#$<(d9fk8%HGjo>Nk;5JBx4w#MZ)Zx=dYZwqh~04Y?n_H&$Tpu_-G8yu z?%9HddR^Hx6^+hDfn4##sS`4~Ckr%8{Ks;(_N)8+$bTowpE;lY$;=?v5P$MgNursb zeQlT|qvRI3*FQe<*YkYOMh->mO*#s`SMlumisd}F1>14Vytn3 zoxemcdPQAh8-GK`X}xpju7S=T;tOcgl$dxh^zt9MFU*m$yM z&TxGDckQ%)mVehzvG3|L{K0;?k!8ZAbN5S~8x*c=gS9I!-30Kfb^E`u^rWcHV~5 zX1kmgnN^Ezce}kebEynxgJj05Kf$-7|FAMwznaN#Ero+cbuqgY`>_raU0r_XmKSyx z96Z?UR29n~-*wMC?>onZp7tMCT@WjKzzM}OZkHuG=E9ZD8=Tuji*K|vYu~^hgKw$bx?YB3b zLLObelD_F{&)b@5DUSu6_?R0Uw`g7zN)Qmbxopx#hQwS35ycnoC!08zO}Y?}Q{i+- zP(V9!YI>pBzJ&UM2kbA;cgfDS3ptUOqc1RdV&?%N1~=tZCEK6%9Ai}Yu~>+~motI; z;QY(ym;W);-2c|??t=*|4vF9I+|`fJ`JMmb*HgJ>5p4}V>2uruaac{8lf%~YKii)1 zpbzJ=MBRpn^vf( zu>7J%SkJD^gK^s%y!w_ktvWE{y4sQ%xivB+OJeF9lGznSW`4`I>FCK^Fss3qxvA}IH=Y}fHOBekP`Z6*ePPw4f zC3pH>#g?~gAM^hbypnc=DM^jx+k?%!{vb=ZZ8&_2s+p5^Upos^jbR60I zRm{`hi^YN`f+gTeHs4n(nQ1yw4a$tGzZt)fJmFGi78ko*BV6-*h}@YBx3&m}W|95u zLJYsH4}2HA_)@*WeXIP*gx8nqPTimJDf|L&(Z50~F2)Ow&Lq#@w|nz%XV16$^nWuI zZ3^o4Y|uX!_hrGiy_sSuw|B+LcRSvSGdA z-@=sH30!Ma*Zn(~<-B@VeEgK$M@p+6Zn?(xy!_-6g(nU33e|+z#U`#w`mDWU2XDOU z`z^Zj7}rHHOc7q8RriJ4 zr#qQ*L5uj}!~M>UOF?6MSG&(&?oG6>Gv!p+)W7F&P;TwFSaME(KCjDq%iG_SnK3eMik-qyeuZ^* zb@x`hvdNa%&QVR6F)mC}?$2Ubb!)z~Y!_R054_1>ZX=X_#*vTVP;&(Bl6`)&qu zHhep0|4#C6(eu#mx=Qu(9!VE&i7$uyd>zgevYl<5$Ko@ealeff^VK%@4c`A7)@>6h z(6^Jh!#ua)%}MnjfBWg14;xkeo`2H8SEjP{@&8vM+>YAuSF|!hvl!-IX6R7O{Hr@j zB(!8!LlfgNL&a;_qy*oqn%%bi0-~9Z)bavrpWnl-e>h%2G&v%P=&-M2bs(7Om zU}BUZm|(sA9+PZ?86SfO!z_lI|Bl~xGLxNpZP%@N@v`;HzRUAJz50CtBg@Mx#<#vD zOj@Ct@IJls)Ha_Fix*zu|DYA=HCZld`^LYk7w+6J`G-m7XZ8&pDz0DetvJiGi_2_U z-)D=PZzA%n%i^#6;0XJvsw8;O zf0tkJ>*(o45B~?BI=?je-8bv$>slEyzTZinzjbr*GxkY;|L)+~5m@K(`N{%$ljRaT zL23&R9(iVRjAH`hlDFXt&Xzv<@_Ose{^^zT3*VJ8o_fHc{@|s?2FmctFRBo@RAE~1A3g&NP>M&tzNYJZhoBC<;->*4_oLN5`Y+SsUYem%@bi|#Rxd&l(GAHV%KTKi{lRq?6$9;vV1&z`id zlR=|jvf}pM`}<N zs%+o$&bMJLdwtyko2TEOUoheDTU0x%5tGAb{oHp26KWk zi(H|}-3KarFU$P+SHl}8T@pQ^R;qfzbAy*W0q?5R&EGX<{#qPmrpbPU$3eSwm3ZK( z1^Sy<|E-<=$MR|WpL0|8H{>-vm%sc~fMM>=iu0GJcR%IV7du_fx0HKsNW$ACPQF4C zjW$X$=8InCAAEJV=<2rX8}4oFv{^viK$i<d&8Z6E5Gs)@;Ar-t%X2@V~ROYahQ> zYM4@X?(V;q`i(JqaW}qqtMW@2b!cv2JNBG^MY-#R3elL|kE7fbj=eSZOjv(#PpsR2 z$!h(>2Jya38TJt>`|>sWJ=_+{X`D##RatQ*y!g+Lrj!YM>yA9`@pu&RHwZ9< zUEjOehm*myP3x$o!U}^an%3(-!u<`@(;h?qgofd%|?V z;(igiExd-NEO&nWna1%WBIf?9De_AFpQpF~{xyI5B8Ka}44PlX^XvaSFFQSVeY}p? zbBjIZ*SgNcT22*Nrn10mNAZ!+0>AdB*Cw`IIJ};@>Q#qR+7x-2RX#Ob?cN%>Yo2s2 z`6}4Y_~9zAVBbdv!3)CPv8)nI{8!6(CDYCx{LK*|oH@alVZx@%8z#;2NUq#=ajWK% zg^L}ozW%w)mu0)fFM%)fB-YMd|HS!h?Q)k-&%YQ_R?C$q zrvE%OckbMD_IX}BD(9lww#~ZE!t_sc_iV>|6Z;HAM9v7D?>}hwNcx5NBHb4z@!MD% z{xVFG=s4i2H8IitrQgbbZQ>569+mbk2|sD|z>H7e)7g@u*ek0l%;n6d-dPpM@nyzR zk7H~Sd;eaXqU%;}cYpCto(Iq7HLmz+(V3@eWBRRE|KOhW!Ad`iPw{_>5B_JO_4faE z63_v|J1?7|1?e(HWqFOc>m$EpWW1N3Rj;V>`rSuTPb!T zSZKqye=Dc_`RQ1+`Tp`{f9~g{I`;K4Xqa=^eA~A^R_E9A?|+Lp=h;adNisOxS#8Ik z$GQ03npg4LPQS2q5^|i)(mac45u-=8)AE1Ji%Yk}aWc7Y@a9nZE17ch*8i#+M!(RT zg*A4{8y{w{SgrV4<9hSil8v|iPE7gG7cosVe$~kZx8lyppV|9kh6SUgSKWkPP8{E~ zvl;jg+SP>D-G8uce%BP)`uo#9*)KJE`d!!ixF^Gw;)k66cDuL#_;)nv=f0zg9-%&y zW-wXRn6e-A@oY%lBqET+tfq26`Nx9tcsAC;e~npgGX(1<+8ncCAop9gr2zOxgtJikF<(A(%sL5PkH{-tF&&e;@dcRapDOtlZVSV{W!~XwAj-Gp0 zt{VJc&#~L$uEFWW7Z=XkacH)GO!yU^2cj2PtQc$#?x=TQxg^;!L*R>zq-;k6%dy`D zo=lm4-7}-QEq-)yPnx$;;Q<@x%MRxLqt}FznGWo%m^0<%oWo1kob7s?cth;`{x@%@ z?>=0d_|x&0u{FQKO!XxOtlMXZr|piN_nm!?)yw(n-+!E$Qr{4>b-(D#9h?fw_uhWL z=lyN5({t;mY-K39xADvLnK4T~v+v>j^DoDB!(09RbF-LY3q?MCxqHv^hG%`RUz$krGwOrE=cp9$})1aYm#Rn=X!#V_`%t1-*nlYYb$6FfhIQPIbP$+O|H zz@mE9Wr_ELwynCLk;us4!j$lMg7UjI?vw{_ve&uomu{&jcnzdn_M`}Qq`PXsRiv)k9WK=iDT zV}a<}Jmr`(*R}ktEY*J~|84W|v8%9GW({n5#N3;D#cW^atKL=XZ6^P#x*C73(?QW_ zjktF$oBI9w4c~hT?6vwFq}Yo8a$I-%aQd^@u>*n*0FWY7?%sF@GV@vt1*W&u|I@9IveBZg+L48yFv!eC;t*(Za z%ncVfC$&XbO0Ds|yuvxh56(WjSPySqqHEBxFJV%GgG!C&{K8L+i+KVaPoy!)ncr?P zH#xy5oA1L{!E3@T^KwDf;syFkXE1-8B6Dj}71y7$uct*sOC7$mF?#kV=RZED=Fj^y z|K6ibK9_ngF&x{n>(2w`?z!CS{S|7`4qZuhb7n}psmW1Xd~UJFJeB*R>?)QY`xcyj zb9?qqx$KigS-Q>)zjvx!^LUU{sP$E3%aZ5Z4(k1j4zThv&Az=R{myTeCf1FH(h1g# zGj_&&s=4u)kCXXSf9u!Q>*m`Nf8RO&<@%PA>aGL-cTI_(^6CA(N1IG8^Qtz?S!eg@ z!2jrZduN0$NLRDmoxQ}YrX7>tJx5pGUDi9eLf4&)JHk{!YLDFQRI@X{)%(hxZTO?B034 zZkKq}!!0$NiEk=+Lj{iX2ox+Xd*I=GhHIZW|K(gWrG*MR zmi_(FdA@bic7|KqK4(7r(R_*RYR{?P_lk5C%HN)CuGurEi~oVkGU;`UQ@&iN6ECY< z9{t7NSN>b)V#N!-Hf5??TUxGUPkFRUnL%T5;XmsQVZQsS6s4~AGrBXSu$p8sywf=x zcrj;!5&QOcjmP|)bIM$@7(a9>vMlSGW#qub^RMm7{8hi7&OO8Ur{duGmu6w08CKZRv)vdh-`anAFnikW=kf6#3U|bgh~H3P z{Lx<8bNh^T+8s-mTU8Cun~unYUz{2KUQMoY?|+k(wtaOh!DioizD30eDR0bJxpv95 z$490&{%d16;O={J!^}iI4X@oc-yXbX-0Az`|H|mTr@PcOo{CSk{kwCoJ^%Zw&hHeI z74KY{UJ%TwuzYpD&5z@MXL5fppEz&gs>6|;AD=VZgjI;5uKCU7$J)N%=yshnJ9m$O^2^&A)k{)?&OLp-+4terwqwq(ogHr4ZdmU= zY5S`Wi7od&PCU)&`F;7HOH=C2o~E9){#wp>bF$Nd=q0LmN|-z{cT~M>w7+q4_lDWi z=Fgi~*}3<($;vNZ9?UoUYdVR+>e{tNN4{`%8SjuC5f`?#9B@u+ZV6MmxbXZIId=ou z816$XOTr${P-E|FI9#^#HT!nwQXtA(W0)TvDEUO0`Crbd z^q_mI_wP0})~qo4(Be=c%<(Wxz`^f#xZd~udpB>d@%eSh-@E_jV(SB^k9TV7?+!?x z-?ZRvrMqZuwJe*_nG)uS^)>vD`7@@T%-!B}W7Vyvd|%@jzb{z6Zx5r&gbj{Gep^+% zl|E;lla^lba9QS@-7giQ)l8YU@jY2PMP7;j`PAp1erswi*f-(R&spauPGWrS!!?s9 z!urI{_0P*ngtst|JV1`;m5(E)$3!XedNq`h|8>E)?wYU#IZ|~;atNZ z^Zu*F7w0R?Vv7%3b}cE@(eZg`#{G-wvz8@a|1D-^>Zv4iAmMs8d!1&0?1lEEKbk9M z&UmtQt)=L`(!AF@7GFKTt>pXr^6VhJoU&hkxkMT6Py6J0!iVkWcNtB^Cudw5QXXt= zp7#F1jS~;VgcvSfUCkf+D&wQ{!uRth$-VSUtnqkv_4qY|)%UI%J*!gEHuc@Ytkfwq zOE~Y&H$G|QeD!YEd&|4ruf#u6SklJTuqE}?R@DswvNdWmR(_osyE(qn#g;vpC++f+ zUF>rm&R(149QVf~ll$QR^bogiXZh>u)T(9J1G~7u(vhQ7D^>L4i?Iw@EU!Ki#u4@is>Vdz-2d>rz>;K!j^rb1E+5+JjMUDTJ zp4u-++WdCnt=~Vtzfs&|&V9k;LA*cHPxA{`!V7%$KTH#L@O!Pzzd!fl+{#>@hng$% z_IR&oTRw|rZJU#yZszIJ`{r(F;#+)X&a1Oh3}&^bQ? z_MDn!4goT~Z^O?vs5#`Ve5_m4AiHhn)zf{x&zNFTHcJ-V_+_s9SnS6Coeh$I@)t1G zg&h5OJw4}@^VEH=V)IPO?!2Ee)Rr-d}rtG_#KO`nlE|v zKe6`B?{&>}|9&ng6gc3w`d~)mttSm-wn9u2i;(80@a z;(_;X@0DHW&Jd0Fs{Ql99-<-S7Im4KVLCja;^O`v~bR3?| z{`4oG`_H#Kx=i)oei;8Kyg%n~>%sk*d{+NIWL#go#Qp01Y`)?~7xqa9JXIPDV|V`d zUH|&rosTxRBI|rtDa@Z@aiC9?LHYlaB|)=Rh2(k2d|nkP`iv`TT6ajeXSiLyDvN}n zhEN7asii`o%O3Wd?~ZFN5N&6?U>8U0=R zbTreyHy^5%x99;q z@z-a4JMVOPpRZ4L#@?U%bm6{;SAEKVpRGH((a(2YyVQK{-mlV>5-}cKh z?=64Q)Af#QR}Fsf)9_n)N$HftTv3g~4dokuupJZo-FUyo`IoD~DZSl(+&x_7WsB8Crzi>(HNm|`u#@Fv=ZGM_3U+fgj_ULwS zop$cLo!<@mwu@=9Y=7g)IDMwTHPiI}2Oey?u9TE~|B0oa9b?bS-0$&q6ZYkOe*+pI zX`6mz-~RPHrt6l4e&pQ{yn4Cfx~-SG8*P_Q+h(>JpC7OtvqHyVl>eKhRw7S6Y6=*qblTp;Gs$LXdT!ruP|{CzC_#%O2!T z`cgM@#n%J7?yr8y@z3(@f$eGrnkjD+-hO&8t+LiBAliu`c~{BPOVM$X=kNVYukDGy zUi!DXRj(|q=HMUuAlZN3_w!lw|BJY=hd51{@woEW*&{It7bMqQnQfBveeoQTgu{+c zL~b9={b^|ycS`<(#s0#goN0ycqKY5f=XyE6{nM%BAMuM;UQwIXB>#PT-|^ySCX6b~ z`Nz*^a;#l;)Q4^LtiqXpS%vQ(G7|*V+4_5*UOoNvZT^e$w)MU$9}X^jUzYVaVdCUS z;S&)rS8>JHZb)u*(VHK9?bXybj68hhpLTFuve}WZ$LG3gV*Nfo!8zMK*UpQanslbI zobS$tTDA|5<9XV1#1dAS`k2np{Ncai$@$h(zPXW~9^d%O?)l~L63aswnT*A!=dt{G zzbrvTjnSINBv+pCiOBcw_y67c`*-vC*m-xW3*~p8_i`zai}|$1V6w;GT@fu3i$K*=go~VYm?2G=g(TT@2>q$xoM|! z?=zj8Z(o-8?6lc`u5I~2e`8%gthrb7!16-=q;Kt(%S3}WWL}uKR;^){-rt}9mU`=V zrsvhin)B$t+5PWs#himqj9dni+5Dz?o^m`fGe5`MC-XHLpZ{MKwBodp=uB(9EB9C2 zPcD*JZ+^-=@_x(u74IJ{$le-Sdv9Chuk-TVu9k%^6By^`{$+ppLFeiv<>*&`9bf;c zj;RbbNebSVW60R^^3tnMxA!-TJ+Cj{8M8+vlj*zSs!wUDhHeab=VJ7WkXmy1cMweS-FZ`TKtz4p~ySx-;J|O5TO7_}A?@#}4c-PHT*}*Z3_GJ8#Kl z2L{vFf0w^bJsmGN{dt}3x6}G>cYW9*qoy*;Vr5tC(^5sI`*!wuujYsuv^)LRRX&=r ze)(F#c{WctT;p=yK6g6!@p?*d{-j%Lllhn5I&T{N=shQMec!Xw2hJOQX`i_^ee#w0 zx63CidtlmNsqp*hVut(fqVvj_BrZE2{&(2k;?4ViVf#1#*|dbW#);{(rP&?UWiwf3 z@LIk2ZJ#uSv2ub%UhaIR*xXl}*4s+3&3irl@7ndUKW4xEz4PnKPl*oiw{{CpC`vSX zv3WE9;$MA1CwznE-`O`!%+Ot|u2T5K^E2#z5;9Mcnbm~7=9n>^(6oIp)jlSE@2934 zbN}Al@FHGh*So!+MV^K{wf!#X5GAqy%#EO^Elo*+TNrk(+*gsCzw3O{BGU)Yw$7Nj zw&=yDht+SlUVEBRL`^--JTIAH0)yI9 z+g_Ffne`HSe2bMXUZkg@P(PBAC zZRa%+^^1HBn?6eYn^*QTK|szWtN8jSsi&RWz8b$U{$b3)H|f9(vz32~f3J64JmdEX z-LLo6+3cF_nd|mHTfO9NlYA-5fwv2Hzx;WBOWfKxzq^n8cWwB5apC*o8yQ0+@=|~ z4&*gYSa5pd{r#Gi4Dws}x975cC_XjSmiYmf=7;sJvG#n?IhVH<{!|FncXAVzZ=65> zr_syJpUbW>%sO8*i&McqTCKt6SC-r~%l?b^g74HH-r)B>CF0N0b@BH+7v7%9@_mN? z!G*31KXRF0=a&gRG4HYx)0B7YtN&hS+m*fX^X(-ChaBbyPb<)2-1+-;Vbc$V+y^^^ zo8&_O%&~o;Ke3PR$jtkP*L*qu(nQGNUqBAi6F$MpC0BizY~-qyT6cK4d`Lf>_0^#D z=-h>`_1jXUPc$s%a3~Ww6_C&?y=m%&8-T%V6yYBW~=>{Z4**&8NX_#DT7KwnpI$PlU#7p%lVD-)suGE zKD7?ow^=WK#qUj>6XHGb$-`3a8=N^|`# z-F|wu_Um@0A3K{ipX#1#ZnpF_YtHLCmbuA(t%qv&#oIT}RkgkS#nSkI{!N=&b*Vq2GL+ zo*5iJ`*!|ypJfg97r)i|@zj*$JuOkr{r1@J$E4?be(=;x`!+}L3(p_#B`znP```K# z{n8|hVW-lDU-Lu%uVMXQlUq46J?m@o>gQADGQVu)@4w>dRB7+VowGyt|2ghO*|Q^` z8$3JoEO&Nu`Lf%71|M$z+dJ>uAL)gRKWd9_+dK%|yFbXd$Y;DSr|4qQcl+huvT}nD=9z5A z4WEW-%ktF3*>60MzIDdzt$m9dX3x=Sx4oW!`^%!6)%^#piUpZTorN>QB_mEkX>|jDR^G zVcAQAv{#)TJC!zDosRoEc>-V2hKk$w+@CAH*z4TC^v~_=y6CeW4E)y~Z`{d0`&ZwW zd4AfA1`{IBrYn62)-LR3oaerw_wA`U>-+XGb^TV2+acg6k%9#ECnAD=BNw( z;E~};-g~V?S)!EhO>u*JdYt#A&0Eqhq;J?SYSc78v@YlM{r2kE%g1fjvP=**V>gm4 z$-A9+>*k-Tx|>(Sx>v8_E%9Qxck=eP1KaiLA8%>c`8zL5a7Simyb#lKiyJR3*O_YAOql-Z zXVr19G5!(lx6gU&TY1&F&(a*WDHIy&kc@I$*;CM zyg&G!lfnAok8jm^hmUWoIpZU-=2mjC9MeA!zI}(6%zj$AD^$~dK0oKP0}AK**WTW- z(m?+B=Pi%#yjj~Q7TtH`n#WonHR-nWRfZi0-{(cDY~0wBdGAd||Cj2+LQV6o^M9Hq z|809}w(J(!gLj#%s$Lr!9B01weV6mg{sZ}~FF#qmKmK{$q~5{<2KS^tUG%eNd~!r( z!ofL?b7!+izUg22^V_Wa#ZE$NcohUI)RoMX&-q?`)46c@%CLi}b3|^Jaoc%iHU3++ z)N;bFzyEIRynnc1-_~5=7tsms2P&)YmG61zAj@CDVwbjGo^ii_?gjbXTk@B>GlT4C z*vNRp^ToDb-Lo9j8jDjp8In0>@cenR=Eg1qceXvXd5hfjKASB$+*FfSDV!+dc8S0D z{_^_nnY>YdR4L*eLM8|G)EcBpMAUMv;Xx?ev`X0<@iL2k~O>q?uzeJjtDl3 zKlpBwtHxv^7+~yRlyUa!H6<2*sed+gY=8FNT*+FtKmYQ^;(O;UALJ`FEMD%bK+w2g>lfZ+%?+sqc{Gg6*>&M>X7Uzd!BoJI3>S-$h-L zV=fT*V!_34^?r}=dlySb`CgU@mtz(POkylkw!0l+$9-H;^38=S=>-CcTV%S5PqQ=D z_i^xb<pF9uwInC?-q;GY9xqLkrr|vF(V|&bK%e?PLmbqIqCO9cQP&`_2 zEmWn|_Q%mTo(o*rB&OLcSFErR5%nW$IRUZwmc>(4Dd{?CQ^-tuw*2kWzEPo9%vN?)&85d8R^&%Mkis#~`< zPfISG{PV&m%^W4RMa`ZGewXA~iuix{@>%kK61%aNs)KUFpBQ&0UjdRjmAlXq}k@ z+k@*uPuWlVcF-{+@u@V^C9 z=1=?-@BQ;{aNYbV_pP5Uzw-XmI?esXr~235*Xb9Sn(Scljd`)d&Xw2u#Xt6a(eTeu zGf*se&B!kHw5!zr)O(j!k$Emlg`S=I5$@UfaHi(}wNvKL{dC{&=i=ahKc?8v^in-P z)i(Us^I*l!`bnR}m;Xr(Vfb`D_}{B3`=@bL7FN8GKc|K?+&r~@&G9{p=1o8UpZ{yr{lN8e-%o7(qZ`7ZJ^itC(7#<%_NzTz z@AXqV=wFf6g1W*}`fJWlng6zU#e|2;m;T{j(eV56>)$7q{Cucc&mpSx_rR3@RkiIu zW9_DLJ>2JBd;VwbefQQ0pUro4=PXg*{^G~Fhc~Rh$R^agTa|ojIiSYS=W=A0)4HCW zLC;Rj)Xtpd5owfKRH3gGb@=zvKa3$-mDAGBFADl;?3v>Ey*&7zgcifk-_Y+%%IO}+Qz`Zz~JfX=d#Wzp-F>* zfkA+QfkAfK=ec`Ya3#o0%CI->@<={$XKc{LjF^@Rxys;Tr=3!+iz@ zhV={#3@r={48aTx3^K?jiSV+i3bM1@=4EF3&(F&IUyzOEKOYP8e@-T*|4a-FAag+Y zHvgd9UteD0zp^O* ze@PyW|9q^>|Ct%#ZUC8a7Niekw~Qcn6v!?eY0>|F)<*wR1Kj>+1bO_AbhY{KVyyk& zOkLr>ww&aDDSn>+%&6{&1nHOJC8&-vfORQG>Fdd&Z|6Po^SoZR-mr#St8ytmVT zR};LPsd;Y&YzyJTPx&8l3 z<3s;@nj8E#RagA4qagiXgohKR7ev1R>6hW-_~l}x^}oL?>;I`$bN+w3fA0VHM;HH} zSv&uKLuUN{06X*l7FsI*HDtv9i*T|3XGFxsFOYs?MX8tJPL}^?*BAdkxN!3SS0{J= z|8U{J|6|K%{_iSE|DWjR_TSIe?7zLC_J1vTss9`-%pki#7;L|fncmin0Js118jAmK zoY?&T__FE$@9tjx|Jt?{|F_NR{XeI<;(u33*8hsM*#DvK4*vzYI6!(q80>$4YlEna z0N4LBs&fCY?{E0Oe{SFZ#|Jn3|Nij&|BqLX{=d6#{r>|?r~jYVR{cLQ(DT1AH(WnB z{6iTSn4_G`&y_{{|6kTx_W%6K>Hk09KK}pD?|1)ye|!G_+rxAJ@9tUie|b;+{~$-p z|GaE0pfG^PKPU_$?aWmZ+-+XAq(=PTJ+=A&$6H7K|NHm*|G$5K|Nr{(=>PqFtN*v= z#Q!%|k^Rrl#{80vfk73d4;gzKXsU)-8=TAVarobz8To(vjF$f=*3A6BV@B)$hLrIC z9wyrVb!5fPO7pT|=tVMz*;rmIN?n+HtFENLODh%WUw-DgFP-#Mwi?KbMZwZ6l6s1n z4;cUdKfv(+e*?q+{|t~weljyM{sZMqec0?_ z=VND{%*D*~i=C12KL-=je=aEIU}XFcEjvNx!!XDWnE6*27#NN-FfgoRU|=X=U|^6( z_Jo80hrBp1=Xn8EW^frQ#K!Vpl!NuZ2nXwbAvTB@H)gpAvImr>LFFsR{Rg3CYzPAb z13%1s1qlIp1yTM-Vw|l1CAiuDD+uxYR}&ZfuPQG1UtW;szc?4`e*qTe{~+^0e!}99 zw+svnNem1OMhpxLqG)lzt|l#VURHqXzoIbDe_c6oaG7kRB=cWKM(n?eDF1&MJ`S*7 z_@Q+J7V|;=ILE-izz%aWj8+m8s#g%^{jVg<3ohRs^)>#x8SDIaG1UBTudDjsTumM< zt}Y?;UqOf$>_1T1kHd~Sn7JUDpPxZMR)G7Xf*|*Qb#a0Jw%RKHgYC`!CwM!7>x4va zr~hG2mj6A>^#9xFsQx!mmH)4+AoX8GLioQJ566F2)VcxWhmX)I7-T$yG#_WWG%v@0 z1p%)A#!6EELmbTim&6AB?<>jpKfNX&Ojjm_|4;C7{_ksT{NKq)`@fB@`hRmR6-e37 z%>gR6K;|OjG_c_e3=DFD-0MMcry|1p-_OeEe|bXK|AnoU{|_&o`v2Uz`TzGXnDl>c zQ|bS*gs}hNF4o|B$wpTVTsIi2EB%)j6~vZiLG?Y@3Ryw!d*U3d|IJin|7V7H{hwZ2 z2(BaEoZ0>V%k|^`pC8-yf6u%LkUGQH<-fa`{(nm?<^M)1a$xt%iSYktV*^*H-#}URznYZje@T9>|LmCNgW4@%E93>Zzv)Pe{EuY0;Q#9G+W#9SHviu(jgc-`~IC z|Jn5m|L<8a`TyD}9slRI*Zl7(&--7P5b@teU-LgVE4+OGvIm|%?e*ma!W_&$7KZ!$ zpHi0je`RO&|IL${|L>dA^Z&xyx&J?2KJ@?llS|SJ6i|JNjh{GVNu_kYWzrvE3GO#c65-|GM0 zpI!R@`^U5YzrH^H|Krv5|6gt#`~UdRrvFD*%>2K&v-W>ceAs_IMd|;Xi14WgnTd=& zR8-iboh{BMd)fW3PYV6Ns-xonxn-08zdyb0|F3UP{{Q>;2i$J>`{(2TpYLz||8(Wx z{|oEq|6kBr@juqf@xLM{4?yY$P#uR+zJctDax|2WcCvVs8|3!CJ16%4#_p>B_qQ+n z|KsgVu-X6q{R7j#zCHT?@zTEkd*}50FN+KMZ=8`~UZ`(Eo3w zB>i7SOyIl(2M0=Bg={V|TSbL|-Ck3^!9h#mldZb!e>YvF|B-fv|Fe7@{#Qi#{I7`d z`=9FL_}|l1`@e~@^hX^Tv3eCOWj?Z**x35~`~vF2Jn1Te9P8CYc7|giSl$#C|X^|Np_j@c#n?!~X{i4F3->F#c~~Uz##ws1B3nlKMdez*ndzX3e?wgKx&6IkmqY=Xy4AAfq|izfq`Kf0|Ubb z1_p-13=9mQdKuJq0bvmP5L9j^0|P@X0|P@W0|SE%v^~R();1s}tU)dYwTD1$muHL& z4Bwa-82*6TZJ<6RTD$EZ0|Ub!1_p*73=9lk7#JAdF)%PZWnf^q!N9-(a@RZt28LV) z1_m<*1_mBtEC+R;S(zB0vN1CL0rfdyn4O8~Kd6lds^dZKfR;a?@(qMR?f|vBK0w=# zj~N&kZZR-0Tx4KiI0kLwfx;0KMNGKd!OzJSB*4z{f|rFE+?E8jy+Ca+US_8MAPnjU zgW8uMagaOE!UE(DP&*dnhDT`jZ)IR$n83im5Y52AAcn<$W+_3QiDEn)|G@1{X6FAO zT9}>XzbGf`e^5J7goEY35IZxdYyrCkO2sC}PGcYjtBe#d3 z?qF6F6Iw1K!2J(w9|y~SDIRuk-yhU>2VqcqPnwVOzZfUWe?eB}|GdnQwkc>_0Td2s zaRIU)gx^5>5(^m^7|IzK80?|_1*o+UN<~U^qLP@vKM8KO|I&OM|CL4g{%c8#{@0U} z0JmK=r9{AOR#|>daJyIt)E;MM2DkG;X#!+FjxYeF2~he2^-E4 z1h~L$a7{_!|HjHP;IRt}bp>#{SzliAzZTRDAa#;F?EghMSitcEN*kawi6uRP{13t) zHw2;D3gHPeFz_mi3qF$<;`y&A#PeT6LI`Z1lL4eX?P{$3-`PL|+@=PNrGVU}Cnx@2 zLsA$#rXnlA{a>7$4eSn3Jm7HyC_nN-Y(&0rR*i~#pDP(K9ZcU3X|{}yWU|9!0t|Hped{?Cu_`(K$D z{=X_I;(uve$p4%$pa1dRj{k$~&HuZb>ixIZ*Z6OxtpaWr8mh?s*OZm`FC)nNpBphQ zf^0X4{fvQuffZ~s0|SF3AD6o{Kj(jGUiSa;0-XO1+(Mk?SWB<=@F8jZ8R?q(vD`x#awQBbN zee);&U*28!zq=^?e?e5h|4296|6Z1cVEaL1T9(?X;QooBs=|K-F+t?E6U<&1-3zl{ zNsRvxs7#UMV*78XAo)Ms+3J5)a>V~x^~K=+!>ygG{=Yf3>;Ky`yZ_(cx9tx8oBVe$(EM+qsr=tWP4T}Ws2`>z3$`Cm9DwQ*uoL72d7g-}vw+jR zqn`Tzj9{<-J;fRS*G_2ue{sX2|L@Q5`~UsH`TyS^Uikm%%HjXlx3BoWVR9R|Uy~K; z^FPSJ0^BzP*$;|;9eJt$YSLo=rNL={3B7*+^ZygD<&b{PS1E3`|2i_F;Iv(l82*2D zL&^W`vwHr6{QmXE@&7+wUjP5&_09j^?wtPrVE=~y+h_IupHQCjKQAiaf0&ESe^*nz z|5iGx|BY1T!DExk62jpA4>t?bePYALnWRzcn}M z|B{aC|9j^4gYExv?dbm>&#(Uf@#6abZ?{i@+xt6a_x+z#nfJdaCis7pyZwJJP@ly> z6PyS16lK8v2ju~NHWqCCU=;h+#Q2}+%ZdND*HQVO9_SA4_b%_M{l9Bg@Bd317yW;G zdKcK8k{ogjT=l_)I{Qu>N5#Tfs>}dJl)4~wkKeNi*{cGwVzKcNJ&;Z^%mcUy&01zc?ZMe{M9Se;48D{NK@3AKZro zjd7vXi}3tstF7!FWNZ9C#n<_Nag_i6X_Yzum$g^@U(;I;9>3i+tNZ`4rPKaj-ni)h z&0P%<2EXc5?gwCEfM^XEj&+@2@KO-o8W^znL7{Daz(;PhwAz`z<{W%w-7)Bb-&eDMECr5XR1wO9UM3%0-M|MqF^{|_#h z`2XVi`H-}5>*W8hH&6Wkc$`$@Kr5X7v1D+}-ehLUrN)^3>S> ze)g9C#{p2vUyvh$tPN|ToGt(7g?j$)%1it|zoF>=>h7BVTP8RE-#fee|M4YL z{@>cR^#7M@N1%D(+W+s5FaG~}>*W9U=l1=7aA4#Ai<_4GKd@x_|8-M3{!ecx{a=|D z`ybSomlftiw;#R!Veqq3=Z$o-coyYk@xL_6?|*N8(*GsRCE)(*;rV_4udJK%|MiiL z|Gz)E@c-BM$IyQ3qyInN-2DIT!P);GFCO^+@W2M}82^?T-T!Acl>RSC2>WlRukl}k zmlHV;g8YtA|AN9G(!o49+R5Tyyqop^vS`2m6AF|6Z|tr8e{4bD|9jgO{r`Mv@Bg2l z@BRP#_v`;GFD=KTMB zdGG&U-=F>m^>-mXod4kQ_&W2(>d_9_MQHFE7~re@klk|G8CJ|94Gl`2TRnqW|BXocaIf z_eZe3Aa{WJ_kVuB`Tza(mH%%~ZTo+4e&7GD{G|U8&Q|{|G!*`;iVOS~<6`|M%*nEx zgt2;NZxii_p|(cj@63$)zql^<|A~2B{~sS%`TxU>ga5xgJp2Fiy%Yam zo!$Qb%KF*=*K}3>Z%m8$AMa`R-^EB1+&|Zl6#6GG$OG~>s1JwF>lktl+KR!hdMeNS z%(VXpTI&9furvCf=4JE0I@;%dUv~8WS!JpJ=T>F@pHZInzcVZ5e@T?@|5!Jh|E@-w z|IJinKyA2Z8WKWy;~T?LGy!#CVO~=Oi5f#$(Px^Xy#F;sxc}=(3j8-$6#ws}t?)n4 zQtyAPtL6U~SIhtYRtEp=w3YswC`mrkl@X}{=|wY+5-zKA z!1#cHf&Bmj1AhYp13L&q(=R9;5rsi|Kp14k0|o|p5dOfxVE>1K;r~C-#8Lx0I2(gH z!JxUK1mtlFd^2LC1q>)Igc%qZG#MBeTp1V`5*Zj6su&m;`WYA)Kz%{bcqeF%2{fMs z8qWY>P~E(nfq`Ko17wbADs-NyfPsMlH0K5~2jpZ~=sXG=8RHs+e8I`Uz+lM0z>vkj zz%U0o-*uaTfdMqv@|}@^;SVDN!#_NI&A$u`44}Dn*jNZCT0wJ74;dI3u0h8kKw~%{ zzm+gBFa$!U>_FoIpm9h>LJq_u&jlS@0EORmCI*K0%#4h`L31u34C*6;FdGvSxLyK@ z;ps2Jk|k)a>@x!c1IQnszC38I88of|3KP(L;8Ezf0%%+VGTz1_lODJnVwz z8_*aJC|iTpbb#^*ibK&k{Os&XqC6ZYg}K=N39vE$2e}XAHjsNk<6WTnLC`!TXebZiZ@h6Xgw37V${l^LM`5(0YM^#J!JVvD{E%IMeO5{Ii9vC#|3>xPG zjqictM3{{QJiiSaM+ApIXnYr1J`somkY7OMEXd6{&~;Ft@nz6jD3H5xmZNfF{7M=! zVy{)ih5m!W5){@TcZ24#H6(>VYeW9)D@gt~P>=%CI;fR5aVLAs!NNVRF@V7&oP1A4O%k=avNwI z12i{erY8U2TwUS6nW`Lk9MeES64WOAuPG(`A2h}W@&jlbRg#D8zc?o=cx{&;3uHVO z<_B;-X2h5yMs_zc8=Ph)mHiMpr`)d zUQg}6t&YlnYfYv9pfOle71{rWicli@eupm9kB7ETSX;2t|{2~e;mu3R5bp!c< zh%^N92dHet=o`Wv4P}7Vf+&j#enED-swn?|Eol*OID^(bfYv~O)}Mi}yQ%JfHxupu zE=F4a9re}!+v%wMx6)Ju$BUt&G{i5`BH+FfXnY-#H#ot08&sBo%3)BMN}xOixgUf< z^Dc5wr(&h#h52SH2=o01tqB3GHBlDf{SOL9Jvni3IJ=qXfX7|~ZB74&IGFzrbF>7n zvj}mp_#bFz3LX;%jsJrDV+UQ^XQCqa-vI0viT|3?qW@JTgu!#GG6LNH#kkr2gVrB{ z>Ng_&09uEL)$PKZoKkW^ygxv34H|z30|ltF6=LF32XmWJRpg?4)C|E;u?|C^~R{x?>Y`)>eR zA0sdMUqf2#zmk~Xe<^(ZkCgVv)I#RUD&3h@T7O9^+j{_kgF z^55M|@4u6w)_*%ajsMm@AmiaistW&+#{&h}agGOq(hf4lnh#_IxUYiBZO~Xb zXpJRkyc`tv)*6cceJl+AN4wen&kpteU!5HJA2d!rr84*btoow=vm1*4&!{cof!~N_3zdW%WJpTLS(5C;_wk`jEWa;$(>n65<*E=<4#{bWa z@cSR*>F__m))c&k3X}(|py}TPl%K(K5(?n(SCJA0uMGp0MFiSKAb)`7(BbY^66HS# zDtAQKSpG|Lv4X?iPDd3yFIF5I3{LO!Tgv}$1FZpCHy^x~<*< zm&dpNzp`a1cwS|3dlh&Nqa-dAGKTJ8`QO7_A3UE0TBl{IrT|{6rLQFWUspjIJg=oF zCIk+DQ2UOEatPFqhPxlMUJTUE0l6QPca0RK!1H)1{%-$4b2*^#{8haT;4}hS=LH&X z|90oh|L;$){QvRv3V0p!=gWt{^B>1n%>2Kmzxn^firoKIDN+B^gFOF-xmbhq0B9}@ zG#_W8rT|_GrlTMQUhAwPDe@mQ7Awrj3a*!la6hPT19!h95Bqyixg){J`d?L)@4trTzcZnu7ne>9OGTYmsht|9z~D|AWdP&^kO&T>x6k2AWS&2h{`OLjR=&c>W7< zK-^D6yY4;Q@t}!*F4ixg@=}(M<3DKKo2!x5e^4D&nH2GVQbq3nrJXhZx6SDKe`@ud z|BnuA`2XeVk^kQxU;6*^_09hv_y2fy^*<;bJwLV`ye@9z)Q50nQ?b-{Cyp!qP+d=x0{Xvm2DR|2_TkoP|*{6S?X5q$$tTLSI{32wIcpt)gC zePyH|30{K)%D0V~ao~B;l|2prcg*Yo&r^ctNj_aX`2YL;bKtdnp!qY<8u+i*kNtmo zWGi@_eACp9|1;`}{x@bNfY|G8m4;QDAzL&<+o+TT8->;JK3GydP) zvEu)mlRN)|+zavpX#Vlr?bH83ege(^om@2soPI!S6I*hU{ujjt|4;UJ`yb(I``_Qz z?7xS(!G9-Xo&UD_8sIY2SWWT2uA0ov+u9 z{r__L(ErybcmBV%Yc+VCC}<89G|$;ykovzoDdIn9E;ZiA<$t)V?f*ari~l}0rvE)G z4F5Zu=>4}d)cS9!qYmCPp(QW%UrtouzYrH2`n(FL%tXd0<&Tk~)J)JEqr0&-cVP6O8+nKs`Yty3t5B9G6|MK{@|8Gz2`v3am4se+Y zssoNLoADo%eipP=g6CmDX{a| z%H+SJi5|E<)>e@DFDbx-Z+;x)50vuXTvaX=G`1dOYw|zT-t>Qro6Z07xZwYj%Cr8@ zZz}!2tfTt>+P=pBo2Rsa*Jy(FU0mF-@c*qHE5UIA%G01Vp;xyq`+s8P?EgDw_x)eh z*Z3c_{&Ye`?*F#@)c>^^asSJcBL5e~h5pZr4*s7V8Sp1qB~5EBBgv%r%FQ0iZEC23VRW37K-j^_X4J?;M|`#AkCiwy$1e_?YO*!`e2 ztm`K<{ogvZ{r{dhec*XV(0bNuTbBO6y=ygi{`1bBHUF<{Sq5I)x^Ll>|68Va|6kSL z{C`nL&Hvd=<^QMF7X6=4ng74LH0OVNQ6_jUvN|K-e{o{e|ICQM|54tq|DDYY|0_$1 z{^w=IxlRP5{R1k9T#YoZg3^7mx8whuV9);*al!v5mSp?~xqn4x_5U?Jb^q7(HU8f? zsrmo5X&wLf&4u{k{JQzzdE^(zw*7y5diQ_Oo|-2|w*0@dXWjqHAiu1c_y6G18UJ_A zn+Tr6Tr;Wd|I*&Z|MS{v{!eQt``=rU|Gz0e?SE-<^#7z_pZ{)_#{WTW34V4KTzMFy z{Rwuzp=M2po$3ELciaE@Vc!3%6T|-Z6{h}Q&{*;x?EYSm`y0UXzFViZ{@*>T`~TrZ zlmDMxJ^TNiU90|oIJXyECV|!@e7Sn`|Hlgl|Gzr5>;J<;oB!X~x%&V4jf?*uTQ&Fp zzQxo2Zv)K>Pw4>9$Ifgj|KDAn`@beL@qb#F-+wm?qyM1#SdfDi*BTYj_z%d*DA>hN zT`I`N_(!C(<^K$Sm;cp?q5r${lm5@H&Hulwt>XWho?7spp)HeJ{_mLH{(tZ6p8rP| zO$M(ge!PF(|1Vb$|Ns8r9JpQvm#KG7gZ=aI!U6DHGH7qnm95MFpI*1%|G{N5|8Ji= z;s2V+?f(~a)c&7XUHHE~C;5L`sPBI#6TSb+62joRm=$w;0%!jV;*|64L+!0VqEHWvS1*;(~}eP8|mZBtso^UjACO!$9##q|F-HZS`B=J?kC-|n9J z|KsJg|3BZ{{Qu+CjsHKMT>-C40j)(~R!_ zOL`jqPX^W1*-8JC13mxS=xc!cl{oriIQzdK_j_4t%11g`e2H*0{~zaO{l7HI|9@Ln z?Ek4{ng16zm;7JXQ}ch@l$QSo=JxzQwRFn=E9>X}f3kP=|IZiqf!AFB{CpoghYgy? z{`L96|DW$}|NrqE)V@3W|Le6Q|KFY2{r}OyjsLG~UH1R@%31%n&+hxbytncHl$yf- zH5qaLW4xXJn`v^vJc>R*_h$^RrzJMf%) zb9&VO=@psaHQd`KH~&90w+Fm;?B2G;|KA_q^8f4YWB-4Cy7T|{?^pl-{CWTX&z}$f z|NMFT|M#!w|9^dX@c+k~8{oZVU_YGR_5a@9HUH19oBx0Jyb1qT^fvsTT%G^FB02JZ zu#?q)eI@DtlDr%!eM!)K4if_d)_yN2+>x=du`z43v*pPcXUqRlPUinJ{G9)nNBjS8 z&4~U#zb^m(mI)3256|uXe`(e9|4()=|Nr^i&i~(@p8Nm%*R%is{(b-d54Sf zzklEUgXYkG{dn^K$6Jse&i?;$<>3DpM>hYzvU$<}eG4Z3U(r+lzrQRS+)i>e(FL!y z1C6VI`T?MR3}{@1m64I?@i&lPqHIi*Vq7fW#JO7iPxiF?Ul8i~za}C0e_wv${}rvJ z;CcUZODFw*ykqhI&*yjk|Nipg|381;fal)96NgX&G7tY793Q`aJo^9R)z$xBZyo#p z_QbaTS2r*Ezh`dm|AnpP{~I!6|3|pm{0EIoDv9#{2d!lVwc$YRP);VM*X#@oN+5TU zfp6O8@Vj*7E<#s%ih<9$EMQ>w^>j zfB$?A4tuE6k*I&*brqoa`SJGJ|F5@>{(pUR)Bkg;XZ>H>-|)XPKk0vxkJEoALrw5l zFv$I&xdYJH96u}bHy#%7dR9`L4p$p!sqY?M7Wr};f^7b;bEd- z?`y8}Cc+++pKbr=2D$#PiuM2Bo*wysYH`y4mCc3!56^1*e{b{L|L;z3{{Qv<@&7+w zU;Y2{<2~^D6;POedvxmm`wKh&-`~09|Dn0v{}(hA{BKH&{GSo%`X98<#@SE&hv)>ZzRaHPiVA@<*b(_5Vyi$Nxp49{+1%{r-1m zMEsv!p89`%N9q4VGg|(iUpnFcwKdcK-&i;E|CLo!|DRmY`+vuzhX0Ej3;y>NB>t~T z2>PEH;QBwp$>P78vDSY}b$RgEwT`UVKXnPglXCp*B&T;&7i023Yq_k|WojI>6u-FX ztNwR4Q2Xz$ulnE9Q0;$!nb!X(&>9j?>;FZ;uK#OdeE+v32ZR09lO6NFCnxrQdq&j% z+Qi`hh2h@+Q+yo$huE3?cQH^0yWLnx>c5Vx=od{1Ay64f)Hpw;3yBjkR1lCiRg#!v zrYP~lNKW*>wm9E^4H2III^ul)jbw%YTPjQbchHpk?_r?&KhQ$=e~gp){}gZg|7pIC z{}Vm!{zo{P{r5K4{co?M4DRb0%8UQdkrth;DIq|xZX(Ww#Hlir6_(PK5~@)b;k~9T z$oWrMfb+kKAlH92VebE+^)$MYg8z->#s1rB$p3dYRR8Z|rt{y+RQtcPzS@5)b-91W zijr6LA!}){jwci63hJm*e zZveUM|9?;;kb&XAUs1{ud74>1lzgN@?{ z?Sluc@g literal 0 HcmV?d00001 diff --git a/project/jni/application/enigma/src/enigma.rc b/project/jni/application/enigma/src/enigma.rc new file mode 100644 index 000000000..4ac20e7f4 --- /dev/null +++ b/project/jni/application/enigma/src/enigma.rc @@ -0,0 +1 @@ +enigma ICON "enigma.ico" diff --git a/project/jni/application/enigma/src/errors.hh b/project/jni/application/enigma/src/errors.hh new file mode 100644 index 000000000..3acfad3d5 --- /dev/null +++ b/project/jni/application/enigma/src/errors.hh @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ERRORS_HH +#define ERRORS_HH + +#include "ecl_error.hh" +#include + +namespace enigma { +/* -------------------- Exceptions -------------------- */ + + class XLevelLoading : public ecl::XGeneric { + public: + XLevelLoading (const std::string &msg) + : XGeneric (msg) + {} + }; + + class XLevelPackInit : public ecl::XGeneric { + public: + XLevelPackInit (const std::string &msg) + : XGeneric (msg) + {} + }; + + class XLevelRuntime : public ecl::XGeneric { + public: + XLevelRuntime (const std::string &msg) + : XGeneric (msg) + {} + }; + + class XFrontend : public ecl::XGeneric { + public: + XFrontend (const std::string &msg) + : XGeneric (msg) + {} + }; +} // namespace enigma +#endif diff --git a/project/jni/application/enigma/src/file.cpp b/project/jni/application/enigma/src/file.cpp new file mode 100644 index 000000000..9cfb9d421 --- /dev/null +++ b/project/jni/application/enigma/src/file.cpp @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "file.hh" +#include "enigma.hh" +#include "video.hh" +#include "main.hh" + +#include "ecl_system.hh" + +#include "config.h" + +#include +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#include +#include +#include + +using namespace enigma; +using namespace ecl; +using namespace std; + +DirIter::DirIter() {} +DirIter::~DirIter() {} + +namespace +{ + +/* -------------------- DirIter (POSIX) -------------------- */ + +#ifdef HAVE_DIRENT_H + + class DirIterOS : DirIter { + public: + DirIterOS (const std::string &path) : m_dir (NULL), m_entry (NULL) { + open (path); + dir_path = path; + } + virtual ~DirIterOS () { + if (m_dir != NULL) + closedir (m_dir); + } + + virtual bool open (const std::string &path) { + m_dir = opendir (path.c_str()); + return m_dir != 0; + } + virtual bool get_next (DirEntry &entry) { + if (m_dir == 0) return false; + m_entry = readdir(m_dir); + if (m_entry != NULL) { + entry.name = m_entry->d_name; +// entry.is_dir = false; + entry.is_dir = ecl::FolderExists(dir_path + "/" + entry.name); + return true; + } + return false; + } + private: + std::string dir_path; + DIR *m_dir; + struct dirent *m_entry; + }; + + +/* -------------------- DirIter (Win32) -------------------- */ + +#elif defined (_MSC_VER) + +#include + + class DirIterOS : DirIter { + public: + DirIterOS (const std::string &path) + : m_handle (INVALID_HANDLE_VALUE) + { + open (path); + } + ~DirIterOS () { + close(); + } + + bool open (const std::string &path) { + std::string glob (path); + glob += "\\*.*"; + m_handle = FindFirstFile (glob.c_str(), &m_dir); + return m_handle != INVALID_HANDLE_VALUE; + } + bool get_next (DirEntry &entry) { + if (m_handle != INVALID_HANDLE_VALUE) { + entry.name = m_dir.cFileName; +// entry.is_dir = false; + entry.is_dir = m_dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + if (!FindNextFile (m_handle, &m_dir)) + close(); + return true; + } + return false; + } + private: + void close () { + if (m_handle != INVALID_HANDLE_VALUE) { + FindClose (m_handle); + m_handle = INVALID_HANDLE_VALUE; + } + } + + // Variables. + WIN32_FIND_DATA m_dir; + HANDLE m_handle; + }; + +#endif + +} + +DirIter * DirIter::instance(const std::string &path) { + return reinterpret_cast(new DirIterOS(path)); +} + + +/* -------------------- FileHandle_Dir -------------------- */ +// +// FileHandle_Dir::FileHandle_Dir (const std::string &name) +// : m_name (name) +// { +// } +// +// bool FileHandle_Dir::exists() const +// { +// return true; +// } +// +// void FileHandle_Dir::read (ByteVec &buffer) +// { +// std::ifstream ifs(m_name.c_str()); +// enigma::readfile (ifs, buffer); +// } +// + + +/* -------------------- GameFS implementation -------------------- */ + +GameFS::GameFS() +: entries () +{ +} + +void GameFS::append_dir (const string &path) +{ + std::string full_path = ecl::ExpandPath (path); + entries.push_back (FSEntry (FS_DIRECTORY, full_path)); +} + +void GameFS::prepend_dir (const string &path) +{ + std::string full_path = ecl::ExpandPath (path); + entries.insert (entries.begin(), FSEntry (FS_DIRECTORY, full_path)); +} + +void GameFS::prepend_zip (const std::string &filename) +{ + std::string path = ecl::ExpandPath (filename); + entries.insert (entries.begin(), FSEntry (FS_ZIPFILE, path)); +} + +void GameFS::setDataPath (const string &p) +{ + clear(); + + std::vector datapaths; + split_copy (p, *ecl::PathsSeparator, back_inserter(datapaths)); + for (unsigned i=0; i0) + path += ecl::PathsSeparator; + path += e.location; + } + return path; +} + +std::vector GameFS::getPaths() { + std::vector paths; + for (unsigned i=0, size=entries.size(); i < size; ++i) { + const FSEntry &e = entries[i]; + paths.push_back(e.location); + } + return paths; +} + +bool GameFS::findFile (const string &filename, string &dest) const +{ + for (unsigned i=0; i &isresult) const { + std::string::size_type slpos = filename.rfind('/'); + std::string zipName; + std::string zippedFilename1, zippedFilename2; + bool searchZip = false; + std::string complete_name; + + if (slpos != std::string::npos) { + // file may be zipped - for "levels/Sokoban/mic_60.xml" we will look for + // "mic_60.xml" and "Sokoban/mic_60.xml" at "levels/Sokoban.zip" + searchZip = true; + zipName = filename.substr(0, slpos) + ".zip"; + zippedFilename1 = filename.substr(slpos + 1); + std::string::size_type slpos2 = filename.rfind('/', slpos-1); + zippedFilename2 = filename.substr(slpos2 + 1); + } + for (unsigned i=0; i +GameFS::findSubfolderFiles(const string &folder, const string &filename) const +{ + std::list matches; + + for (unsigned i=0; igfxdir) + basename; + if (!findFile(fname, filename)) { + fname = string ("gfx/") + basename; + return findFile(fname, filename); + } + return true; +} + +/* -------------------- Helper functions -------------------- */ + +std::istream & +enigma::Readfile (std::istream &is, ByteVec &dest, int blocksize) +{ + size_t len = dest.size(); + int nread=0; + do { + dest.resize(dest.size() + blocksize); + is.read (&dest[len], blocksize); + nread = is.gcount(); + len += nread; + } while (nread == blocksize); + dest.resize(len); + return is; +} + +bool enigma::Copyfile(std::string fromPath, std::string toPath) { + ByteVec content; + ifstream ifs(fromPath.c_str(), ios::binary | ios::in); + Readfile (ifs, content); + ofstream ofs(toPath.c_str(), ios::binary | ios::out); + ofs.write(&content[0], content.size()); + ofs.close(); + return !ofs.fail(); +} diff --git a/project/jni/application/enigma/src/file.hh b/project/jni/application/enigma/src/file.hh new file mode 100644 index 000000000..4d7dc5882 --- /dev/null +++ b/project/jni/application/enigma/src/file.hh @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef FILE_HH_INCLUDED +#define FILE_HH_INCLUDED + +#include +#include +#include +#include +#include "ecl_error.hh" + +namespace enigma +{ + typedef std::vector ByteVec; + typedef std::string FileName; + using std::string; + + enum FSType { + FS_DIRECTORY, + FS_ZIPFILE, + }; + + struct FSEntry { + FSType type; + std::string location; + + FSEntry (FSType type_, const std::string &location_) + : type (type_), location (location_) + {} + }; + + struct DirEntry { + std::string name; + bool is_dir; + }; + + class DirIter { + public: + static DirIter * instance(const std::string &path); + virtual ~DirIter (); + virtual bool open(const std::string &path) = 0; + virtual bool get_next (DirEntry &entry) = 0; + protected: + DirIter(); + }; + + /* -------------------- FileHandles --------------------*/ +// +// class FileHandle { +// public: +// virtual ~FileHandle() {} +// +// // FileHandle interface. +// virtual bool exists() const = 0; +// virtual void read (ByteVec &buffer) = 0; +// }; +// +// class FileHandle_Dir : public FileHandle { +// std::string m_name; +// std::string m_path; +// bool m_exists; // File exists +// +// public: +// FileHandle_Dir (const std::string &name); +// +// // FileHandle interface. +// bool exists() const; +// void read (ByteVec &buffer); +// }; +// +// class FileHandle_Zip : public FileHandle { +// public: +// FileHandle_Zip(); +// +// // FileHandle interface. +// bool exists() const; +// void read (ByteVec &buffer); +// }; + + /** + * A GameFS is a list of directories that are searched when + * Enigma tries to find a data file (for example a png image). The + * data path is usually specified as a single string like + * ".:~/.enigma:/usr/local/share/enigma" list with entries separated by + * the OS dependent separating ":" or ";" (ecl::PathsSeparator). + * + *

When searching for files, the file name may of course include + * subdirectory names. For example:

+ *

+ * findFile("graphics/bomb.png") + * -> "/usr/local/share/enigma/graphics/bomb.png" + * or "/home/user/.enigma/graphics/bomb.png" + *

+ *

Preconfigured GameFS instances are accessable via the Application + * instance, f.e. app.resourceFS

+ */ + class GameFS { + public: + GameFS(); + + void clear() { entries.clear(); } + + void append_dir (const string &path); + void prepend_dir (const string &path); + + void prepend_zip (const string &filename); + void setDataPath (const string &p); + std::string getDataPath(); + std::vector getPaths(); + + /** + * Search first occurence of a file on the GameFS. The file can be + * a path component like "levels/index.lua". + * @param filename the searched filename + * @param dest the expanded full path of the first occurence. + * @return has a file been found. + */ + bool findFile(const string &filename, string &dest) const; + + /** + * Search first occurence of a file on the GameFS. The file can be + * a path component like "levels/e1/hello.lua". Zip archives are searched, + * too. The path above would be looked up in "levels/e1.zip" as "hello.lua" + * and as "e1/hello.lua". Plain files preceed zipped onces on every + * search directory. + * @param filename the searched filename + * @param dest the expanded full path of the first occurence. + * @param isptr an opened istream of the first occurence in case + * the file is zipped + * @return has a file been found. + */ + bool findFile(const string &filename, string &dest, + std::auto_ptr &isptr) const; + +// FileHandle *findFile (const FileName &); + + /** + * Search first occurence of a file on the GameFS. The file can be + * a path component like "levels/index.lua". If the file can not be + * located a message is logged - use this method if you have good + * reasons to expect the file being found. + * @param filename the searched filename + * @return the expanded full path of the first occurence or "" + */ + std::string findFile(const string &filename); + + /** + * Lists the paths of all files with a given name that reside in + * a subfolder of the given basepath. + * The basepath itself is not searched for files. Only direct one + * level deep subfolders are searched. Of course all components of + * the GameFS are searched for! Used for searching index like files. + * @param folder the basefolder path component, f.e. "levels" + * @param filename the searched filename, f.e. "index.lua" + * @return a list of fully expanded paths to matching files + */ + std::list findSubfolderFiles (const string &folder, + const string &filename) const; + + /** Find an image file named `f' in the resolution-dependent + * graphics directories "gfx??" or in "gfx" and store the + * path in `dst_path'. Returns true if successful. + */ + bool findImageFile (const FileName &f, std::string &dst_path); + + private: + // Variables + std::vector entries; + }; + +/* -------------------- Helper functions -------------------- */ + + /*! Load a complete file/input stream `is' into `dst'. */ + std::istream &Readfile (std::istream &is, ByteVec &dst, int blocksize=512); + bool Copyfile(std::string fromPath, std::string toPath); + + // banned code to file_zip.cc due to macro clashes + bool findInZip(std::string zipPath, std::string zippedFilename1, + std::string zippedFilename2, string &dest, + std::auto_ptr &isresult); + + bool writeToZip(std::ostream &zipStream, std::string filename, unsigned size, std::istream &contents); + bool readFromZipStream(std::istream &zipFile, std::ostream &contents); + +} // namespace enigma +#endif diff --git a/project/jni/application/enigma/src/file_zip.cpp b/project/jni/application/enigma/src/file_zip.cpp new file mode 100644 index 000000000..60531640a --- /dev/null +++ b/project/jni/application/enigma/src/file_zip.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// Whoever knows how to integrate this code into file.cc should do it! +// The problem to solve is the clashing macro definitions in "zipios-config.h" +// and "config.h" +// The static var "zip" should be a private ivar of GameFS + +#include "file.hh" +#include "zipios++/zipfile.h" +#include "zipios++/zipoutputstream.h" +#include "zipios++/zipoutputstreambuf.h" +#include "zipios++/zipinputstreambuf.h" +#include +#include +#include + +using namespace enigma; +using namespace std; +using namespace zipios; + +static std::auto_ptr zip; +static std::string lastZipPath; + + +bool enigma::findInZip(std::string zipPath, std::string zippedFilename1, + std::string zippedFilename2, string &dest, + std::auto_ptr &isresult) { + + // reuse last opened zip if possible + if (lastZipPath != zipPath) { + zip.reset (new zipios::ZipFile (zipPath)); + lastZipPath = zipPath; + } + std::auto_ptr isptr (zip->getInputStream (zippedFilename2)); + if(isptr.get() != 0) { + isresult = isptr; + dest = zippedFilename2; + return true; + } + isptr.reset(zip->getInputStream (zippedFilename1)); + if(isptr.get() != 0) { + isresult = isptr; + dest = zippedFilename1; + return true; + } + return false; +} + +bool enigma::writeToZip(std::ostream &zipStream, std::string filename, unsigned size, std::istream &contents) { + ZipOutputStreambuf zos(zipStream.rdbuf()); + ZipCDirEntry ze(filename); + ze.setSize(size); + ze.setTime(time(NULL)); // seems not to be implemented in zipios ! + zos.putNextEntry(ze); + std::ostream ozs( &zos ); + ozs << contents.rdbuf(); + return true; +} + +bool enigma::readFromZipStream(std::istream &zipFile, std::ostream &contents) { + ZipInputStreambuf zis(zipFile.rdbuf()); + std::istream is( &zis ); + contents << is.rdbuf(); + return true; +} diff --git a/project/jni/application/enigma/src/floors.cpp b/project/jni/application/enigma/src/floors.cpp new file mode 100644 index 000000000..41654d03d --- /dev/null +++ b/project/jni/application/enigma/src/floors.cpp @@ -0,0 +1,1021 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "Inventory.hh" +#include "errors.hh" +#include "player.hh" +#include "server.hh" +#include "world.hh" +#include "main.hh" + +#include + +using namespace std; +using namespace enigma; +using namespace world; + +Floor::Floor(const char *kind, double friction_, double mfactor, + FloorFlags flags, FloorFireType flft, + const char *firetransform_, const char *heattransform_) +: GridObject (kind), + traits (kind, friction_, mfactor, flags, flft, firetransform_, heattransform_), + heating_animation(false), + fire_countdown(1) +{} + +Floor::Floor (const FloorTraits &tr) +: GridObject (tr.name.c_str()), + traits (tr), + heating_animation(false), + fire_countdown(1) +{} + +Floor *Floor::clone() { + return new Floor(*this); +} + +void Floor::dispose() { + delete this; +} + +Value Floor::message(const string &msg, const Value &val) { + // "init" : Start burning, if "initfire" is set. + // "heat" : Heat the item, heat-transform floor + // or maybe set fire to it (if burnable). + // "setfire" : Just try to make fire (if burnable). + // "forcefire": Force fire, even on unburnable floor. + // "stopfire" : Stop fire, put ash but don't transform floor. + if(msg == "init" && has_firetype(flft_initfire)) + return force_fire(); + if(msg == "heat") + return try_heating(NODIR, flhf_message); + if((msg == "ignite" || msg == "expl") && has_firetype(flft_ignitable)) + return try_ignite(NODIR, flhf_message); + if(msg == "setfire") + return try_ignite(NODIR, flhf_message); + if(msg == "forcefire") + return force_fire(); + if(msg == "stopfire") + return stop_fire(true); + return Object::message(msg, val); +} + +ecl::V2 Floor::process_mouseforce (Actor *a, ecl::V2 force) { + if (a->controlled_by(player::CurrentPlayer())) + return mousefactor() * force; + else + return ecl::V2(); +} + +void Floor::get_sink_speed (double &sinkspeed, double &raisespeed) const { +// sinkspeed = raisespeed = 0.0; +} + +double Floor::friction() const +{ + if (const Value *v = this->get_attrib("friction")) + return to_double(*v); + return traits.friction; +} + +double Floor::mousefactor() const +{ + if (const Value *v = this->get_attrib("mousefactor")) + return to_double(*v); + return traits.mousefactor; +} + +bool Floor::is_destructible() const +{ + return true; +} + +void Floor::set_model (const std::string &mname) +{ + display::SetModel (GridLoc(GRID_FLOOR, get_pos()), mname); +} + +display::Model *Floor::get_model () +{ + return display::GetModel (GridLoc(GRID_FLOOR, get_pos())); +} + +void Floor::kill_model (GridPos p) +{ + display::KillModel (GridLoc (GRID_FLOOR, p)); +} + +void Floor::add_force (Actor *, V2 &f) +{ + // Note that actor == 0 is possible from lightpassenger-calculation. + if(const Value *x = this->get_attrib("force_x")) { + f[0] += to_double(*x); + } + if(const Value *y = this->get_attrib("force_y")) { + f[1] += to_double(*y); + } +} + +/* -------------- Fire Handling -------------- */ + +/* The fire system is a confusing tangle of bools and flags. + * + * A short overview: + * A floor is allowed to catch fire when it is: + * - burnable by default + * - the "burnable"-attribute is set and in Enigma-comp.mode + * - it-burnable or it-burnable_oil is on it + * Fire on a burnable floor can be ignited by: + * - "setfire"-message + * - "expl" or "ignite" message if the "ignitable"-attribute is set + * - thereby by it-dynamite or it-*bomb + "ignitable" + * - fire in the neighborhood (see below) + * - on initialisation ("init"-message) when "initfire"-attribute is set + * On the floor itself it does + * - inform stones on it -> e.g. kill st-wood, st-hay + * - keep on burning by chance or if eternal-attribute set + * - when stop burning + * - replace floor by floor given in floor-transform, + * or defaults in non-Enigma-compatibility mode. + * - put ash, if noash-attribute of *new* floor is not set + * When a floor tile is burning, it does the following to its neighbor: + * - ignite items by chance or if secure-attribute is set and + * source fire stops next time + * - start heat-transformation on first fire call but not yet + * transforming and only in Enigma-mode + * - inform neighboring stones about fire + * - In Enigma-mode (resp. non-Enigma-mode), spread to other burnable + * floors (resp. floors of the same kind) by chance or message + * or last+secure, if none of the first two methods worked, but + * delayed by firecountdown to simulate the old system, but not + * if message or last+secure or fastfire-attribute set: Then spread + * without countdown. Don't do any of these when there is a movable + * stone on it. + * - And don't do any of the former three if there is a stone on it, + * which is not floating (in non-Enigma-mode: neither movable). + * Have I forgotten something? + */ + +string Floor::get_firetransform() { + if(server::GameCompatibility == GAMET_ENIGMA) + return traits.firetransform; + // In non-Enigma-compatibility-modes, only fl-wood and fl-stwood + // transform. Note: This might have been a bug, as fl-stwood1/2 + // and fl-wood1/2 were not included (at least fl-wood1/2 were of + // a newer date). However, we include them here. + string model = this->get_kind(); + if ( model == "fl-wood" || model == "fl-wood1" || model == "fl-wood2" + || model == "fl-stwood" || model == "fl-stwood1" || model == "fl-stwood2") + return "fl-abyss"; + else + return ""; +} +string Floor::get_heattransform(bool override_mode) { + // The bool is needed to correctly exit the heating-animation + // in case the level switched to non-Enigma-mode in between. + if(server::GameCompatibility == GAMET_ENIGMA || override_mode) + return traits.heattransform; + // In non-Enigma-compatibility-modes, there is no heat-transformation. + return ""; +} +int Floor::get_fire_countdown() { + if(Item *it = GetItem(get_pos())) + if(get_id(it) == it_burnable || get_id(it) == it_burnable_oil) + return 0; + return fire_countdown; +} + +Value Floor::force_fire() { + SetItem(get_pos(), it_burnable_ignited); + fire_countdown = 0; + return Value(1.0); +} + +Value Floor::try_ignite(Direction sourcedir, FloorHeatFlags flhf) { + GridPos p = get_pos(); + + // Don't disturb heating-transformation + if(heating_animation) + return Value(); + + // No or floating stone -> Burn items and replicate. + // Movable stone && enigma-mode -> Burn items and replicate. + // Movable stone && non-enigma-mode -> Only burn items. + // Else -> Don't do anything. + // Special case: "st-flrock": No fire at all! + bool no_closing_stone = true; + if (Stone *st = GetStone(p)) { + string model = st->get_kind(); + if (st->is_movable()) + no_closing_stone = false; + else if(!st->is_floating()) + return Value(); + } + + if(server::GameCompatibility == GAMET_ENIGMA) { + if(has_firetype(flft_burnable)) { + // has_firetype also checks whether floor is already burning or ignited + // (via it-burnables), but not which stone is above it. + if(Item *it = GetItem(p)) { + // The item didn't respond to the "heat"-message, so we + // could assume it's burnable. However, as there might also + // be a "setfire"-message from the user or a lazy "message"- + // implementation, we still have to check the burnable-flag + // of this item: + if(!has_flags(it, itf_fireproof)) + return force_fire(); + } else { + // Just spread. Use the fire-countdown to delay fire without + // it-burnable, but not in case of a message or a secure+last + // call or a fastfire-floor. + if( (get_fire_countdown() == 0) || (flhf == flhf_message) + || (has_firetype(flft_fastfire)) + || (((bool) (flhf & flhf_last)) && has_firetype(flft_secure))) + return force_fire(); + fire_countdown = max(fire_countdown - 1, 0); + } + } + } else { // non-Enigma-mode + if(has_firetype(flft_burnable) || flhf == flhf_message) { + // We only get here when there is a burnable item or the + // burnable attribute is set (e.g. as part of replication). + // A fireproof item doesn't allow to get here. (Note: We + // don't check a second time for no_closing_stone.) + return force_fire(); + } else { + // This floor doesn't burn by default or by item. But, in + // non-Enigma-compatibility-mode it should, when its neighbor + // is of the same kind and burns also. + if(Floor *fl = GetFloor(move(p, sourcedir))) { + string sourcekind = fl->get_kind(); + string mykind = this->get_kind(); + if(no_closing_stone && sourcekind == mykind) + if(has_firetype(flft_fastfire)) + return force_fire(); + else + this->set_attrib("burnable", Value(1.0)); + } + } + } + return Value(); // meaning: no fire +} + +Value Floor::try_heating(Direction sourcedir, FloorHeatFlags flhf) { + // First of all: How are we allowed to react at all? + // There are four branches of heating: + // + // 1) item-transformation (e.g. igniting it-dynamite, *not* burning!) + // 2) floor-heat-transformation (e.g. melting ice) + // 3) stone-heat-transformation (e.g. fireblocker st-flrock, itemfreeze) + // 4) fire + // + // a) Always do (1),(2),(3),(4) if try_heating is called from + // a "heat"-message by the user/level. + // b) Always do (2) if this is the first call of a regular fire + // (checked in on_heattransform). + // c) Always do (1), (3) and (4) if this is the last call of a regular + // fire and the secure-attribute is set (so ignition is safe). + // d) Else, do (1), (3) and (4) with probability 0.7, and always both. + // e) However, if (1), (2) or (3) led to success, then never do (4), + // this way we prevent fire to disturb any transformations. + // + bool secure = ((bool) (flhf & flhf_last)) && has_firetype(flft_secure); + bool doItem = (flhf == flhf_message) || secure || DoubleRand(0, 1) > 0.3; + bool doStone = doItem; + bool doIgnite = doItem; + bool reaction_happened = false; + // Heat item -> destroy cracks, ignite bombs... + if(doItem) + if(Item *it = GetItem(get_pos())) + if(to_int(SendMessage(it, "heat", Value(sourcedir))) != 0.0) + reaction_happened = true; + // Maybe also transform floor? + reaction_happened = on_heattransform(sourcedir, flhf) || reaction_happened; + // Maybe transform stone, or stone blocks fire? + if(doStone) + if(Stone *st = GetStone(get_pos())) + if(to_int(SendMessage(st, "heat", Value(sourcedir))) != 0.0) + reaction_happened = true; + // Not item nor floor nor stone reacted? Then try to ignite the floor! + // (Note: try_ignite also tests for the heating animation: + // No fire during transformation allowed!) + if(doIgnite && !reaction_happened) + return this->try_ignite(sourcedir, flhf); + // Else: return reaction_happened from item or heat-transform + return reaction_happened ? Value(1.0) : Value(); +} + +bool Floor::on_heattransform(Direction sourcedir, FloorHeatFlags flhf) { + // return true to forbid fire (message caught) + bool doHeatTransform = (flhf == flhf_message) || ((bool) (flhf & flhf_first)); + if(!doHeatTransform || get_heattransform(false) == "") + return false; + if(doHeatTransform && get_heattransform(false) != "" && !heating_animation) { + set_anim(((string) this->get_kind()) + "-heating"); + heating_animation = true; + } + return true; +} + +void Floor::heat_neighbor(Direction dir, FloorHeatFlags flhf) { + if(Floor *fl = GetFloor(move(get_pos(), dir))) { + fl->try_heating(reverse(dir), flhf); + } +} + +Value Floor::stop_fire(bool is_message) { + // stop burning + // -> kill burnable-item + // -> transform floor? + // -> put ash? (depends on the new floor!) + // -> reset fire-countdown to 1 + GridPos p = get_pos(); + + // is_message indicates use of the stopfire-message, + // so we have to check if there is fire at all. + if(is_message) + if(Item *it = GetItem(p)) { + ItemID id = get_id(it); + if(id != it_burnable_burning && id != it_burnable_ignited) + return Value(); // no fire + } else + return Value(); // no item == no fire + + KillItem(p); + fire_countdown = 1; + if(!is_message && get_firetransform() != "") + SetFloor(p, MakeFloor(get_firetransform().c_str())); + // Remember, at this point "this" may be destroyed. + if(!GetFloor(p)->has_firetype(flft_noash)) + SetItem(p, it_burnable_ash); + return Value(1.0); // fire extinguished +} + +void Floor::on_burnable_animcb(bool justIgnited) { + GridPos p = get_pos(); + // Analyse and maybe kill stone: May the fire spread? + bool spread = true; + if( Stone *st = GetStone(p)) { + // Return true on fire-message to allow fire to spread. + // Floating stones also allow spreading. Don't use an + // OR-statement as the message might kill the stone. + spread = st->is_floating(); + if(to_int(SendMessage(st, "fire")) != 0.0) + spread = true; + } + // Will we stop this time with burning? + bool cont_fire = justIgnited || has_firetype(flft_eternal) || DoubleRand(0,1) < 0.7; + FloorHeatFlags flhf = (FloorHeatFlags) (flhf_fire + | (justIgnited ? flhf_first : flhf_fire) | (cont_fire ? flhf_fire : flhf_last)); + if(spread) { + heat_neighbor(NORTH, flhf); + heat_neighbor(EAST, flhf); + heat_neighbor(SOUTH, flhf); + heat_neighbor(WEST, flhf); + } + if(cont_fire) + // continue burning + // -> put animation + SetItem(p, it_burnable_burning); + else + stop_fire(false); +} + +bool Floor::has_firetype(FloorFireType selector) { + if(Item *it = GetItem(get_pos())) { + ItemID id = get_id(it); + if(selector == flft_burnable || selector == flft_ignitable) { + if( id == it_burnable || id == it_burnable_oil ) + return true; + if( id == it_burnable_ash || id == it_burnable_fireproof + || id == it_burnable_ignited || id == it_burnable_burning ) + return false; + // In non-Enigma-compatibility-modes, the item decides about + // burnability and only it_burnable[_oil] is ignitable: + if(server::GameCompatibility != GAMET_ENIGMA) + return (selector == flft_burnable) && !has_flags(it, itf_fireproof); + } + } + if(selector == flft_burnable) + if(const Value *v = get_attrib("burnable")) return to_int(*v) == 1; + if(selector == flft_ignitable) + if(const Value *v = get_attrib("ignitable")) return to_int(*v) == 1; + if(selector == flft_secure) + if(const Value *v = get_attrib("secure")) return to_int(*v) == 1; + if(selector == flft_eternal) + if(const Value *v = get_attrib("eternal")) return to_int(*v) == 1; + if(selector == flft_noash) + if(const Value *v = get_attrib("noash")) return to_int(*v) == 1; + if(selector == flft_fastfire) + if(const Value *v = get_attrib("fastfire")) return to_int(*v) == 1; + if(selector == flft_initfire) + if(const Value *v = get_attrib("initfire")) return to_int(*v) == 1; + if(server::GameCompatibility == GAMET_ENIGMA) + return traits.firetype & selector; + // In non-Enigma-modes, without items on them, all floors behave the same: + return false; +} + +void Floor::animcb() { + // Probably the heating-animation ended. + if(heating_animation) { + if(this->get_heattransform(true) != "") + SetFloor(get_pos(), MakeFloor(get_heattransform(true).c_str())); + heating_animation = false; + } +} + +/* =============== Special Floors ================ */ + +namespace +{ + +/* -------------------- Abyss -------------------- */ + + class Abyss : public Floor { + CLONEOBJ(Abyss); + public: + Abyss() : Floor("fl-abyss", 2.0, 1, flf_indestructible, flft_noash) {} + private: +// void actor_enter(Actor* a) {SendMessage(a, "fall");} + void actor_contact (Actor* a) {SendMessage(a, "fall");} + bool is_destructible() const {return false;} + }; + +/* -------------------- Ice -------------------- */ + + class Ice : public Floor { + CLONEOBJ (Ice); + public: + Ice() : Floor ("fl-ice", 0.1, 0.1, flf_default, flft_default, "", + "fl-water") { } + + virtual double friction() const { + return 0.1 * server::IceFriction; + } + }; + +/* -------------------- Water -------------------- */ + + class Water : public Floor { + CLONEOBJ(Water); + public: + Water() : Floor("fl-water", 5, 1, flf_indestructible, flft_default, "", + "fl-swamp") {} + private: + + bool is_destructible() const {return false;} + + void get_sink_speed (double &sink_speed, double &raise_speed) const { + sink_speed = server::WaterSinkSpeed; + raise_speed = 1000; // always sink in water + } + + }; + +/* -------------------- Swamp -------------------- */ + + class Swamp : public Floor { + CLONEOBJ(Swamp); + public: + Swamp() : Floor("fl-swamp", 13, 1.0, flf_indestructible, flft_default, "", + "fl-dunes") {} + private: + bool is_destructible() const {return false;} + + void get_sink_speed (double &sink_speed, double &raise_speed) const { + sink_speed = server::SwampSinkSpeed; + raise_speed = 6.0; + } + }; + +/* -------------------- Space with force -------------------- */ + + class SpaceForce : public Floor { + CLONEOBJ (SpaceForce); + + // Floor interface + void add_force (Actor *, V2 &f) { + f[1] -= server::FlatForce; + } + + public: + SpaceForce() : Floor("fl-space-force", 0, 0) { + } + }; + +/* -------------------- FallenBox -------------------- */ + + class FallenBox : public Floor { + CLONEOBJ(FallenBox); + public: + FallenBox(const char *kind) + : Floor(kind, 6.4, 2.0, flf_default, flft_burnable, "fl-abyss") + // uses same traits as fl-wood + {} + + private: + const char *modify_kind(const char *kind) { + if (0 == strcmp(kind, "fl-stwood")) { + return enigma::IntegerRand(0, 1) ? "fl-stwood1" : "fl-stwood2"; + } + return kind; + } + }; + +/* -------------------- DummyFloor -------------------- */ + + class DummyFloor : public Floor { + CLONEOBJ(DummyFloor); + public: + DummyFloor() : Floor("fl-dummy", 4.0, 2.5) {} + private: + void actor_enter(Actor *) { + static int lastCode = -1; + int code = int_attrib("code"); + if (lastCode != code) { + fprintf(stderr, "Entering floor 0x%x\n", code); + lastCode = code; + } + } + }; +} + + +/* -------------------- Gradient -------------------- */ + +/** \page fl-gradient Gradient Floor + +This is a sloped floor that accelerates in a particular direction. + +\subsection gradienta Attributes + +- \b type: number between 0 and 15 (see below) + +\subsection gradiente Examples + +*/ +namespace +{ + class Gradient : public Floor { + CLONEOBJ(Gradient); + int type; + bool use_forcefac; + double forcefac; + public: + Gradient(int type=MINTYPE); + private: + int get_type() const; + enum { MINTYPE=1, MAXTYPE=24 }; + + // Object interface + virtual void set_attrib (const string& key, const Value &val); + + // GridObject interface. + virtual void init_model(); + + // Floor interface + virtual void add_force(Actor *a, V2 &f); + }; +} + +Gradient::Gradient(int type_) +: Floor("fl-gradient", 4, 2), + type (type_), + use_forcefac (false), + forcefac (1.0) +{ + set_attrib ("type", Value(type_)); +// set_attrib ("force", Value(1.0)); +} + +void Gradient::set_attrib (const string& key, const Value &val) +{ + if (key == "type") { + int t=to_int (val); + if (t < MINTYPE || t>MAXTYPE) { + enigma::Log << "Gradient: Illegal type="<< int(t) << std::endl; + t=MINTYPE; + } + type = t; + } + else if (key == "force") { + use_forcefac = true; + forcefac = to_double (val); + } + Object::set_attrib (key, val); +} + + +int Gradient::get_type() const +{ + return type; +} + +void Gradient::init_model() +{ + set_model(ecl::strf("fl-gradient%d", get_type())); +} + +void Gradient::add_force(Actor *a, V2 &f) +{ + ecl::V2 force; + int t = get_type(); + + static int xforce[MAXTYPE-MINTYPE+1] = { + 0, 0, 1, -1, + 1, -1, 1, -1, + 1, 1, -1, -1, + -1, -1, 1, 1, + 1, -1, 1, -1, + 0, 0, 1, -1 + }; + static int yforce[MAXTYPE-MINTYPE+1] = { + 1, -1, 0, 0, + 1, 1, -1, -1, + 1, -1, 1, -1, + 1, -1, 1, -1, + -1, -1, 1, 1, + 1, -1, 0, 0 + }; + force = ecl::V2(xforce[t-MINTYPE], yforce[t-MINTYPE]); + force.normalize(); + + // use enigma::SlopeForce if no "force" attribute is set + double factor = server::SlopeForce; + if (use_forcefac) + factor = forcefac; + force *= factor; + f += force; + Floor::add_force(a, f); +} + + +/* -------------------- Bridge -------------------- */ + +/** \page fl-bridge Bridge Floor + +This Floor can be opened and closed much like a bridge. +The actor can move over this floor if the bridge is closed, +and will fall into abyss when the bridge is open. + +\subsection bridgea Attributes + +- \b type The type of the bridge, currently only 'a' is possible + +\subsection bridgem Messages +- \b open open the bridge so actors cannot pass it +- \b close close the bridge so actors can pass it +- \b openclose toggle the state of the bridge +- \b signal same as \b openclose + +*/ +namespace +{ + class Bridge : public Floor { + CLONEOBJ(Bridge); + public: + Bridge(bool open=true); + virtual Value message(const string &m, const Value &); + private: + enum State { + OPEN, CLOSED, OPENING, CLOSING, // normal states + CLOSING_BYSTONE, CLOSED_BYSTONE // when stones are moved onto the bridge + } state; + // the BYSTONE-states look like closed, but act like open + + char get_type() const { + string type = "a"; + string_attrib("type", &type); + return type[0]; + } + +// void actor_enter(Actor *); + void actor_contact (Actor* a) {if (state!=CLOSED) SendMessage(a, "fall");} + void init_model(); + void stone_change(Stone *st); + + void change_state( State newstate); + void animcb(); + }; +} + +Bridge::Bridge(bool open) : Floor("fl-bridge", 5, 1) +{ + set_attrib("type", "a"); + state=open ? OPEN : CLOSED; +} + +void Bridge::stone_change(Stone *st) { + if (st && !st->is_floating()) { + if (state == OPEN || state == OPENING) { + change_state(CLOSING_BYSTONE); + } + } + else { + if (state == CLOSED_BYSTONE || state == CLOSING_BYSTONE) { + change_state(OPENING); + } + } +} + +Value Bridge::message(const string &m, const Value &) +{ + if (m == "open" && (state==CLOSED || state==CLOSING)) + change_state(OPENING); + else if (m=="close") + switch (state) { + case OPEN: + case OPENING: + case CLOSING_BYSTONE: + change_state(CLOSING); + break; + case CLOSED_BYSTONE: + change_state(CLOSED); + break; + case CLOSED: + case CLOSING: + break; // already closed + + } + else if (m=="openclose" || m=="signal") + switch (state) { + case OPEN: + case OPENING: + case CLOSING_BYSTONE: + change_state(CLOSING); + break; + case CLOSED_BYSTONE: + change_state(CLOSED); + break; + case CLOSED: + case CLOSING: + change_state(OPENING); + break; + } + return Value(); +} + +void Bridge::init_model() +{ + set_model(ecl::strf("fl-bridge%c-%s", get_type(), + (state==OPEN) ? "open" : "closed")); +} + +void Bridge::change_state( State newstate) +{ + if (state != newstate) { + string mname = string("fl-bridge")+get_type(); + + switch( newstate) { + case OPENING: { + Stone *st = GetStone(get_pos()); + if (st && !st->is_floating()) { + if (state == CLOSED || state == CLOSED_BYSTONE) + newstate = CLOSED_BYSTONE; + else if (state == CLOSING || state == CLOSING_BYSTONE) + newstate = CLOSING_BYSTONE; + // here the model is already correct! + } + else { // no stone or floating stone : + if( state == CLOSING || state == CLOSING_BYSTONE) + get_model()->reverse(); + else + set_anim(mname+"-opening"); + } + break; + } + case CLOSING: + case CLOSING_BYSTONE: + if( state == OPENING) + get_model()->reverse(); + else if (state != CLOSING && state != CLOSING_BYSTONE) + set_anim(mname+"-closing"); + break; + case OPEN: + case CLOSED: + case CLOSED_BYSTONE: + state = newstate; + init_model(); + break; + } + state = newstate; + } +} + +void Bridge::animcb() +{ + switch (state) { + case OPENING: change_state(OPEN); break; + case CLOSING: change_state(CLOSED); break; + case CLOSING_BYSTONE: change_state(CLOSED_BYSTONE); break; + default : + ASSERT(0, XLevelRuntime, "Bridge: animcb called after unknown animation"); + break; + } +} + +/* -------------------- Thief Floor -------------------- */ +namespace{ + class Thief : public Floor { + CLONEOBJ(Thief); + public: + Thief(); + private: + string modelname; + enum State { IDLE, EMERGING, RETREATING, CAPTURED } state; + Actor *m_affected_actor; + int affected_player; + Item *bag; + string get_modelname(); + void init_model(); + void actor_enter(Actor* a); + void animcb(); + void steal(); + virtual Value message(const string &msg, const Value &v); + }; +} + +Thief::Thief() +: Floor("fl-thief", 2.0, 1), + state(IDLE), m_affected_actor (0), affected_player (-1), modelname(""), bag(NULL) { } + +string Thief::get_modelname() { + if(modelname == "") { + // initialize on first call + int r = IntegerRand(1,4); + modelname = string("fl-thief") + + ((string) (r==1?"1":r==2?"2":r==3?"3":"4")); + } + return modelname; +} + +void Thief::init_model() { + set_model(get_modelname()); +} + +void Thief::actor_enter(Actor *a) { + ActorID id = get_id(a); + if (state == IDLE) { + set_anim(get_modelname() + string("-emerge")); + state = EMERGING; + m_affected_actor = a; + affected_player = -1; + m_affected_actor->int_attrib("player", &affected_player); + } +} + +void Thief::animcb() { + switch (state) { + case EMERGING: + steal(); + state = RETREATING; + set_anim(get_modelname() + string("-retreat")); + break; + case RETREATING: + state = IDLE; + init_model(); + break; + case CAPTURED: + // Floor is not killed or replaced - it just keeps inactive. + init_model(); + break; + default: + ASSERT(0, XLevelRuntime, "Thief (floor): animcb called with inconsistent state"); + } +} + +void Thief::steal() +{ + bool didSteal = false; + // steal from player -- the actor that hit the thief may no longer exist! + if (m_affected_actor && affected_player >= 0 && + player::HasActor(affected_player, m_affected_actor) && + !m_affected_actor->has_shield()) { + enigma::Inventory *inv = player::GetInventory(m_affected_actor); + if (inv && inv->size() > 0) { + if (bag == NULL) + bag = world::MakeItem(it_bag); + int i = IntegerRand (0, int (inv->size()-1)); + dynamic_cast(bag)->add_item(inv->yield_item(i)); + player::RedrawInventory (inv); + } + } + // steal from grid + if(Item *it = GetItem(get_pos())) { + if (!(it->get_traits().flags & itf_static)) { + if (bag == NULL) + bag = world::MakeItem(it_bag); + dynamic_cast(bag)->add_item(world::YieldItem(get_pos())); + didSteal = true; + } + } + if (didSteal) + sound_event("thief"); +} + +Value Thief::message(const string &msg, const Value &v) { + if(msg == "capture" && state == IDLE) { + state = CAPTURED; + Item * it = world::GetItem(get_pos()); + + // add items on grid pos that can be picked up to our bag + if (it != NULL && !(it->get_traits().flags & itf_static) && bag != NULL) { + dynamic_cast(bag)->add_item(world::YieldItem(get_pos())); + } + // drop bag if pos is not occupied by a static item + if (world::GetItem(get_pos()) == NULL) + world::SetItem(get_pos(), bag); + bag = NULL; + set_anim(get_modelname() + string("-captured")); + return Value(1); + } else + return Floor::message(msg, v); +} + +//---------------------------------------- +// Black and white tiles +//---------------------------------------- +namespace +{ + class BlackTile : public Floor { + CLONEOBJ(BlackTile); + public: + BlackTile() : Floor ("fl-acblack", 5.2, 2.0) {} + + ecl::V2 process_mouseforce (Actor *, ecl::V2 force) { + if (player::CurrentPlayer() == 0) + return mousefactor() * force; + else + return ecl::V2(); + } + }; + + class WhiteTile : public Floor { + CLONEOBJ(WhiteTile); + public: + WhiteTile() : Floor ("fl-acwhite", 5.2, 2.0) {} + + ecl::V2 process_mouseforce (Actor *, ecl::V2 force) { + if (player::CurrentPlayer() == 1) + return mousefactor() * force; + else + return ecl::V2(); + } + }; +} + + +void world::InitFloors() +{ + // Floors (most floors are defined in init.lua) + Register(new Abyss); + Register(new Ice); + Register(new Water); + Register(new Swamp); + Register(new DummyFloor); + Register(new FallenBox("fl-stwood")); + Register(new FallenBox("fl-stwood1")); + Register(new FallenBox("fl-stwood2")); + Register(new Bridge); + Register("fl-bridge-open", new Bridge(true)); + Register("fl-bridge-closed", new Bridge(false)); + Register(new Thief); + Register(new WhiteTile); + Register(new BlackTile); + Register(new SpaceForce); + + Register(new Gradient); + Register("fl-gradient1", new Gradient(1)); + Register("fl-gradient2", new Gradient(2)); + Register("fl-gradient3", new Gradient(3)); + Register("fl-gradient4", new Gradient(4)); + Register("fl-gradient5", new Gradient(5)); + Register("fl-gradient6", new Gradient(6)); + Register("fl-gradient7", new Gradient(7)); + Register("fl-gradient8", new Gradient(8)); + Register("fl-gradient9", new Gradient(9)); + Register("fl-gradient10", new Gradient(10)); + Register("fl-gradient11", new Gradient(11)); + Register("fl-gradient12", new Gradient(12)); + Register("fl-gradient13", new Gradient(22)); + Register("fl-gradient14", new Gradient(21)); + Register("fl-gradient15", new Gradient(24)); + Register("fl-gradient16", new Gradient(23)); +} diff --git a/project/jni/application/enigma/src/floors.hh b/project/jni/application/enigma/src/floors.hh new file mode 100644 index 000000000..abf264268 --- /dev/null +++ b/project/jni/application/enigma/src/floors.hh @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef FLOORS_HH_INCLUDED +#define FLOORS_HH_INCLUDED + +namespace world +{ + enum FloorFlags { + flf_default = 0, + flf_indestructible = 0x01, + flf_no_items = 0x02, + flf_norespawn = 0x04, + }; + + enum FloorID { + fl_abyss, + fl_water, + fl_swamp, + fl_space, + fl_thief + }; + + enum FloorFireType { + flft_default = 0, + flft_burnable = 0x01, // Floor behaves as if it-burnable lies on it. + flft_ignitable = 0x02, // Ignite on bomb explosions. Not used. + flft_noash = 0x04, // Don't leave ash behind; floor might burn again. + flft_burnagain = 0x08, // Not used yet, nor implemented; see flft_noash. + flft_eternal = 0x10, // Fire doesn't stop burning by itself. + flft_secure = 0x20, // Secures fire and heat-effects when neighbors burn. + flft_fastfire = 0x40, // Suppress use of fire_countdown, resulting in faster fire. + flft_initfire = 0x80 // Start burning (forcefire on init-message). + // Note that only flft_burnable and flft_noash are really used as traits. + // The others are 0 by default for all floors, but used as selectors + // for has_firetype. (Future use for special floors not excluded.) + }; + + struct FloorTraits { + // Variables + string name; + double friction; + double mousefactor; + FloorFlags flags; + FloorFireType firetype; + string firetransform; // fire on the same tile + string heattransform; // fire on neighboring tile + + // Constructor + FloorTraits (const char *n, double f, double m, + FloorFlags flags_, FloorFireType flft = flft_default, + const char *ft = "", const char *ht = "") + : name(n), friction(f), mousefactor(m), flags(flags_), + firetype(flft), firetransform(ft), heattransform(ht) + {} + }; + + enum FloorHeatFlags { + // These are used as second argument to try_heating and try_ignite, + // they contain the context of a call. + flhf_message = 0, // Source is a user-message. + flhf_fire = 0x01, // Source is fire. + flhf_first = 0x02, // First heat message from a burning site. + flhf_last = 0x04 // Last heat message from a burning site. + }; + + class Floor : public GridObject { + public: + Floor (const FloorTraits &tr); + Floor (const char *kind, double friction_, double mfactor, + FloorFlags flags=flf_default, FloorFireType flft = flft_default, + const char *firetransform_ = "", const char *heattransform_ = ""); + + // Object interface + Floor *clone(); + void dispose(); + virtual Value message(const string& msg, const Value &val); + + + // Floor interface + virtual ecl::V2 process_mouseforce (Actor *a, ecl::V2 force); + virtual void add_force(Actor *, ecl::V2 &); // Note: actor = 0 must be allowed! + + virtual void on_drop (Item *) {} + virtual void on_pickup (Item *) {} + + virtual void stone_change(Stone *) {} + virtual void actor_contact (Actor *) {} + + virtual double friction() const; + virtual double mousefactor() const; + + virtual void get_sink_speed (double &sinkspeed, double &raisespeed) const; + virtual bool is_destructible() const; + + virtual void animcb(); + void on_burnable_animcb(bool justIgnited); // Called by burnable-items on it. + + protected: + // GridObject interface + void set_model (const std::string &mname); + display::Model *get_model (); + void kill_model (GridPos p); + // Fire interface + virtual bool has_firetype(FloorFireType selector); + virtual string get_firetransform(); + virtual string get_heattransform(bool override_mode); + void heat_neighbor(Direction dir, FloorHeatFlags flhf); + int get_fire_countdown(); + virtual bool on_heattransform(Direction sourcedir, FloorHeatFlags flhf); + + private: + virtual void on_actorhit(Actor * /*a*/) {} + // Fire logic + Value try_heating(Direction sourcedir, FloorHeatFlags flhf); + Value try_ignite(Direction sourcedir, FloorHeatFlags flhf); + Value force_fire(); + Value stop_fire(bool is_message); + + FloorTraits traits; + bool heating_animation; + int fire_countdown; // used to delay ignition, default is 1. + }; + + void InitFloors(); + +} + +#endif diff --git a/project/jni/application/enigma/src/fwd.hh b/project/jni/application/enigma/src/fwd.hh new file mode 100644 index 000000000..595d1dd31 --- /dev/null +++ b/project/jni/application/enigma/src/fwd.hh @@ -0,0 +1,55 @@ +//====================================================================== +// Copyright (C) 2002 Daniel Heck +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//====================================================================== +#ifndef FWD_HH +#define FWD_HH + +namespace enigma_player +{ + class Inventory; +} + +namespace enigma_levels +{ + class Level; + class LevelPack; +} + +namespace tools +{ + class Value; +} + +namespace display +{ + class Model; +} + +namespace world +{ + class Object; + class Stone; + class Floor; + class Item; + class Actor; + struct ActorInfo; + struct StoneContact; + struct Impulse; + struct Message; +} + +#endif diff --git a/project/jni/application/enigma/src/game.cpp b/project/jni/application/enigma/src/game.cpp new file mode 100644 index 000000000..26fd7c5bb --- /dev/null +++ b/project/jni/application/enigma/src/game.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "game.hh" +#include "errors.hh" +#include "main.hh" +#include "options.hh" +#include "video.hh" +#include "client.hh" +#include "server.hh" +#include "world.hh" +#include "sound.hh" +#include "lev/PersistentIndex.hh" + +#include "ecl_sdl.hh" +#include + +using namespace enigma; +using namespace std; + + +/* -------------------- Global variables -------------------- */ + +namespace +{ + Uint32 last_tick_time; +} + + +/* -------------------- Level previews -------------------- */ + +bool game::DrawLevelPreview (ecl::GC &gc, lev::Proxy *levelProxy) +{ + bool success = false; + + sound::TempDisableSound(); + try { + server::Msg_LoadLevel (levelProxy, true); + + display::DrawAll(gc); + success = true; + } + catch (XLevelLoading &err) { + // log the message as we cannot display it on the screen + Log << "DrawLevelPreview load error:\n" << err.what(); + success = false; + } + sound::TempReEnableSound(); + return success; +} + + + +/* -------------------- Functions -------------------- */ + +void game::StartGame () +{ + lev::Index *ind = lev::Index::getCurrentIndex(); + server::InitNewGame(); + + video::HideMouse(); + sdl::TempInputGrab grab(enigma::Nograb ? SDL_GRAB_OFF : SDL_GRAB_ON); + + server::Msg_LoadLevel(ind->getCurrent(), false); + + double dtime = 0; + last_tick_time=SDL_GetTicks(); + while (!client::AbortGameP()) { + try { + client::Tick (dtime); + server::Tick (dtime); + } + catch (XLevelRuntime& err) { + client::Msg_Error (string("Server Error: level runtime error:\n") + + err.what()); + server::Msg_Panic(true); + } + + int sleeptime = 10 - (SDL_GetTicks()-last_tick_time); + if (sleeptime >= 3) // only sleep if relatively idle + SDL_Delay(sleeptime); + + Uint32 current_tick_time = SDL_GetTicks(); + dtime = (current_tick_time - last_tick_time)/1000.0; + + if (abs(1-dtime/0.01) < 0.2) { + // less than 20% deviation from desired frame time? + dtime = 0.01; + last_tick_time += 10; + } else { + last_tick_time = current_tick_time; + } + + if (dtime > 500.0) /* Time has done something strange, perhaps + run backwards */ + dtime = 0.0; +// else if (dtime > 0.5) +// dtime = 0.5; + } + // add last played level + lev::PersistentIndex::addCurrentToHistory(); + + video::ShowMouse(); +} + +void game::ResetGameTimer() +{ + last_tick_time=SDL_GetTicks(); +} + + diff --git a/project/jni/application/enigma/src/game.hh b/project/jni/application/enigma/src/game.hh new file mode 100644 index 000000000..c3ef092d0 --- /dev/null +++ b/project/jni/application/enigma/src/game.hh @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ENIGMA_GAME_HH +#define ENIGMA_GAME_HH + +#include "fwd.hh" +#include "ecl_fwd.hh" +#include "lev/Proxy.hh" + +namespace enigma_game +{ + using enigma_levels::LevelPack; + using enigma_levels::Level; + + void ResetGameTimer(); + + void StartGame(); + + /*! Draw a level preview into video::BackBuffer(). Return true if + successful. */ + bool DrawLevelPreview (ecl::GC &, enigma::lev::Proxy *levelProxy); + +} + +namespace enigma +{ + namespace game = enigma_game; +} +#endif diff --git a/project/jni/application/enigma/src/gui/ErrorMenu.cpp b/project/jni/application/enigma/src/gui/ErrorMenu.cpp new file mode 100644 index 000000000..ba5c14d1f --- /dev/null +++ b/project/jni/application/enigma/src/gui/ErrorMenu.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/ErrorMenu.hh" +#include "enigma.hh" +#include "video.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + /* -------------------- HelpMenu -------------------- */ + + ErrorMenu::ErrorMenu(std::string message, std::string quitTitle) : + text (message), rejectQuit (false), laterQuit (false), + quit (new gui::StaticTextButton(quitTitle, this)) { + const video::VMInfo *vminfo = video::GetInfo(); + add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40)); + } + + ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle) : + text (message), rejectQuit (false), laterQuit (false), + quit (new gui::StaticTextButton(quitTitle, this)), + reject (new gui::StaticTextButton(rejectTitle, this)) { + const video::VMInfo *vminfo = video::GetInfo(); + add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40)); + add(reject, Rect(vminfo->width-340, vminfo->height-60, 150, 40)); + } + + ErrorMenu::ErrorMenu(std::string message, std::string quitTitle, std::string rejectTitle, + std::string laterTitle) : + text (message), rejectQuit (false), laterQuit (false), + quit (new gui::StaticTextButton(quitTitle, this)), + reject (new gui::StaticTextButton(rejectTitle, this)), + later (new gui::StaticTextButton(laterTitle, this)) { + const video::VMInfo *vminfo = video::GetInfo(); + add(quit, Rect(vminfo->width-170, vminfo->height-60, 150, 40)); + add(later, Rect(vminfo->width-340, vminfo->height-60, 150, 40)); + add(reject, Rect(vminfo->width-510, vminfo->height-60, 150, 40)); + } + + bool ErrorMenu::isRejectQuit() { + return rejectQuit; + } + + bool ErrorMenu::isLaterQuit() { + return laterQuit; + } + + bool ErrorMenu::on_event (const SDL_Event &e) + { + if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) { + Menu::quit(); + return true; + } + return false; + } + + void ErrorMenu::on_action (gui::Widget *w) { + if (w == quit) { + Menu::quit(); + } else if (w == reject) { + rejectQuit = true; + Menu::quit(); + } else if (w == later) { + laterQuit = true; + Menu::quit(); + } + } + + void ErrorMenu::draw_background (ecl::GC &gc) { + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + Font *f = enigma::GetFont("menufont"); + + vector lines; + + ecl::split_copy (text, '\n', back_inserter(lines)); + int x = 60; + int y = 60; + int yskip = 25; + const video::VMInfo *vminfo = video::GetInfo(); + int width = vminfo->width - 120; + for (unsigned i=0; irender(gc, x, y, lines[i].substr(0,breakPos).c_str()); + y += yskip; + if (breakPos != lines[i].size()) { + // process rest of line + lines[i] = lines[i].substr(breakPos); + } else { + // process next line + i++; + } + } + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/ErrorMenu.hh b/project/jni/application/enigma/src/gui/ErrorMenu.hh new file mode 100644 index 000000000..43c0254a5 --- /dev/null +++ b/project/jni/application/enigma/src/gui/ErrorMenu.hh @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_ERRORMENU_HH_INCLUDED +#define GUI_ERRORMENU_HH_INCLUDED + + +#include "gui/Menu.hh" +#include "ecl.hh" + +namespace enigma { namespace gui { + class ErrorMenu : public Menu { + public: + ErrorMenu (std::string message, std::string quitTitle); + ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle); + ErrorMenu (std::string message, std::string quitTitle, std::string rejectTitle, + std::string laterTitle); + bool isRejectQuit(); + bool isLaterQuit(); + private: + bool on_event (const SDL_Event &e); + void on_action (gui::Widget *w); + void draw_background (ecl::GC &gc); + + std::string text; + gui::Widget *quit; + gui::Widget *reject; + gui::Widget *later; + bool rejectQuit; + bool laterQuit; + }; +}} // namespace enigma::gui +#endif // GUI_ERRORMENU_HH_INCLUDED + diff --git a/project/jni/application/enigma/src/gui/GameMenu.cpp b/project/jni/application/enigma/src/gui/GameMenu.cpp new file mode 100644 index 000000000..0fb4c3bf9 --- /dev/null +++ b/project/jni/application/enigma/src/gui/GameMenu.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/GameMenu.hh" +#include "gui/OptionsMenu.hh" +#include "gui/LevelInspector.hh" +#include "client.hh" +#include "display.hh" +#include "ecl.hh" +#include "main.hh" +#include "nls.hh" +#include "server.hh" +#include "video.hh" +#include "lev/Index.hh" +#include "lev/Proxy.hh" + +#include + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { +/* -------------------- GameMenu -------------------- */ + + GameMenu::GameMenu (int zoomxpos_, int zoomypos_) + : zoomed(0), + zoomxpos(zoomxpos_), + zoomypos(zoomypos_) + { + resume = new gui::StaticTextButton(N_("Resume Level"), this); + restart = new gui::StaticTextButton(N_("Restart Level"), this); + options = new gui::StaticTextButton(N_("Options"), this); + info = new gui::StaticTextButton(N_("Level Info"), this); + abort = new gui::StaticTextButton(N_("Abort Level"), this); + + add(resume, Rect(0,0,180,40)); + add(restart, Rect(0,45,180,40)); + add(options, Rect(0,90,180,40)); + add(info, Rect(0,135,180,40)); + add(abort, Rect(0,180,180,40)); + center(); + } + + GameMenu::~GameMenu() { + delete(zoomed); + } + + void GameMenu::draw_background(ecl::GC &gc) + { + const video::VMInfo *vminfo = video::GetInfo(); + + if (!zoomed) { + Rect game_area = display::GetGameArea(); + int part_width = game_area.w/3; + int part_height = (part_width*vminfo->height)/vminfo->width; + + if (part_height > game_area.h) { + part_height = game_area.h/3; + part_width = (part_height*vminfo->width)/vminfo->height; + assert(part_width <= game_area.w); + } + + // randomly choose ball offset + int x, y; + for (int trials = 5; trials; --trials) { + x = IntegerRand(0, 5); + y = IntegerRand(0, 3); + + // try to avoid menu-ball overlap: + if (x<2 || x>3 || y<1 || y>2 || (trials == 1)) { + int ax = zoomxpos-game_area.x; + int ay = zoomypos-game_area.y; + + // upper left corner of part + x = ax/32-1-x; + y = ay/32-1-y; + + // ensure part is inside game_area + x = max(0, min(x, (game_area.w-part_width)/32-1)); + y = max(0, min(y, (game_area.h-part_height)/32-1)); + + // adjust to game fields + x = x*32+24; + y = y*32+16; + + break; + } + } + + // Be sure to redraw everything, or actors may appear on top + // of the stones (actors are drawn in one pass and only + // clipped to the screen boundary). + display::RedrawAll(video::GetScreen()); + + // get the selected part from screen + // SDL_Surface *back = video::GetScreen()->get_surface(); + Rect src_area(game_area.x+x, game_area.y+y, part_width, part_height); + Surface *src = Grab(video::GetScreen()->get_surface(), src_area); + + // zoom multiple times for softer image + // const double stepsize = 0.3; + // for (double zoom = 0.4; zoom < 0.9; zoom += stepsize) { + // int sx = round_down(zoom * vminfo->width); + // int sy = round_down(zoom * vminfo->height); + // Surface *tmp = src->zoom(sx, sy); + + // delete src; + // src = tmp; + // } + zoomed = src->zoom(vminfo->width, vminfo->height); + delete src; + } + + ecl::blit(gc, 0,0, zoomed); + } + + bool GameMenu::on_event (const SDL_Event &e) + { + if (e.type == SDL_MOUSEBUTTONDOWN + && e.button.button == SDL_BUTTON_RIGHT) + { + Menu::quit(); + return true; + } + return false; + } + + void GameMenu::on_action(gui::Widget *w) { + lev::Index *ind = lev::Index::getCurrentIndex(); + if (w == resume) { + Menu::quit(); + } + else if (w == abort) { + client::Msg_Command("abort"); + Menu::quit(); + } + else if (w == restart) { + if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) { + // force a reload from file + lev::Proxy * curProxy = lev::Proxy::loadedLevel(); + if (curProxy != NULL) + curProxy->release(); + } + client::Stop (); + server::Msg_LoadLevel(ind->getCurrent(), false); + Menu::quit(); + + } + else if (w == options) { + enigma::gui::ShowOptionsMenu (0); + invalidate_all(); +// Menu::quit(); + } + else if (w == info) { + LevelInspector m(ind->getCurrent()); + m.manage(); + invalidate_all(); +// Menu::quit(); + } + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/GameMenu.hh b/project/jni/application/enigma/src/gui/GameMenu.hh new file mode 100644 index 000000000..fb7ab4b7e --- /dev/null +++ b/project/jni/application/enigma/src/gui/GameMenu.hh @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_GAMEMENU_HH_INCLUDED +#define GUI_GAMEMENU_HH_INCLUDED + +#include "gui/Menu.hh" + +namespace enigma { namespace gui { +/* -------------------- GameMenu -------------------- */ + class GameMenu : public Menu { + public: + GameMenu(int zoomxpos_, int zoomypos_); + virtual ~GameMenu(); + private: + bool on_event (const SDL_Event &e); + void on_action(gui::Widget *w); + void draw_background(ecl::GC &gc); + + gui::Widget *resume, *restart, *options, *info, *abort; + ecl::Surface *zoomed; + int zoomxpos, zoomypos; // position to be zoomed + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/HelpMenu.cpp b/project/jni/application/enigma/src/gui/HelpMenu.cpp new file mode 100644 index 000000000..83b53ab84 --- /dev/null +++ b/project/jni/application/enigma/src/gui/HelpMenu.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2003,2004 Daniel Heck, Ralf Westram + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/HelpMenu.hh" +#include "enigma.hh" +#include "video.hh" +#include "nls.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + /* -------------------- HelpMenu -------------------- */ + + HelpMenu::HelpMenu (const char **helptext_, int xoffset_) : + helptext (helptext_), + ok (new gui::StaticTextButton(N_("Ok"), this)), + cfg (xoffset_) + { + const video::VMInfo &vminfo = *video::GetInfo(); + + add(ok, Rect(vminfo.width-170,vminfo.height-60,150,40)); + } + + bool HelpMenu::on_event (const SDL_Event &e) + { + if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_RIGHT) + { + Menu::quit(); + return true; + } + return false; + } + + void HelpMenu::on_action (gui::Widget *w) + { + if (w == ok) + Menu::quit(); + } + + void HelpMenu::draw_background (ecl::GC &gc) + { + const video::VMInfo &vminfo = *video::GetInfo(); + + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + Font *f = enigma::GetFont(cfg.fontname.c_str()); + + int y = cfg.y0 + (vminfo.height - 480)/2; + int x = (vminfo.width-640)/2; + for (int i = 0; helptext[i]; i += 2) + { + f->render (gc, cfg.x0 + x, y, _(helptext[i])); // translate + f->render (gc, cfg.x1 + x, y, _(helptext[i+1])); // translate + y += cfg.yskip; + } + } + + /* -------------------- Functions -------------------- */ + + void displayHelp(const char **helptext, int xoffset) + { + FX_Fade (video::FADEOUT); + HelpMenu menu(helptext, xoffset); + menu.draw_all(); + FX_Fade (video::FADEIN); + menu.manage(); + } + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/HelpMenu.hh b/project/jni/application/enigma/src/gui/HelpMenu.hh new file mode 100644 index 000000000..9cc49b8cc --- /dev/null +++ b/project/jni/application/enigma/src/gui/HelpMenu.hh @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2002,2003 Daniel Heck, Ralf Westram + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_HELP_HH +#define ENIGMA_HELP_HH + + +#include "gui/Menu.hh" +#include "ecl.hh" + +namespace enigma { namespace gui { + struct HelpMenuConfig { + int x0, x1; // x coordinates of first and second column + int y0; // y coordinate + int yskip; + std::string fontname; + + HelpMenuConfig (int xoffset) { + x0 = 40; + x1 = x0 + xoffset; + y0 = 40; + yskip = 30; + fontname = "menufont"; + } + }; + + class HelpMenu : public Menu { + public: + HelpMenu (const char **helptext_, int xoffset); + private: + bool on_event (const SDL_Event &e); + void on_action (gui::Widget *w); + void draw_background (ecl::GC &gc); + + const char **helptext; + gui::Widget *ok; + HelpMenuConfig cfg; + }; + + void displayHelp (const char **helptext, int xoffset); + +}} // namespace enigma::gui +#endif // ENIGMA_HELP_HH + diff --git a/project/jni/application/enigma/src/gui/InfoMenu.cpp b/project/jni/application/enigma/src/gui/InfoMenu.cpp new file mode 100644 index 000000000..ff160efdc --- /dev/null +++ b/project/jni/application/enigma/src/gui/InfoMenu.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "gui/InfoMenu.hh" +#include "ecl.hh" +#include "enigma.hh" +#include "video.hh" +#include "nls.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + InfoMenu::InfoMenu(const char **infotext, int pages) : info (infotext), + curPage (0), numPages (pages) { + const video::VMInfo &vminfo = *video::GetInfo(); + + but_ok = new StaticTextButton(N_("Ok"), this); + pgup = new ImageButton("ic-up", "ic-up1", this); + pgdown = new ImageButton("ic-down", "ic-down1", this); + + add(but_ok, Rect(vminfo.width-120, vminfo.height-60, 100, 40)); + add(pgup, Rect(vminfo.width-30, vminfo.height/2, 20, 50)); + add(pgdown, Rect(vminfo.width-30, vminfo.height/2 +70, 20, 50)); + } + + void InfoMenu::draw_background(ecl::GC &gc) { + const video::VMInfo &vminfo = *video::GetInfo(); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + + Font *f = enigma::GetFont("menufont"); + int row = 0; + for (int p=0; prender (gc, 40 + (vminfo.width-640)/2, + 20 + (vminfo.height-480)/2 + i*f->get_height(), t); + } + } + + void InfoMenu::on_action (gui::Widget *w) { + if (w == but_ok) { + Menu::quit(); + } else if (w == pgup) { + if (curPage > 0) { + curPage--; + invalidate_all(); + } + } else if (w == pgdown) { + if (curPage < numPages - 1) { + curPage++; + invalidate_all(); + } + } + } + + void displayInfo(const char **infotext, int pages) { + FX_Fade (video::FADEOUT); + InfoMenu menu(infotext, pages); + menu.draw_all(); + FX_Fade (video::FADEIN); + menu.manage(); + } + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/InfoMenu.hh b/project/jni/application/enigma/src/gui/InfoMenu.hh new file mode 100644 index 000000000..da88cb7eb --- /dev/null +++ b/project/jni/application/enigma/src/gui/InfoMenu.hh @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_INFOMENU_HH_INCLUDED +#define GUI_INFOMENU_HH_INCLUDED + +#include "gui/Menu.hh" + +namespace enigma { namespace gui { + class InfoMenu : public gui::Menu { + public: + InfoMenu(const char **infotext, int pages); + + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + private: + const char **info; + int curPage; + int numPages; + Widget *but_ok; + Widget *pgup; + Widget *pgdown; + }; + + void displayInfo(const char **helptext, int pages); +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LPGroupConfig.cpp b/project/jni/application/enigma/src/gui/LPGroupConfig.cpp new file mode 100644 index 000000000..2e17b1ea1 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LPGroupConfig.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LPGroupConfig.hh" +#include "gui/LevelPackConfig.hh" +#include "ecl.hh" +#include "errors.hh" +#include "nls.hh" +#include "video.hh" +#include "lev/Index.hh" + +#include "main.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + LPGroupConfig::LPGroupConfig(std::string groupName) : oldGroupName (groupName) { + const video::VMInfo &vminfo = *video::GetInfo(); + groups = lev::Index::getGroupNames(); + + position = -1; + for (int i = 0; i < groups.size(); i++) { + if (groups[i] == groupName) { + position = i; + break; + } + } + oldPosition = position; + if (position < 0) { + groups.push_back(groupName); + position = groups.size() - 1; + } + + VList * titleVList = new VList; + titleVList->set_spacing(12); + titleVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + titleVList->set_default_size(160, 35); + Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT); + titleVList->add_back(groupLabel); + + VList * groupsVList = new VList; + groupsVList->set_spacing(12); + groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + groupsVList->set_default_size(160, 35); + + Label * positionLabel = new Label(N_("Position:"), HALIGN_LEFT); + pre2Group = new UntranslatedLabel(""); + pre1Group = new UntranslatedLabel(""); + tf_groupName = new TextField(groupName); + post1Group = new UntranslatedLabel(""); + post2Group = new UntranslatedLabel(""); + Label * dummyLabel = new Label(""); + + groupsVList->add_back(positionLabel); + groupsVList->add_back(pre2Group); + groupsVList->add_back(pre1Group); + groupsVList->add_back(tf_groupName); + groupsVList->add_back(post1Group); + groupsVList->add_back(post2Group); + groupsVList->add_back(dummyLabel); + + VList * scrollVList = new VList; + scrollVList->set_spacing(12); + scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + scrollVList->set_default_size(40, 35); + + scrollUp = new ImageButton("ic-up", "ic-up1", this); + scrollDown = new ImageButton("ic-down", "ic-down1", this); + scrollVList->add_back(scrollUp); + scrollVList->add_back(scrollDown); + + this->add(titleVList, Rect(vminfo.width/2 - 290, 0, 160, vminfo.height-100)); + this->add(groupsVList, Rect(vminfo.width/2 - 80, 0, 160, vminfo.height-100)); + this->add(scrollVList, Rect(vminfo.width/2 + 130, 0, 40, vminfo.height-100)); + + errorLabel = new Label("", HALIGN_CENTER); + this->add(errorLabel, Rect(10, vminfo.height-100, vminfo.width-20, 35)); + + // Create buttons - positioning identical to Levelmenu + but_newPack = new StaticTextButton(N_("New Pack"), this); + but_delete = new StaticTextButton(N_("Delete Group"), this); + but_ignore = new StaticTextButton(N_("Undo"), this); + but_ok = new StaticTextButton(N_("Ok"), this); + + HList * commandHList = new HList; + commandHList->set_spacing(10); + commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + commandHList->set_default_size(140, 35); + commandHList->add_back(but_newPack); + commandHList->add_back(but_delete); + commandHList->add_back(but_ignore); + commandHList->add_back(but_ok); + this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35)); + + updateGroupList(); + } + + void LPGroupConfig::updateGroupList() { + pre2Group->set_text((position > 1) ? groups[position - 2] : ""); + pre1Group->set_text((position > 0) ? groups[position - 1] : ""); + post1Group->set_text((position < groups.size() - 1) ? groups[position + 1] : ""); + post2Group->set_text((position < groups.size() - 2) ? groups[position + 2] : ""); + } + + bool LPGroupConfig::doChanges() { + // rename first for consistency + std::string newName = tf_groupName->getText(); + std::string::size_type lastChar = newName.find_last_not_of(" "); + if (lastChar == std::string::npos) { + // the name is effectively an empty string + errorLabel->set_text(N_("Error: empty group name not allowed - press \"Undo\" to exit without modifications")); + return false; + } + + // strip off trailing and leading spaces + newName = newName.substr(0 , lastChar + 1); + newName = newName.substr(newName.find_first_not_of(" ")); + + // check if new group name is unique + for (int i = 0; i < groups.size(); i++) { + if (i != position && groups[i] == newName) { + errorLabel->set_text(N_("Error: group name is a duplicate of an existing group")); + return false; + } + } + if (newName == INDEX_EVERY_GROUP) { + errorLabel->set_text(N_("Error: \"Every group\" is a reserved group name")); + return false; + } + if (newName.size() > 2 && newName[0] == '[' + && newName[newName.size() -1] == ']') { + errorLabel->set_text(N_("Error: group name must not be enclosed in square brackets")); + return false; + } + + + if (oldGroupName.empty()) { + // menu called without an existing group + lev::Index::insertGroup(newName, position); + } else { + // menu called with an existing group + if (newName != oldGroupName) + lev::Index::renameGroup(oldGroupName, newName); + + if (oldPosition >= 0 && position != oldPosition) { + // move the group to the new position + lev::Index::moveGroup(newName, position); + } + } + return true; + } + + void LPGroupConfig::on_action(Widget *w) { + if (w == but_ok) { + if (doChanges()) + Menu::quit(); + else + invalidate_all(); + } else if (w == but_ignore) { + Menu::quit(); + } else if (w == scrollUp) { + if (position > 0) { + std::string tmp = groups[position]; + groups[position] = groups[position - 1]; + groups[position - 1] = tmp; + position--; + updateGroupList(); + invalidate_all(); + } + } else if (w == scrollDown) { + if (position < groups.size() - 1) { + std::string tmp = groups[position]; + groups[position] = groups[position + 1]; + groups[position + 1] = tmp; + position++; + updateGroupList(); + invalidate_all(); + } + } else if (w == but_delete) { + std::vector * indices = lev::Index::getGroup(oldGroupName); + if (indices != NULL) { + // reassign remaining indices from back to front to keep the + // group vector valid + for (int i = indices->size() - 1; i >= 0; i--) { + if ((*indices)[i]->getGroupName() != INDEX_EVERY_GROUP) { + LevelPackConfig m((*indices)[i]->getName(), oldGroupName, true); + if (!m.manage() || m.isUndoQuit()) { + errorLabel->set_text(N_("Error: group not empty")); + invalidate_all(); + return; + } + } + } + lev::Index::deleteGroup(oldGroupName); + } + Menu::quit(); + } else if (w == but_newPack) { + if (doChanges()) { + LevelPackConfig m(""); + m.manage(); + Menu::quit(); + } else { + invalidate_all(); + } + } + } + + void LPGroupConfig::draw_background(ecl::GC &gc) { + video::SetCaption(("Enigma - Level Pack Group Configuration")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } + + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LPGroupConfig.hh b/project/jni/application/enigma/src/gui/LPGroupConfig.hh new file mode 100644 index 000000000..87279d448 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LPGroupConfig.hh @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_LPGROUPCONFIG_HH_INCLUDED +#define GUI_LPGROUPCONFIG_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/TextField.hh" + +namespace enigma { namespace gui { + class LPGroupConfig : public gui::Menu { + public: + LPGroupConfig (std::string groupName); + + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + private: + void updateGroupList(); + bool doChanges(); + + TextField *tf_groupName; + Label *pre2Group; + Label *pre1Group; + Label *post1Group; + Label *post2Group; + Widget *scrollUp; + Widget *scrollDown; + Label *errorLabel; + Widget *but_newPack; + Widget *but_delete; + Widget *but_ignore; + Widget *but_ok; + std::vector groups; + int position; // new position of group that the user selected + int oldPosition; // position of group when entering menu, -1 for new group + std::string oldGroupName; + }; + +}} // namespace enigma::gui +#endif + diff --git a/project/jni/application/enigma/src/gui/LevelInspector.cpp b/project/jni/application/enigma/src/gui/LevelInspector.cpp new file mode 100644 index 000000000..78765cec1 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelInspector.cpp @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "errors.hh" +#include "gui/LevelInspector.hh" +#include "main.hh" +#include "enigma.hh" +#include "nls.hh" +#include "video.hh" +#include "ecl_util.hh" +#include "gui/MonospacedLabel.hh" +#include "gui/LevelPreviewCache.hh" +#include "gui/ScreenshotViewer.hh" +#include "lev/RatingManager.hh" +#include "lev/ScoreManager.hh" +#include "StateManager.hh" + + +#include + +using namespace ecl; +using namespace enigma; + +namespace enigma { namespace gui { + + class IntelligenceButton : public ValueButton { + int get_value() const { + return theRatingMgr->getIntelligence(theLevel); + } + void set_value(int value) { + theRatingMgr->setIntelligence(theLevel, value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + IntelligenceButton(lev::Proxy *aLevel) : ValueButton(0, 5), + theLevel (aLevel) { + theRatingMgr = lev::RatingManager::instance(); + init(); + } + private: + lev::RatingManager *theRatingMgr; + lev::Proxy *theLevel; + }; + + class DexterityButton : public ValueButton { + int get_value() const { + return theRatingMgr->getDexterity(theLevel); + } + void set_value(int value) { + theRatingMgr->setDexterity(theLevel, value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + DexterityButton(lev::Proxy *aLevel) : ValueButton(0, 5), + theLevel (aLevel) { + theRatingMgr = lev::RatingManager::instance(); + init(); + } + private: + lev::RatingManager *theRatingMgr; + lev::Proxy *theLevel; + }; + + class PatienceButton : public ValueButton { + int get_value() const { + return theRatingMgr->getPatience(theLevel); + } + void set_value(int value) { + theRatingMgr->setPatience(theLevel, value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + PatienceButton(lev::Proxy *aLevel) : ValueButton(0, 5), + theLevel (aLevel) { + theRatingMgr = lev::RatingManager::instance(); + init(); + } + private: + lev::RatingManager *theRatingMgr; + lev::Proxy *theLevel; + }; + + class KnowledgeButton : public ValueButton { + int get_value() const { + return theRatingMgr->getKnowledge(theLevel); + } + void set_value(int value) { + theRatingMgr->setKnowledge(theLevel, value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + KnowledgeButton(lev::Proxy *aLevel) : ValueButton(0, 6), + theLevel (aLevel) { + theRatingMgr = lev::RatingManager::instance(); + init(); + } + private: + lev::RatingManager *theRatingMgr; + lev::Proxy *theLevel; + }; + + class SpeedButton : public ValueButton { + int get_value() const { + return theRatingMgr->getSpeed(theLevel); + } + void set_value(int value) { + theRatingMgr->setSpeed(theLevel, value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + SpeedButton(lev::Proxy *aLevel) : ValueButton(0, 5), + theLevel (aLevel) { + theRatingMgr = lev::RatingManager::instance(); + init(); + } + private: + lev::RatingManager *theRatingMgr; + lev::Proxy *theLevel; + }; + + class RatingButton : public ValueButton { + int get_value() const { + return theScoreMgr->getRating(theLevel); + } + void set_value(int value) { + theScoreMgr->setRating(theLevel, value); + if (get_parent() != NULL) { + get_parent()->invalidate_all(); + } + } + + string get_text(int value) const { + return (value == -1) ? "-" : ecl::strf("%d", value); + } + public: + RatingButton(lev::Proxy *aLevel) : ValueButton(-1, 10), + theLevel (aLevel) { + theScoreMgr = lev::ScoreManager::instance(); + init(); + } + private: + lev::ScoreManager *theScoreMgr; + lev::Proxy *theLevel; + }; + +LevelInspector::LevelInspector(lev::Proxy *aLevel, bool showDeveloperInfo): + levelProxy(aLevel), isDeveloperMode(showDeveloperInfo), annotation (new TextField()), + back (new StaticTextButton(N_("Ok"), this)), + screenshot (new StaticTextButton(N_("Screenshot"), this)) + { + bool didGenerate; // dummy + previewImage = LevelPreviewCache::instance()->getPreview(aLevel, true, didGenerate); + const video::VMInfo *vminfo = video::GetInfo(); + vspacing = vminfo->height < 500 ? 2 :(vminfo->height < 650 ? 3 : 4); + vspacing2 = vminfo->height < 500 ? 16 :(vminfo->height < 650 ? 14 : 16); + vmargin = vminfo->height < 500 ? 10 :(vminfo->height < 650 ? 20 : 30); + hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 30); + bool highres = vminfo->height > 650 ? true : false; + bool lowres = vminfo->height < 600 ? true : false; + + add(back, Rect(vminfo->width-130-2*hmargin,vminfo->height-50,130,35)); + add(screenshot, Rect(vminfo->width-260-3*hmargin,vminfo->height-50,130,35)); + + try { + aLevel->loadMetadata(true); + } + catch (XLevelLoading &err) { + std::vector lines; + std::string errmsg = _("Server Error: could not load level '") + + aLevel->getNormLevelPath() + "'\n" + + err.what(); + ecl::split_copy (errmsg, '\n', back_inserter(lines)); + int x = 60; + int y = 60; + int yskip = 25; + for (unsigned i=0; iwidth-80,yskip)); + y += yskip; + } + return; + } + + std::string tmp, tmp2; + lev::RatingManager *theRatingMgr = lev::RatingManager::instance(); + lev::ScoreManager *theScoreMgr = lev::ScoreManager::instance(); + withEasy = aLevel->hasEasymode(); + ratingInherited = theScoreMgr->isRatingInherited(aLevel); + ecl::Font *menufont = enigma::GetFont("menufont"); + levelPathString = + (levelProxy->getNormPathType() == lev::Proxy::pt_resource) ? + levelProxy->getAbsLevelPath() : levelProxy->getNormLevelPath(); + // substitute all backslashes by slashes + for (std::string::size_type slpos = levelPathString.find('\\'); + slpos != std::string::npos; slpos = levelPathString.find('\\')) + levelPathString.replace(slpos, 1, 1, '/'); + BuildVList authorT(this, Rect(hmargin,vmargin,110,25), vspacing); + authorT.add(new Label(N_("Level: "), HALIGN_RIGHT)); + authorT.add(new Label((""), HALIGN_RIGHT)); // subtitle + authorT.add(new Label(N_("Author: "), HALIGN_RIGHT)); + authorT.add(new Label(N_("Contact: "), HALIGN_RIGHT)); + authorT.add(new Label(N_("Homepage: "), HALIGN_RIGHT)); + + BuildVList author(this, Rect(hmargin+110+10,vmargin, + vminfo->width-(hmargin+110+10)-10-(vminfo->thumbw+10)-hmargin,25), vspacing); + tmp = levelProxy->getTitle(); + tmp2 = levelProxy->getLocalizedString("title"); + if (tmp != tmp2) + tmp = tmp + " -- " + tmp2; + author.add(new Label( tmp, HALIGN_LEFT)); + tmp = levelProxy->getLocalizedString("subtitle"); + if (tmp == "subtitle") + tmp = ""; + author.add(new Label(tmp, HALIGN_LEFT)); // subtitle + author.add(new Label(levelProxy->getAuthor(), HALIGN_LEFT)); + + BuildVList address(this, Rect(hmargin+110+10,vmargin+3*(25+vspacing), + vminfo->width-(hmargin+110+10)-hmargin,25), vspacing); + address.add(new Label(levelProxy->getContact(), HALIGN_LEFT)); + address.add(new Label(levelProxy->getHomepage(), HALIGN_LEFT)); + + BuildVList ratingPubT(this, Rect(hmargin+65,vmargin+5*25+4*vspacing+vspacing2, 130,25), 2); + ratingPubT.add(new Label(N_("Public Ratings"), HALIGN_CENTER)); + BuildVList ratingPubST(this, Rect(hmargin,vmargin+6*25+5*vspacing+vspacing2, 130,25), 2); + ratingPubST.add(new Label(N_("Intelligence: "), HALIGN_RIGHT)); + ratingPubST.add(new Label(N_("Dexterity: "), HALIGN_RIGHT)); + ratingPubST.add(new Label(N_("Patience: "), HALIGN_RIGHT)); + ratingPubST.add(new Label(N_("Knowledge: "), HALIGN_RIGHT)); + ratingPubST.add(new Label(N_("Speed: "), HALIGN_RIGHT)); + if (!lowres) ratingPubST.add(new Label(N_("Difficulty: "), HALIGN_RIGHT)); + + + BuildVList ratingPub(this, Rect(hmargin+130+15,vmargin+6*25+5*vspacing+vspacing2, 30,25), 2); + if (WizardMode) { + ratingPub.add(new IntelligenceButton(aLevel)); + ratingPub.add(new DexterityButton(aLevel)); + ratingPub.add(new PatienceButton(aLevel)); + ratingPub.add(new KnowledgeButton(aLevel)); + ratingPub.add(new SpeedButton(aLevel)); + if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + } else { + ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getIntelligence(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDexterity(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getPatience(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getKnowledge(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getSpeed(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + if (!lowres) ratingPub.add(new MonospacedLabel(ratingToString(theRatingMgr->getDifficulty(aLevel)).c_str(),'8', " 0123456789", HALIGN_CENTER)); + } + + BuildVList scoresT(this, Rect(vminfo->width/2-100-20,vmargin+5*25+4*vspacing+vspacing2,100,25), 2); + scoresT.add(new Label(N_("Scores"), HALIGN_RIGHT)); + + BuildVList scoresST(this, Rect(vminfo->width/2-100-20,vmargin+6*25+5*vspacing+vspacing2,100,25), 2); + scoresST.add(new Label(N_("You: "), HALIGN_RIGHT)); + scoresST.add(new Label(N_("World: "), HALIGN_RIGHT)); +// TRANSLATORS: PAR = professional average rate - an expression used by golfers + scoresST.add(new Label(N_("PAR: "), HALIGN_RIGHT)); + scoresST.add(new Label(N_("Author: "), HALIGN_RIGHT)); + scoresST.add(new Label(N_("Solved %: "), HALIGN_RIGHT)); + if (!lowres) scoresST.add(new Label(N_("Solved #: "), HALIGN_RIGHT)); + + BuildVList scores(this, Rect(vminfo->width/2-15+(withEasy?0:20), + vmargin+6*25+5*vspacing+vspacing2,(withEasy?117:54),25), 2); + scores.add(new MonospacedLabel(scoreToString(theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_EASY), + theScoreMgr->getBestUserScore(aLevel, DIFFICULTY_HARD),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER)); + scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getBestScoreEasy(aLevel), + theRatingMgr->getBestScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER)); + scores.add(new MonospacedLabel(scoreToString(theRatingMgr->getParScoreEasy(aLevel), + theRatingMgr->getParScoreDifficult(aLevel),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER)); + scores.add(new MonospacedLabel(scoreToString(aLevel->getEasyScore(), + aLevel->getDifficultScore(),aLevel,true).c_str(),'8', " 0123456789", HALIGN_CENTER)); + scores.add(new MonospacedLabel(withEasy ? (theRatingMgr->getPcSolvedEasy(aLevel) + + " /" + theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str() : + (theRatingMgr->getPcSolvedDifficult(aLevel) + " ").c_str(), + '8', " 0123456789", HALIGN_CENTER)); + if (!lowres) scores.add(new MonospacedLabel(withEasy ? (ecl::strf("%5d", theRatingMgr->getNumSolvedEasy(aLevel)) + + " /" + ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str() : + (ecl::strf("%5d", theRatingMgr->getNumSolvedDifficult(aLevel)) + " ").c_str(), + '8', " 0123456789", HALIGN_CENTER)); + + BuildVList versionT(this, Rect(vminfo->width-100/2-90-2*hmargin,vmargin+5*25+4*vspacing+vspacing2,100,25), 2); + versionT.add(new Label(N_("Version"), HALIGN_CENTER)); + BuildVList versionST(this, Rect(vminfo->width-110-90-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,110,25), 2); + if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED) + versionST.add(new Label(N_("Score: "), HALIGN_RIGHT)); + else + versionST.add(new Label(N_("Status: "), HALIGN_RIGHT)); + versionST.add(new Label(N_("Release: "), HALIGN_RIGHT)); + versionST.add(new Label(N_("Revision: "), HALIGN_RIGHT)); + if (!lowres) + versionST.add(new Label(N_("Status: "), HALIGN_RIGHT)); + versionST.add(new Label(N_("Control: "), HALIGN_RIGHT)); + versionST.add(new Label(N_("Target: "), HALIGN_RIGHT)); + + BuildVList version(this, Rect(vminfo->width-80-2*hmargin,vmargin+6*25+5*vspacing+vspacing2,80+2*hmargin,25), 2); + if (!lowres || aLevel->getLevelStatus() == lev::STATUS_RELEASED) + version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getScoreVersion()).c_str(), + '8', " 0123456789", HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_STABLE) + version.add(new Label(N_("stable"), HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_TEST) + version.add(new Label(N_("test"), HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL) + version.add(new Label(N_("experimental"), HALIGN_LEFT)); + else + version.add(new Label(N_("unknown"), HALIGN_LEFT)); + + version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getReleaseVersion()).c_str(), + '8', " 0123456789", HALIGN_LEFT)); + version.add(new MonospacedLabel(ecl::strf("%6d", aLevel->getRevisionNumber()).c_str(), + '8', " 0123456789", HALIGN_LEFT)); + if (!lowres) + if (aLevel->getLevelStatus() == lev::STATUS_RELEASED) + version.add(new Label(N_("released"), HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_STABLE) + version.add(new Label(N_("stable"), HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_TEST) + version.add(new Label(N_("test"), HALIGN_LEFT)); + else if (aLevel->getLevelStatus() == lev::STATUS_EXPERIMENTAL) + version.add(new Label(N_("experimental"), HALIGN_LEFT)); + else + version.add(new Label(N_("unknown"), HALIGN_LEFT)); + + switch (aLevel->getControl()) { + case lev::force: + version.add(new Label(N_("force"), HALIGN_LEFT)); + break; + case lev::balance: + version.add(new Label(N_("balance"), HALIGN_LEFT)); + break; + case lev::key: + version.add(new Label(N_("key"), HALIGN_LEFT)); + break; + default: + version.add(new Label(N_("unknown"), HALIGN_LEFT)); + break; + } +#if 0 + // fake gettext to register the following strings for I18N + _("time") + _("pushes") + _("moves") +#endif + version.add(new Label(aLevel->getScoreTarget().c_str(), HALIGN_LEFT)); + + int bestScoreHolderLines = 0; + int creditsLines = 0; + int dedicationLines = 0; + int levelPathLines = 0; + int annotationLines = 0; + int compatibilityLines = 0; + int idLines = 0; + int vnext = vmargin+ (lowres?11:12)*25+(lowres?9:10)*vspacing+2*vspacing2; + int textwidth = vminfo->width-3*hmargin-110-10; + dispatchBottomLines(bestScoreHolderLines, creditsLines, dedicationLines, + levelPathLines, annotationLines, compatibilityLines, idLines, + (vminfo->height-vnext-vmargin-25-vspacing2)/27, textwidth); + if (bestScoreHolderLines == 1) { + add(new Label(N_("World Record Holders: "), HALIGN_RIGHT),Rect(hmargin,vnext,200,25)); + std::string holders; + if (withEasy) { + holders = theRatingMgr->getBestScoreEasyHolder(aLevel); + if (holders.empty()) + holders = " - "; + holders += " / "; + } + if (theRatingMgr->getBestScoreDifficultHolder(aLevel).empty()) + holders += " -"; + else + holders += theRatingMgr->getBestScoreDifficultHolder(aLevel); + Label *wrLabel = new Label(holders, HALIGN_LEFT); + add(wrLabel, Rect(hmargin+200+10,vnext,textwidth-90,25)); + if (!wrLabel->text_fits()) { + int cutEasy = 0; + int cutDiff = 0; + std::string diffHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel); + if (withEasy) { + std::string easyHolders = theRatingMgr->getBestScoreEasyHolder(aLevel); + bool hasEasyHolders = !easyHolders.empty(); + bool hasDiffHolders = !diffHolders.empty(); + int limit = 10; + do { + std::string cutHolders; + wrLabel->set_text(easyHolders); + if (!wrLabel->text_fits(0.48)) { + cutHolders = theRatingMgr->getBestScoreEasyHolder(aLevel, ++cutEasy); + if (cutHolders.empty()) + cutEasy--; + else + easyHolders = cutHolders; + } + wrLabel->set_text(diffHolders); + if (!wrLabel->text_fits(0.48)) { + cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff); + if (cutHolders.empty()) + cutDiff--; + else + diffHolders = cutHolders; + } + holders = (hasEasyHolders ? easyHolders : std::string(" - ")) + + " / " + (hasDiffHolders ? diffHolders : std::string(" -")); + wrLabel->set_text(holders); + limit--; + } while (!wrLabel->text_fits() && limit > 0); + } else { + std::string cutHolders; + do { + cutHolders = theRatingMgr->getBestScoreDifficultHolder(aLevel, ++cutDiff); + wrLabel->set_text(cutHolders); + } while (!wrLabel->text_fits()); + if (cutHolders.empty()) { + // we did cut off to many holders, take last attempt even if it was too long + wrLabel->set_text(theRatingMgr->getBestScoreDifficultHolder(aLevel, --cutDiff)); + } + } + } + vnext += 25 + vspacing; + } + if (creditsLines >= 1) { + add(new Label(N_("Credits: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + std::string creditsString = levelProxy->getCredits(true); + for (int i = 0; i< creditsLines; i++) { + std::string::size_type breakPos = breakString(menufont, creditsString, + " ", textwidth); + add(new Label(creditsString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25)); + creditsString = creditsString.substr(breakPos); + vnext += (25 + vspacing); + } + } + if (dedicationLines >= 1) { + add(new Label(N_("Dedication: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + std::string dedicationString = levelProxy->getDedication(true); + for (int i = 0; i< dedicationLines; i++) { + std::string::size_type breakPos = breakString( menufont, dedicationString, + " ", textwidth); + add(new Label(dedicationString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25)); + dedicationString = dedicationString.substr(breakPos); + vnext += (25 + vspacing); + } + } + if (levelPathLines >= 1) { + add(new Label(N_("Level Path: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + std::string workString = levelPathString; + for (int i = 0; i< levelPathLines - 1; i++) { + std::string::size_type breakPos = breakString(menufont, workString, + "/", textwidth); + add(new Label(workString.substr(0,breakPos), HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25)); + workString = workString.substr(breakPos); + vnext += (25 + vspacing); + } + // show as much as possible from last line + if (menufont->get_width(workString.c_str()) > textwidth) { + // show the filename at the end - skip leading parts if necessary + add(new Label(workString, HALIGN_RIGHT), Rect(hmargin+110+10,vnext,textwidth,25)); + } else { + // display up to the last character + add(new Label(workString, HALIGN_LEFT), Rect(hmargin+110+10,vnext,textwidth,25)); + } + vnext += (25 + vspacing); + } + if (idLines >= 1) { + add(new Label(N_("Id: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + add(new Label(levelProxy->getId(), HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25)); + vnext += (25 + vspacing)*idLines; + } + if (compatibilityLines >= 1) { + add(new Label(N_("Compatibility: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + std::string compString = ecl::strf("Enigma v%.2f / ", levelProxy->getEnigmaCompatibility()) + + GetGameTypeName(levelProxy->getEngineCompatibility()); + add(new Label(compString , HALIGN_LEFT),Rect(hmargin+110+10, vnext, textwidth, 25)); + vnext += (25 + vspacing)*compatibilityLines; + } + annotation->set_text(app.state->getAnnotation(levelProxy->getId())); // field needs to initialized for saves + if (annotationLines >= 1) { + add(new Label(N_("Annotation: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + add(annotation, Rect(hmargin+110+10, vnext, textwidth, 25)); + vnext += (25 + vspacing)*annotationLines; + } + vnext += vspacing2 - vspacing; + add(new Label(N_("Rating: "), HALIGN_RIGHT),Rect(hmargin,vnext,110,25)); + add(new RatingButton(aLevel),Rect(hmargin+110+10,vnext,40,25)); + add(new Label(N_("Average: "), HALIGN_RIGHT),Rect(hmargin+110+10+40+20,vnext,105,25)); + add(new Label(theRatingMgr->getAverageRating(aLevel), HALIGN_RIGHT),Rect(hmargin+110+10+40+18+105+6,vnext,31,25)); + } + + LevelInspector::~LevelInspector () { + } + + bool LevelInspector::isEndDeveloperMode() { + return isDeveloperMode; + } + + bool LevelInspector::on_event (const SDL_Event &e) { + bool handled = false; + if (e.type == SDL_KEYDOWN) { + handled=true; + switch (e.key.keysym.sym) { + case SDLK_F2: + if (!isDeveloperMode) { + if (!annotation->getText().empty() || + !app.state->getAnnotation(levelProxy->getId()).empty()) { + app.state->setAnnotation(levelProxy->getId(), annotation->getText()); + } + LevelInspector m(levelProxy, true); + m.manage(); + if (m.isEndDeveloperMode()) { + // reinit user input fields + annotation->set_text(app.state->getAnnotation(levelProxy->getId())); + invalidate_all(); + } else { + Menu::quit(); + } + } else { + if (!annotation->getText().empty() || + !app.state->getAnnotation(levelProxy->getId()).empty()) { + app.state->setAnnotation(levelProxy->getId(), annotation->getText()); + } + Menu::quit(); + } + break; + default: handled=false; break; + } + } + return handled; + } + + void LevelInspector::on_action(gui::Widget *w) { + if (w == back) { + // save annotation - but avoid to save unnecessary empty annotations + if (!annotation->getText().empty() || + !app.state->getAnnotation(levelProxy->getId()).empty()) { + app.state->setAnnotation(levelProxy->getId(), annotation->getText()); + } + isDeveloperMode = false; + Menu::quit(); + } else if (w == screenshot) { + ScreenshotViewer m(levelProxy); + m.manage(); + invalidate_all(); + } + } + + void LevelInspector::draw_background(ecl::GC &gc) { + const video::VMInfo *vminfo = video::GetInfo(); + video::SetCaption((std::string("Enigma - Level ") + + (isDeveloperMode ? "Developer " : "") + "Inspector").c_str()); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + blit(gc, vminfo->width-vminfo->thumbw-10-hmargin, vmargin, previewImage); + Surface *img_hard = enigma::GetImage("completed"); + if (withEasy) { + Surface *img_easy = enigma::GetImage("completed-easy"); + blit (gc, vminfo->width/2-4, vmargin+5*25+4*vspacing+vspacing2, img_easy); + blit (gc, vminfo->width/2-4+63, vmargin+5*25+4*vspacing+vspacing2, img_hard); + } else { + blit (gc, vminfo->width/2-4+20, vmargin+5*25+4*vspacing+vspacing2, img_hard); + } + Surface *img_changed = enigma::GetImage("changed"); + ratingInherited = lev::ScoreManager::instance()->isRatingInherited(levelProxy); + if (ratingInherited) { + int numLines = vminfo->height < 500 ? 14 :(vminfo->height < 650 ? 18 : 19); + blit (gc, hmargin+110+10+40, vmargin + numLines*25 + + (numLines-3)*vspacing + 3*vspacing2, img_changed); + } + } + + void LevelInspector::tick(double dtime) { + } + + std::string LevelInspector::ratingToString(int value) { + if (value == 0) { + // no rating available + return " "; + } else { + return ecl::strf("%3d", value); + } + } + + std::string LevelInspector::scoreToString(int easy, int difficult, + lev::Proxy *aLevel, bool constLengthForCenteredClipping) { + if (withEasy) { + if (!constLengthForCenteredClipping) + return scoreToString(easy, aLevel) + " / " + + scoreToString(difficult, aLevel); + else + if (aLevel->getScoreUnit() == lev::duration) + // + return (easy >= 0 ? "- " : ": ") + + scoreToString(easy, aLevel) + " / " + + scoreToString(difficult, aLevel) + + (difficult >= 0 ? " -" : " :") ; + else + return (easy >= 0 ? "- " : " ") + + scoreToString(easy, aLevel) + " / " + + scoreToString(difficult, aLevel) + + (difficult >= 0 ? " -" : " ") ; + } else { + return scoreToString(difficult, aLevel); + } + } + + std::string LevelInspector::scoreToString(int score, lev::Proxy *aLevel) { + if (aLevel->getScoreUnit() == lev::duration) + if (score >= 0 && score <= (99*60+59)) + return ecl::strf("%2d:%02d", score/60, score%60); + else + return " - "; + else + if (score >= 0 && score <= 9999) + return ecl::strf("%4d", score); + else + return " -"; + } + + void LevelInspector::dispatchBottomLines(int &bestScoreHolderLines, + int &creditsLines, int &dedicationLines, int &levelPathLines, + int &annotationLines, int &compatibilityLines, int &idLines, int numLines, int width) { + enum botType {holder, credits, dedication, path, annotation, compatibility, id}; + const int sequenceSize = 13; + botType sequence1[sequenceSize] = {credits, dedication, annotation, path, + holder, annotation, path, compatibility, credits, dedication, + annotation, credits, annotation}; + botType sequence2[sequenceSize] = {id, path, compatibility, holder, path, + annotation, annotation, credits, dedication, + credits, dedication, annotation, annotation}; + botType *sequence = isDeveloperMode ? sequence2 : sequence1; + int j = 0; + std::string creditsString = levelProxy->getCredits(true); + std::string dedicationString = levelProxy->getDedication(true); + std::string pathWorkString = levelPathString; + ecl::Font *menufont = enigma::GetFont("menufont"); + for (int i = 0; iset_listener (this); + + + // Create buttons + hll = new HList; + hll->set_spacing (10); + hll->set_alignment (HALIGN_CENTER, VALIGN_TOP); + hll->set_default_size (c.ibuttonw, c.buttonh); + hll->add_back (but_advancemode); + hll->add_back (but_next); + hll->add_back (but_difficulty); + + hlr = new HList; + hlr->set_spacing (10); + hlr->set_alignment (HALIGN_CENTER, VALIGN_TOP); + hlr->set_default_size (c.buttonw, c.buttonh); + hlr->add_back (but_levelpack); + hlr->add_back (but_back); + + hl = new HList; + hl->set_spacing (10); + hl->set_alignment (HALIGN_CENTER, VALIGN_TOP); + hl->set_default_size (2*c.buttonw + 10, c.buttonh); + hl->add_back (hll); + hl->add_back (hlr); + this->add (hl, Rect(c.leftborder, Y3, vminfo.width-20, c.buttonh)); + + // Add navigation buttons + pgup = new ImageButton("ic-up", "ic-up1", this); + pgdown = new ImageButton("ic-down", "ic-down1", this); + start = new ImageButton("ic-top", "ic-top1", this); + end = new ImageButton("ic-bottom", "ic-bottom1", this); + + Rect r(vminfo.width-30, c.thumbsy, 20, 50); + r.y = c.thumbsy; + add (pgup, r); + r.y += 60; + add (pgdown, r); + r.y = c.thumbsy + 240; + add (start, r); + r.y += 60; + add (end, r); + + // Information area + hl = new HList; + hl->add_back (lbl_levelname, List::EXPAND); + hl->add_back (lbl_lpinfo, List::TIGHT); + this->add (hl, Rect (5, Y2, vminfo.width - 10, 28)); + + hl_info_stat = new HList; + hl_info_stat->add_back (lbl_levelinfo, List::EXPAND); //Rect (c.leftborder, Y2+20,305, 28)); + hl_info_stat->add_back (lbl_statistics, List::TIGHT); + this->add (hl_info_stat, Rect (5, Y2+20, vminfo.width - 10, 28)); + + // Prepare level selection widget + levelwidget = new LevelWidget(); + levelwidget->set_listener(this); + levelwidget->realize (c.previewarea); + levelwidget->set_area (c.previewarea); + + this->add (levelwidget); + + updateIndex(); + } + + void LevelMenu::tick(double dtime) + { + levelwidget->tick(dtime); + static double timeaccu = 0.0; + + // info texts disappear after some time + if (shown_text_ttl>0.0) { + shown_text_ttl -= dtime; + if (shown_text_ttl <= 0.0) + shown_text = ""; + } + timeaccu += dtime; + if (timeaccu > 0.1) { + update_info(); + timeaccu = 0.0; + } + + } + + static const char *helptext_levelmenu[] = { + N_("Escape:"), N_("Skip to main menu"), + "F1:", N_("Show this help"), + "F5:", 0, // see below + N_("Arrows:"), N_("Select level"), + N_("Return:"), N_("Play selected level"), + N_("Back/Space:"), N_("Previous/next levelpack"), + "u", N_("Mark current level as Unsolved"), + // "s", N_("Mark current level as Solved"), + N_("Alt+Return:"), N_("Switch between fullscreen and window"), + N_("Left click:"), N_("Play selected level"), + N_("Right or control click:"), N_("Inspect selected level"), + 0 + }; + + bool LevelMenu::on_event (const SDL_Event &e) + { + // Pass all events to the level widget first + bool handled=levelwidget->on_event(e); + + if (!handled) { + if (e.type == SDL_KEYDOWN) { + handled=true; + switch (e.key.keysym.sym) { + case SDLK_SPACE: next_levelpack(); break; + case SDLK_BACKSPACE: previous_levelpack(); break; + case SDLK_F1: + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) + helptext_levelmenu[5] = N_("Select next level for world record hunt"); + else + helptext_levelmenu[5] = N_("Select next unsolved level"); + + displayHelp(helptext_levelmenu, 200); + draw_all(); + break; + case SDLK_F5: + next_unsolved(); + break; + case SDLK_u: { + lev::ScoreManager::instance()->markUnsolved(lev::Index::getCurrentProxy(), + app.state->getInt("Difficulty")); + invalidate_all(); + break; + } + case SDLK_s: + lev::ScoreManager::instance()->markSolved(lev::Index::getCurrentProxy(), + app.state->getInt("Difficulty")); + invalidate_all(); + break; + default: handled=false; break; + } + } + else + handled = Menu::on_event (e); + } + return handled; + } + + void LevelMenu::on_action(Widget *w) + { + if (w==levelwidget) { + lev::Index *ind = lev::Index::getCurrentIndex(); + int ilevel = ind->getCurrentPosition(); + if (w->lastModifierKeys() & KMOD_CTRL && w->lastModifierKeys() & KMOD_SHIFT) { + // force a reload from file + lev::Proxy * curProxy = lev::Proxy::loadedLevel(); + if (curProxy != NULL) + curProxy->release(); + } + + if ((unsigned)ilevel < ind->size()) { + if (ind->mayPlayLevel(ilevel+1)) { + game::StartGame(); + ilevel = ind->getCurrentPosition(); + invalidate_all(); + ind->setCurrentPosition(ilevel); + levelwidget->syncFromIndexMgr(); + } + else + show_text(_("You are not allowed to play this level yet.")); + } + } else if (w == but_back) { + main_quit = true; + Menu::quit(); + } else if (w == pgup) { + levelwidget->page_up(); + } else if (w == pgdown) { + levelwidget->page_down(); + } else if (w == start) { + levelwidget->start(); + } else if (w == end) { + levelwidget->end(); + } else if (w == but_next) { + next_unsolved(); + } else if (w == but_levelpack) { + main_quit = false; + Menu::quit(); + } else if (w == but_difficulty) { + but_difficulty->on_action(w); + invalidate_all(); + } + } + + void LevelMenu::update_info() { + // Note: all format strings have to be translated directly + // as the formatted strings can no longer be translated. + // The instant language change is guaranteed by the frequent + // call of is method! + + lev::Index *ind = lev::Index::getCurrentIndex(); + int size = ind->size(); + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::Proxy *curProxy = ind->getCurrent(); + int difficulty = app.state->getInt("Difficulty"); + + lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"), + ind->getName().c_str(), size)); + + if (size == 0) { + // empty level pack + lbl_statistics->set_text ("-"); + lbl_levelname->set_text ("-"); + lbl_levelinfo->set_text ("-"); + } + else { + int iselected = ind->getCurrentPosition(); + + // Display levelpack statistics (percentage of solved levels) + + if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_NOT_BEST) { + int pct = 100* scm->countBestScore(ind, difficulty)/ size; + lbl_statistics->set_text(ecl::strf(_("%d%% best"), pct)); + } + else if (app.state->getInt("NextLevelMode") == lev::NEXT_LEVEL_OVER_PAR) { + int pct = 100* scm->countParScore(ind, difficulty)/ size; + double hcp = scm->calcHCP(ind, difficulty); + lbl_statistics->set_text(ecl::strf(_("%d%% par, hcp %.1f"), pct, hcp)); + } + else { + int pct = 100* scm->countSolved(ind, difficulty) / size; + lbl_statistics->set_text(ecl::strf(_("%d%% solved"), pct)); + } + + // Display level name + if (enigma::WizardMode) { + // add level path info - we just can display the normalized path + // as we did not yet locate the absolute path - the user can + // use the inspector to check the absolute path! + lbl_levelname->set_text(ecl::strf("#%d: %s (%s)", + ind->getCurrentLevel(), curProxy->getTitle().c_str(), + curProxy->getNormLevelPath().c_str())); + } else { + lbl_levelname->set_text(ecl::strf("#%d: %s", + ind->getCurrentLevel(), curProxy->getTitle().c_str())); + } + + // Display best time + if (shown_text.length()) { + lbl_levelinfo->set_text(shown_text); + } + else { + // TODO prepare for scores that are not time based! + char txt[200]; + lev::RatingManager *ratingMgr = lev::RatingManager::instance(); + int wr_time = ratingMgr->getBestScore(curProxy, difficulty); + int par_time = ratingMgr->getParScore(curProxy, difficulty); + bool is_par = scm->parScoreReached(curProxy, difficulty); + int best_user_time = scm->getBestUserScore(curProxy, difficulty); + string wr_name = ratingMgr->getBestScoreHolder(curProxy, difficulty); + bool wr_name_displayed = false; + + string your_time; + string wr_text; + + if (best_user_time>0) { + your_time = strf(_("Your time: %d:%02d"), + best_user_time/60, best_user_time%60); + + if (wr_time>0) { + int below = wr_time - best_user_time; + if (below == 0) + wr_text = _("That's world record."); + else if (below>0) + wr_text = strf(_("That's %d:%02d below world record."), + below/60, below%60); + } + } + + if (wr_text.length() == 0 && wr_time>0) { + if (wr_name.length()) { + wr_name_displayed = true; + } else + if (is_par || par_time < 0) + wr_text = strf(_("World record: %d:%02d"), wr_time/60, wr_time%60); + else + wr_text = strf(_("Par: %d:%02d World record: %d:%02d"), + par_time/60, par_time%60, wr_time/60, wr_time%60); + } + + if (!your_time.empty()) + your_time += " "; + + int wr_cut = 0; + do { + if (wr_name_displayed) { + std::string tmp = ratingMgr->getBestScoreHolder(curProxy, difficulty, wr_cut++); + if (!tmp.empty()) + wr_name = tmp; + if (is_par || par_time < 0) + wr_text = strf(_("World record by %s: %d:%02d"), + wr_name.c_str(), wr_time/60, wr_time%60); + else + wr_text = strf(_("Par: %d:%02d World record by %s: %d:%02d"), + par_time/60, par_time%60, wr_name.c_str(), wr_time/60, wr_time%60); + } + lbl_levelinfo->set_text(your_time + wr_text); + } while (!hl_info_stat->fits() && wr_name_displayed && (wr_cut < 20)); + } + } + } + + void LevelMenu::updateIndex() + { + levelwidget->syncFromIndexMgr(); + + update_info(); + } + + void LevelMenu::draw_background(ecl::GC &gc) + { + video::SetCaption(("Enigma - Level Menu")); + sound::PlayMusic (options::GetString("MenuMusicFile")); + + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } + + void LevelMenu::next_unsolved() + { + lev::Index *ind = lev::Index::getCurrentIndex(); + if (ind->advanceLevel(lev::ADVANCE_NEXT_MODE)) { + levelwidget->syncFromIndexMgr(); + } else + show_text(_("No further unsolved level available!")); + } + + void LevelMenu::next_levelpack() + { + lev::Index::setCurrentIndex(lev::Index::nextGroupIndex()->getName()); + updateIndex(); + } + + void LevelMenu::previous_levelpack() { + lev::Index::setCurrentIndex(lev::Index::previousGroupIndex()->getName()); + updateIndex(); + } + + void LevelMenu::show_text(const string& text) { + shown_text = text; + shown_text_ttl = 2.0; // show for two seconds + } + + bool LevelMenu::isMainQuit() { + return main_quit; + } + + /* -------------------- DifficultyButton -------------------- */ + + DifficultyButton::DifficultyButton() : ImageButton("ic-easymode","ic-easymode",this) { + update(); + } + + void DifficultyButton::update() { + if (app.state->getInt("Difficulty") == DIFFICULTY_EASY) + ImageButton::set_images("ic-easymode","ic-normalmode"); + else + ImageButton::set_images("ic-normalmode","ic-easymode"); + } + + void DifficultyButton::on_action(Widget *) + { + int newdifficulty = (DIFFICULTY_EASY+DIFFICULTY_HARD) - app.state->getInt("Difficulty"); + app.state->setProperty("Difficulty", newdifficulty); update(); + invalidate(); + } + + void DifficultyButton::draw(ecl::GC &gc, const ecl::Rect &r) { + update(); + ImageButton::draw(gc, r); + } + + /* -------------------- AdvanceModeButton -------------------- */ + + AdvanceModeButton::AdvanceModeButton() : ImageButton("","",this) { + update(); + } + + void AdvanceModeButton::update() { + switch (app.state->getInt("NextLevelMode")) { + case lev::NEXT_LEVEL_UNSOLVED : + ImageButton::set_images("ic-unsolved", "par"); + break; + case lev::NEXT_LEVEL_OVER_PAR : + ImageButton::set_images("par", "ic-worldrecord"); + break; + case lev::NEXT_LEVEL_NOT_BEST : + ImageButton::set_images("ic-worldrecord", "ic-strictlynext"); + break; + case lev::NEXT_LEVEL_STRICTLY : // use as default, too + default: + ImageButton::set_images("ic-strictlynext","ic-unsolved"); + break; + } + } + + void AdvanceModeButton::on_action(Widget *) + { + switch (app.state->getInt("NextLevelMode")) { + case lev::NEXT_LEVEL_STRICTLY : + app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_UNSOLVED); + break; + case lev::NEXT_LEVEL_UNSOLVED : + app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_OVER_PAR); + break; + case lev::NEXT_LEVEL_OVER_PAR : + app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_NOT_BEST); + break; + case lev::NEXT_LEVEL_NOT_BEST : + default: + app.state->setProperty("NextLevelMode", lev::NEXT_LEVEL_STRICTLY); + } + update(); + invalidate(); + } + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelMenu.hh b/project/jni/application/enigma/src/gui/LevelMenu.hh new file mode 100644 index 000000000..7f80a61a8 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelMenu.hh @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_LEVELMENU_HH_INCLUDED +#define GUI_LEVELMENU_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/LevelWidget.hh" +#include "lev/Proxy.hh" +#include "lev/Index.hh" + + +namespace enigma { namespace gui { +/* -------------------- LevelMenu -------------------- */ + + class LevelMenu : public Menu { + public: + LevelMenu(); + + // Rotate through levels and packs + void next_levelpack(); + void previous_levelpack(); + void show_text(const string& text); + bool isMainQuit(); + + private: + void update_info(); + void next_unsolved(); + + void updateIndex(); + + // Menu interface. + void tick (double time); + void draw_background(ecl::GC &gc); + + // Widget interface. + bool on_event (const SDL_Event &e); + + // ActionListener interface. + void on_action(Widget *w); + + // Variables. + + + Widget *pgup, *pgdown, *start, *end; + Widget *but_advancemode; // Next unsolved level button + Widget *but_next; + Widget *but_back; // "Back" button + Widget *but_difficulty; // "Difficulty" button + TextButton *but_levelpack; // "Levelpack" button + HList *hl_info_stat; + Label *lbl_lpinfo; // Levelpack information + Label *lbl_statistics; // percentage solved + Label *lbl_levelname; + Label *lbl_levelinfo; + LevelWidget *levelwidget; + string shown_text; // info text (disappears automatically) + double shown_text_ttl; // rest duration for shown_text + bool main_quit; + }; + +/* -------------------- Buttons -------------------- */ + + class DifficultyButton : public ImageButton { + // ActionListener interface. + void on_action(Widget *); + public: + DifficultyButton(); + virtual void draw(ecl::GC &gc, const ecl::Rect &r); + private: + void update(); + }; + + class AdvanceModeButton : public ImageButton { + // ActionListener interface. + void on_action(Widget *); + public: + AdvanceModeButton(); + private: + void update(); + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LevelPackComposer.cpp b/project/jni/application/enigma/src/gui/LevelPackComposer.cpp new file mode 100644 index 000000000..6e289ea81 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackComposer.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LevelPackComposer.hh" +#include "gui/HelpMenu.hh" +#include "ecl.hh" +#include "enigma.hh" +#include "errors.hh" +#include "nls.hh" +#include "sound.hh" +#include "video.hh" +#include "lev/Index.hh" + +#include "main.hh" + +#include + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + lev::PersistentIndex * LevelPackComposer::clipboard = NULL; + + static const char *helptext[] = { + N_("Shift click:"), N_("Add to clipboard"), + N_("Shift delete:"), N_("Clear clipboard"), + N_("F8:"), N_("Insert clipboard as reference"), + N_("F9:"), N_("Insert clipboard as copy"), +// N_("F10:"), N_("Move clipboard levels"), + N_("Alt left arrow:"), N_("Exchange level with predecessor"), + N_("Alt right arrow:"), N_("Exchange level with successor"), + N_("Delete:"), N_("Delete level"), + N_("F5:"), N_("Update index from levels"), + 0 + }; +#if 0 + // fake gettext to register the following strings for I18N + _("F10") + _("Move clipboard levels") +#endif + + + LevelPackComposer::LevelPackComposer(bool enableEdit) : + isEditable (enableEdit), isModified (false) { + if (clipboard == NULL) { + std::vector dummy; + clipboard = new lev::PersistentIndex(" ", false); // mark as incomplete + } + + curIndex = dynamic_cast(lev::Index::getCurrentIndex()); + + const video::VMInfo &vminfo = *video::GetInfo(); + + // Add navigation buttons + pgup = new ImageButton("ic-up", "ic-up1", this); + pgdown = new ImageButton("ic-down", "ic-down1", this); + start = new ImageButton("ic-top", "ic-top1", this); + end = new ImageButton("ic-bottom", "ic-bottom1", this); + + Rect r(vminfo.width-30, 60, 20, 50); + r.y = 60; + add (pgup, r); + r.y += 60; + add (pgdown, r); + r.y = 60 + 240; + add (start, r); + r.y += 60; + add (end, r); + + // Prepare level selection widget + levelwidget = new LevelWidget(false, isEditable); + levelwidget->set_listener(this); + ecl::Rect previewarea(10, 60, vminfo.width-50, vminfo.height-130); + levelwidget->realize (previewarea); + levelwidget->set_area (previewarea); + + this->add(levelwidget); + + // Information area + lbl_lpinfo = new Label(); + lbl_clipinfo = new Label(); + lbl_levelname = new Label(); + lbl_clipcontent = new Label(); + + HList *hl = new HList; + hl->set_spacing(10); + hl->set_alignment(HALIGN_CENTER, VALIGN_TOP); + hl->set_default_size(vminfo.width/2 - 10, 28); + hl->add_back (lbl_lpinfo); + hl->add_back (lbl_clipinfo); + this->add (hl, Rect (5, 10, vminfo.width - 10, 28)); + + hl = new HList; + hl->set_spacing(10); + hl->set_alignment(HALIGN_CENTER, VALIGN_TOP); + clipContentWidth = vminfo.width/2 - 10; + hl->set_default_size(clipContentWidth, 28); + hl->add_back (lbl_levelname); + hl->add_back (lbl_clipcontent); + this->add (hl, Rect (5, 10+20, vminfo.width - 10, 28)); + + // Create buttons - positioning identical to Levelmenu + but_ignore = new StaticTextButton(N_("Undo"), this); + but_back = new StaticTextButton(N_("Ok"), this); + + HList * commandHList = new HList; + commandHList->set_spacing(10); + commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + commandHList->set_default_size(140, 35); + commandHList->add_back(new Label()); + commandHList->add_back(new Label()); + commandHList->add_back(but_ignore); + commandHList->add_back(but_back); + this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35)); + + } + + void LevelPackComposer::tick(double dtime) + { + levelwidget->tick(dtime); + static double timeaccu = 0.0; + +// // info texts disappear after some time +// if (shown_text_ttl>0.0) { +// shown_text_ttl -= dtime; +// if (shown_text_ttl <= 0.0) +// shown_text = ""; +// } + timeaccu += dtime; + if (timeaccu > 0.1) { + update_info(); + timeaccu = 0.0; + } + } + + bool LevelPackComposer::on_event (const SDL_Event &e) { + // Pass all events to the level widget first + bool handled=levelwidget->on_event(e); + + if (!handled) { + if (e.type == SDL_KEYDOWN) { + switch (e.key.keysym.sym) { + case SDLK_DELETE: + if (SDL_GetModState() & KMOD_SHIFT) { + // delete clipboard + clipboard->clear(); + handled=true; + } else { + // delete level + if (isEditable) { + int pos = curIndex->getCurrentPosition(); + lev::Proxy * curProxy = curIndex->getCurrent(); + if (curProxy == NULL) { + // levelpack is empty + handled=true; + break; + } + if (curIndex->isSource(curProxy) && + backups.find(curProxy->getNormLevelPath()) == backups.end()) { + // mark as deletion candidate - the final check + // if we delete it really occurs on save + deletions.insert(curProxy->getNormLevelPath()); + } + curIndex->erase(pos); + if (pos >= curIndex->size() && pos > 0) + curIndex->setCurrentPosition(pos-1); + levelwidget->syncFromIndexMgr(); + isModified = true; + invalidate_all(); + handled=true; + } + } + break; + case SDLK_F8: + if (isEditable) { + int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1; + for (int i = 0; i < clipboard->size(); i++) { + lev::Variation var = clipboard->getVariation(i); + curIndex->insertProxy(pos++, clipboard->getProxy(i), true, + var.ctrl, var.unit, var.target, var.extensions); + isModified = true; + } + invalidate_all(); + handled=true; + } + break; + case SDLK_F9: + if (isEditable && !curIndex->isCross()) { + int pos = (curIndex->size() == 0) ? 0 : curIndex->getCurrentPosition() + 1; + for (int i = 0; i < clipboard->size(); i++) { + lev::Variation var = clipboard->getVariation(i); + lev::Proxy *levelCopy = clipboard->getProxy(i)->copy(app.userPath + "/levels", + curIndex->getPackPath(), true); + if (levelCopy == NULL) { + // insert a crossreference + curIndex->insertProxy(pos++, clipboard->getProxy(i), true, + var.ctrl, var.unit, var.target, var.extensions); + } else { + // insert reference to our copy + curIndex->insertProxy(pos++, levelCopy, true, + var.ctrl, var.unit, var.target, var.extensions); + backups.insert(levelCopy->getNormLevelPath()); + deletions.erase(levelCopy->getNormLevelPath()); + } + isModified = true; + } + invalidate_all(); + handled=true; + } + break; + case SDLK_LEFT: + if (isEditable && (SDL_GetModState() & KMOD_ALT)) { + int pos = curIndex->getCurrentPosition(); + if (pos > 0) { + curIndex->exchange(pos, pos-1); + levelwidget->syncFromIndexMgr(); + isModified = true; + } + invalidate_all(); + handled=true; + } + break; + case SDLK_RIGHT: + if (isEditable && (SDL_GetModState() & KMOD_ALT)) { + int pos = curIndex->getCurrentPosition(); + if (pos < curIndex->size() - 1) { + curIndex->exchange(pos, pos+1); + levelwidget->syncFromIndexMgr(); + isModified = true; + } + invalidate_all(); + handled=true; + } + break; + case SDLK_F5: + if (isEditable) { + curIndex->updateFromProxies(); + isModified = true; + invalidate_all(); + handled=true; + } + break; + case SDLK_F1: + displayHelp(helptext, 200); + invalidate_all(); + handled=true; + break; + default: + break; + } + } + else + handled = Menu::on_event (e); + } + return handled; + } + + void LevelPackComposer::on_action(Widget *w) { + if (w==levelwidget) { + lev::Index *ind = lev::Index::getCurrentIndex(); + int ilevel = ind->getCurrentPosition(); + if (w->lastModifierKeys() & KMOD_SHIFT) { + lev::Variation var; + lev::Proxy * curProxy = lev::Index::getCurrentProxy(); + if (curProxy->getNormPathType() != lev::Proxy::pt_absolute) { + // all but absolute commandline proxies may be put on the clipboard + if (curIndex != NULL) + var = curIndex->getVariation(curIndex->getCurrentPosition()); + clipboard->appendProxy(curProxy, var.ctrl, + var.unit, var.target, var.extensions); + sound::EmitSoundEvent ("menuok"); + } else { + sound::EmitSoundEvent ("menustop"); + } + } + } else if (w == but_back) { + if (isModified) { + // save index + curIndex->save(true); + // delete levelfiles + std::set::iterator it; + for (it = deletions.begin(); it != deletions.end(); it++) { + if (!curIndex->hasNormLevelPath(*it)) { + // delete plain files on user path - ignore system and zip levels + std::string path = app.userPath + "/levels/" + (*it); + std::remove((path + ".xml").c_str()); + std::remove((path + ".lua").c_str()); + } + } + // delete backups + std::string base = app.userPath + "/levels/"; + for (it = backups.begin(); it != backups.end(); it++) { + std::remove((base + *it + ".xml~").c_str()); + std::remove((base + *it + ".lua~").c_str()); + } + } + Menu::quit(); + } else if (w == but_ignore) { + if (isModified) { + // we need to reload the index + curIndex->loadDoc(); + // restore backups + std::string base = app.userPath + "/levels/"; + std::set::iterator it; + for (it = backups.begin(); it != backups.end(); it++) { + std::remove((base + *it + ".xml").c_str()); + std::rename((base + *it + ".xml~").c_str(), (base + *it + ".xml").c_str()); + std::remove((base + *it + ".lua").c_str()); + std::rename((base + *it + ".lua~").c_str(), (base + *it + ".lua").c_str()); + } + } + Menu::quit(); + } else if (w == pgup) { + levelwidget->page_up(); + } else if (w == pgdown) { + levelwidget->page_down(); + } else if (w == start) { + levelwidget->start(); + } else if (w == end) { + levelwidget->end(); + } + } + void LevelPackComposer::update_info() { + // Note: all format strings have to be translated directly + // as the formatted strings can no longer be translated. + // The instant language change is guaranteed by the frequent + // call of is method! + + lev::Index *ind = lev::Index::getCurrentIndex(); + int size = ind->size(); + lev::Proxy *curProxy = ind->getCurrent(); + + lbl_lpinfo->set_text(ecl::strf(_("%s: %d levels"), + ind->getName().c_str(), size)); + + if (size == 0) { + // empty level pack + lbl_levelname->set_text ("-"); + } + else { + lbl_levelname->set_text(ecl::strf("#%d:(%s)", + ind->getCurrentLevel(), + curProxy->getNormLevelPath().c_str())); + } + + int csize = clipboard->size(); + lbl_clipinfo->set_text(ecl::strf(_("Clipboard: %d levels"), csize)); + if (csize == 0) { + // empty level pack + lbl_clipcontent->set_text ("-"); + } + else { + std::string clipstring = clipboard->getProxy(0)->getTitle(); + for (int i = 1; i < csize; i++) + clipstring += ", " + clipboard->getProxy(i)->getTitle(); + lbl_clipcontent->set_text(clipstring); + if (enigma::GetFont("menufont")->get_width(clipstring.c_str()) > clipContentWidth) + lbl_clipcontent->set_alignment(HALIGN_RIGHT); + else + lbl_clipcontent->set_alignment(HALIGN_CENTER); + } + + } + + void LevelPackComposer::draw_background(ecl::GC &gc) { + video::SetCaption(("Enigma - Level Pack Composer")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + if (isModified) + + blit(gc, 0,0, enigma::GetImage("changed")); + } + + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelPackComposer.hh b/project/jni/application/enigma/src/gui/LevelPackComposer.hh new file mode 100644 index 000000000..89809b9d3 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackComposer.hh @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_LEVELPACKCOMPOSER_HH_INCLUDED +#define GUI_LEVELPACKCOMPOSER_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/LevelWidget.hh" +#include "lev/Proxy.hh" +#include "lev/Index.hh" +#include "lev/PersistentIndex.hh" + +#include + +namespace enigma { namespace gui { + + class LevelPackComposer : public gui::Menu { + public: + LevelPackComposer(bool enableEdit); + + void tick(double dtime); + bool on_event (const SDL_Event &e); + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + private: + void update_info(); + + static lev::PersistentIndex * clipboard; + bool isEditable; + bool isModified; + lev::PersistentIndex * curIndex; + std::set deletions; // normLevelPath + std::set backups; // normLevelPath + Widget *pgup, *pgdown, *start, *end; + LevelWidget *levelwidget; + Label *lbl_lpinfo; // Levelpack information + Label *lbl_levelname; + Label *lbl_clipinfo; + Label *lbl_clipcontent; + int clipContentWidth; + Widget *but_ignore; + Widget *but_back; + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LevelPackConfig.cpp b/project/jni/application/enigma/src/gui/LevelPackConfig.cpp new file mode 100644 index 000000000..6b650aa22 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackConfig.cpp @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LevelPackConfig.hh" +#include "gui/LevelPackComposer.hh" +#include "ecl.hh" +#include "errors.hh" +#include "nls.hh" +#include "video.hh" +#include "lev/Index.hh" + +#include "main.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + GroupButton::GroupButton(std::vector groups, int pos) : + ValueButton(0, groups.size() - 1), + position (pos), groupNames (groups) { + init(); + } + + int GroupButton::get_value() const { + return position; + } + + void GroupButton::set_value(int value) { + position = value; + } + + std::string GroupButton::get_text(int value) const { + return groupNames[value]; + } + + /* ------------------- LevelmodeButton -------------------- */ + + LevelmodeButton::LevelmodeButton(bool initialMode) : + ImageButton("ic-link_copy","ic-link_copy",this), mode (initialMode) { + update(); + } + + bool LevelmodeButton::isLinkOnly() { + return mode; + } + + void LevelmodeButton::update() { + if (mode) + ImageButton::set_images("ic-link","ic-link_copy"); + else + ImageButton::set_images("ic-link_copy","ic-link"); + } + + void LevelmodeButton::on_action(Widget *) + { + mode = !mode; + update(); + invalidate(); + } + + void LevelmodeButton::draw(ecl::GC &gc, const ecl::Rect &r) { + update(); + ImageButton::draw(gc, r); + } + + + + LevelPackConfig::LevelPackConfig(std::string indexName, std::string groupName, + bool forceGroupReasign) : isReasignOnly (forceGroupReasign), + undo_quit (false), didEditMetaData (false), titleTF (NULL) { + const video::VMInfo &vminfo = *video::GetInfo(); + + if (indexName.empty()) + // new levelpack + packIndex = new lev::PersistentIndex(" ", false, + INDEX_DEFAULT_PACK_LOCATION, "", + INDEX_STD_FILENAME, lev::Index::getCurrentGroup()); // mark as incomplete + else + packIndex = lev::Index::findIndex(indexName); + ASSERT (packIndex != NULL, XFrontend, "not existing index Name"); + persIndex = dynamic_cast(packIndex); + isPersistent = (persIndex != NULL); + isEditable = isPersistent ? persIndex->isUserEditable() : false; + + // build a list of allowed group + std::vector groups = lev::Index::getGroupNames(); + // eliminate pseudo group "All Packs" + std::vector::iterator itg = groups.begin(); + while (itg != groups.end()) { + if (*itg == INDEX_ALL_PACKS) { + itg = groups.erase(itg); + break; + } + if (itg != groups.end()) + itg++; + } + // add pseudo group "[Every Group]" + groups.push_back(std::string("[") + INDEX_EVERY_GROUP +"]"); + intialGroupPosition = groups.size() - 1; // INDEX_EVERY_GROUP as default + // mark index's default group with square brackets and find current group + bool defaultGroupFound = false; + for (int i = 0; i < groups.size(); i++) { + if (groups[i] == packIndex->getGroupName()) { + intialGroupPosition = i; + } + if (groups[i] == packIndex->getDefaultGroupName()) { + groups[i] = std::string("[") + groups[i] +"]"; + defaultGroupFound = true; + } + } + if (!defaultGroupFound) { + groups.push_back(std::string("[") + packIndex->getDefaultGroupName() +"]"); + } + groupButton = new GroupButton(groups, intialGroupPosition); + + // index location list setup + std::vector * allIndices = lev::Index::getGroup(INDEX_ALL_PACKS); + for (int i = 0; i < allIndices->size(); i++) + locationList.push_back((*allIndices)[i]->getName()); + position = -1; + for (int i = 0; i < locationList.size(); i++) { + if (locationList[i] == indexName) { + position = i; + break; + } + } + oldPosition = position; + if (position < 0) { + // append new levelpack as last + locationList.push_back(indexName); + position = locationList.size() - 1; + } + + VList * titleLeftVList = new VList; + titleLeftVList->set_spacing(11); + titleLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + titleLeftVList->set_default_size(140, 35); + Label * titleLabel = new Label(N_("Levelpack:"), HALIGN_RIGHT); + Label * ownerLabel = new Label(N_("Owner:"), HALIGN_RIGHT); + Label * groupLabel = new Label(N_("Group:"), HALIGN_RIGHT); + Label * loactionLabel1 = new Label(N_("Location"), HALIGN_LEFT); + Label * loactionLabel2 = new Label(N_("in [All Packs]:"), HALIGN_RIGHT); + titleLeftVList->add_back(titleLabel); + if (!isReasignOnly) { + titleLeftVList->add_back(ownerLabel); + } + titleLeftVList->add_back(groupLabel); + if (!isReasignOnly) { + titleLeftVList->add_back(new Label()); + titleLeftVList->add_back(loactionLabel1); + titleLeftVList->add_back(loactionLabel2); + titleLeftVList->add_back(new Label()); + titleLeftVList->add_back(new Label()); + } + + valueLeftVList = new VList; + valueLeftVList->set_spacing(11); + valueLeftVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + valueLeftVList->set_default_size(160, 35); + + titleValueLabel = new UntranslatedLabel(indexName, HALIGN_CENTER); + ownerValueLabel = new UntranslatedLabel(isPersistent ? persIndex->getOwner() : "System"); + + pre2Index = new UntranslatedLabel(); + pre1Index = new UntranslatedLabel(); + thisIndex = new UntranslatedLabel(); + post1Index = new UntranslatedLabel(); + post2Index = new UntranslatedLabel(); + + valueLeftVList->add_back(titleValueLabel); + if (!isReasignOnly) { + valueLeftVList->add_back(ownerValueLabel); + } + valueLeftVList->add_back(groupButton); + if (!isReasignOnly) { + valueLeftVList->add_back(pre2Index); + valueLeftVList->add_back(pre1Index); + valueLeftVList->add_back(thisIndex); + valueLeftVList->add_back(post1Index); + valueLeftVList->add_back(post2Index); + } + + VList * scrollVList = new VList; + scrollVList->set_spacing(12); + scrollVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + scrollVList->set_default_size(30, 35); + + scrollUp = new ImageButton("ic-up", "ic-up1", this); + scrollDown = new ImageButton("ic-down", "ic-down1", this); + scrollVList->add_back(scrollUp); + scrollVList->add_back(scrollDown); + + VList * metaVList = new VList; + metaVList->set_spacing(12); + metaVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + metaVList->set_default_size(140, 35); + + if (isEditable) + but_metadata = new StaticTextButton(N_("Edit Metadata"), this); + else + but_metadata = new Label(); + Label * releaseLabel = new Label(N_("Release:"), HALIGN_RIGHT); + Label * revisionLabel = new Label(N_("Revision:"), HALIGN_RIGHT); + Label * compatibilityLabel = new Label(N_("Compatibility:"), HALIGN_RIGHT); + Label * defLocationLabel = new Label(N_("Default Location:"), HALIGN_RIGHT); + Label * crossmodeLabel = new Label(N_("Level types:"), HALIGN_RIGHT); + + if (!isReasignOnly) { + metaVList->add_back(but_metadata); + metaVList->add_back(new Label()); + if (WizardMode) { + metaVList->add_back(releaseLabel); + metaVList->add_back(revisionLabel); + } else { + metaVList->add_back(new Label()); + metaVList->add_back(new Label()); + } + metaVList->add_back(crossmodeLabel); + if (WizardMode) { + metaVList->add_back(compatibilityLabel); + } else { + metaVList->add_back(new Label()); + } + metaVList->add_back(defLocationLabel); + metaVList->add_back(new Label()); + } + + valueMetaVList = new VList; + valueMetaVList->set_spacing(12); + valueMetaVList->set_alignment(HALIGN_CENTER, VALIGN_CENTER); + valueMetaVList->set_default_size(75, 35); + Widget * levelmodeWidget; + if (indexName.empty()){ + levelmode = new LevelmodeButton(false); + levelmodeWidget = levelmode; + } else { + levelmodeWidget = new Image(isPersistent && !(persIndex->isCross()) ? + "ic-link_copy" : "ic-link"); + } + defLocationValueLabel = new Label(ecl::strf("%g", packIndex->getDefaultLocation())); + releaseValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRelease()) : "-"); + revisionValueLabel = new Label(isPersistent ? ecl::strf("%d", persIndex->getRevision()) : "-"); + compatibilityValueLabel = new Label(isPersistent ? ecl::strf("%.2f", persIndex->getCompatibility()) : "-"); + + if (!isReasignOnly) { + valueMetaVList->add_back(new Label()); + valueMetaVList->add_back(new Label()); + if (WizardMode) { + valueMetaVList->add_back(releaseValueLabel); + valueMetaVList->add_back(revisionValueLabel); + } else { + valueMetaVList->add_back(new Label()); + valueMetaVList->add_back(new Label()); + } + valueMetaVList->add_back(levelmodeWidget); + if (WizardMode) { + valueMetaVList->add_back(compatibilityValueLabel); + } else { + valueMetaVList->add_back(new Label()); + } + valueMetaVList->add_back(defLocationValueLabel); + valueMetaVList->add_back(new Label()); + } + + + if (isReasignOnly) { + this->add(titleLeftVList, Rect(vminfo.width/2 - 270, 15, 140, vminfo.height-97)); + this->add(valueLeftVList, Rect(vminfo.width/2 - 80, 15, 160, vminfo.height-97)); + } else { + this->add(titleLeftVList, Rect(vminfo.width/2 - 300, 15, 140, vminfo.height-97)); + this->add(valueLeftVList, Rect(vminfo.width/2 - 140, 15, 160, vminfo.height-97)); + this->add(scrollVList, Rect(vminfo.width/2 + 30, 15+3*(35+12) + (vminfo.height-480)/2, 30, 5*35+4*12)); + this->add(metaVList, Rect(vminfo.width/2 + 80, 15, 140, vminfo.height-97)); + this->add(valueMetaVList, Rect(vminfo.width/2 + 235, 15, 75, vminfo.height-97)); + } + + errorLabel = new Label("", HALIGN_CENTER); + this->add(errorLabel, Rect(10, vminfo.height-97, vminfo.width-20, 35)); + + if (isReasignOnly) + errorLabel->set_text(N_("Please reasign levelpack to another group for group deletion")); + + // Create buttons - positioning identical to Levelmenu + but_edit = new StaticTextButton(N_("Compose Pack"), this); + if (isPersistent && persIndex->isUpdatable() && persIndex->isCross()) { + but_update = new StaticTextButton(N_("Update Pack"), this); + } else { + but_update = new Label(); + } + but_ignore = new StaticTextButton(N_("Undo"), this); + but_back = new StaticTextButton(N_("Ok"), this); + + HList * commandHList = new HList; + commandHList->set_spacing(10); + commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + commandHList->set_default_size(140, 35); + if (isReasignOnly) { + commandHList->add_back(new Label()); + commandHList->add_back(new Label()); + } else { + commandHList->add_back(but_edit); + commandHList->add_back(but_update); + } + commandHList->add_back(but_ignore); + commandHList->add_back(but_back); + this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35)); + + updateLocationList(); + if (indexName.empty()) + // new levelpack + switchToMetadataEdit(); + } + + void LevelPackConfig::updateLocationList() { + pre2Index->set_text((position > 1) ? locationList[position - 2] : ""); + pre1Index->set_text((position > 0) ? locationList[position - 1] : ""); + thisIndex->set_text(didEditMetaData ? titleTF->getText() : packIndex->getName()); + post1Index->set_text((position < locationList.size() - 1) ? locationList[position + 1] : ""); + post2Index->set_text((position < locationList.size() - 2) ? locationList[position + 2] : ""); + } + + void LevelPackConfig::switchToMetadataEdit() { + if (!didEditMetaData) { + didEditMetaData = true; + titleTF = new TextField(titleValueLabel->getText(), this); + valueLeftVList->exchange_child(titleValueLabel, titleTF); + delete titleValueLabel; + ownerTF = new TextField(ownerValueLabel->getText()); + valueLeftVList->exchange_child(ownerValueLabel, ownerTF); + delete ownerValueLabel; + defLocationTF = new TextField(defLocationValueLabel->getText()); + valueMetaVList->exchange_child(defLocationValueLabel, defLocationTF); + delete defLocationValueLabel; + if (WizardMode) { + releaseTF = new TextField(releaseValueLabel->getText()); + valueMetaVList->exchange_child(releaseValueLabel, releaseTF); + delete releaseValueLabel; + revisionTF = new TextField(revisionValueLabel->getText()); + valueMetaVList->exchange_child(revisionValueLabel, revisionTF); + delete revisionValueLabel; + compatibilityTF = new TextField(compatibilityValueLabel->getText()); + valueMetaVList->exchange_child(compatibilityValueLabel, compatibilityTF); + delete compatibilityValueLabel; + } + } + } + + bool LevelPackConfig::isUndoQuit() { + return undo_quit; + } + + bool LevelPackConfig::doChanges() { + // change metadata + if (didEditMetaData) { + // the Index is persistent, user editabel and the user did switch to edit mode + bool needSave = false; + bool isNewIndex = persIndex->getName().empty(); + + // check for valid input + // title + std::string newtitle = titleTF->getText(); + std::string::size_type lastChar = newtitle.find_last_not_of(" "); + if (lastChar == std::string::npos) { + // the title is effectively an empty string + errorLabel->set_text(N_("Error: empty title not allowed - press \"Undo\" to exit without modifications")); + return false; + } + // strip off trailing and leading spaces + newtitle = newtitle.substr(0 , lastChar + 1); + newtitle = newtitle.substr(newtitle.find_first_not_of(" ")); + if (newtitle != persIndex->getName()) { + if (isNewIndex) { + // check for filename usability of title + const std::string validChars("_- .#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (newtitle.find_first_not_of(validChars, 0) != std::string::npos || + (newtitle.length() >= 1 && newtitle[0] == '.')) { + errorLabel->set_text(N_("Error: use only \"a-zA-Z0-9 _-#\" for levelpack title")); + return false; + } + + // set packPath to cross if link only + if (levelmode->isLinkOnly()) + persIndex->markNewAsCross(); + } + if (!persIndex->setName(newtitle)) { + errorLabel->set_text(N_("Error: title already in use - choose another title")); + return false; + } + needSave = true; + } + + // check rest for need of save + if (ownerTF->getText() != persIndex->getOwner()) { + persIndex->setOwner(ownerTF->getText()); + needSave = true; + } + if (defLocationTF->getText() != ecl::strf("%g", packIndex->getDefaultLocation())) { + double d = 0; + // check value - keep old value on error + if ((sscanf(defLocationTF->getText().c_str(),"%lg", &d) == 1) && + d > 0) { + packIndex->setDefaultLocation(d); + needSave = true; + } + } + + if (WizardMode) { + if (releaseTF->getText() != ecl::strf("%d", persIndex->getRelease())) { + int i = 0; + // check value - keep old value on error + if ((sscanf(releaseTF->getText().c_str(),"%d", &i) == 1) && + i > 0) { + persIndex->setRelease(i); + needSave = true; + } + } + if (revisionTF->getText() != ecl::strf("%d", persIndex->getRevision())) { + int i = 0; + // check value - keep old value on error + if ((sscanf(revisionTF->getText().c_str(),"%d", &i) == 1) && + i > 0) { + persIndex->setRevision(i); + needSave = true; + } + } + if (compatibilityTF->getText() != ecl::strf("%.2f", persIndex->getCompatibility())) { + double d = 0; + // check value - keep old value on error + if ((sscanf(compatibilityTF->getText().c_str(),"%lg", &d) == 1) && + d >= 1) { + persIndex->setCompatibility(d); + needSave = true; + } + } + } + + // save + if (needSave) + persIndex->save(); + if (isNewIndex) { + lev::Index::registerIndex(persIndex); + lev::Index::setCurrentIndex(persIndex->getName()); + } + } + // regroup + if (groupButton->get_value() != intialGroupPosition) { + std::string newGroupName = groupButton->get_text(groupButton->get_value()); + // strip off square brackets used to mark default and pseudo groups + if (newGroupName.size() > 2 && newGroupName[0] == '[' && + newGroupName[newGroupName.size() -1] == ']') { + newGroupName = newGroupName.substr(1, newGroupName.size() - 2); + } + packIndex->moveToGroup(newGroupName); + } else if (isReasignOnly) { + // the user did not reasign - take as an undo request + undo_quit = true; + } + // relocate + if (position != oldPosition) + packIndex->locateBehind(position > 0 ? locationList[position - 1] : ""); + + return true; + } + + void LevelPackConfig::on_action(Widget *w) { + if (w == but_back) { + if (doChanges()) + Menu::quit(); + else + invalidate_all(); + } else if (w == but_ignore) { + if (packIndex->getName().empty()) { + delete packIndex; + } + undo_quit = true; + Menu::quit(); + } else if (w == but_update) { + if (isPersistent && doChanges()) { + persIndex->load(false, true); + persIndex->save(true); + Menu::quit(); + } + invalidate_all(); + } else if (w == but_edit) { + if (doChanges()) { + LevelPackComposer m(isEditable); + m.manage(); + Menu::quit(); + } else { + invalidate_all(); + } + } else if (w == scrollUp) { + if (position > 0) { + std::string tmp = locationList[position]; + locationList[position] = locationList[position - 1]; + locationList[position - 1] = tmp; + position--; + updateLocationList(); + invalidate_all(); + } + } else if (w == scrollDown) { + if (position < locationList.size() - 1) { + std::string tmp = locationList[position]; + locationList[position] = locationList[position + 1]; + locationList[position + 1] = tmp; + position++; + updateLocationList(); + invalidate_all(); + } + } else if (w == but_metadata && !didEditMetaData) { + switchToMetadataEdit(); + invalidate_all(); + } else if (w == titleTF && w != NULL) { + thisIndex->set_text(titleTF->getText()); + } + } + + void LevelPackConfig::draw_background(ecl::GC &gc) { + video::SetCaption(("Enigma - Level Pack Configuration")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } + + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelPackConfig.hh b/project/jni/application/enigma/src/gui/LevelPackConfig.hh new file mode 100644 index 000000000..90a883a70 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackConfig.hh @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_LEVELPACKCONFIG_HH_INCLUDED +#define GUI_LEVELPACKCONFIG_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/TextField.hh" +#include "lev/Index.hh" +#include "lev/PersistentIndex.hh" + +namespace enigma { namespace gui { + + class GroupButton : public ValueButton { + public: + GroupButton(std::vector groups, int pos); + int get_value() const; + void set_value(int value); + std::string get_text(int value) const; + private: + std::vector groupNames; + int position; + }; + + class LevelmodeButton : public ImageButton { + // ActionListener interface. + void on_action(Widget *); + public: + LevelmodeButton(bool initialMode = false); + bool isLinkOnly(); + virtual void draw(ecl::GC &gc, const ecl::Rect &r); + private: + void update(); + bool mode; + }; + + + class LevelPackConfig : public gui::Menu { + public: + LevelPackConfig (std::string indexName, std::string groupName = "", + bool forceGroupReasign = false); + + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + bool isUndoQuit(); + private: + void updateLocationList(); + void switchToMetadataEdit(); + bool doChanges(); + + lev::Index *packIndex; + lev::PersistentIndex *persIndex; + bool isPersistent; + bool isEditable; + TextField *tf_packName; + GroupButton *groupButton; + int intialGroupPosition; + VList *valueLeftVList; + Label *titleValueLabel; + TextField *titleTF; + Label *ownerValueLabel; + TextField *ownerTF; + VList *valueMetaVList; + LevelmodeButton *levelmode; + Label *defLocationValueLabel; + TextField *defLocationTF; + Label *releaseValueLabel; + TextField *releaseTF; + Label *revisionValueLabel; + TextField *revisionTF; + Label *compatibilityValueLabel; + TextField *compatibilityTF; + Label *pre2Index; + Label *pre1Index; + Label *thisIndex; + Label *post1Index; + Label *post2Index; + Widget *scrollUp; + Widget *scrollDown; + Label *errorLabel; + Widget *but_metadata; + Widget *but_up; + Widget *but_down; + Widget *but_edit; + Widget *but_update; + Widget *but_ignore; + Widget *but_back; + bool isReasignOnly; + bool didEditMetaData; + bool undo_quit; + std::vector locationList; + int position; // new position of index in locationList that the user selected + int oldPosition; // position of index when entering menu, -1 for new index + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LevelPackMenu.cpp b/project/jni/application/enigma/src/gui/LevelPackMenu.cpp new file mode 100644 index 000000000..6813ba313 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackMenu.cpp @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LevelPackMenu.hh" +#include "gui/LevelMenu.hh" +#include "gui/LPGroupConfig.hh" +#include "gui/LevelPackConfig.hh" +#include "gui/SearchMenu.hh" +#include "gui/HelpMenu.hh" +#include "ecl.hh" +#include "errors.hh" +#include "nls.hh" +#include "video.hh" +#include "lev/Index.hh" +#include "lev/PersistentIndex.hh" + +#include "main.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { +/* -------------------- Level Pack Menu -------------------- */ + std::map LevelPackMenu::groupLastSelectedIndex; + std::map LevelPackMenu::groupLastSelectedColumn; + std::string LevelPackMenu::lastGroupName; + int LevelPackMenu::firstDisplayedGroup = 0; + + static const char *helptext[] = { + N_("Left column:"), N_("Levelpack groups"), + N_("Right columns:"), N_("Levelpacks of selected group"), + N_("Left click:"), N_("Select group or levelpack"), + N_("Right or control click:"), N_("Configure group or levelpack"), + 0 + }; + + LevelPackMenu::LevelPackMenu() : packsHList (NULL), groupsVList (NULL), + scrollLeft (NULL), scrollRight (NULL), scrollUp (NULL), + scrollDown (NULL), isLevelMenuSubmenu (false) { + const video::VMInfo &vminfo = *video::GetInfo(); + vm = vminfo.videomode; + + // Create buttons - positioning identical to Levelmenu + but_new = new StaticTextButton(N_("New Group"), this); + but_search = new StaticTextButton(N_("Search"), this); + but_level = new StaticTextButton(N_("Start Game"), this); + but_main = new StaticTextButton(N_("Main Menu"), this); + + commandHList = new HList; + commandHList->set_spacing(10); + commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + commandHList->set_default_size(140, 35); + commandHList->add_back(but_new); + commandHList->add_back(but_search); + commandHList->add_back(but_level); + commandHList->add_back(but_main); + this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35)); + + } + + void LevelPackMenu::setupMenu() { + static struct SpacingConfig { + int packcolumns, rows; + int vmargin, vrow_row; + int hmargin, hgroup_pack, hscrollbutton, hscroll_pack, hpack_pack; + } param[video::VM_COUNT] = { + { // VM_640x480 + 2, 9, + 15, 10, + 20, 36, 22, 10, 20 + }, + { // VM_640x512 + 2, 9, + 15, 10, + 20, 36, 22, 10, 20 + }, + { // VM_800x600 + 3, 11, + 15, 13, + 15, 36, 22, 10, 15 + }, + { // VM_1024x768 + 4, 15, + 15, 10, + 30, 36, 22, 12, 20 + } + }; + + if (groupsVList != NULL) { + groupsVList->clear(); + remove_child(groupsVList); + delete groupsVList; + groupsVList = NULL; + scrollUp = NULL; // deleted with groupsVList + scrollDown = NULL; // deleted with groupsVList + } + + if (packsHList != NULL) { + packsHList->clear(); + remove_child(packsHList); + delete packsHList; + packsHList = NULL; + } + + if (scrollLeft != NULL) { + remove_child(scrollLeft); + delete scrollLeft; + scrollLeft = NULL; + } + if (scrollRight != NULL) { + remove_child(scrollRight); + delete scrollRight; + scrollRight = NULL; + } + + packButtons.clear(); + groupButtons.clear(); + + std::vector groupNames = lev::Index::getGroupNames(); + int groupCount = groupNames.size(); + std::string curGroupName = lev::Index::getCurrentGroup(); + bool needUpScroll = false; + bool needDownScroll = false; + int numDisplayGroups = param[vm].rows; + int usedGroupRows = (groupCount > numDisplayGroups) ? numDisplayGroups : groupCount; + + // correct scroll attempts and screen resolution changes + firstDisplayedGroup = ecl::Clamp(firstDisplayedGroup, 0, + (groupCount > numDisplayGroups) ? groupCount - numDisplayGroups : 0); + needUpScroll = firstDisplayedGroup > 0; + needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups; + if (curGroupName != lastGroupName) { + // group changed by indirect user action - ensure it is visible + int curGroupPos = getGroupPosition(&groupNames, curGroupName); + if (curGroupPos != -1) { + if (curGroupPos <= firstDisplayedGroup ) { + if (curGroupPos <= 1) { + needUpScroll = false; + firstDisplayedGroup = 0; + } else { + needUpScroll = true; + firstDisplayedGroup = curGroupPos -1; + } + needDownScroll = firstDisplayedGroup < groupCount - numDisplayGroups; + } else if (curGroupPos >= firstDisplayedGroup + numDisplayGroups - 1) { + if (curGroupPos >= groupCount - 2) { + needDownScroll = false; + firstDisplayedGroup = groupCount - numDisplayGroups; + } else { + needDownScroll = true; + firstDisplayedGroup = curGroupPos - numDisplayGroups + 2; + } + if (firstDisplayedGroup < 0) + firstDisplayedGroup = 0; + needUpScroll = firstDisplayedGroup > 0; + } + } + } + + + groupsVList = new VList; + groupsVList->set_spacing(param[vm].vrow_row); + groupsVList->set_alignment(HALIGN_LEFT, VALIGN_CENTER); + groupsVList->set_default_size(160, 35); + + for (int i = 0; i < usedGroupRows; i++) { + if (i == 0 && needUpScroll) { + scrollUp = new ImageButton("ic-up", "ic-up1", this); + groupsVList->add_back(scrollUp); + } else if (i == usedGroupRows -1 && needDownScroll) { + scrollDown = new ImageButton("ic-down", "ic-down1", this); + groupsVList->add_back(scrollDown); + } else { + TextButton * button = new UntranslatedStaticTextButton( + groupNames[firstDisplayedGroup + i], this);; + groupButtons.push_back(button); + groupsVList->add_back(button); + } + } + + this->add(groupsVList, Rect(param[vm].hmargin, param[vm].vmargin, + 160, param[vm].rows * 35 + + (param[vm].rows - 1) * param[vm].vrow_row)); + + lastGroupName = curGroupName; + std::vector * group = lev::Index::getGroup(curGroupName); + ASSERT(group != NULL, XFrontend,""); + unsigned packCount = group->size(); + + int posCurrentIndex = getIndexPosition(group, lev::Index::getCurrentIndex()->getName()); + int selectedColumn = lev::Index::getGroupSelectedColumn(curGroupName); + int colCurrentIndex = 0; + int nextPack = 0; // pack displayed at top of first display column + + if (selectedColumn != INDEX_GROUP_COLUMN_UNKNOWN || + groupLastSelectedIndex.find(curGroupName) == groupLastSelectedIndex.end()) { + colCurrentIndex = checkColumn(param[vm].rows, param[vm].packcolumns, + packCount, posCurrentIndex, selectedColumn); + nextPack = (posCurrentIndex / param[vm].rows - colCurrentIndex) * param[vm].rows; + } else { + // the user selected a new level pack and the column was not yet + // calculated: we try to keep the display unchanged in respect of + // of the last selected pack and if necessary scroll minimum amount + // of columns + int posLastIndex = getIndexPosition(group,groupLastSelectedIndex[curGroupName]); + int colLastIndex = checkColumn(param[vm].rows, param[vm].packcolumns, + packCount, posLastIndex, groupLastSelectedColumn[curGroupName]); + nextPack = (posLastIndex / param[vm].rows - colLastIndex) * param[vm].rows; + if (posCurrentIndex < nextPack) { + // current index would be left of display - we need to scroll left + nextPack -= (((nextPack - posCurrentIndex - 1)/param[vm].rows) + 1) * + param[vm].rows; + colCurrentIndex = 0; + } else if (posCurrentIndex < nextPack + param[vm].rows * param[vm].packcolumns) { + // current index is still visible - keep nextPack + colCurrentIndex = (posCurrentIndex - nextPack) / param[vm].rows; + } else { + // current index would be right of display - we need to scroll right + nextPack += (((posCurrentIndex - nextPack)/param[vm].rows) - + (param[vm].packcolumns - 1)) * param[vm].rows; + colCurrentIndex = param[vm].packcolumns - 1; + } + } + + bool needRightScroll = packCount > nextPack + param[vm].rows * param[vm].packcolumns; + bool needLeftScroll = nextPack > 0; + + lev::Index::setGroupSelectedColumn(curGroupName, colCurrentIndex); + groupLastSelectedIndex[curGroupName] = lev::Index::getCurrentIndex()->getName(); + groupLastSelectedColumn[curGroupName] = colCurrentIndex; + + packsHList = new HList; + packsHList->set_spacing(param[vm].hpack_pack); + packsHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + packsHList->set_default_size(160, param[vm].rows*35 + + (param[vm].rows - 1) * param[vm].vrow_row); + + for (int col = 0; col < param[vm].packcolumns; col++) { + if (packCount - nextPack > 0) { + VList * pl = new VList; + pl->set_spacing (param[vm].vrow_row); + // first column is centered - if it is full it is like top alignment: + pl->set_alignment (HALIGN_LEFT, col == 0 ? VALIGN_CENTER : VALIGN_TOP); + pl->set_default_size (160, 35); + for (int row = 0; row < param[vm].rows; row++) { + if (nextPack < packCount) { + lev::Index *ind = (*group)[nextPack]; + TextButton * button = new UntranslatedStaticTextButton(ind->getName(), this); + packButtons.push_back(button); + pl->add_back(button); + nextPack++; + } else + break; + } + packsHList->add_back(pl); + } else + break; + } + + this->add(packsHList, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack + + param[vm].hscrollbutton + param[vm].hscroll_pack, + param[vm].vmargin, + param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) * + param[vm].hpack_pack, + param[vm].rows * 35 + + (param[vm].rows - 1) * param[vm].vrow_row)); + + if (needLeftScroll) { + scrollLeft = new ImageButton("ic-left", "ic-left1", this); + this->add(scrollLeft, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack, + param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row), + param[vm].hscrollbutton, 35)); + } + + if (needRightScroll) { + scrollRight = new ImageButton("ic-right", "ic-right1", this); + this->add(scrollRight, Rect(param[vm].hmargin + 160 + param[vm].hgroup_pack + + param[vm].hscrollbutton + 2 * param[vm].hscroll_pack + + param[vm].packcolumns * 160 + (param[vm].packcolumns - 1) * + param[vm].hpack_pack, + param[vm].vmargin + param[vm].rows / 2 * (35 + param[vm].vrow_row), + param[vm].hscrollbutton, 35)); + } + } + + void LevelPackMenu::manageLevelMenu() { + bool finished = false; + while (!finished) { + { + LevelMenu m; + if (!m.manage() || m.isMainQuit()) { + // ESC or Main button has been pressed in LevelMenu - + // the user wants to return + finished = true; + } + } + if (!finished) { + // the user left LevelMenu via LevelPack button + this->isLevelMenuSubmenu = true; + if (this->manage()) { + // not ESC - the user pressed Main button + finished = true; + } else { + // the user pressed ESC - return to LevelMenu + } + } + } + } + + bool LevelPackMenu::manage() { + setupMenu(); + updateHighlight(); + return Menu::manage(); + } + + bool LevelPackMenu::on_event (const SDL_Event &e) { + switch (e.type) { + case SDL_KEYDOWN: + SDLKey keysym = e.key.keysym.sym; + switch (keysym) { + case SDLK_F1: + displayHelp(helptext, 200); + invalidate_all(); + return true; + default: + break; + } + break; + } + return false; + } + + void LevelPackMenu::on_action(Widget *w) { + if (w == but_main) { + Menu::quit(); + } else if (w == but_new) { + LPGroupConfig m(""); + m.manage(); + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == but_level) { + LevelMenu m; + if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) { + // ESC in LevelMenu in case we are a submenu of LevelMenu or + // Main button has been pressed in LevelMenu + Menu::quit(); + } + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == but_search) { + SearchMenu ml; + ml.manage(); + if (ml.isSearchQuit()) { + // show search result levelpack + LevelMenu ml; + if (!ml.manage() && isLevelMenuSubmenu || ml.isMainQuit()) { + // ESC in LevelMenu in cade we are a submenu of LevelMenu or + // Main button has been pressed in LevelMenu + Menu::quit(); + } + } + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == scrollUp) { + firstDisplayedGroup--; + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == scrollDown) { + firstDisplayedGroup++; + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w->get_parent() == groupsVList){ + lev::Index::setCurrentGroup(dynamic_cast(w)->get_text()); + if ((w->lastMouseButton() == SDL_BUTTON_RIGHT || + w->lastModifierKeys() & KMOD_CTRL) && + dynamic_cast(w)->get_text() != INDEX_ALL_PACKS) { + // configure group + // INDEX_ALL_PACKS cannot be renamed, deleted, no packs can be created + LPGroupConfig m(dynamic_cast(w)->get_text()); + m.manage(); + lastGroupName = ""; // the group may have moved, force a recalc + } + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w->get_parent()->get_parent() == packsHList){ + lev::Index::setCurrentIndex(dynamic_cast(w)->get_text()); + if (w->lastMouseButton() == SDL_BUTTON_RIGHT || + w->lastModifierKeys() & KMOD_CTRL) { + // configure levelpack index + LevelPackConfig m(dynamic_cast(w)->get_text()); + m.manage(); + } else { + LevelMenu m; + if (!m.manage() && isLevelMenuSubmenu || m.isMainQuit()) { + // ESC in LevelMenu in case we are a submenu of LevelMenu or + // Main button has been pressed in LevelMenu + Menu::quit(); + return; + } + } + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == scrollLeft) { + std::string curGroupName = lev::Index::getCurrentGroup(); + lev::Index::setGroupSelectedColumn(curGroupName, + lev::Index::getGroupSelectedColumn(curGroupName) + 1); + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } else if (w == scrollRight) { + std::string curGroupName = lev::Index::getCurrentGroup(); + lev::Index::setGroupSelectedColumn(curGroupName, + lev::Index::getGroupSelectedColumn(curGroupName) - 1); + reset_active_widget(); // we will delete it with setup + setupMenu(); + updateHighlight(); + invalidate_all(); + } + } + + void LevelPackMenu::updateHighlight() { + for (int i = 0; i < packButtons.size(); i++) { + TextButton * button = packButtons[i]; + if (button->get_text() == lev::Index::getCurrentIndex()->getName()) + button->setHighlight(true); + else + button->setHighlight(false); + } + for (int i = 0; i < groupButtons.size(); i++) { + TextButton * button = groupButtons[i]; + if (button->get_text() == lev::Index::getCurrentGroup()) + button->setHighlight(true); + else + button->setHighlight(false); + } + } + + void LevelPackMenu::draw_background(ecl::GC &gc) { + video::SetCaption(("Enigma - Level Pack Menu")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } + + int LevelPackMenu::getGroupPosition(std::vector * groups, std::string groupName) { + std::vector::iterator it; + int i = 0; + for (it = groups->begin(); it != groups->end(); it++, i++) { + if ((*it) == groupName) { + return i; + } + } + return -1; + } + + int LevelPackMenu::getIndexPosition(std::vector * group, std::string indexName) { + std::vector::iterator it; + int i = 0; + for (it = group->begin(); it != group->end(); it++, i++) { + if ((*it)->getName() == indexName) { + return i; + } + } + return -1; + } + + int LevelPackMenu::checkColumn(int rows, int columns, int size, + int position, int oldColumn) { + int naturalColumn = position / rows; + int numColumns = (size - 1) / rows + 1; + if (oldColumn == INDEX_GROUP_COLUMN_UNKNOWN) + return (naturalColumn > columns) ? columns - 1 : naturalColumn; + else + return ecl::Clamp(oldColumn, naturalColumn - + ((numColumns > columns) ? (numColumns - columns) : 0), + naturalColumn); + } + + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelPackMenu.hh b/project/jni/application/enigma/src/gui/LevelPackMenu.hh new file mode 100644 index 000000000..72622c12b --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPackMenu.hh @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_LEVELPACKMENU_HH_INCLUDED +#define GUI_LEVELPACKMENU_HH_INCLUDED + +#include "gui/Menu.hh" +#include "video.hh" +#include "lev/Index.hh" + +#include +#include + +namespace enigma { namespace gui { +/* -------------------- LevelPackMenu -------------------- */ + + /** + * The levelpack selection menu. + * + * The used strategy for horizontal scrolling of the levelpack columns + * is to avoid scrolling as long as the target levelpack is still visible. + * The current levelpack may be scrolled out of visibility by using the + * scoll buttons within this menu. But if the user changes the current + * levelpack via indirect methods besides + * using the levelpack menu, f.e. the jumpto command, we ensure that + * the new current levelpack is visible when the menu is redisplayed. + * If it is necessary to scroll we scroll only the minimum amount of columns + * needed to display the current levelpack. Even on screen resolution changes + * we try to keep the column of the current levelpack unchanged. + */ + class LevelPackMenu : public Menu { + public: + LevelPackMenu(); + void manageLevelMenu(); + virtual bool manage(); + + bool on_event (const SDL_Event &e); + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + + private: + static std::map groupLastSelectedIndex; + static std::map groupLastSelectedColumn; + static std::string lastGroupName; + static int firstDisplayedGroup; + + video::VideoModes vm; + std::vector packButtons; + std::vector groupButtons; + + HList *packsHList; + ImageButton *scrollLeft; + ImageButton *scrollRight; + ImageButton *scrollUp; + ImageButton *scrollDown; + VList *groupsVList; + HList *commandHList; + Widget *but_search; + Widget *but_new; + Widget *but_level; + Widget *but_main; + + bool isLevelMenuSubmenu; + + void setupMenu(); + void updateHighlight(); + int getGroupPosition(std::vector * groups, std::string groupName); + int getIndexPosition(std::vector * group, std::string indexName); + int checkColumn(int rows, int columns, int size, int position, int oldColumn); + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LevelPreviewCache.cpp b/project/jni/application/enigma/src/gui/LevelPreviewCache.cpp new file mode 100644 index 000000000..d5fc67ac9 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPreviewCache.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck, Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LevelPreviewCache.hh" +#include "ecl.hh" +#include "file.hh" +#include "game.hh" +#include "main.hh" +#include "video.hh" +#include "lev/Proxy.hh" + +#include "SDL.h" +#include + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + /* -------------------- LevelPreviewCache -------------------- */ + + LevelPreviewCache *LevelPreviewCache::theSingleton = 0; + + LevelPreviewCache* LevelPreviewCache::instance() { + if (theSingleton == 0) { + theSingleton = new LevelPreviewCache(); + } + return theSingleton; + } + + LevelPreviewCache::LevelPreviewCache() : cachedIndex (NULL) { + } + + void LevelPreviewCache::clear() { + Log << "LevelPreviewCache clear\n"; + cache.clear(); + imgCache.clear(); + } + + Surface *LevelPreviewCache::getPreview(lev::Proxy *levelProxy, + bool allowGeneration, bool &didGenerate) { + didGenerate = false; + std::string mapIndex = levelProxy->getId() + + ecl::strf("#%d", levelProxy->getReleaseVersion()); + // first look in cache + PreviewMap::iterator i = cache.find(mapIndex); + if (i != cache.end()) + return i->second; + + // we have to cache a new preview - check if we should clear cache first + if (cachedIndex == NULL) { + // remember the index we are caching + cachedIndex = lev::Index::getCurrentIndex(); + } else if (cachedIndex != lev::Index::getCurrentIndex()) { + //the index changed - cache only previews from new index + clear(); + cachedIndex = lev::Index::getCurrentIndex(); + } + + std::string previewSubPath = makePreviewPath(levelProxy); + Surface *surface = 0; + + // load preview from file bundled with the level itself + std::string absLevelPath ; + std::auto_ptr isptr; + ByteVec imageData; + if(levelProxy->getNormPathType() == lev::Proxy::pt_resource && + app.resourceFS->findFile ("levels/" + levelProxy->getNormLevelPath() + ".png", + absLevelPath, isptr)) { + // load plain image file or zipped image + if (isptr.get() != NULL) { + // zipped file + Readfile (*isptr, imageData); + } else { + // plain file + basic_ifstream ifs(absLevelPath.c_str(), ios::binary | ios::in); + Readfile (ifs, imageData); + } + SDL_RWops *imageHandle = SDL_RWFromMem(&(imageData[0]), imageData.size()); + surface = ecl::LoadImage(imageHandle, 1); + imgCache.store(previewSubPath, surface); // insert in imgCache + } + + // load preview from stored file if possible + string previewFullPath; + if (!surface && app.resourceFS->findFile(previewSubPath, previewFullPath)) + surface = imgCache.get(previewFullPath); + + // generate new preview otherwise + if (!surface && allowGeneration) { + surface = newPreview(levelProxy); + if (surface) { + imgCache.store(previewSubPath, surface); // insert in imgCache + savePreview(levelProxy, surface); // save on filesystem + } else { + surface = enigma::GetImage("error"); + } + didGenerate = true; + } + + // update index + if (surface) + cache[mapIndex] = surface; + return surface; + } + + ecl::Surface *LevelPreviewCache::newPreview (lev::Proxy *levelProxy) { + const video::VMInfo &vminfo = *video::GetInfo(); + Surface *surface = 0; + ecl::GC gc(video::BackBuffer()); + if (game::DrawLevelPreview (gc, levelProxy)) { + surface = Resample (video::BackBuffer(), + vminfo.gamearea, vminfo.thumbw, vminfo.thumbh); + } + return surface; + } + + Surface *LevelPreviewCache::updatePreview (lev::Proxy *levelProxy) { + if (Surface *surface = newPreview (levelProxy)) { + std::string idx = levelProxy->getId() + + ecl::strf("#%d", levelProxy->getReleaseVersion()); + cache[idx] = surface; + + string preview_name = makePreviewPath(levelProxy); + imgCache.store (preview_name, surface); // insert in imgCache + savePreview(levelProxy, surface); // save on filesystem + return surface; + } + return 0; + } + + std::string LevelPreviewCache::makePreviewPath(lev::Proxy *levelProxy) { + return "thumbs/" + + levelProxy->getLocalSubstitutionLevelPath() + + ecl::strf("#%d", levelProxy->getReleaseVersion()) + ".png"; + } + + void LevelPreviewCache::savePreview(lev::Proxy *levelProxy, ecl::Surface *s) { + std::string savePath = app.userImagePath + "/" + makePreviewPath(levelProxy); + Log << "savePreview to " << savePath << "\n"; + // auto-create the directory if necessary + string directory; + if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) { + ecl::FolderCreate (directory); + } + ecl::SavePNG(s, savePath); + } + + void LevelPreviewCache::makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath) { + std::string savePath = systemDataPath + "/levels/" + levelProxy->getNormLevelPath() + ".png"; + // auto-create the directory if necessary -- on an installed Enigma + // distribution this is of course unnecessary, but you start Enigma + // without prior installation. This is useful to get a directory with + // just the previews. + string directory; + if (ecl::split_path (savePath, &directory, 0) && !ecl::FolderExists(directory)) { + ecl::FolderCreate (directory); + } + ecl::Surface * s = newPreview(levelProxy); + if (s != NULL) + ecl::SavePNG(s, savePath); + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelPreviewCache.hh b/project/jni/application/enigma/src/gui/LevelPreviewCache.hh new file mode 100644 index 000000000..507166368 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelPreviewCache.hh @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_LEVELPREVIEWCACHE_HH_INCLUDED +#define GUI_LEVELPREVIEWCACHE_HH_INCLUDED + +#include "lev/Index.hh" +#include "lev/Proxy.hh" +#include + +namespace enigma { namespace gui { + /** + * A singleton manager for level previews with autocaching. + * Clients just need to know the level proxy to request a preview. + *

+ * Previews will be loaded from levelpack bundles if provided. These + * preview files have the same name as the levels themself but the + * suffix ".png". They just sit aside the levels in the same directory + * or in the same zip archive. Only local stored previews will be looked + * for.

+ * If no bundled preview exists the preview will be loaded from prior + * generated and saved instances. Previews will be looked up at the + * resourceFS with play time generated previews stored at the userImagePath + * and installation time generated previews at the system path. The previews + * will be stored at "data/thumbs" with a subpath that reflectes the level + * subpath with critical characters replaced for url and other special levels. + * The level release number is attached to the previewname as "#n" to allow + * different previews for different releases to exist in parallel.

+ * + * If no stored preview exists a new one will be generated by loading the + * level. The preview will be stored to the userImagePath for future use.

+ * + * All loaded previews will be autocached. Futher requests will be served + * by the cache. The cache will be autocleared when a change in the + * current Index is detected.

+ * + * TODO remove unused preview on filesystem + * TODO autogenerate previews on install + */ + class LevelPreviewCache { + public: + static LevelPreviewCache *instance(); + static void makeSystemPreview(lev::Proxy *levelProxy, std::string systemDataPath); + ~LevelPreviewCache(); + + ecl::Surface *getPreview (lev::Proxy *levelProxy, + bool allowGeneration, bool &didGenerate); + ecl::Surface *updatePreview (lev::Proxy *levelProxy); + void clear(); + protected: + LevelPreviewCache(); + private: + static LevelPreviewCache *theSingleton; + + /** + * A mapping of "levelId+levelRelease" to preview surfaces + */ + typedef std::map PreviewMap; + + // ---------- Internal methods ---------- + + static ecl::Surface *newPreview (lev::Proxy *levelProxy); + std::string makePreviewPath(lev::Proxy *levelProxy); + void savePreview(lev::Proxy *levelProxy, ecl::Surface *s); + + // ---------- Variables ---------- + + PreviewMap cache; // a second mapping to avoid searched on the filesystem + enigma::ImageCache imgCache; // the owner of the preview surfaces - + // cannot be used as mapping as this cache + // uses the filepath as index and autoloads + // files on "get"-access + lev::Index *cachedIndex; // the index that is currently cached + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/LevelWidget.cpp b/project/jni/application/enigma/src/gui/LevelWidget.cpp new file mode 100644 index 000000000..eed3a08e4 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelWidget.cpp @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * Copyright (C) 2006,2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/LevelWidget.hh" +#include "gui/LevelMenu.hh" +#include "gui/LevelInspector.hh" + +#include "ecl.hh" +#include "main.hh" +#include "nls.hh" +#include "options.hh" +#include "sound.hh" +#include "StateManager.hh" +#include "video.hh" +#include "file.hh" +#include "lev/Proxy.hh" +#include + +using namespace ecl; +using namespace std; + + +namespace enigma { namespace gui { + /* -------------------- LevelWidget -------------------- */ + + LevelWidget::LevelWidget(bool withScoreIcons, bool withEditBorder) : + displayScoreIcons (withScoreIcons), displayEditBorder (withEditBorder), + width (0), height (0), m_areas(), + listener(0), isInvalidateUptodate (true), lastUpdate (0) + { + const video::VMInfo &vminfo = *video::GetInfo(); + + buttonw = vminfo.thumbw + 20; + buttonh = vminfo.thumbh + 28; + curIndex = lev::Index::getCurrentIndex(); + iselected = curIndex->getCurrentPosition(); + ifirst = curIndex->getScreenFirstPosition(); + preview_cache = LevelPreviewCache::instance(); + scoreMgr = lev::ScoreManager::instance(); + img_link = enigma::GetImage("ic-link"); + img_copy = enigma::GetImage("ic-copy"); + img_feather = enigma::GetImage("ic-feather"); + img_easy = enigma::GetImage("completed-easy"); + img_hard = enigma::GetImage("completed"); + img_changed = enigma::GetImage("changed"); + img_unavailable = enigma::GetImage("unavailable"); +// img_unknown = enigma::GetImage("unknown"); + img_par = enigma::GetImage("par"); + img_wrEasy = enigma::GetImage("ic-wr-easy"); + img_wrDifficult = enigma::GetImage("ic-wr-difficult"); + img_border = enigma::GetImage("thumbborder"); + img_editborder = enigma::GetImage("editborder"); + } + + void LevelWidget::syncFromIndexMgr() { + if (curIndex != lev::Index::getCurrentIndex()) { + curIndex = lev::Index::getCurrentIndex(); + iselected = curIndex->getCurrentPosition(); + ifirst = curIndex->getScreenFirstPosition(); + invalidate(); + sound::EmitSoundEvent ("menumove"); + } else if (iselected != curIndex->getCurrentPosition()) { + sound::EmitSoundEvent ("menumove"); + } + // repair ifirst on boot and screen resolution changes + set_selected(curIndex->getScreenFirstPosition(), + curIndex->getCurrentPosition()); + } + + void LevelWidget::syncToIndexMgr() { + curIndex->setCurrentPosition(iselected); + curIndex->setScreenFirstPosition(ifirst); + } + + void LevelWidget::realize (const ecl::Rect &area_) + { + Widget::realize (area_); + width = area_.w / buttonw; + height = area_.h / buttonh; + } + + void LevelWidget::trigger_action() { + if (listener) { + listener->on_action(this); + } + } + + void LevelWidget::scroll_up (int nlines) + { + int newFirst = ifirst; + int newSelected = iselected; + for (; nlines; --nlines) { + if (newFirst+width*height >= curIndex->size()) + break; + newFirst += width; + if (newSelected < newFirst) + newSelected += width; + } + set_selected (newFirst, newSelected); + } + + void LevelWidget::scroll_down(int nlines) + { + int newFirst = ifirst; + int newSelected = iselected; + for (; nlines; --nlines) { + if (newFirst == 0) { + break; + } else if (newFirst < width) { + newFirst = 0; + if (newSelected >= width*height) + newSelected = width*height - 1; + } else { + newFirst -= width; + if (newSelected >= newFirst+width*height) + newSelected -= width; + } + } + set_selected (newFirst, newSelected); + } + + void LevelWidget::page_up() { + set_selected ((ifirst >= width*height ? ifirst - width*height : 0), + (iselected >= width*height ? iselected - width*height : 0)); + syncToIndexMgr(); + } + + void LevelWidget::page_down() + { + int s = lev::Index::getCurrentIndex()->size() - 1; // last position + int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0); + + // make sure last page is shown as a whole + int first = std::min (lastPageFirst, ifirst + width*height); + // set_selected (first, s-1); + set_selected (first, iselected + width*height); + syncToIndexMgr(); + } + + void LevelWidget::start() { + set_selected(0,0); + syncToIndexMgr(); + } + + void LevelWidget::end() { + int s = lev::Index::getCurrentIndex()->size() - 1; // last position + int lastPageFirst = (s >= width*height ? (s / width - height + 1) * width : 0); + set_selected(lastPageFirst, s); + syncToIndexMgr(); + } + + void LevelWidget::set_current (int newsel) + { + set_selected (ifirst, newsel); + } + + void LevelWidget::set_selected (int newfirst, int newsel) + { + int numlevels = curIndex->size(); + newsel = Clamp (newsel, 0, numlevels-1); + if (newsel < 0) newsel = 0; + + if (newsel < newfirst) + newfirst = (newsel/width)*width; + if (newsel >= newfirst+width*height) + newfirst = (newsel/width-height+1)*width; + + newfirst = Clamp (newfirst, 0, numlevels-1); + if (newfirst < 0) newfirst = 0; + + size_t oldsel = iselected; + if (newfirst != ifirst) { + ifirst = newfirst; + iselected = newsel; + + if (!m_areas.empty()) { + sound::EmitSoundEvent ("menumove"); + if (oldsel != newsel) + sound::EmitSoundEvent ("menuswitch"); + invalidate(); + } + } + else if (newsel != iselected) { + iselected = newsel; + + if (!m_areas.empty()) { + sound::EmitSoundEvent ("menuswitch"); + invalidate_area(m_areas[oldsel-ifirst]); // old selection + invalidate_area(m_areas[iselected-ifirst]); // new selection + } + } + } + + bool LevelWidget::draw_level_preview (ecl::GC &gc, int x, int y, + lev::Proxy *proxy, bool selected, bool isCross, bool locked, + bool allowGeneration, bool &didGenerate) { + // Draw button with level preview + + Surface *img = preview_cache->getPreview(proxy, allowGeneration, didGenerate); + if (img == NULL) + return false; + + if (selected) { + blit (gc, x-4, y-4, displayEditBorder ? img_editborder : img_border); + blit (gc, x, y, img); + } + else { + img->set_alpha (127); + blit (gc, x, y, img); + img->set_alpha(255); + } + + // Shade unavailable levels + if (locked) + blit (gc, x, y, img_unavailable); + + if (displayScoreIcons) { + // Draw solved/changed icons on top of level preview + // Easy/difficult mode and solved status: + // None: Level not beaten - no easy mode available + // Feather: Level not beaten - easy mode available + // Feather + Gold: Level beaten in normal mode - easy available + // Silver: Level beaten in easy mode (normal mode available) + // Gold: Level beaten in normal mode - easy not availabe + // Silver + Gold: Level beaten in all modes - easy available + Surface *useAsEasy = NULL; + Surface *useAsDifficult = NULL; + if (proxy->hasEasymode()) { + useAsEasy = img_feather; + if (scoreMgr->isSolved(proxy, DIFFICULTY_EASY)) + useAsEasy = img_easy; + } + if (scoreMgr->isSolved(proxy, DIFFICULTY_HARD)) + useAsDifficult = img_hard; + + if (app.state->getInt("Difficulty") == DIFFICULTY_HARD) { + // draw golden medal over silber medal + if (useAsEasy != NULL) + blit (gc, x, y, useAsEasy); + if (useAsDifficult != NULL) + blit (gc, x+5, y, useAsDifficult); + } + else { + // draw silver medal over golden medal + if (useAsDifficult != NULL) + blit (gc, x+5, y, useAsDifficult); + if (useAsEasy != NULL) + blit (gc, x, y, useAsEasy); + } + + // Add warning sign if level has been changed since player solved it + if (scoreMgr->isOutdated(proxy, app.state->getInt("Difficulty"))) + blit (gc, x-3, y-3, img_changed); + + // Add icon if worldrecord or par + if (scoreMgr->bestScoreReached(proxy, app.state->getInt("Difficulty"))) { + blit(gc, x+35, y+5, + (app.state->getInt("Difficulty") != DIFFICULTY_HARD && + proxy->hasEasymode()) ? img_wrEasy : img_wrDifficult); + } else if (scoreMgr->parScoreReached(proxy, app.state->getInt("Difficulty"))){ + blit(gc, x+30, y+12, img_par); + } + } else { + // Draw solved/changed icons on top of level preview + if (isCross) + blit (gc, x+4, y+4, img_link); + else + blit (gc, x+4, y+4, img_copy); + } + return true; + } + + void LevelWidget::draw (ecl::GC &gc, const ecl::Rect &r) + { + const video::VMInfo &vminfo = *video::GetInfo(); + const int imgw = vminfo.thumbw; // Size of the preview images + const int imgh = vminfo.thumbh; + + const int hgap = Max(0, (get_w() - width*buttonw) / (width)); + const int vgap = Max(0, (get_h() - height*buttonh)/ (height-1)); + + unsigned i=ifirst; // level index + bool allowGeneration = true; + + for (int y=0; y= curIndex->size()) + goto done_painting; + + int xpos = get_x() + hgap/2 + x*(buttonw + hgap); + int ypos = get_y() + y*(buttonh + vgap); + + Rect buttonarea(xpos, ypos, buttonw, buttonh); + if (!(r.overlaps(buttonarea) || r.w == 0)) + continue; // r.w==0 if repainting whole screen + + if( (i-ifirst) >= m_areas.size()) { + m_areas.push_back(buttonarea); + pending_redraws.push_back(false); + } else { + m_areas[(i-ifirst)] = buttonarea; + } + // Draw level preview + lev::Proxy *levelProxy = curIndex->getProxy(i); + int imgx = xpos+(buttonw-imgw)/2; + int imgy = ypos + 4; + if (levelProxy != NULL) { + bool didGenerate; + bool didDraw = draw_level_preview (gc, imgx, imgy, levelProxy, + i == iselected, !curIndex->isSource(levelProxy), + !curIndex->mayPlayLevel(i+1), + allowGeneration, didGenerate); + if (didGenerate) { + // do not generate more than 1 preview from level source + // per draw call + allowGeneration = false; + } + if (didDraw) { + pending_redraws[(i-ifirst)] = false; + } else { + // the button is not drawn - mark it to be drawn on + // a future tick + pending_redraws[(i-ifirst)] = true; + isInvalidateUptodate = false; + } + } + // Draw level name + Font *smallfnt = enigma::GetFont("levelmenu"); + Font *altsmallfnt = enigma::GetFont("smallalternative");; + std::string caption = levelProxy->getTitle(); + smallfnt->render (gc, + xpos + buttonw/2 - ecl::Min(smallfnt->get_width(caption.c_str(), altsmallfnt)/2, (buttonw+hgap)/2), + imgy + imgh + 2, + caption, altsmallfnt, buttonw + hgap); + } + } + done_painting: + m_areas.resize (i-ifirst); // Remove unused areas (if any) from the list + return; + } + + void LevelWidget::tick (double time) { + if (!isInvalidateUptodate) { + // invalidate just 1 button for redraw + bool isFirst = true; + for (int i = 0; i < pending_redraws.size(); i++) { + if (pending_redraws[i] == true) { + if (isFirst) { + invalidate_area(m_areas[i]); + isInvalidateUptodate = true; + isFirst = false; + } else { + isInvalidateUptodate = false; + return; + } + } + } + } + } + + bool LevelWidget::on_event(const SDL_Event &e) + { + bool handled = Widget::on_event(e); + + switch (e.type) { + case SDL_MOUSEMOTION: + if (get_area().contains(e.motion.x, e.motion.y)) { + int newsel=iselected; + for (unsigned i=0; ibutton.button) { + case SDL_BUTTON_LEFT: + for (unsigned i=0; ibutton.x, e->button.y)) + { + sound::EmitSoundEvent ("menuok"); + iselected = ifirst+i; + syncToIndexMgr(); + if (SDL_GetModState() & KMOD_CTRL && !(SDL_GetModState() & KMOD_SHIFT)) { + // control key pressed - level inspector + LevelInspector m(curIndex->getProxy(iselected)); + m.manage(); + get_parent()->draw_all(); + } else { + // no control key - start level + trigger_action(); + } + return true; + } + break; + case SDL_BUTTON_RIGHT: + for (unsigned i=0; ibutton.x, e->button.y)) + { + sound::EmitSoundEvent ("menuok"); + iselected = ifirst+i; + syncToIndexMgr(); + LevelInspector m(curIndex->getProxy(iselected)); + m.manage(); + get_parent()->draw_all(); + return true; + } + break; + case 4: scroll_down(1); return true; + case 5: scroll_up(1); return true; + } + return false; + } + + bool LevelWidget::handle_keydown (const SDL_Event *e) + { + switch (e->key.keysym.sym) { + + case SDLK_t: + // Generate new level preview for current level + preview_cache->updatePreview(curIndex->getProxy(iselected)); + invalidate(); + break; + + case SDLK_LEFT: + if (!(SDL_GetModState() & KMOD_ALT)) { + set_current (iselected>1 ? iselected-1 : 0); + break; + } else + return false; + case SDLK_RIGHT: + if (!(SDL_GetModState() & KMOD_ALT)) { + set_current (iselected+1); + break; + } else + return false; + case SDLK_DOWN: set_current (iselected+width); break; + case SDLK_UP: set_current (iselected>width ? iselected-width : 0); break; + case SDLK_PAGEDOWN: page_down(); break; + case SDLK_PAGEUP: page_up(); break; + case SDLK_HOME: start(); break; + case SDLK_END: end(); break; + + case SDLK_RETURN: + trigger_action(); + break; + + default: + return false; // key not handled + } + return true; + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/LevelWidget.hh b/project/jni/application/enigma/src/gui/LevelWidget.hh new file mode 100644 index 000000000..8b6535505 --- /dev/null +++ b/project/jni/application/enigma/src/gui/LevelWidget.hh @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_LEVELWIDGET_HH_INCLUDED +#define GUI_LEVELWIDGET_HH_INCLUDED + +#include "gui/widgets.hh" +#include "gui/LevelPreviewCache.hh" +#include "lev/Index.hh" +#include "lev/ScoreManager.hh" + + +namespace enigma { namespace gui { +/* -------------------- LevelWidget -------------------- */ + + class LevelMenu; + + /** + * + */ + class LevelWidget : public Widget { + public: + LevelWidget(bool withScoreIcons = true, bool withEditBorder = false); + + //---------- Widget interface ----------// + void draw(ecl::GC &gc, const ecl::Rect &r); + void tick (double time); + + void set_listener(ActionListener *al) { + listener = al; + } + void trigger_action(); + + virtual void realize (const ecl::Rect &r); + + //---------- Cursor motion ----------// + + void page_up(); + void page_down(); + void start(); + void end(); + + bool on_event(const SDL_Event &e); + void syncFromIndexMgr(); // external change of currentIndex, currentLevel + + private: + //---------- Private functions ----------// + void syncToIndexMgr(); + void set_current (int newsel); + void scroll_up(int lines); + void scroll_down(int lines); + void set_selected (int newfirst, int newsel); + bool draw_level_preview (ecl::GC &gc, int x, int y, + lev::Proxy *proxy, bool selected, bool isCross, bool locked, + bool allowGeneration, bool &didGenerate); + + bool handle_keydown (const SDL_Event *e); + bool handle_mousedown (const SDL_Event *e); + + //---------- Variables ----------// + bool displayScoreIcons; + bool displayEditBorder; + LevelPreviewCache *preview_cache; + lev::ScoreManager *scoreMgr; + lev::Index *curIndex; + ActionListener *listener; + + int ifirst; // Index of "upper left" level + int iselected; // Index of selected level + int width; // number of buttons in a row + int height; // number of buttons in a column + int buttonw; // pixelwidth of a button + int buttonh; // pixelheight of a button + std::vector m_areas; // Screen areas occupied by level previews + std::vector pending_redraws; + bool isInvalidateUptodate; + double lastUpdate; + + // some image pointers for efficiency + ecl::Surface *img_link; + ecl::Surface *img_copy; + ecl::Surface *img_feather; + ecl::Surface *img_easy; + ecl::Surface *img_hard; + ecl::Surface *img_changed; + ecl::Surface *img_unavailable; + // Surface *img_unknown; + ecl::Surface *img_par; + ecl::Surface *img_wrEasy; + ecl::Surface *img_wrDifficult; + ecl::Surface *img_border; + ecl::Surface *img_editborder; + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/MainMenu.cpp b/project/jni/application/enigma/src/gui/MainMenu.cpp new file mode 100644 index 000000000..fdfb574ac --- /dev/null +++ b/project/jni/application/enigma/src/gui/MainMenu.cpp @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/MainMenu.hh" +#include "gui/LevelMenu.hh" +#include "gui/OptionsMenu.hh" +#include "gui/HelpMenu.hh" +#include "gui/InfoMenu.hh" +#include "gui/LevelPackMenu.hh" +#include "gui/LevelPreviewCache.hh" +#include "display.hh" +#include "ecl.hh" +#include "main.hh" +#include "nls.hh" +#include "options.hh" +#include "sound.hh" +#include "video.hh" +#include "world.hh" + +#include "netgame.hh" +#include "editor.hh" + + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { +/* -------------------- Helper routines -------------------- */ + +// namespace +// { + /*! Change the video mode. Because this opens a new screen with a + new resolution, the display engine must be re-initialized to + load the appropriate models. */ + void ChangeVideoMode() + { + world::PrepareLevel(); // make sure no references to models remain + video::ChangeVideoMode(); + LevelPreviewCache::instance()->clear(); + enigma::ClearImageCache(); + display::Shutdown(); + display::Init(); + } +// } + + + /* -------------------- NetworkMenu -------------------- */ + + NetworkMenu::NetworkMenu () + { + const video::VMInfo *vminfo = video::GetInfo(); + + BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5); + m_startgame = b.add(new StaticTextButton(N_("Start Game"), this)); + m_joingame = b.add(new StaticTextButton(N_("Join Game"), this)); + m_back = b.add(new StaticTextButton(N_("Back"), this)); + } + + NetworkMenu::~NetworkMenu () + { + } + + bool NetworkMenu::on_event (const SDL_Event &e) + { + return false; + } + + void NetworkMenu::on_action(gui::Widget *w) + { + if (w == m_startgame) { + netgame::Start(); + } + else if (w == m_joingame) { + netgame::Join("localhost", 12345); + } + if (w == m_back) + Menu::quit(); + } + + void NetworkMenu::draw_background(ecl::GC &gc) + { + video::SetCaption (("Enigma - Network Menu")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } + + void NetworkMenu::tick(double dtime) + { + } + + + + /* -------------------- Main menu -------------------- */ + static const char *credit_text[] = { + N_("Main developers of the 1.0 release:"), + N_(" RONALD LAMPRECHT (lead)"), + " RAOUL BOURQUIN", + " ANDREAS LOCHMANN", + " ", + N_("Special Thanks To:"), + N_(" DANIEL HECK (project founder)"), + N_(" MEINOLF SCHNEIDER (game idea, level design)"), + " ", + N_("Please refer to the manual or the next pages for full credits."), + " ", + N_("Home Page: http://www.nongnu.org/enigma"), + N_("Contact: enigma-devel@nongnu.org"), + " ", + N_("Enigma is free software and may be distributed under the"), + N_("terms of the GNU General Public License, version 2."), + " ", + N_("Copyright (C) 2002-2007 Daniel Heck and contributors."), + 0, + N_("Main developer of all releases:"), + " ", + " Raoul Bourquin (Level admin. & design, all over invaluable additions)", + " Siegfried Fennig (Level design, graphics)", + " Martin Hawlisch (Level design, graphics, programming)", + " Daniel Heck (Main developer, graphics, documentation)", + " Ronald Lamprecht (XML, Gui, portability, core prog., documentation)", + " Andreas Lochmann (Programming, level admin. & design, documentation)", + " Petr Machata (Level design, programming)", + " Nat Pryce (Level design)", + " Jacob Scott (Level design)", + " Sven Siggelkow (Level design and special Oxyd expertise)", + " Ralf Westram (Programming, level design)", + 0, + N_("Special Thanks:"), + " Johannes Fortmann (Mac OS X port, some programming, graphics)", + " illmind (Forum mag-heut.net administration, Level design)", +// waiting for licence to add the sounds +// " \'Cellar of Rats\' (Sound effects)", + " Jennifer Robertson (Graphics second generation)", + " Jeremy Sawicki (Oxydlib)", + " Erich Schubert (Debian/Ubuntu packages, level design)", + " Lukas Schüller (Level design)", + " Andrew \'Necros\' Sega (Menu music \'Pentagonal Dreams\')", + " David W. Skinner (Many Sokoban Levels)", + " Clifford J. Tasner (Music second generation, Proof reading)", + 0, + N_("Contributors:"), + " Roberto Bardin (Level design)", + " Alain Busser (Level design, French translation, manual)", + " Guy Busser (Level design)", + " Richi Bützer (Level design)", + " Capkoh (Level design)", + " Christoph & Anita (Level design)", + " Дремук Сергей - Serge Dremuk (Russian translation)", + " Joseph Dunne (Level design)", + " Xerxes M. Dynatos (Level design)", + " Edward (Level design)", + " Stephanie Fabian (Invaluable bug reports)", + " Roberto García (Spanish translation)", + " Andy Geldmacher (Level design)", + " Edwin Groothuis (FreeBSD port)", + " Immanuel Herrmann (Level design)", + " Máté Lehel Juhász (Hungarian translation)", + 0, + " Samuele Kaplun (Italian translation)", + " Jens-Christian Korth (Level design)", + " Manuel König (Level design, bug reports)", + " Johannes Laire (Level design)", + " Joona Laire (Level design)", + " Markus Laire (Level design)", + " Dominik Lehmann (Level design)", + " Edward Leuf (Feedback, bug reports)", + " Ingo van Lil (Feedback, bug reports)", + " Frank van der Loo (Dutch translation)", + " Sidney Markowitz (Mac OS X port)", + " Barry & Lori Mead (Level design)", + " Linda Mihalic (English proof reading)", + " moonpearl (Level design)", + " Krishnamurti L.L.V. Nunes (Portuguese translation)", + " Daniel Nylander (Swedish translation)", + 0, + " Andreas Persenius (Level design)", + " Peter Santo (Level design)", + " Tobias Schmidbauer (Windows installer and icon)", + " Achim Settelmeier (RPM specfile)", + " Jon Sneyers (Level design)", + " Spaceman (Level design)", + " Ulf Stegemann (Level design)", + " Jürgen Sticht (Level design)", + " Mikke Surakka (Finnish translation)", + " Andrzej Szombierski (Level design)", + " Tacvek (Lua 5.1 upgrade)", + " Michael Terry (.desktop file)", + " Ray Wick (Level design)", + " Joe Wreschnig (Manual page)", + " Юрий Жиромский - Yuriy Zhyromskiy (Russian Manual)", + 0, + }; + + MainMenu::MainMenu() + { + build_menu(); + } + + void MainMenu::build_menu() + { + const video::VMInfo *vminfo = video::GetInfo(); + + BuildVList b(this, Rect((vminfo->width - 150)/2,150,150,40), 5); + m_startgame = b.add(new StaticTextButton(N_("Start Game"), this)); + m_levelpack = b.add(new StaticTextButton(N_("Level Pack"), this)); + #ifdef ENABLE_EXPERIMENTAL + m_netgame = b.add (new StaticTextButton (N_("Network Game"), this)); + leveled = b.add(new StaticTextButton(N_("Editor"), this)); + #endif + options = b.add(new StaticTextButton(N_("Options"), this)); + credits = b.add(new StaticTextButton(N_("Credits"), this)); + quit = b.add(new StaticTextButton(N_("Quit"), this)); + } + + void MainMenu::draw_background(ecl::GC &gc) + { + const video::VMInfo *vminfo = video::GetInfo(); + + video::SetCaption (("Enigma - Main Menu")); + sound::PlayMusic (options::GetString("MenuMusicFile")); + + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + + Font *f = enigma::GetFont("levelmenu"); + Surface * logo(enigma::GetImage("enigma_logo3")); + int x0=(vminfo->width - logo->width())/2; + int y0=30; + blit(gc, x0, y0, logo); + f->render (gc, 5, vminfo->height - 20, app.getVersionInfo().c_str()); + } + + bool MainMenu::on_event (const SDL_Event &e) { + switch (e.type) { + case SDL_KEYDOWN: + SDLKey keysym = e.key.keysym.sym; + switch (keysym) { + case SDLK_F2: + show_paths(); + invalidate_all(); + return true; + default: + break; + } + break; + } + return false; + } + + void MainMenu::on_action(Widget *w) + { + if (w == m_startgame) { + LevelPackMenu m; + m.manageLevelMenu(); + invalidate_all(); + } else if (w == m_levelpack) { + LevelPackMenu m; + m.manage(); + invalidate_all(); + } else if (w == credits) { + displayInfo(credit_text, 6); + invalidate_all(); + } else if (w == options) { + ShowOptionsMenu(0); + + #ifdef ENABLE_EXPERIMENTAL + } else if (w == m_netgame) { + ShowNetworkMenu(); + } else if (w == leveled) { + editor::Run(); + #endif + } else if (w == quit) { + Menu::quit(); + } else + return; + invalidate_all(); + } + + void MainMenu::tick(double /* dtime */) + { + bool option_fullscreen = options::GetInt ("FullScreen") != 0; + if (options::GetInt ("VideoMode") != video::GetVideoMode() + || option_fullscreen != video::IsFullScreen()) + { + ChangeVideoMode(); + clear(); + reset_active_widget (); + build_menu(); + invalidate_all(); + } + } + + void MainMenu::show_paths() { + const char *pathtext[25]; + std::string pathstrings[25]; + std::string work; + Font *menufont = enigma::GetFont("menufont"); + const video::VMInfo *vminfo = video::GetInfo(); + int width = vminfo->width - 80; + int i = 0; + + pathtext[i++] = N_("Preferences Path:"); + work = app.prefPath; + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = " "; + pathtext[i++] = N_("User Path:"); + work = app.userPath; + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = " "; + pathtext[i++] = N_("User Image Path:"); + work = app.userImagePath; + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = " "; + pathtext[i++] = N_("System Path:"); + work = app.systemFS->getDataPath(); + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = " "; + pathtext[i++] = N_("Resource Paths:"); + work = app.resourceFS->getDataPath(); + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = " "; + pathtext[i++] = N_("L10n Path:"); + work = app.l10nPath; + do { + std::string::size_type breakPos = breakString(menufont, work,"/\\", width); + pathstrings[i] = " " + work.substr(0,breakPos); + pathtext[i] = pathstrings[i].c_str(); + work = work.substr(breakPos); + i++; + } while(!work.empty() ); + pathtext[i++] = 0; + displayInfo(pathtext, 1); + } + +/* -------------------- Functions -------------------- */ + + void ShowMainMenu() { + MainMenu m; + m.manage(); + } + + void ShowNetworkMenu() + { + NetworkMenu m; + m.manage(); + } + +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/MainMenu.hh b/project/jni/application/enigma/src/gui/MainMenu.hh new file mode 100644 index 000000000..d96def575 --- /dev/null +++ b/project/jni/application/enigma/src/gui/MainMenu.hh @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_MAINMENU_HH_INCLUDED +#define GUI_MAINMENU_HH_INCLUDED + +#include "gui/Menu.hh" + +namespace enigma { namespace gui { +/* -------------------- MainMenu -------------------- */ + + class MainMenu : public Menu { + public: + MainMenu(); + private: + // Menu interface + void draw_background(ecl::GC &gc); + void tick(double dtime); + + // Widget interface + virtual bool on_event(const SDL_Event &e); + + // ActionListener interface. + void on_action(Widget *w); + + // Private methods. + void show_paths(); + void build_menu(); + + // Variables. + Widget *m_startgame; + Widget *m_levelpack; + Widget *m_netgame; + Widget *leveled; + Widget *manual; + Widget *options; + Widget *credits; + Widget *quit; + Widget *lpack; + }; + +/* -------------------- NetworkMenu -------------------- */ + + class NetworkMenu : public gui::Menu { + public: + NetworkMenu (); + ~NetworkMenu (); + private: + // ActionListener interface. + bool on_event (const SDL_Event &e); + void on_action(gui::Widget *w); + + // Menu interface. + void draw_background(ecl::GC &gc); + void tick(double dtime); + + // Variables. + gui::Widget *m_startgame; + gui::Widget *m_joingame; + gui::Widget *m_back; + }; + + +/* -------------------- Functions -------------------- */ + void ShowMainMenu(); + void ShowNetworkMenu(); +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/Menu.cpp b/project/jni/application/enigma/src/gui/Menu.cpp new file mode 100644 index 000000000..73337343e --- /dev/null +++ b/project/jni/application/enigma/src/gui/Menu.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/Menu.hh" +#include "sound.hh" +#include "video.hh" +#include "options.hh" +#include "nls.hh" +#include "ecl.hh" +#include +#include +#include + + +using namespace ecl; +using namespace std; + +#define SCREEN ecl::Screen::get_instance() + +namespace enigma { namespace gui { + /* -------------------- Menu -------------------- */ + + Menu::Menu() + : active_widget(0), quitp(false), abortp(false) { + } + + void Menu::add(Widget *w) { + Container::add_child(w); + } + + void Menu::add(Widget *w, ecl::Rect r) { + w->move (r.x, r.y); + w->resize (r.w, r.h); + add(w); + } + + + void Menu::quit() { + quitp=true; + } + + void Menu::abort() { + abortp=true; + } + + bool Menu::manage() { + quitp=abortp=false; + SDL_Event e; + Uint32 enterTickTime = SDL_GetTicks(); // protection against ESC D.o.S. attacks + while (SDL_PollEvent(&e)) {} // clear event queue + draw_all(); + while (!(quitp || abortp)) { + SCREEN->flush_updates(); + while (SDL_PollEvent(&e)) { + handle_event(e); + } + SDL_Delay(10); + if(active_widget) active_widget->tick(0.01); + tick (0.01); + refresh(); + } + sound::EmitSoundEvent ("menuexit"); + // protection against ESC D.o.S. attacks + Uint32 menuTickDuration = SDL_GetTicks() - enterTickTime; + Uint32 minMenuTickDuration = 300; + if (menuTickDuration < minMenuTickDuration) + SDL_Delay(minMenuTickDuration - menuTickDuration); + while (SDL_PollEvent(&e)) {} // clear event queue + return !abortp; + } + + void Menu::goto_adjacent_widget(int xdir, int ydir) { + Widget *next_widget = 0; + if (active_widget) { + next_widget = find_adjacent_widget(active_widget, xdir, ydir); + } + else { // no active_widget yet + if ((xdir+ydir)>0) { // take first + next_widget = *begin(); + } + else { // take last + iterator e = end(); + for (iterator i = begin(); i != e; ++i) { + next_widget = *i; + } + } + } + + if (next_widget) { + switch_active_widget(next_widget); + } + else { // no more widgets into that direction found + sound::EmitSoundEvent ("menustop"); + } + } + + void Menu::handle_event(const SDL_Event &e) + { + if (e.type == SDL_KEYDOWN && + e.key.keysym.sym == SDLK_RETURN && + e.key.keysym.mod & KMOD_ALT) + { + video::ToggleFullscreen(); + return; + } + + if (on_event(e)) + return; + + switch (e.type) { + case SDL_QUIT: + abort(); + break; + case SDL_MOUSEMOTION: + track_active_widget( e.motion.x, e.motion.y ); + break; + case SDL_KEYDOWN: + if (!active_widget || !active_widget->on_event(e)) { + // if not handled by active_widget + switch (e.key.keysym.sym) { + case SDLK_ESCAPE: + abort(); + break; + case SDLK_DOWN: goto_adjacent_widget( 0, 1); break; + case SDLK_UP: goto_adjacent_widget( 0, -1); break; + case SDLK_RIGHT: goto_adjacent_widget( 1, 0); break; + case SDLK_LEFT: goto_adjacent_widget(-1, 0); break; + default: + break; + } + } + + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + track_active_widget( e.button.x, e.button.y ); + if (active_widget) active_widget->on_event(e); + break; + case SDL_VIDEOEXPOSE: + draw_all(); + break; + default: + if (active_widget) active_widget->on_event(e); + } + } + + void Menu::switch_active_widget(Widget *to_activate) { + if (to_activate != active_widget) { + if (active_widget) + active_widget->deactivate(); + if (to_activate) + to_activate->activate(); + active_widget = to_activate; + } + } + + void Menu::track_active_widget( int x, int y ) { + switch_active_widget(find_widget(x, y)); + } + + + void Menu::center() { + if (m_widgets.size() > 0) { + using std::min; + using std::max; + + Rect a = m_widgets[0]->get_area(); + for (unsigned i=1; iget_area(); + a.x = min(r.x, a.x); + a.y = min(r.y, a.y); + a.w += max(0, r.x+r.w-a.x-a.w); + a.h += max(0, r.y+r.h-a.y-a.h); + } + Rect c=ecl::center(SCREEN->size(), a); + int dx = c.x-a.x; + int dy = c.y-a.y; + + for (unsigned i=0; iget_area(); + r.x += dx; + r.y += dy; + + m_widgets[i]->move (r.x, r.y); + m_widgets[i]->resize (r.w, r.h); + } + } + } + + void Menu::draw (ecl::GC &gc, const ecl::Rect &r) + { + clip(gc, r); + draw_background(gc); + Container::draw(gc,r); + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/Menu.hh b/project/jni/application/enigma/src/gui/Menu.hh new file mode 100644 index 000000000..4d2a81898 --- /dev/null +++ b/project/jni/application/enigma/src/gui/Menu.hh @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_MENU_HH_INCLUDED +#define GUI_MENU_HH_INCLUDED + +#include "gui/widgets.hh" +#include "gui/Menu.hh" +#include "ecl_fwd.hh" +#include "ecl_geom.hh" +#include "SDL.h" +#include +#include + +namespace enigma { namespace gui { +/* -------------------- Menu -------------------- */ + + class Menu : public Container { + public: + Menu(); + + //! true: ok, false: menu aborted by user + virtual bool manage(); + + void add(Widget *w); + void add(Widget *w, ecl::Rect r); + void center(); + + void draw (ecl::GC &gc, const ecl::Rect &r); + + virtual void quit(); + void abort(); + + protected: + void reset_active_widget() + { active_widget = 0; } + + // Menu interface. + virtual void draw_background(ecl::GC &/*gc*/) {} + + private: + void handle_event(const SDL_Event &e); + + void switch_active_widget(Widget *to_activate); + void track_active_widget( int x, int y ); // used by mouse + void goto_adjacent_widget(int xdir, int ydir); // used by keyboard + + // Variables. + Widget *active_widget; + bool quitp, abortp; + }; + + class BuildVList { + ecl::Rect r; + Menu *container; + int skip; + public: + BuildVList(Menu *cc, const ecl::Rect &rr, int s) + : r(rr), container(cc), skip(s) + {} + + Widget *add(Widget *w) { + container->add(w, r); + r.y += r.h+skip; + return w; + } + + ecl::Rect pos() const { return r; } + }; + + class BuildHList { + ecl::Rect r; + Menu *container; + int skip; + public: + BuildHList(Menu *cc, const ecl::Rect &rr, int s) + : r(rr), container(cc), skip(s) + {} + + Widget * add(Widget *w) { + container->add(w, r); + r.x += r.w+skip; + return w; + } + Widget *add (Widget *w, int width) { + ecl::Rect rr(r.x, r.y, width, r.h); + container->add(w, rr); + r.x += width + skip; + return w; + } + + ecl::Rect pos() const { return r; } + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/MonospacedLabel.cpp b/project/jni/application/enigma/src/gui/MonospacedLabel.cpp new file mode 100644 index 000000000..d1bef6d62 --- /dev/null +++ b/project/jni/application/enigma/src/gui/MonospacedLabel.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "gui/MonospacedLabel.hh" +#include "ecl.hh" +#include "nls.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + MonospacedLabel::MonospacedLabel (const std::string &text, char widthSample, + std::string monospaceCharacters, HAlignment halign, VAlignment valign) : + Label (text, halign, valign), sampleChar (widthSample), + monoChars (monospaceCharacters) { + } + + void MonospacedLabel::draw (ecl::GC &gc, const ecl::Rect &) { + Font *f = m_font; + int w, h; + naturalsize (w, h); + + int x = get_x(), y=get_y(); + switch (m_halign) { + case HALIGN_LEFT: break; + case HALIGN_RIGHT: x += get_w() - w; break; + case HALIGN_CENTER: x += (get_w()-w)/2; break; + } + switch (m_valign) { + case VALIGN_TOP: break; + case VALIGN_BOTTOM: y += get_h() - h; break; + case VALIGN_CENTER: y += (get_h()-h)/2; break; + } + // translate if not an empty string + const char * translation = _(m_text.c_str()); + int len = strlen(translation); + int monoWidth = m_font->get_width(sampleChar); + char c[] = " "; + for (int i = 0; iget_width(c[0]); + // centere char into monoWodth + f->render (gc, x + (monoWidth-charWidth)/2, y, c); + x += monoWidth; + } else { + f->render (gc, x, y, c); + x += m_font->get_width(c); + } + } + } + + void MonospacedLabel::naturalsize (int &w, int &h) const { + h = m_font->get_height(); + w = 0; + const char * translation = _(m_text.c_str()); + int len = strlen(translation); + int monoWidth = m_font->get_width(sampleChar); + for (int i = 0; iget_width(translation[i]); + } + } + } +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/gui/MonospacedLabel.hh b/project/jni/application/enigma/src/gui/MonospacedLabel.hh new file mode 100644 index 000000000..67995d81e --- /dev/null +++ b/project/jni/application/enigma/src/gui/MonospacedLabel.hh @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_MONOSPACEDLABEL_HH_INCLUDED +#define GUI_MONOSPACEDLABEL_HH_INCLUDED + +#include "gui/widgets.hh" + + +namespace enigma { namespace gui { + /** + * A text label that uses the same width for each character even for + * proportional fonts. Character positions of the label text is predictable + * and alignment of several labels is thus possible. Usefull for output of + * formatted numbers like score values.

+ * Not all characters have to be monospaced. The set of characters that + * should have a constant width can be limited f.e. to the numbers 0-9. + */ + class MonospacedLabel : public Label { + public: + /** + * The standard constructor. + * @arg text The constant output text. + * @arg widthSample The character that defines the width. Default is 'm'. + * @arg monospaceCharacters The set of monospace characters. An empty + * string means all characters. Example "0123456789". + * @arg halign + * @arg valign + */ + MonospacedLabel (const std::string &text="", char widthSample = 'm', + std::string monospaceCharacters = "", + HAlignment halign=HALIGN_CENTER, VAlignment valign=VALIGN_CENTER); + + // Widget interface + virtual void draw (ecl::GC &gc, const ecl::Rect &r); + virtual void naturalsize (int &w, int &h) const; + protected: + char sampleChar; + std::string monoChars; + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/OptionsMenu.cpp b/project/jni/application/enigma/src/gui/OptionsMenu.cpp new file mode 100644 index 000000000..52588afe2 --- /dev/null +++ b/project/jni/application/enigma/src/gui/OptionsMenu.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/OptionsMenu.hh" +#include "ecl.hh" +#include "enigma.hh" +#include "lev/ScoreManager.hh" +#include "LocalToXML.hh" +#include "main.hh" +#include "nls.hh" +#include "options.hh" +#include "oxyd.hh" +#include "sound.hh" +#include "Utf8ToXML.hh" +#include "video.hh" +#include "XMLtoLocal.hh" +#include "XMLtoUtf8.hh" + +#include + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + +/* -------------------- Buttons for Options -------------------- */ + + class MouseSpeedButton : public ValueButton { + int get_value() const { + return ecl::round_nearest(options::GetMouseSpeed()); + } + void set_value(int value) { + options::SetMouseSpeed (value); + } + + string get_text(int value) const { + return strf("%d", value); + } + public: + MouseSpeedButton() + : ValueButton(options::MIN_MouseSpeed, options::MAX_MouseSpeed) + { init(); } + }; + + class SoundVolumeButton : public ValueButton { + int get_value() const { + return round_nearest(options::GetDouble("SoundVolume")*10.0); + } + void set_value(int value) { + options::SetOption("SoundVolume", value/10.0); + options::UpdateVolume(); + } + + string get_text(int value) const { + if (value == 0) { + return _("muted"); + } + else { + return strf("%d", value); + } + } + public: + SoundVolumeButton() : ValueButton(0, 10) { init(); } + }; + + class MusicVolumeButton : public ValueButton { + int get_value() const { + return round_nearest (options::GetDouble("MusicVolume")*10.0); + } + void set_value(int value) { + options::SetOption("MusicVolume", value/10.0); + options::UpdateVolume(); + } + + string get_text(int value) const { + if (value == 0) + return _("muted"); + else + return strf("%d", value); + } + public: + MusicVolumeButton() : ValueButton(0, 10) { init(); } + }; + + class InGameMusicButton : public BoolOptionButton { + void on_action(Widget *) { + if (toggle()) + sound::PlayMusic (options::GetString("LevelMusicFile")); + else + sound::StopMusic (options::GetString("LevelMusicFile")); + } + public: + InGameMusicButton() : + BoolOptionButton("InGameMusic", N_("Music in game"), N_("No music in game"), this) + { } + }; + + struct SkipSolvedButton : public BoolOptionButton { + SkipSolvedButton() : BoolOptionButton("SkipSolvedLevels", N_("Yes"), N_("No"), this) {} + }; + + struct TimeHuntButton : public BoolOptionButton { + TimeHuntButton() : BoolOptionButton("TimeHunting", N_("Yes"), N_("No"), this) {} + }; + + struct RatingsUpdateButton : public BoolOptionButton { + RatingsUpdateButton() : BoolOptionButton("RatingsAutoUpdate", N_("Auto"), N_("Never"), this) {} + }; + + class VideoModeButton : public TextButton { + + video::VideoModes get_mode() const { + int mode = Clamp(options::GetInt("VideoMode"), 0, int(video::VM_COUNT)); + return static_cast(mode); + } + string get_text() const { + return GetInfo(get_mode())->name; + } + void on_action(Widget *) { + int mode = get_mode(); + + // cycle at most once through all available video modes + do { + mode += 1; + if (mode >= video::VM_COUNT) + mode = 0; + + const video::VMInfo *vminfo = GetInfo (static_cast(mode)); + if (vminfo->available) { + options::SetOption("VideoMode", mode); + invalidate(); + break; + } + } while (mode != get_mode()); + } + public: + VideoModeButton() : TextButton(this) { } + }; + + + /* -------------------- SoundSetButton -------------------- */ + + SoundSetButton::SoundSetButton() : ValueButton(0, 1) { + int numAvail = sound::GetOptionSoundSetCount(); + setMaxValue(numAvail - 1); + init(); + } + + int SoundSetButton::get_value() const { + return sound::GetOptionSoundSet(); + } + + void SoundSetButton::set_value(int value) { + sound::SetOptionSoundSet(value); + } + + string SoundSetButton::get_text(int value) const { + return _(sound::GetOptionSoundSetText(value).c_str()); + } + + + /* -------------------- StereoButton -------------------- */ + + StereoButton::StereoButton() : ValueButton(-1, 1) + { + init(); + } + + int StereoButton::get_value() const + { + double separation = options::GetDouble("StereoSeparation"); + if (separation == 0) + return 0; + else + return (separation > 0) ? 1 : -1; + } + void StereoButton::set_value(int value) + { + if (value == 0) + options::SetOption("StereoSeparation", 0.0); + else if (value > 0) + options::SetOption("StereoSeparation", 10.0); + else + options::SetOption("StereoSeparation", -10.0); + } + + string StereoButton::get_text(int value) const + { + switch (value) { + case -1: return _("reversed"); + case 0: return _("mono"); + case 1: return _("normal"); + } + assert(0); + return string(); + } + + + /* -------------------- FullscreenButton -------------------- */ + + FullscreenButton::FullscreenButton() + : BoolOptionButton("FullScreen", N_("Yes"), N_("No"), this) + { + } + + /* -------------------- LanguageButton -------------------- */ + + struct Language { + const char *name; + const char *localename; + }; + + Language languages[] = { + { "default", "" }, + { "Deutsch", "de_DE" }, + { "English", "en_EN" }, + { "Español", "es_ES" }, + { "Français", "fr_FR" }, + { "Italiano", "it_IT" }, + { "Nederlands", "nl_NL" }, + { "Svenska", "sv_SE" }, + { "Русский", "ru_RU" }, + { "Magyar", "hu_HU" }, + { "Português", "pt_BR" }, + { "Suomi", "fi_FI" }, + }; + + int LanguageButton::get_value() const + { + string localename; // = ecl::DefaultMessageLocale (); + options::GetOption ("Language", localename); + + int lang = 0; // unknown language + for (size_t i=0; ion_action(this); + } + } + + string LanguageButton::get_text(int value) const + { + if (value == -1) + return _("unknown"); + else + return languages[value].name; + } + + LanguageButton::LanguageButton (ActionListener *al) + : ValueButton(0, NUMENTRIES(languages)-1), myListener(al) + { + inInit = true; + init(); + inInit = false; + } + + /* -------------------- GammaButton -------------------- */ + + + GammaButton::GammaButton() + : ValueButton(1, 10) + { + init(); + } + + void GammaButton::set_value(int value) + { + double gamma = double(value) / 5.0; + options::SetOption ("Gamma", gamma); + video::UpdateGamma(); + } + + int GammaButton::get_value() const + { + double gamma = options::GetDouble ("Gamma"); + int value = round_down(gamma * 5.0); + return value; + } + + string GammaButton::get_text(int value) const + { + return ecl::strf ("%d", value-5); + } + + + + /* -------------------- Options Menu -------------------- */ + + OptionsMenu::OptionsMenu(ecl::Surface *background_) + : back(new StaticTextButton(N_("Back"), this)), + fullscreen(new FullscreenButton), + m_restartinfo (new Label("")), + background(background_), + previous_caption(video::GetCaption()) + { + const int spacing = 5; + const int big_spacing = 60; + const int label_width = 180; + const int but_width = 100; + const int but_height = 30; + const video::VMInfo *vminfo = video::GetInfo(); + int hmargin = vminfo->width < 660 ? 10 : (vminfo->width < 900 ? 20 : 80); + int midspacing = vminfo->width - 2*hmargin - 2*but_width - 2*label_width; + + BuildVList leftlabels (this, Rect(-label_width, 0, label_width, but_height), spacing); + BuildVList left (this, Rect(0, 0, but_width, but_height), spacing); + BuildVList rightlabels (this, Rect(but_width+midspacing, 0, label_width, but_height), spacing); + BuildVList right(this, Rect(but_width+midspacing+label_width, 0, but_width, but_height), spacing); + leftlabels.add (new Label(N_("Language: "), HALIGN_RIGHT)); + leftlabels.add (new Label(N_("Fullscreen: "), HALIGN_RIGHT)); + leftlabels.add (new Label(N_("Video mode: "), HALIGN_RIGHT)); + leftlabels.add (new Label(N_("Gamma correction: "), HALIGN_RIGHT)); + leftlabels.add (new Label(N_("Mouse speed: "), HALIGN_RIGHT)); + + language = new LanguageButton(this); + left.add (language); + left.add (fullscreen); + left.add (new VideoModeButton); + left.add (new GammaButton); + left.add (new MouseSpeedButton); + + rightlabels.add (new Label(N_("Sound volume: "), HALIGN_RIGHT)); + rightlabels.add (new Label(N_("Sound set: "), HALIGN_RIGHT)); + rightlabels.add (new Label(N_("Music volume: "), HALIGN_RIGHT)); + rightlabels.add (new Label(N_("Stereo: "), HALIGN_RIGHT)); + rightlabels.add (new Label(N_("Ratings update: "), HALIGN_RIGHT)); + + right.add (new SoundVolumeButton); + right.add (new SoundSetButton); + right.add (new MusicVolumeButton); +// right.add (new InGameMusicButton);Über + right.add (new StereoButton); + right.add (new RatingsUpdateButton); + + Rect l = left.pos(); + Rect r = right.pos(); + + BuildVList bottomlabels (this, Rect(-label_width, Max(l.y, r.y), label_width, but_height), spacing); + BuildVList bottom (this, Rect(0, Max(l.y, r.y), vminfo->width - 2*hmargin - label_width, but_height), spacing); + bottomlabels.add (new Label(N_("User name: "), HALIGN_RIGHT)); + bottomlabels.add (new Label(N_("User path: "), HALIGN_RIGHT)); + bottomlabels.add (new Label(N_("User image path: "), HALIGN_RIGHT)); + userNameTF = new TextField(app.state->getString("UserName")); + userNameTF->setMaxChars(20); + userNameTF->setInvalidChars("+"); + bottom.add (userNameTF); + userPathTF = new TextField(XMLtoUtf8(LocalToXML(app.userPath.c_str()).x_str()).c_str()); + bottom.add (userPathTF); + userImagePathTF = new TextField(XMLtoUtf8(LocalToXML(app.userImagePath.c_str()).x_str()).c_str()); + bottom.add (userImagePathTF); + +// add (m_restartinfo, Rect (l.x, l.y + 15, 400, 20)); +// m_restartinfo->set_alignment (HALIGN_LEFT); +// update_info(); + + Rect b = bottom.pos(); + l.x = (l.x+r.x)/2; + l.y = b.y+big_spacing; + + add(back, l); + } + + OptionsMenu::~OptionsMenu() { + video::SetCaption(previous_caption.c_str()); + } + +// void OptionsMenu::update_info() +// { +// if (options::MustRestart) +// m_restartinfo->set_text ( +// N_("Please restart Enigma to activate your changes!")); +// else +// m_restartinfo->set_text (""); +// } + + void OptionsMenu::quit() { + std::string tfUserPathLocal = XMLtoLocal(Utf8ToXML(userPathTF->getText().c_str()).x_str()).c_str(); + std::string tfUserImageLocal = XMLtoLocal(Utf8ToXML(userImagePathTF->getText().c_str()).x_str()).c_str(); + if ((app.state->getString("UserName") != userNameTF->getText()) + || (app.userPath != tfUserPathLocal ) || (app.userImagePath != tfUserImageLocal)) { + // ensure that enigma.score is saved with new Username or to new location + lev::ScoreManager::instance()->markModified(); + } + // strip off leading and trailing whitespace from user name + std::string userName = userNameTF->getText(); + std::string::size_type firstChar = userName.find_first_not_of(" "); + std::string::size_type lastChar = userName.find_last_not_of(" "); + if (firstChar != std::string::npos) + app.state->setProperty("UserName", userName.substr(firstChar, lastChar - firstChar + 1)); + else + app.state->setProperty("UserName", std::string("")); + app.setUserPath(tfUserPathLocal.c_str()); + app.setUserImagePath(tfUserImageLocal.c_str()); + Menu::quit(); + } + + bool OptionsMenu::on_event (const SDL_Event &e) + { + bool handled=false; + if (e.type == SDL_MOUSEBUTTONDOWN + && e.button.button == SDL_BUTTON_RIGHT) + { + quit(); + handled = true; + } + else if (e.type == SDL_KEYUP) { + if ((e.key.keysym.sym==SDLK_RETURN) && + (e.key.keysym.mod & KMOD_ALT)) + { + // update state of FullscreenButton : + fullscreen->invalidate(); + handled = true; + } + } + return handled; + } + + void OptionsMenu::on_action(Widget *w) + { + if (w == back) + quit(); + else if (w == language) + // language changed - retranslate and redraw everything + invalidate_all(); + } + + void OptionsMenu::tick (double) + { +// update_info(); + } + + void OptionsMenu::draw_background(ecl::GC &gc) + { + video::SetCaption(("Enigma - Options Menu")); + // blit(gc, 0,0, enigma::GetImage("menu_bg")); + blit(gc, 0,0, background); + } + +/* -------------------- Functions -------------------- */ + + void ShowOptionsMenu(Surface *background) { + if (background == 0) + background = enigma::GetImage("menu_bg", ".jpg"); + OptionsMenu m(background); + m.center(); + m.manage(); + } + +}} // namespace enigma::gui + diff --git a/project/jni/application/enigma/src/gui/OptionsMenu.hh b/project/jni/application/enigma/src/gui/OptionsMenu.hh new file mode 100644 index 000000000..9a08e7055 --- /dev/null +++ b/project/jni/application/enigma/src/gui/OptionsMenu.hh @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2002,2003,2004,2005,2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef GUI_OPTIONSMENU_HH_INCLUDED +#define GUI_OPTIONSMENU_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/TextField.hh" + +namespace enigma { namespace gui { +/* -------------------- OptionsMenu -------------------- */ + + class OptionsMenu : public Menu { + public: + OptionsMenu(ecl::Surface *background_); + ~OptionsMenu(); + virtual void quit(); + private: + void update_info(); + + // ActionListener interface. + bool on_event (const SDL_Event &e); + void on_action(gui::Widget *w); + + // Menu interface. + void draw_background(ecl::GC &gc); + void tick(double dtime); + + // Variables. + gui::Widget *back, *fullscreen, *language; + gui::TextField *userNameTF; + gui::TextField *userPathTF; + gui::TextField *userImagePathTF; + gui::Label *m_restartinfo; + ecl::Surface *background; + std::string previous_caption; + }; + +/* -------------------- Options Buttons -------------------- */ + + struct FullscreenButton : public BoolOptionButton { + FullscreenButton(); + }; + + + class StereoButton : public ValueButton { + int get_value() const; + void set_value(int value); + std::string get_text(int value) const; + public: + StereoButton(); + }; + + + class SoundSetButton : public ValueButton { + public: + SoundSetButton(); + int get_value() const; + void set_value(int value); + std::string get_text(int value) const; + }; + + class LanguageButton : public ValueButton { + int get_value() const; + void set_value(int value); + std::string get_text(int value) const; + bool inInit; + ActionListener *myListener; + public: + // second user action listener: first one is misused by ValueButton + LanguageButton (ActionListener *al = 0); + + }; + + class GammaButton : public ValueButton { + int get_value() const; + void set_value(int value); + std::string get_text(int value) const; + public: + GammaButton (); + + }; + +/* -------------------- Functions -------------------- */ + + void ShowOptionsMenu(ecl::Surface *background); + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/ScreenshotViewer.cpp b/project/jni/application/enigma/src/gui/ScreenshotViewer.cpp new file mode 100644 index 000000000..ec123feb1 --- /dev/null +++ b/project/jni/application/enigma/src/gui/ScreenshotViewer.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/ScreenshotViewer.hh" +#include "gui/HelpMenu.hh" +#include "ecl.hh" +#include "enigma.hh" +#include "main.hh" +#include "nls.hh" +#include "video.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + static const char *helptext[] = { + N_("Escape:"), N_("Back"), + "F1:", N_("Show this help"), + N_("Page Up:"), N_("Show previous screenshot"), + N_("Page Down:"), N_("Show next screenshot"), + 0 + }; + + ScreenshotViewer::ScreenshotViewer(lev::Proxy *aLevel) : + levelProxy (aLevel), shotNumber (0) { + } + + ScreenshotViewer::~ScreenshotViewer() { + } + + bool ScreenshotViewer::on_event (const SDL_Event &e) { + switch (e.type) { + case SDL_MOUSEBUTTONDOWN: + if (e.button.button == SDL_BUTTON_RIGHT) { + Menu::quit(); + return true; + } + break; + case SDL_KEYDOWN: + SDLKey keysym = e.key.keysym.sym; + switch (keysym) { + case SDLK_PAGEUP: + if (shotNumber > 0 ) + --shotNumber; + invalidate_all(); + return true; + case SDLK_PAGEDOWN: + shotNumber++; + invalidate_all(); + return true; + // eat up widget activation keys - we have no widgets + case SDLK_DOWN: + case SDLK_UP: + case SDLK_RIGHT: + case SDLK_LEFT: + return true; + case SDLK_F1: + displayHelp(helptext, 200); + invalidate_all(); + return true; + default: + break; + } + break; + } + return false; + } + + void ScreenshotViewer::draw_background (ecl::GC &gc) { + std::string filename = "screenshots/" + + levelProxy->getLocalSubstitutionLevelPath() + + (shotNumber > 0 ? ecl::strf("#%d", shotNumber) : "") + ".png"; + std::string fullPath; + if (app.resourceFS->findFile(filename, fullPath)) { + const video::VMInfo *vminfo = video::GetInfo(); + ecl::Surface * image = ecl::LoadImage(fullPath.c_str()); + if (image->width() == vminfo->width && image->height() == vminfo->height) { + blit(gc, 0,0, image); + } else { + ecl::Surface * imageZoomed = image->zoom(vminfo->width, vminfo->height); + blit(gc, 0,0, imageZoomed); + delete imageZoomed; + } + delete image; + } else { + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + Font *f = enigma::GetFont("menufont"); + f->render (gc, 30, 60, _("No screenshot available:")); + f->render (gc, 30, 100, filename.c_str()); + } + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/ScreenshotViewer.hh b/project/jni/application/enigma/src/gui/ScreenshotViewer.hh new file mode 100644 index 000000000..972c966b3 --- /dev/null +++ b/project/jni/application/enigma/src/gui/ScreenshotViewer.hh @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_SCREENSHOTVIEWER_HH_INCLUDED +#define GUI_SCREENSHOTVIEWER_HH_INCLUDED + + +#include "gui/Menu.hh" +#include "lev/Proxy.hh" + +namespace enigma { namespace gui { + + /** + * TODO delete CTRL-D with confirmation panel - rename of following shots? + * TODO shift up, down for reorder/rename of shots + */ + class ScreenshotViewer : public gui::Menu { + public: + ScreenshotViewer (lev::Proxy *aLevel); + ~ScreenshotViewer (); + + // Widget interface + virtual bool on_event (const SDL_Event &e); + protected: + // Menu interface. + virtual void draw_background (ecl::GC &gc); + private: + lev::Proxy *levelProxy; + std::string basePath; + int shotNumber; + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/SearchMenu.cpp b/project/jni/application/enigma/src/gui/SearchMenu.cpp new file mode 100644 index 000000000..cc265d1fc --- /dev/null +++ b/project/jni/application/enigma/src/gui/SearchMenu.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/SearchMenu.hh" +#include "ecl.hh" +#include "enigma.hh" +#include "errors.hh" +#include "lev/Index.hh" +#include "lev/Proxy.hh" +#include "nls.hh" +#include "video.hh" + +#include "main.hh" + +using namespace ecl; +using namespace std; + +namespace enigma { namespace gui { + + SearchMenu::SearchMenu() : didSearch (false) { + const video::VMInfo &vminfo = *video::GetInfo(); + ecl::Font *menufont = enigma::GetFont("menufont"); + + Label * shallowTitle = new Label(N_("Shallow Search:"), HALIGN_LEFT); +// TRANSLATORS: the translation can have double size of the english text + std::string helpText = _("case independent search in title, author, filename"); + std::string workString = helpText; + std::string::size_type breakPos = breakString(menufont, workString, + " ", 380); + Label * shallowHelp1 = new UntranslatedLabel(workString.substr(0,breakPos), HALIGN_LEFT); + Label * shallowHelp2 = new UntranslatedLabel(workString.substr(breakPos), HALIGN_LEFT); + shallowSearch = new TextField("", this); + + this->add(shallowTitle, Rect(vminfo.width/2 - 190, vminfo.height/2 - 100, 380, 35)); + this->add(shallowHelp1, Rect(vminfo.width/2 - 190, vminfo.height/2 - 40, 380, 25)); + this->add(shallowHelp2, Rect(vminfo.width/2 - 190, vminfo.height/2 - 10, 380, 25)); + this->add(shallowSearch, Rect(vminfo.width/2 - 190, vminfo.height/2 + 55, 380, 35)); + + // Create buttons - positioning identical to Levelmenu + Label * dummy1 = new Label(); + Label * dummy2 = new Label(); + but_ignore = new StaticTextButton(N_("Undo"), this); + but_search = new StaticTextButton(N_("Search"), this); + + HList * commandHList = new HList; + commandHList->set_spacing(10); + commandHList->set_alignment(HALIGN_CENTER, VALIGN_TOP); + commandHList->set_default_size(140, 35); + commandHList->add_back(dummy1); + commandHList->add_back(dummy2); + commandHList->add_back(but_ignore); + commandHList->add_back(but_search); + this->add(commandHList, Rect(10, vminfo.height-50, vminfo.width-20, 35)); + } + + bool SearchMenu::isSearchQuit() { + return didSearch; + } + + void SearchMenu::on_action(Widget *w) { + if (w == but_search || + (w == shallowSearch && shallowSearch->wasLastActionReturn())) { + lev::Index::setCurrentIndex(lev::Proxy::search(shallowSearch->getText())); + didSearch = true; + Menu::quit(); + } else if (w == but_ignore) { + Menu::quit(); + } + } + + void SearchMenu::draw_background(ecl::GC &gc) { + video::SetCaption(("Enigma - Search Menu")); + blit(gc, 0,0, enigma::GetImage("menu_bg", ".jpg")); + } +}} // namespace enigma::gui diff --git a/project/jni/application/enigma/src/gui/SearchMenu.hh b/project/jni/application/enigma/src/gui/SearchMenu.hh new file mode 100644 index 000000000..71a10df08 --- /dev/null +++ b/project/jni/application/enigma/src/gui/SearchMenu.hh @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GUI_SEARCHMENU_HH_INCLUDED +#define GUI_SEARCHMENU_HH_INCLUDED + +#include "gui/Menu.hh" +#include "gui/TextField.hh" + +namespace enigma { namespace gui { + + class SearchMenu : public gui::Menu { + public: + SearchMenu (); + + void on_action(Widget *w); + void draw_background(ecl::GC &gc); + bool isSearchQuit(); + + private: + TextField *shallowSearch; + Widget *but_ignore; + Widget *but_search; + bool didSearch; + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/TextField.cpp b/project/jni/application/enigma/src/gui/TextField.cpp new file mode 100644 index 000000000..e3d452ae8 --- /dev/null +++ b/project/jni/application/enigma/src/gui/TextField.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "main.hh" +#include "gui/widgets.hh" +#include "gui/TextField.hh" +#include "ecl_utf.hh" +#include "enigma.hh" +#include "sound.hh" +#include "video.hh" +#include "options.hh" +#include "nls.hh" +#include "ecl.hh" +#include +#include +#include + +using namespace enigma::gui; +using namespace ecl; +using namespace std; + +#define SCREEN ecl::Screen::get_instance() + +ecl::Font *TextField::menufont = 0; + +TextField::TextField(const std::string &t, ActionListener *al) : cursorTime(0), + showCursor(true), isLastActionReturn (false), invalidChars(""), + isLimitedToValidChars(false), maxChars(-1) { + if (menufont == 0) { + menufont = enigma::GetFont("menufont"); + } + set_listener(al); + textPreCursor = t; + ecl::utf8CharSizes(textPreCursor, charSizesPreCursor); + textPostCursor= ""; +} + +void TextField::set_text(const std::string &t) { + textPreCursor = t; + charSizesPreCursor.clear(); + ecl::utf8CharSizes(textPreCursor, charSizesPreCursor); + textPostCursor= ""; + charSizesPostCursor.clear(); +} + +std::string TextField::getText() { + string total = textPreCursor; + total += textPostCursor; + return total; +} + +void TextField::setInvalidChars(std::string forbiddenChars) { + invalidChars = forbiddenChars; +} + +void TextField::setMaxChars(int max) { + maxChars = max; +} +bool TextField::wasLastActionReturn() { + return isLastActionReturn; +} + +void TextField::tick (double dtime) { + cursorTime += dtime; + if (cursorTime > 0.5) { + cursorTime = 0.0; + showCursor = !showCursor; + invalidate(); + } +} + +void TextField::draw(ecl::GC &gc, const ecl::Rect &r) { + Button::draw(gc,r); + Font *f = menufont; + int h = f->get_height(); + int w_pre = f->get_width(textPreCursor.c_str()); + int w_post = f->get_width(textPostCursor.c_str()); + int w_cursor = m_activep ? 1 : 0; + int x = get_x() + (get_w()- w_pre - w_post - w_cursor )/2; + int y = get_y() + (get_h()-h)/2; + + // cursor always visible + if (x + w_pre < get_x() + 5) + // cursor would be left of textfield - shift centered text right + x = get_x() + 5 - w_pre; + + if (x + w_pre > get_x() + get_w() - 5) + // cursor would be right of textfiled - shift centered text left + x = get_x() + get_w() - 5 - w_pre; + + f->render (gc, x, y, textPreCursor.c_str()); + + x += w_pre; + if (m_activep) { + if (showCursor) { + set_color(gc, 200,200,200); + vline(gc, x, y, h); + } + x += w_cursor; + } + + f->render (gc, x, y, textPostCursor.c_str()); + +} + +bool TextField::on_event(const SDL_Event &e) { + bool handeled = false; + bool modified = false; + + switch (e.type) { + case SDL_MOUSEBUTTONDOWN: + // set cursor + break; + case SDL_KEYDOWN: + switch (e.key.keysym.sym) { + case SDLK_RETURN: + handeled = true; + isLastActionReturn = true; + invoke_listener(); + break; + case SDLK_RIGHT: + if(textPostCursor.size() > 0) { + int size = charSizesPostCursor.back(); + charSizesPostCursor.pop_back(); + charSizesPreCursor.push_back(size); + textPreCursor.append(textPostCursor, 0, size); + textPostCursor.erase(0, size); + } + invalidate(); + handeled = true; + break; + case SDLK_LEFT: + if(textPreCursor.size() > 0) { + int size = charSizesPreCursor.back(); + charSizesPreCursor.pop_back(); + charSizesPostCursor.push_back(size); + textPostCursor.insert(0, textPreCursor.substr(textPreCursor.size() - size)); + textPreCursor.erase(textPreCursor.size() - size); + } + invalidate(); + handeled = true; + break; + case SDLK_INSERT: + handeled = true; + break; + case SDLK_HOME: + if(textPreCursor.size() > 0) { + int i; + int preChars = charSizesPreCursor.size(); + for (i = 0; i < preChars; i++) { + int size = charSizesPreCursor.back(); + charSizesPreCursor.pop_back(); + charSizesPostCursor.push_back(size); + } + textPostCursor.insert(0, textPreCursor); + textPreCursor.clear(); + } + invalidate(); + handeled = true; + break; + case SDLK_END: + if(textPostCursor.size() > 0) { + int size; + int i; + int postChars = charSizesPostCursor.size(); + for (i = 0; i < postChars; i++) { + size = charSizesPostCursor.back(); + charSizesPostCursor.pop_back(); + charSizesPreCursor.push_back(size); + } + textPreCursor.append(textPostCursor); + textPostCursor.clear(); + } + invalidate(); + handeled = true; + break; + case SDLK_DELETE: + if(textPostCursor.size() > 0) { + int size = charSizesPostCursor.back(); + textPostCursor.erase(0, size); + charSizesPostCursor.pop_back(); + } + invalidate(); + handeled = true; + modified = true; + break; + case SDLK_BACKSPACE: + if(textPreCursor.size() > 0) { + int size = charSizesPreCursor.back(); + textPreCursor.erase(textPreCursor.size() - size); + charSizesPreCursor.pop_back(); + } + invalidate(); + handeled = true; + modified = true; + break; + case SDLK_ESCAPE: + case SDLK_DOWN: + case SDLK_UP: + // menu active widget movements + break; + default: + // get char + if (e.key.keysym.unicode != 0 ) { + UTF16 realChar; + if (e.key.keysym.unicode >= 0x20 && + (e.key.keysym.unicode < 0x80 || // key pad + e.key.keysym.sym < 0x80)) { // windows umlaute + // the unicode is correct in these cases + realChar = e.key.keysym.unicode; + } + else if (e.key.keysym.unicode >= 0x80 && + e.key.keysym.sym < 0x100) { + // Linux: bad unicode but sym is ISO-8859-1 + + // incomplete workaround - runs only for some lower + // case umlauts + // we would need to handle shift key in language + // dependent manner -- or fix SDL Linux + realChar = e.key.keysym.sym; + } + else { + // chars like ctrl-a - ctrl-z + sound::EmitSoundEvent ("menustop"); + break; + } + if ((maxChars >= 0 && (charSizesPreCursor.size() + charSizesPostCursor.size()) >= maxChars) || + realChar < 0x100 && invalidChars.find((char)realChar) != std::string::npos) { + // string too long or invalid char + sound::EmitSoundEvent ("menustop"); + break; + } + unsigned char utf8Char[4]; + UTF16 const * utf16Ptr = (UTF16 *)&realChar; + UTF8 * utf8Ptr = utf8Char; + ConversionResult result; + result = ConvertUTF16toUTF8 (&utf16Ptr, utf16Ptr + 1, + &utf8Ptr, utf8Char + 4, strictConversion); + *utf8Ptr = 0; + textPreCursor += (const char *)utf8Char; + charSizesPreCursor.push_back(utf8Ptr - utf8Char); + + invalidate(); + handeled = true; + modified = true; + break; + } + if (e.key.keysym.sym < 300 || e.key.keysym.sym > 314 ){ + // chars like PageUp, F1 but not key state modifier + // like shift, alt,... + sound::EmitSoundEvent ("menustop"); + } + break; + } + break; + default: + break; + } + if (modified) { + isLastActionReturn = false; + invoke_listener(); + } + return handeled; +} + diff --git a/project/jni/application/enigma/src/gui/TextField.hh b/project/jni/application/enigma/src/gui/TextField.hh new file mode 100644 index 000000000..881d3bdd4 --- /dev/null +++ b/project/jni/application/enigma/src/gui/TextField.hh @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GUI_TF_HH_INCLUDED +#define GUI_TF_HH_INCLUDED + + +#include "gui/widgets.hh" + +namespace enigma { namespace gui { + + /** + * Gui widget for simple text insertion. Cursor keys, backspace and delete, + * home and end are supported. As a widget it handels utf-8 strings. But + * in contrast to other widget there is of course no gettext l10n translation. + * This widget is designed to be part of a menu. + * + * ToDo: supply full Linux umlaut support; + * add hook for external or subclass char insertion validation + * (NumberField, ...); + * move mouse cursor out of Textfield without deselection + */ + class TextField : public Button { + public: + /** + * Creates a boarderd text input field. + * @param t preset utf-8 input string + * @param al currently unused + */ + TextField(const std::string &t = "", ActionListener *al=0); + + /** + * resets the input string. + * @param t new utf-8 input string + */ + void set_text(const std::string &t); + + /** + * returns the inserted text + * @return the utf-8 coded string + */ + std::string getText(); + + void setInvalidChars(std::string forbiddenChars); // currently limited to 1st coding page + void setMaxChars(int max); + bool wasLastActionReturn(); + + // Widget interface. + virtual void tick (double dtime); + virtual bool on_event(const SDL_Event &/*e*/); + void draw(ecl::GC &gc, const ecl::Rect &r); + + protected: + double cursorTime; + bool showCursor; + + private: + std::string textPreCursor; + std::string textPostCursor; + /** + * a byte vector describing for each utf-8 character the number of + * occupied bytes in the string. + */ + std::vector charSizesPreCursor; + + /** + * a byte vector describing for each utf-8 character the number of + * occupied bytes in the string. This vector is in reverse order to + * the string itself!! + */ + std::vector charSizesPostCursor; + bool isLimitedToValidChars; + std::string validChars; + std::string invalidChars; + int maxChars; + bool isLastActionReturn; + static ecl::Font *menufont; + }; +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/gui/widgets.cpp b/project/jni/application/enigma/src/gui/widgets.cpp new file mode 100644 index 000000000..013405802 --- /dev/null +++ b/project/jni/application/enigma/src/gui/widgets.cpp @@ -0,0 +1,943 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gui/widgets.hh" +#include "enigma.hh" +#include "sound.hh" +#include "video.hh" +#include "options.hh" +#include "nls.hh" +#include "ecl.hh" +#include "ecl_font.hh" +#include +#include +#include +#include + +using namespace enigma::gui; +using namespace ecl; +using namespace std; + +#define SCREEN ecl::Screen::get_instance() + +/* -------------------- Widget -------------------- */ + +Widget::Widget(Container *parent) +: area(), m_parent(parent), m_listener(0) +{} + +void Widget::invalidate() { + if (m_parent) + m_parent->invalidate_area(get_area()); +} +void Widget::invalidate_area(const ecl::Rect& r) { + if (m_parent) + m_parent->invalidate_area(r); +} + +void Widget::reconfigure() +{ + if (m_parent) + m_parent->reconfigure_child(this); +} + +void Widget::invoke_listener() { + if (m_listener) + m_listener->on_action(this); +} + +void Widget::move(int x, int y) { + area.x = x; area.y = y; +} + +void Widget::resize (int w, int h) { + area.w = w; area.h = h; +} + +bool Widget::on_event(const SDL_Event &e) { + switch (e.type) { + case SDL_KEYDOWN: + modifierKeys = e.key.keysym.mod; + mouseButton = SDL_BUTTON_LEFT; + break; + case SDL_MOUSEBUTTONDOWN: + modifierKeys = SDL_GetModState(); + mouseButton = e.button.button; + break; + } + return false; +} + +/* -------------------- Image -------------------- */ + +void Image::draw (ecl::GC &gc, const ecl::Rect &/*r*/) { +// if (ecl::Surface *s = enigma::GetImage(imgname.c_str())) +// blit(gc, get_x(), get_y(), s); + if (ecl::Surface *s = enigma::GetImage(imgname.c_str())) { + int w=s->width(); + int h=s->height(); + int x = get_x() + (get_w()-w)/2; + int y = get_y() + (get_h()-h)/2; + blit(gc, x, y, s); + } +} + + +/* -------------------- AreaManager -------------------- */ + +AreaManager::AreaManager(Container *c) + : top_container(c) +{ + assert(top_container->get_parent() == 0); // otherwise it's not the top_container +} + +void AreaManager::invalidate_area(const ecl::Rect &r) { + dirtyrects.add(r); +} + +void AreaManager::invalidate_all() { + dirtyrects.clear(); + dirtyrects.push_back(SCREEN->size()); +} + +void AreaManager::refresh() { + if (!dirtyrects.empty()) { + video::HideMouse(); + GC gc(SCREEN->get_surface()); + for (RectList::iterator i = dirtyrects.begin(); i!=dirtyrects.end(); ++i) { + top_container->draw(gc, *i); + SCREEN->update_rect(*i); + } + video::ShowMouse(); + dirtyrects.clear(); + } +} + +/* -------------------- Container -------------------- */ + +Container::Container() + : managed_by(0) +{ +} +Container::~Container() { + clear(); + delete managed_by; +} + +AreaManager *Container::getAreaManager() { + if (Container *p = get_parent()) { + assert(!managed_by); + return p->getAreaManager(); + } + + if (!managed_by) { + managed_by = new AreaManager(this); + } + return managed_by; +} + +void Container::invalidate_area(const ecl::Rect &r) { getAreaManager()->invalidate_area(r); } +void Container::invalidate_all() { getAreaManager()->invalidate_all(); } +void Container::refresh() { getAreaManager()->refresh(); } + +void Container::clear() { + delete_sequence(m_widgets.begin(), m_widgets.end()); + m_widgets.clear(); +} + +void Container::add_child (Widget *w) { + if (w && w != this) { + m_widgets.push_back(w); + w->set_parent(this); +// w->move (get_x() + w->get_x(), +// get_y() + w->get_y()); + } +} + +void Container::remove_child (Widget *w) { + for (iterator it = begin(); it != end(); it++) { + if (*it == w) { + m_widgets.erase(it); + return; + } + } +} + +void Container::exchange_child (Widget *oldChild, Widget *newChild) { + for (int i = 0; i < m_widgets.size(); i++) { + if (m_widgets[i] == oldChild) { + m_widgets[i] = newChild; + newChild->set_parent(this); + return; + } + } +} + +void Container::draw (ecl::GC& gc, const ecl::Rect &r) { + for (iterator i=begin(); i!=end(); ++i) { + Widget *w = *i; + Rect rr = intersect(r, w->get_area()); + clip(gc, rr); + w->draw(gc,rr); + } +} + +void Container::draw_all() { + invalidate_all(); + refresh(); +} + +void Container::reconfigure_child (Widget *) +{ +} + + +Widget * Container::find_widget(int x, int y) { + for (iterator i=begin(); i!=end(); ++i) { + Widget *w = *i; + if (w->get_area().contains(x,y)) { + Container *c = dynamic_cast (w); + if (c) { + w = c->find_widget (x, y); + return w ? w : c; + } + return w; + } + } + return 0; +} + +Widget * Container::find_adjacent_widget(Widget *from, int x, int y) { + // valid values for x/y : 1/0, -1/0, 0/1, 0/-1 + assert(from && x>=-1 && x<=1 && y>=-1 && y<=1 && abs(x+y) == 1); + + if (!from) return 0; + + int best_distance = INT_MAX; + Widget *best_widget = 0; + ecl::Rect farea = from->get_area(); + + for (iterator i=begin(); i!=end(); ++i) { + Widget *w = *i; + ecl::Rect warea = w->get_area(); + bool adjacent = true; + int distance = 0; + + if (x) { // check for y-overlap + if (farea.y>(warea.y+warea.h-1) || warea.y>(farea.y+farea.h-1)) { + adjacent = false; + } + else { + distance = (warea.x-farea.x)*x; + } + } + else { // check for x-overlap + if (farea.x>(warea.x+warea.h-1) || warea.x>(farea.x+farea.h-1)) { + adjacent = false; + } + else { + distance = (warea.y-farea.y)*y; + } + } + + if (adjacent && distance>0 && distanceset_area (a); + + int dx = x-get_x(); + int dy = y-get_y(); + + for (iterator i=begin(); i!=end(); ++i) { + Widget *w = *i; + w->move(dx + w->get_x(), dy+w->get_y()); + } +} + +ecl::Rect Container::boundingbox() { + if (!m_widgets.empty()) { + iterator i=begin(); + Rect bbox=(*i)->get_area(); + for (++i; i!=end(); ++i) + bbox = ecl::boundingbox(bbox, (*i)->get_area()); + return bbox; + } else + return get_area(); +} + + +/* -------------------- List -------------------- */ + +List::List (int spacing) +: m_spacing(spacing), + has_default_size (false), + defaultw (0), + defaulth (0), + m_halign (HALIGN_LEFT), + m_valign (VALIGN_TOP) +{} + +void List::remove_child (Widget *w) { + Container::remove_child(w); + recalc(); +} + +void List::exchange_child(Widget *oldChild, Widget *newChild) { + Container::exchange_child(oldChild, newChild); + recalc(); +} + +void List::set_spacing (int pixels) +{ + m_spacing = pixels; +} + +int List::get_spacing () const +{ + return m_spacing; +} + +int List::calc_minimum_height() const +{ + int sum=0; + const WidgetList &wl = m_widgets; + if (!wl.empty()) { + sum = (wl.size() - 1) * m_spacing; + for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) { + int nw, nh; + get_size (*i, nw, nh); + sum += nh; + } + } + return sum; +} + +int List::calc_minimum_width () const +{ + int sum=0; + const WidgetList &wl = m_widgets; + if (!wl.empty()) { + sum = (wl.size() - 1) * m_spacing; + for (WidgetList::const_iterator i=wl.begin(); i!=wl.end(); ++i) { + int nw, nh; + get_size (*i, nw, nh); + sum += nw; + } + } + return sum; +} + +void List::set_default_size (int w, int h) +{ + has_default_size = true; + defaultw = w; + defaulth = h; +} + + +void List::get_size (const Widget *widget, int &w, int &h) const +{ + if (has_default_size) + w = defaultw, h = defaulth; + else + widget->naturalsize (w, h); +} + +void List::resize (int w, int h) +{ + Container::resize (w, h); + recalc(); +} + +void List::move (int x, int y) +{ + Container::move (x, y); +// recalc(); +} + +void List::reconfigure_child (Widget *w) +{ + Container::reconfigure_child (w); + recalc(); + invalidate(); +} + +void List::add_back (Widget *w, ExpansionMode m) +{ + add_child (w); + m_expansionmodes.push_back (m); + recalc(); +} + +void List::set_alignment (HAlignment halign, VAlignment valign) +{ + if (halign != m_halign || valign != m_valign) { + m_halign = halign; + m_valign = valign; + recalc(); + } +} + + + +/* -------------------- HList -------------------- */ + +void HList::recalc() +{ + int targetw = this->get_w(); // The available space + int naturalw= calc_minimum_width(); + int excessw = targetw - naturalw; + + int num_expand = std::count (m_expansionmodes.begin(), + m_expansionmodes.end(), + List::EXPAND); + + WidgetList::iterator i = m_widgets.begin(), + end = m_widgets.end(); + int x = get_x(), y = get_y(); + size_t j = 0; + + if (num_expand == 0 && excessw > 0) { + switch (m_halign) { + case HALIGN_CENTER: + x += excessw / 2; + excessw = 0; + break; + default: + break; + } + } + + for (; i != end; ++i, ++j) { + int w, h; + List::get_size (*i, w, h); + + if (excessw > 0 && m_expansionmodes[j] == List::EXPAND) { + w += excessw / num_expand; + excessw -= excessw / num_expand; + num_expand -= 1; + } + (*i)->move (x, y); + (*i)->resize (w, get_h()); + x += w + get_spacing(); + } +} + +bool HList::fits() { + int targetw = this->get_w(); // The available space + int naturalw= calc_minimum_width(); + return targetw >= naturalw; +} + + +/* -------------------- VList -------------------- */ + +void VList::recalc() +{ + int targeth = this->get_h(); // The available space + int naturalh= calc_minimum_height(); + int excessh = targeth - naturalh; + + int num_expand = std::count (m_expansionmodes.begin(), + m_expansionmodes.end(), + List::EXPAND); + + WidgetList::iterator i = m_widgets.begin(), + end = m_widgets.end(); + int x = get_x(), y = get_y(); + size_t j = 0; + + if (num_expand == 0 && excessh > 0) { + switch (m_valign) { + case VALIGN_CENTER: + y += excessh / 2; + excessh = 0; + break; + default: + break; + } + } + + for (; i != end; ++i, ++j) { + int w, h; + List::get_size (*i, w, h); + + if (excessh > 0 && m_expansionmodes[j] == List::EXPAND) { + h += excessh / num_expand; + excessh -= excessh / num_expand; + num_expand -= 1; + } + (*i)->move (x, y); + (*i)->resize (get_w(), h); + y += h + get_spacing(); + } +} + +bool VList::fits() { + int targeth = this->get_h(); // The available space + int naturalh= calc_minimum_height(); + return targeth >= naturalh; +} + + +/* -------------------- Label -------------------- */ + +Label::Label (const std::string &text, + HAlignment halign, + VAlignment valign) +: m_text (text), + m_font(enigma::GetFont("menufont")), + m_halign(halign), + m_valign(valign) +{} + + +void Label::set_text (const std::string &text) { + if (text != m_text) { + m_text = text; + reconfigure(); + invalidate(); + } +} + +string Label::get_text() const { + return _(m_text.c_str()); +} + +string Label::getText() const { + return m_text; +} + +void Label::set_font (ecl::Font *font) { + if (m_font != font) { + m_font = font; + reconfigure(); + invalidate(); + } +} + +bool Label::text_fits(double area_fraction) { + int w, h; + naturalsize (w, h); + return w <= get_w()*area_fraction; +} + +void Label::draw (ecl::GC &gc, const ecl::Rect &) +{ + Font *f = m_font; + int w, h; + naturalsize (w, h); + + int x = get_x(), y=get_y(); + switch (m_halign) { + case HALIGN_LEFT: break; + case HALIGN_RIGHT: x += get_w() - w; break; + case HALIGN_CENTER: x += (get_w()-w)/2; break; + } + switch (m_valign) { + case VALIGN_TOP: break; + case VALIGN_BOTTOM: y += get_h() - h; break; + case VALIGN_CENTER: y += (get_h()-h)/2; break; + } + // translate if not an empty string + f->render (gc, x, y, m_text == "" ? "" : get_text().c_str()); +} + +void Label::set_alignment (HAlignment halign, VAlignment valign) { + if (halign != m_halign || valign != m_valign) { + m_halign = halign; + m_valign = valign; + invalidate(); + } +} + +void Label::naturalsize (int &w, int &h) const +{ + h = m_font->get_height(); + // width of translation if not an empty string + w = m_font->get_width (m_text == "" ? "" : get_text().c_str()); +} + + +/* -------------------- UntranslatedLabel -------------------- */ + +UntranslatedLabel::UntranslatedLabel (const std::string &text, + HAlignment halign, VAlignment valign) : Label(text, halign, valign) { +} + +string UntranslatedLabel::get_text() const { + return Label::m_text; +} + + +/* -------------------- Button -------------------- */ + +Button::Button() : m_activep (false), highlight (false) { +} + +void Button::activate() +{ + sound::EmitSoundEvent ("menuswitch"); + m_activep = true; + invalidate(); +} + +void Button::deactivate() { + m_activep = false; + invalidate(); +} + +void Button::setHighlight(bool shouldHighlight) { + highlight = shouldHighlight; + invalidate(); +} +bool Button::isHighlight() { + return highlight; +} + +void Button::draw(ecl::GC &gc, const ecl::Rect &r) { + const int borderw = 4; + + ecl::Surface *s = enigma::GetImage (m_activep ? "buttonhl" : "button"); + + if (s) { // Ugly, but hey, it works + Rect srcrect (0,0,borderw, borderw); + Rect area = get_area(); + + // background + if (highlight) + set_color (gc, 70, 70, 70); + else + set_color (gc, 0,0,0); + box (gc, smaller(area, borderw)); + + set_color (gc, 0,0,0); + // corners + blit (gc, area.x, area.y, s, srcrect); + srcrect.x += s->width()-borderw; + blit (gc, area.x+area.w-borderw, area.y, s, srcrect); + srcrect.x = 0; + srcrect.y += s->height()-borderw; + blit (gc, area.x, area.y+area.h-borderw, s, srcrect); + srcrect.x += s->width()-borderw; + blit (gc, area.x+area.w-borderw, area.y+area.h-borderw, s, srcrect); + + // horizontal borders + { + int tilew = s->width() - 2*borderw; + int ntiles = (area.w - 2*borderw) / tilew; + int x = area.x + borderw; + for (int i=0; iheight()-borderw, tilew, borderw)); + x += tilew; + } + int restw = (area.w - 2*borderw) - tilew*ntiles; + blit (gc, x, area.y, s, Rect (borderw, 0, restw, borderw)); + blit (gc, x, area.y+area.h-borderw, s, + Rect (borderw, s->height()-borderw, restw, borderw)); + } + // vertical borders + { + int tileh = s->height() - 2*borderw; + int ntiles = (area.h - 2*borderw) / tileh; + int y = area.y + borderw; + for (int i=0; iwidth()-borderw, borderw, borderw, tileh)); + y += tileh; + } + int resth = (area.h - 2*borderw) - tileh*ntiles; + blit (gc, area.x, y, s, Rect (0, borderw, borderw, resth)); + blit (gc, area.x+area.w-borderw, y, s, + Rect (s->width()-borderw, borderw, borderw, resth)); + } + } + else { + set_color (gc, 0,0,0); + box (gc, r); + set_color (gc, 160,160,160); + frame (gc, r); + frame (gc, smaller(r, 1)); + } +} + + +/* -------------------- PushButton -------------------- */ + +PushButton::PushButton() : m_pressedp (false) { +} + +bool PushButton::on_event(const SDL_Event &e) { + Widget::on_event(e); + bool was_pressed = m_pressedp; + + switch (e.type) { + case SDL_KEYDOWN: + if (e.key.keysym.sym != SDLK_RETURN && + e.key.keysym.sym != SDLK_SPACE) break; + // fall-through + case SDL_MOUSEBUTTONDOWN: + m_pressedp = true; + break; + + case SDL_KEYUP: + if (e.key.keysym.sym != SDLK_RETURN && + e.key.keysym.sym != SDLK_SPACE && + e.key.keysym.sym != SDLK_PAGEDOWN && + e.key.keysym.sym != SDLK_PAGEUP) break; + lastUpSym = e.key.keysym.sym; + lastUpBotton = 0; + m_pressedp = false; + break; + case SDL_MOUSEBUTTONUP: + lastUpSym = SDLK_UNKNOWN; + lastUpBotton = e.button.button; + m_pressedp = false; + break; + } + + bool changed = (was_pressed != m_pressedp); + if (changed) { + invalidate(); + if (!m_pressedp) { + if (soundOk()) + sound::EmitSoundEvent("menuok"); + invoke_listener(); + } + } + + return changed; +} + +void PushButton::deactivate() { + m_pressedp = false; + lastUpSym = SDLK_UNKNOWN; + lastUpBotton = 0; + invalidate(); + Button::deactivate(); +} + +SDLKey PushButton::getLastUpSym() { + return lastUpSym; +} + +Uint8 PushButton::getLastUpButton() { + return lastUpBotton; +} + +bool PushButton::soundOk() { + return true; +} + +/* -------------------- TextButton -------------------- */ + +ecl::Font *TextButton::menufont = 0; +ecl::Font *TextButton::menufont_pressed = 0; + +TextButton::TextButton(ActionListener *al) { + if (menufont == 0) { + menufont = enigma::GetFont("menufont"); + menufont_pressed = enigma::GetFont("menufontsel"); + } + set_listener(al); +} + +void TextButton::draw(ecl::GC &gc, const ecl::Rect &r) { + Button::draw(gc,r); + Font *f = is_pressed() ? menufont_pressed : menufont; + string text = get_text(); + int h = f->get_height(); + int w = f->get_width(text.c_str()); + int x = get_x() + (get_w()-w)/2; + int y = get_y() + (get_h()-h)/2; + + f->render (gc, x, y, text.c_str()); +} + + +/* -------------------- StaticTextButton -------------------- */ + +StaticTextButton::StaticTextButton(const string &t, ActionListener *al) + : TextButton(al), + text(t) +{ +} + +void StaticTextButton::set_text(const std::string &t) { + if (t != text) { + text = t; + invalidate(); + } +} + +string StaticTextButton::get_text() const { + return _(text.c_str()); // translate +} + +/* -------------------- UntranslatedStaticTextButton -------------------- */ + +UntranslatedStaticTextButton::UntranslatedStaticTextButton(const string &t, + ActionListener *al) + : StaticTextButton(t, al) +{ +} + + +string UntranslatedStaticTextButton::get_text() const { + return StaticTextButton::text; +} + + +/* -------------------- Buttons for Options -------------------- */ + +BoolOptionButton::BoolOptionButton(const char *option_name, + const string& true_text, const string& false_text, + ActionListener *al) + : TextButton(al), + optionName(option_name), + trueText(true_text), + falseText(false_text) +{ +} + +bool BoolOptionButton::toggle() { + bool newval = !enigma_options::GetBool(optionName); + enigma_options::SetOption(optionName, newval); + invalidate(); + return newval; +} + +void BoolOptionButton::on_action(Widget *) { + toggle(); +} + +string BoolOptionButton::get_text() const { + return enigma_options::GetBool(optionName) ? _(trueText.c_str()) : _(falseText.c_str()); +} + + +/* -------------------- ValueButton -------------------- */ + +ValueButton::ValueButton(int min_value_, int max_value_) +: TextButton(this), + min_value(min_value_), + max_value(max_value_) +{ +} + +void ValueButton::setMaxValue(int max) { + max_value = max; +} + +void ValueButton::init() { + update_value(-1, get_value()); // fixes wrong values (e.g. from .enimarc) +} + +bool ValueButton::inc_value(int offset) { + int old_value = get_value(); + return update_value(old_value, old_value+offset); +} + +string ValueButton::get_text() const { + return get_text(get_value()); +} + +bool ValueButton::update_value(int old_value, int new_value) { + new_value = Clamp(new_value, min_value, max_value); + if (new_value != old_value) { + set_value(new_value); + invalidate(); + return true; + } + return false; +} + + +void ValueButton::on_action(Widget *) { + int incr = 1; + bool stop = false; + if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpButton() == SDL_BUTTON_RIGHT || + getLastUpButton() == 5) { // wheel down + incr = -1; + } + if (getLastUpSym() == SDLK_PAGEDOWN || getLastUpSym() == SDLK_PAGEUP || + getLastUpButton() == SDL_BUTTON_RIGHT || + getLastUpButton() == 4 || getLastUpButton() == 5) { + stop = true; + } + if (inc_value(incr)) { + sound::EmitSoundEvent("menuswitch"); + } else { + if (stop) { + sound::EmitSoundEvent("menustop"); + } else { + sound::EmitSoundEvent("menuswitch"); + if (incr == 1) + update_value(get_value(), min_value); + else + update_value(get_value(), max_value); + } + } +} + +bool ValueButton::soundOk() { + return false; +} + + +/* -------------------- ImageButton -------------------- */ + +ImageButton::ImageButton(const string &unselected, + const string &selected, + ActionListener *al) +: fname_sel(selected), fname_unsel(unselected) +{ + set_listener(al); +} + +void ImageButton::set_images(const string &unselected, const string &selected) { + fname_sel = selected; + fname_unsel = unselected; +} + +void ImageButton::draw(ecl::GC &gc, const ecl::Rect &r) { + Button::draw(gc, r); + string &fname = is_pressed() ? fname_sel : fname_unsel; + + if (Surface *s = enigma::GetImage(fname.c_str())) { + int w=s->width(); + int h=s->height(); + int x = get_x() + (get_w()-w)/2; + int y = get_y() + (get_h()-h)/2; + blit(gc, x, y, s); + } +} diff --git a/project/jni/application/enigma/src/gui/widgets.hh b/project/jni/application/enigma/src/gui/widgets.hh new file mode 100644 index 000000000..6630d44de --- /dev/null +++ b/project/jni/application/enigma/src/gui/widgets.hh @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2002,2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef WIDGETS_HH_INCLUDED +#define WIDGETS_HH_INCLUDED + +#include "ecl_fwd.hh" +#include "ecl_geom.hh" +#include "SDL.h" + +namespace enigma { namespace gui { + + +/* -------------------- Alignment -------------------- */ + + enum HAlignment { + HALIGN_LEFT, + HALIGN_CENTER, + HALIGN_RIGHT + }; + + enum VAlignment { + VALIGN_TOP, + VALIGN_CENTER, + VALIGN_BOTTOM + }; + +/* -------------------- Events & Event Handlers -------------------- */ + class Widget; + + class ActionListener { + public: + virtual ~ActionListener() {} + virtual void on_action(Widget *) {}; + }; + +/* -------------------- GUI Widgets -------------------- */ + + class Container; + + class Widget : public ActionListener { + public: + + /* ---------- Widget interface ---------- */ + virtual void draw (ecl::GC &gc, const ecl::Rect &r) = 0; + virtual void activate() {} + virtual void deactivate() {} + + virtual void realize (const ecl::Rect &r) { + set_area (r); + } + + virtual bool on_event(const SDL_Event &/*e*/); + Uint8 lastMouseButton() {return mouseButton;} + SDLMod lastModifierKeys() { return modifierKeys; } + + + virtual void move (int x, int y); + virtual void resize (int w, int h); + + virtual void naturalsize (int &w, int &h) const { + w = h = 5; + } + + /* ---------- Accessors ---------- */ + void set_size(int w, int h) {area.w = w; area.h = h;} + + ecl::Rect get_area() const { return area; } + void set_area(const ecl::Rect &r) { area = r; } + int get_x() const { return area.x; } + int get_y() const { return area.y; } + int get_w() const { return area.w; } + int get_h() const { return area.h; } + + void set_parent(Container *parent) { m_parent = parent; } + Container *get_parent () const { return m_parent; } + + void set_listener(ActionListener *al) { + m_listener = al; + } + + void invalidate(); + virtual void tick (double /*dtime*/) {} + + protected: + Widget(Container *parent=0); + + /* ---------- Functions ---------- */ + void reconfigure(); + void invalidate_area(const ecl::Rect &r); + void invoke_listener(); + + private: + ecl::Rect area; + Container *m_parent; + ActionListener *m_listener; + SDLMod modifierKeys; + Uint8 mouseButton; + }; + +#if 0 +/* -------------------- EmptyWidget -------------------- */ + class EmptyWidget : public Widget { + public: + EmptyWidget () : Widget () + {} + + virtual void draw (ecl::GC &gc, const ecl::Rect &r) + {} + + virtual void naturalsize (int &w, int &h) const { + w = h = 0; + } + }; +#endif +/* -------------------- AreaManager -------------------- */ + + class AreaManaged { + public: + virtual ~AreaManaged() {} + + virtual void invalidate_area(const ecl::Rect &r) = 0; + virtual void invalidate_all() = 0; + virtual void refresh() = 0; + }; + + // The AreaManager perform refreshes of invalidated regions. + // It gets attached to the top-level Container during the first invalidation + // or refresh request. + class AreaManager : public AreaManaged { + public: + AreaManager(Container *managed); + + void invalidate_area(const ecl::Rect &r); + void invalidate_all(); + void refresh(); + + private: + ecl::RectList dirtyrects; + Container *top_container; + }; + +/* -------------------- Container -------------------- */ + + class Container : public Widget, public AreaManaged { + public: + Container(); + ~Container(); + + void add_child (Widget *w); + virtual void remove_child (Widget *w); + virtual void exchange_child (Widget *oldChild, Widget *newChild); + virtual void reconfigure_child (Widget *w); + + Widget *find_widget(int x, int y); + Widget *find_adjacent_widget(Widget *from, int x, int y); + + void clear(); + void draw_all(); + + // Widget interface. + void draw (ecl::GC& gc, const ecl::Rect &r); + void move (int x, int y); + + // AreaManaged interface. + void invalidate_area(const ecl::Rect &r); + void invalidate_all(); + void refresh(); + + protected: + typedef std::vector WidgetList; + typedef WidgetList::iterator iterator; + + iterator begin() { return m_widgets.begin(); } + iterator end() { return m_widgets.end(); } + WidgetList m_widgets; + + private: + ecl::Rect boundingbox(); + + AreaManager *getAreaManager(); + AreaManager *managed_by; + }; + + +/* -------------------- List, HList, VList -------------------- */ + + class List : public Container { + public: + void set_spacing (int pixels); + + enum ExpansionMode { + EXPAND, + TIGHT + }; + + void add_back (Widget *w, ExpansionMode m = List::TIGHT); + virtual void remove_child (Widget *w); + virtual void exchange_child (Widget *oldChild, Widget *newChild); + + void set_default_size (int w, int h); + void set_alignment (HAlignment halign, VAlignment valign); + virtual bool fits() = 0; + + protected: + List(int spacing=5); + + int calc_minimum_height () const; + int calc_minimum_width () const; + + int get_spacing () const; + + void get_size (const Widget *widget, int &w, int &h) const; + + // ---------- Widget interface ---------- + virtual void move (int x, int y); + virtual void resize(int w, int h); + + // ---------- List interface ---------- + virtual void recalc() = 0; + + // ---------- Container interface ---------- + virtual void reconfigure_child (Widget *w); + + + protected: + std::vector m_expansionmodes; + + private: + int m_spacing; // # of pixels between container items + bool has_default_size; + int defaultw, defaulth; + protected: + HAlignment m_halign; + VAlignment m_valign; + + }; + + + class HList : public List { + public: + HList() : List() {} + virtual bool fits(); + + private: + // List interface + virtual void recalc(); + }; + + class VList : public List { + public: + VList() : List() {} + virtual bool fits(); + + private: + // List interface + virtual void recalc(); + + }; + +/* -------------------- Image -------------------- */ + + class Image : public Widget { + public: + Image (const std::string &iname) : imgname(iname) {} + void draw (ecl::GC &gc, const ecl::Rect &r); + private: + std::string imgname; + }; + +/* -------------------- Label -------------------- */ + + class Label : public Widget { + public: + Label (const std::string &text="", + HAlignment halign=HALIGN_CENTER, + VAlignment valign=VALIGN_CENTER); + + // Widget interface + virtual void draw (ecl::GC &gc, const ecl::Rect &r); + virtual void naturalsize (int &w, int &h) const; + + // Methods + void set_text (const std::string &text); + virtual std::string get_text() const; // translated + std::string getText() const; + void set_font (ecl::Font *font); + void set_alignment (HAlignment halign, VAlignment valign=VALIGN_CENTER); + bool text_fits(double area_fraction = 1.0); + protected: + // Variables. + std::string m_text; + ecl::Font *m_font; + HAlignment m_halign; + VAlignment m_valign; + }; + +/* -------------------- UntranslatedLabel -------------------- */ + + class UntranslatedLabel : public Label { + public: + UntranslatedLabel(const std::string &text="", + HAlignment halign=HALIGN_CENTER, + VAlignment valign=VALIGN_CENTER); + + // TextButton interface. + virtual std::string get_text() const; + }; + +/* -------------------- Button -------------------- */ + + class Button : public Widget { + public: + void setHighlight(bool shouldHighlight); + bool isHighlight(); + protected: + Button(); + + // Widget interface. + void draw(ecl::GC &gc, const ecl::Rect &r); + void activate(); + void deactivate(); + bool m_activep; + bool highlight; + }; + + +/* -------------------- PushButton -------------------- */ + + class PushButton : public Button { + public: + PushButton(); + + bool is_pressed() { return m_pressedp; } + + protected: + bool on_event(const SDL_Event &e); + void deactivate(); + SDLKey getLastUpSym(); + Uint8 getLastUpButton(); + virtual bool soundOk(); + private: + bool m_pressedp; + SDLKey lastUpSym; + Uint8 lastUpBotton; + }; + +/* -------------------- TextButton -------------------- */ + + class TextButton : public PushButton { + public: + TextButton(ActionListener *al=0); + virtual std::string get_text() const = 0; + + private: + // Widget interface. + void draw(ecl::GC &gc, const ecl::Rect &r); + + // Variables. + static ecl::Font *menufont, *menufont_pressed; + }; + +/* -------------------- StaticTextButton -------------------- */ + + class StaticTextButton : public TextButton { + public: + StaticTextButton(const std::string &t, ActionListener *al=0); + virtual void set_text(const std::string &t); + + // TextButton interface. + std::string get_text() const; + + protected: + // Variables. + std::string text; + }; + +/* -------------------- UntranslatedStaticTextButton -------------------- */ + + class UntranslatedStaticTextButton : public StaticTextButton { + public: + UntranslatedStaticTextButton(const std::string &t, ActionListener *al=0); + + // TextButton interface. + std::string get_text() const; + }; + +/* -------------------- BoolOptionButton -------------------- */ + class BoolOptionButton : public TextButton { + public: + BoolOptionButton(const char *option_name, + const std::string& true_text, + const std::string& false_text, + ActionListener *al = 0); + + bool toggle(); // returns new value + void on_action(Widget *); + + // TextButton interface. + std::string get_text() const; + + private: + const char *optionName; + std::string trueText; + std::string falseText; + }; + +/* -------------------- ValueButton -------------------- */ + class ValueButton: public TextButton { + public: + ValueButton(int min_value_, int max_value_); + + virtual int get_value() const = 0; + virtual void set_value(int value) = 0; + void setMaxValue(int max); + + bool inc_value(int offset); + + // TextButton interface. + virtual std::string get_text() const; + + // Widget interface. + virtual void on_action(Widget *w); + protected: + void init(); // called in ctor of derived + virtual bool soundOk(); + private: + int min_value; + int max_value; + + bool update_value(int old_value, int new_value); + virtual std::string get_text(int value) const = 0; + }; + + /* -------------------- ImageButton -------------------- */ + + class ImageButton : public PushButton { + public: + ImageButton(const std::string &unselected, + const std::string &selected, + ActionListener *al = 0); + void set_images(const std::string &unselected, const std::string &selected); + // Widget interface. + virtual void draw(ecl::GC &gc, const ecl::Rect &r); + private: + std::string fname_sel, fname_unsel; + }; + +}} // namespace enigma::gui +#endif diff --git a/project/jni/application/enigma/src/items.cpp b/project/jni/application/enigma/src/items.cpp new file mode 100644 index 000000000..7435f353d --- /dev/null +++ b/project/jni/application/enigma/src/items.cpp @@ -0,0 +1,3701 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "main.hh" +#include "display.hh" +#include "player.hh" +#include "client.hh" +#include "sound.hh" +#include "server.hh" +#include "world.hh" +#include "Inventory.hh" +#include "ItemHolder.hh" +#include "lev/Proxy.hh" + +#include "ecl_util.hh" + +#include +#include +#include + + +using namespace std; +using namespace world; + +using enigma::GridPos; +using enigma::Value; +using enigma::DoubleRand; +using ecl::V2; + + +/* -------------------- Macros -------------------- */ + +#define DEF_ITEM(classname, kindname, kindid) \ + class classname : public Item { \ + CLONEOBJ(classname); \ + DECL_TRAITS; \ + public: \ + classname() {} \ + }; \ + DEF_TRAITS(classname, kindname, kindid) + +#define DEF_ITEMF(classname, kindname, kindid, flags) \ + class classname : public Item { \ + CLONEOBJ(classname); \ + DECL_TRAITS; \ + public: \ + classname() {} \ + }; \ + DEF_TRAITSF(classname, kindname, kindid, flags) + +#define DECL_TRAITS \ + static ItemTraits traits; \ + const ItemTraits &get_traits() const { return traits; } + +#define DECL_TRAITS_ARRAY(n, subtype_expr) \ + static ItemTraits traits[n]; \ + const ItemTraits &get_traits() const { return traits[subtype_expr]; } + +#define DEF_TRAITS(classname, name, id) \ + ItemTraits classname::traits = { name, id, itf_none, 0.0 } + +#define DEF_TRAITSF(classname, name, id, flags) \ + ItemTraits classname::traits = { name, id, flags, 0.0 } + +#define DEF_TRAITSR(classname, name, id, radius) \ + ItemTraits classname::traits = { name, id, 0, radius } + + +/* -------------------- Item implementation -------------------- */ + +Item::Item() +{} + +void Item::kill() { + KillItem(get_pos()); +} + +void Item::replace(ItemID id) +{ + Item *newitem = MakeItem (id); + TransferObjectName (this, newitem); + setup_successor(newitem); // hook for subclasses + SetItem (get_pos(), newitem); +} + +const char *Item::get_kind() const +{ + return get_traits().name; +} + +string Item::get_inventory_model() +{ + return get_kind(); +} + +void Item::init_model() +{ + const ItemTraits &tr = get_traits(); + if (tr.flags & itf_invisible) + set_model("invisible"); + else if (tr.flags & itf_animation) + set_anim(tr.name); + else + set_model(tr.name); +} + +void Item::stone_change (Stone * /*st*/) { +} + +void Item::on_stonehit (Stone * /*st*/) { +} + +void Item::on_laserhit(Direction) +{ + if (get_traits().flags & itf_inflammable) + replace (it_explosion1); +} + + +void Item::on_drop (Actor * /*a*/) { +} + +void Item::drop (Actor *a, GridPos p) { + SetItem (p, this); + on_drop(a); +} + + +void Item::on_pickup (Actor * /*a*/) { +} + +bool Item::can_drop_at (GridPos p) { + return GetItem(p) == 0; +} + +ItemAction Item::activate(Actor* /*a*/, GridPos /*p*/) { + return ITEM_DROP; +} + +void Item::add_force(Actor *, V2 &) { +} + +bool Item::actor_hit(Actor *actor) +{ + const ItemTraits &tr = get_traits(); + if (tr.flags & itf_static) + return false; + else { + double radius = 0.3; + if (tr.radius != 0.0) + radius = tr.radius; + return length(actor->get_pos()-get_pos().center()) < radius; + } +} + + +/* -------------------- OnOffItem -------------------- */ +namespace +{ + class OnOffItem : public Item { + protected: + OnOffItem (bool onoff = false) + { + set_attrib("on", onoff); + } + + bool is_on() const { + return int_attrib("on") == 1; + } + + void set_on (bool newon) { + if (newon != is_on()) { + set_attrib("on", Value(newon)); + init_model(); + notify_onoff(newon); + } + } + + virtual Value message(const string &m, const Value &val) { + if (m=="onoff") + set_on(!is_on()); + else if (m=="signal") + set_on (to_int(val) != 0); + else if (m == "on") + set_on(true); + else if (m=="off") + set_on(false); + return Value(); + } + + // OnOffItem interface + virtual void notify_onoff (bool /*on*/) {} + }; +} + + + +/* -------------------- Various simple items -------------------- */ + +namespace +{ + DEF_ITEM(MagicWand, "it-magicwand", it_magicwand); + DEF_ITEM(Floppy, "it-floppy", it_floppy); + DEF_ITEM(Odometer, "it-odometer", it_odometer); + DEF_ITEM(Wrench, "it-wrench", it_wrench); + DEF_ITEM(BrokenGlasses, "it-glasses-broken", it_glasses_broken); + DEF_ITEMF(Coffee, "it-coffee", it_coffee, itf_inflammable); +} + + +/* -------------------- DummyItem -------------------- */ +namespace +{ + class Dummyitem : public Item { + CLONEOBJ(Dummyitem); + DECL_TRAITS; + + void on_pickup(Actor *) { + int code = int_attrib("code"); + fprintf(stderr, "Picked up item 0x%x\n", code); + } + void on_drop(Actor *) { + int code = int_attrib("code"); + fprintf(stderr, "Dropped item 0x%x\n", code); + } + public: + Dummyitem() {} + }; + DEF_TRAITSF(Dummyitem, "it-dummy", it_dummy, itf_fireproof); + +/* -------------------- Cherry -------------------- */ + + class Cherry : public Item { + CLONEOBJ(Cherry); + DECL_TRAITS; + ItemAction activate(Actor *actor, GridPos) { + SendMessage (actor, "invisibility"); + return ITEM_KILL; + } + + void on_stonehit(Stone *) { + replace(it_squashed); + } + public: + Cherry() { + } + }; + DEF_TRAITS(Cherry, "it-cherry", it_cherry); + +/* -------------------- Squashed Cherry -------------------- */ + + class Squashed : public Item { + CLONEOBJ(Squashed); + DECL_TRAITS; + + virtual Value on_message (const Message &m) { + if (enigma_server::GameCompatibility == GAMET_ENIGMA) { + if (m.message == "brush") + KillItem(this->get_pos()); + } + return Value(); + } + + + public: + Squashed() { + } + }; + DEF_TRAITSF(Squashed, "it-squashed", it_squashed, itf_static); + + +/* -------------------- Weight -------------------- */ + + class Weight : public Item { + CLONEOBJ(Weight); + DECL_TRAITS; + + void on_pickup(Actor *a) { + ActorInfo *ai = a->get_actorinfo(); + ai->mass += 10.0; + } + ItemAction activate(Actor *, GridPos) { + return ITEM_KEEP; + } + public: + Weight() {} + }; + DEF_TRAITS(Weight, "it-weight", it_weight); + +/* -------------------- Pin -------------------- */ + + class Pin : public Item { + CLONEOBJ(Pin); + DECL_TRAITS; + + void on_pickup(Actor *a) { + a->set_spikes(true); + } + void on_drop(Actor *a) { + a->set_spikes(false); + } + public: + Pin() {} + }; + DEF_TRAITS(Pin, "it-pin", it_pin); + +/* -------------------- Banana -------------------- */ + + class Banana : public Item { + CLONEOBJ(Banana); + DECL_TRAITS; + + void on_laserhit(Direction /*d*/) { + sound_event ("itemtransform"); + replace(it_cherry); + } + + void on_stonehit(Stone *) { + replace(it_squashed); + } + + public: + Banana() {} + }; + DEF_TRAITS(Banana, "it-banana", it_banana); + +/* -------------------- Sword -------------------- */ + + class Sword : public Item { + CLONEOBJ(Sword); + DECL_TRAITS; + + void on_laserhit(Direction /*d*/) { + sound_event ("itemtransform"); + replace(it_hammer); + } + public: + Sword() {} + }; + DEF_TRAITS(Sword, "it-sword", it_sword); + +/* -------------------- Hammer -------------------- */ + + class Hammer : public Item { + CLONEOBJ(Hammer); + DECL_TRAITS; + + void on_laserhit(Direction /*d*/) { + if (server::GameCompatibility != enigma::GAMET_PEROXYD) { + sound_event ("itemtransform"); + replace(it_sword); + } + } + public: + Hammer() {} + }; + DEF_TRAITS(Hammer, "it-hammer", it_hammer); +} + +/* -------------------- ExtraLife -------------------- */ +namespace +{ + class ExtraLife : public Item { + CLONEOBJ(ExtraLife); + DECL_TRAITS; + std::string get_inventory_model() { + if (player::CurrentPlayer()==0) + return "inv-blackball"; + else + return "inv-whiteball"; + } + + void on_laserhit(Direction /*d*/) { + sound_event ("itemtransform"); + replace (it_glasses); + } + + public: + ExtraLife() {} + }; + DEF_TRAITS(ExtraLife, "it-extralife", it_extralife); +} + +/* -------------------- Umbrella -------------------- */ +namespace +{ + class Umbrella : public Item { + CLONEOBJ(Umbrella); + DECL_TRAITS; + void on_laserhit (Direction) { + if (server::GameCompatibility != enigma::GAMET_PEROXYD) + replace(it_explosion1); + } + ItemAction activate(Actor *a, GridPos) { + SendMessage(a, "shield"); + return ITEM_KILL; + } + public: + Umbrella() {} + }; + DEF_TRAITS (Umbrella, "it-umbrella", it_umbrella); +} + +/* -------------------- Spoon -------------------- */ +namespace +{ + class Spoon : public Item { + CLONEOBJ(Spoon); + DECL_TRAITS; + + ItemAction activate(Actor *a, GridPos) { + SendMessage(a, "suicide"); + return ITEM_DROP; + } + public: + Spoon() + {} + }; + DEF_TRAITS(Spoon, "it-spoon", it_spoon); +} + +/* -------------------- Keys -------------------- */ +namespace +{ + class Key : public Item { + CLONEOBJ(Key); + DECL_TRAITS_ARRAY(3, subtype); + + virtual Value message (const string &msg, const Value &) { + if (msg == "init") { + // Oxyd uses signals from keys to key switches to + // determine which keys activate which key hole. + GridPos keystonepos; + for (int idx=0; GetSignalTargetPos (this, keystonepos, idx); ++idx) { + Stone *st = GetStone(keystonepos); + if (st && st->is_kind("st-key")) + st->set_attrib("keycode", + int_attrib("keycode")); + } + } + return Value(); + } + + public: + enum SubType { KEY1, KEY2, KEY3 } subtype; + Key(SubType type = KEY1) + : subtype(type) + { + set_attrib("keycode", subtype+1); + } + }; + + ItemTraits Key::traits[3] = { + {"it-key_a", it_key_a, itf_none, 0.0}, + {"it-key_b", it_key_b, itf_none, 0.0}, + {"it-key_c", it_key_c, itf_none, 0.0} + }; +} + +/* -------------------- Booze -------------------- */ + +namespace +{ + class Booze : public Item { + CLONEOBJ(Booze); + DECL_TRAITS; + public: + Booze() { + } + private: + ItemAction activate(Actor *a, GridPos) { + SendMessage(a, "booze"); + return ITEM_DROP; + } + void on_stonehit(Stone *) { + sound_event("shatter"); + replace(it_booze_broken); + } + }; + DEF_TRAITS(Booze, "it-booze", it_booze); +} + +/* -------------------- Broken Booze -------------------- */ +namespace +{ + class BrokenBooze : public Item { + CLONEOBJ(BrokenBooze); + DECL_TRAITS; + + bool actor_hit(Actor *a) { + ActorInfo &ai = * a->get_actorinfo(); + if (!ai.grabbed && a->is_on_floor()) { + SendMessage(a, "shatter"); + } + return false; + } + + virtual Value on_message (const Message &m) { + if (enigma_server::GameCompatibility == GAMET_ENIGMA) { + if (m.message == "brush") + KillItem(this->get_pos()); + } + return Value(); + } + + public: + BrokenBooze() {} + }; + + DEF_TRAITSF(BrokenBooze, "it-booze-broken", it_booze_broken, itf_static | itf_indestructible); +} + +/* -------------------- Brush -------------------- */ +namespace +{ + /* Can "paint" some stones and remove ash. */ + class Brush : public Item { + CLONEOBJ(Brush); + DECL_TRAITS; + + ItemAction activate(Actor *, GridPos p) { + if (Item *it = GetItem(p)) + SendMessage (it, "brush"); + return ITEM_DROP; + } + public: + Brush() {} + }; + DEF_TRAITSF(Brush, "it-brush", it_brush, itf_inflammable); +} + + +/* -------------------- Coins -------------------- */ + +// :value 1,2,4: how many time-units this coin buys +namespace +{ + class Coin1 : public Item { + CLONEOBJ(Coin1); + DECL_TRAITS; + + void on_laserhit (Direction) { + sound_event ("itemtransform"); + replace (it_umbrella); + } + + void on_stonehit(Stone *) { + replace(it_coin2); + } + + public: + Coin1() { + set_attrib ("value", 3.0); + } + }; + DEF_TRAITS(Coin1, "it-coin1", it_coin1); + + class Coin2 : public Item { + CLONEOBJ(Coin2); + DECL_TRAITS; + + void on_laserhit (Direction) { + sound_event ("itemtransform"); + replace (it_hammer); + } + + void on_stonehit(Stone *) { + replace(it_coin4); + } + + public: + Coin2() { + set_attrib("value", 6.0); + } + }; + DEF_TRAITS(Coin2, "it-coin2", it_coin2); + + class Coin4 : public Item { + CLONEOBJ(Coin4); + DECL_TRAITS; + + void on_laserhit (Direction) { + sound_event ("itemtransform"); + replace (it_extralife); + } + public: + Coin4() { + set_attrib("value", 12.0); + } + }; + DEF_TRAITS(Coin4, "it-coin4", it_coin4); +} + + +/* -------------------- Hills and Hollows -------------------- */ + +/** \page it-hills Hills and Hollows + +Hills and hollows are placed on the floor and can +make the movement difficult. + +\subsection hillsm Messages +- \b trigger will convert a hill to a hollow and vice versa +- \b shovel decreases the size of the hill/hollow + +\image html it-hill.png +*/ +namespace +{ + class HillHollow : public Item { + public: + // Object interface. + virtual Value message(const string &m, const Value &); + protected: + enum Type { HILL, HOLLOW, TINYHILL, TINYHOLLOW }; + + HillHollow(Type t); + + void transmute(Type newtype); + V2 vec_to_center (V2 v); + double get_radius() const { return m_radius[m_type]; } + + Type get_type() const { return m_type; } + + private: + double get_forcefac() const { + return m_forcefac[m_type] * server::HoleForce; + } + void shovel(); + + // Item interface + void add_force(Actor *a, V2 &f); + void on_stonehit(Stone *st); + + + // Variables. + static double m_radius[4], m_forcefac[4]; + Type m_type; + }; + + class Hill : public HillHollow { + CLONEOBJ(Hill); + DECL_TRAITS; + public: + Hill() : HillHollow(HILL) {} + }; + DEF_TRAITSF(Hill, "it-hill", it_hill, itf_static | itf_fireproof); + + class TinyHill : public HillHollow { + CLONEOBJ(TinyHill); + DECL_TRAITS; + public: + TinyHill() : HillHollow(TINYHILL) {} + }; + DEF_TRAITSF(TinyHill, "it-tinyhill", it_tinyhill, itf_static | itf_fireproof); + + /* + * Hollows are special in that they can end the current level + * if they have each a small white marble inside them. + */ + class Hollow : public HillHollow { + DECL_TRAITS; + public: + Hollow(Type t = HOLLOW); + protected: + INSTANCELISTOBJ(Hollow); // TinyHollow needs access + virtual void setup_successor(Item *newitem); + private: + // Item interface. + bool actor_hit(Actor *a); + void actor_leave(Actor *a); + + // Functions. + bool near_center_p (Actor *a); + void check_if_level_finished(); + + // Variables. + Actor *whiteball; // The small white ball that is currently being tracked + double enter_time; // ... when did it enter the hollow? + }; + DEF_TRAITSF(Hollow, "it-hollow", it_hollow, itf_static | itf_fireproof); + + + class TinyHollow : public Hollow { + TinyHollow *clone() { + TinyHollow *o = new TinyHollow(*this); + instances.push_back(o); + return o; + } + void dispose() { + instances.erase(find(instances.begin(), instances.end(), this)); + delete this; + } + DECL_TRAITS; + public: + TinyHollow() : Hollow(TINYHOLLOW) {} + }; + DEF_TRAITSF(TinyHollow, "it-tinyhollow", it_tinyhollow, itf_static | itf_fireproof); + +} + + +/* ---------- HillHollow ---------- */ + +double HillHollow::m_radius[4] = {0.5, 0.5, 0.3, 0.3}; +double HillHollow::m_forcefac[4] = {90,-90, 90, -90}; + + +HillHollow::HillHollow (Type t) +: m_type(t) +{} + +void HillHollow::on_stonehit(Stone *) +{ + shovel(); +} + +void HillHollow::shovel() { + switch (get_id (this)) { + case it_hollow: transmute (TINYHOLLOW); break; + case it_hill: transmute (TINYHILL); break; + default: kill(); break; + } +} + +Value HillHollow::message(const string &m, const Value &val) +{ + if (m=="trigger") { + Type flippedkind[] = {HOLLOW,HILL, TINYHOLLOW,TINYHILL}; + transmute(flippedkind[m_type]); + } + else if (m == "signal") { + if (to_double(val) != 0) { + Type flippedkind[] = {HILL,HILL, TINYHILL,TINYHILL}; + transmute(flippedkind[m_type]); + } else { + Type flippedkind[] = {HOLLOW,HOLLOW, TINYHOLLOW,TINYHOLLOW}; + transmute(flippedkind[m_type]); + } + } + else if (m=="shovel") + shovel(); + else + Item::message (m, val); + return Value(); +} + +V2 HillHollow::vec_to_center (V2 v) +{ + return v-get_pos().center(); +} + +void HillHollow::add_force(Actor *a, V2 &f) +{ + V2 v = vec_to_center(a->get_pos()); + double dist = length(v); + + if (dist > get_radius()) + return; + + if (dist <= 0) { // exactly on hill-top + ActorInfo *ai = a->get_actorinfo(); + if (length(ai->vel) <= 0) { // no velocity + // we are never "exactly" on the top! + v = ecl::V2(DoubleRand(0.01, 0.05), DoubleRand(0.01, 0.05)); + } + } + + f += get_forcefac()*v; // get the force +} + +void HillHollow::transmute(Type newtype) +{ + static ItemID newmodel[] = { it_hill, it_hollow, it_tinyhill, it_tinyhollow }; + replace (newmodel[newtype]); +} + + +/* ---------- Hollow ---------- */ + +Hollow::InstanceList Hollow::instances; + +Hollow::Hollow(Type t) +: HillHollow(t), whiteball(0) +{} + +bool Hollow::near_center_p (Actor *a) +{ + return (length(vec_to_center(a->get_pos())) < get_radius()*0.8); +} + +bool Hollow::actor_hit(Actor *a) +{ + ItemID id = get_id (this); + + if (id == it_hollow || id == it_tinyhollow) { + if (whiteball==0 && get_id(a)==ac_meditation && near_center_p(a)) + { + whiteball = a; + enter_time = server::LevelTime; + } + else if (whiteball==a) { + if (!near_center_p(a)) + whiteball = 0; + else + check_if_level_finished(); + } + } + + return false; +} + +void Hollow::actor_leave(Actor *) +{ + whiteball = 0; +} + +/* If (a) every small white ball is in a hollow and (b) each ball has + been inside the hollow for at least MINTIME milliseconds, finish + the level. */ +void Hollow::check_if_level_finished() +{ + const double MINTIME = 1.0; + + unsigned wcnt = 0; // counts normal hollows with whiteball + unsigned ess_wcnt = 0; // counts essential hollows with whiteball + unsigned ess_cnt = 0; // counts all essential hollows + + for (Hollow::InstanceList::const_iterator hi = instances.begin(); + hi != instances.end(); ++hi) + { + const Hollow& h = **hi; + bool essential = h.int_attrib("essential") != 0; + + if (h.whiteball && (server::LevelTime - h.enter_time) >= MINTIME) { + if (essential) ess_wcnt++; + else wcnt++; + } + + if (essential) ess_cnt++; + } + + if (ess_cnt == ess_wcnt && + (wcnt+ess_wcnt) == CountActorsOfKind (world::ac_meditation)) + { + server::FinishLevel(); + } +} + +void Hollow::setup_successor(Item *newitem) { + const Value *essential = get_attrib("essential"); + if (essential != NULL) { + newitem->set_attrib("essential",*essential); + } +} + + +/* -------------------- Springs -------------------- */ + +/** \page it-spring Spring + +Activating a spring will make the marble jump. +A jumping marble does not fall into abyss or water. + +Springs come in two flavors: it-spring1 stays in the inventory, +whereas it-spring2 drops to the floor when you activate it. + +\image html it-spring1.png +*/ +namespace +{ + class Spring1 : public Item { + CLONEOBJ(Spring1); + DECL_TRAITS; + public: + Spring1() {} + private: + ItemAction activate(Actor *a, GridPos) + { + SendMessage(a, "jump"); + return ITEM_KEEP; + } + }; + DEF_TRAITS(Spring1, "it-spring1", it_spring1); + + class Spring2 : public Item { + CLONEOBJ(Spring2); + DECL_TRAITS; + public: + Spring2() {} + private: + ItemAction activate(Actor *a, GridPos p) + { + Item *it = GetItem(p); + if (!it || has_flags(it, itf_static)) { + SendMessage(a, "jump"); + return ITEM_DROP; // drop if grid has no item + } else { + // don't jump if a regular item is on the grid + return ITEM_KEEP; + } + } + }; + DEF_TRAITS(Spring2, "it-spring2", it_spring2); +} + + +/* -------------------- Springboard -------------------- */ +namespace +{ + class Springboard : public Item { + CLONEOBJ(Springboard); + DECL_TRAITS; + + bool actor_hit(Actor *a) { + const double ITEM_RADIUS = 0.3; + ecl::V2 item_center(get_pos().x + 0.5, get_pos().y + 0.5); + double dist = length(a->get_pos() - item_center); + if (dist < ITEM_RADIUS) { + set_anim("it-springboard_anim"); + SendMessage(a, "jump"); + } + return false; + } + + void animcb() { + set_model("it-springboard"); + } + public: + Springboard() {} + }; + DEF_TRAITSF(Springboard, "it-springboard", it_springboard, itf_static); +} + + +/* -------------------- Brake -------------------- */ + +// Brake is only used to hold st-brake while it is in an inventory +namespace +{ + class Brake : public Item { + CLONEOBJ(Brake); + DECL_TRAITS; + public: + Brake() {} + + void on_creation (GridPos p) { + SetStone(p, MakeStone("st-brake")); + kill(); + } + + bool can_drop_at (GridPos p) { + return GetStone(p) == 0; + } + + void drop (Actor *, GridPos p) { + SetStone(p, MakeStone("st-brake")); + kill(); + } + + string get_inventory_model() { + return "st-brake"; + } + + ItemAction activate(Actor *, GridPos) { + return ITEM_DROP; + } + }; + DEF_TRAITS(Brake, "it-brake", it_brake); +} + + +/* -------------------- Explosion -------------------- */ +namespace +{ + class Explosion : public Item { + public: + Explosion () + {} + + private: + void init_model() {set_anim("expl");} + bool actor_hit(Actor *actor) { + SendMessage(actor, "shatter"); + return false; + } + }; + + // Explode but do nothing else. + class Explosion1 : public Explosion { + CLONEOBJ(Explosion1); + DECL_TRAITS; + + void animcb() { + kill(); + } + public: + Explosion1() + {} + }; + DEF_TRAITSF(Explosion1, "it-explosion1", it_explosion1, itf_static | + itf_animation | itf_indestructible | itf_norespawn | itf_fireproof); + + // Explode and leave a hole in the ground. + class Explosion2 : public Explosion { + CLONEOBJ(Explosion2); + DECL_TRAITS; + + void animcb() { + if (Floor *fl = GetFloor(get_pos())) + if (fl->is_destructible()) + replace(it_hollow); + else + kill(); + } + public: + Explosion2() + {} + }; + DEF_TRAITSF(Explosion2, "it-explosion2", it_explosion2, itf_static | + itf_animation | itf_indestructible | itf_norespawn | itf_fireproof); + + + // Explode and shatter the floor. + class Explosion3 : public Explosion { + CLONEOBJ(Explosion3); + DECL_TRAITS; + + void animcb() { + if (Floor *fl = GetFloor(get_pos())) + if (fl->is_destructible()) + replace(it_debris); + else + kill(); + } + public: + Explosion3() + {} + }; + DEF_TRAITSF(Explosion3, "it-explosion3", it_explosion3, itf_static | + itf_animation | itf_indestructible | itf_norespawn | itf_fireproof); +} + + + + +/* -------------------- Document -------------------- */ +namespace +{ + class Document : public Item { + CLONEOBJ(Document); + DECL_TRAITS; + + ItemAction activate(Actor *, GridPos) + { + string txt; + if (string_attrib ("text", &txt)) { + lev::Proxy *level = lev::Proxy::loadedLevel(); + // after complete switch to Proxy as levelloader the following + // conditional can be abolished + if (level) + // translate text + txt = level->getLocalizedString(txt); + client::Msg_ShowText (txt, true); + } + return ITEM_KILL; // remove from inventory + } + virtual Value message(const string &msg, const Value &/*val*/) { + bool explode = false; + + if (msg == "ignite") { + // dynamite does not blow up Documents in Oxyd1 + explode = server::GameCompatibility != GAMET_OXYD1; + } + else if (msg == "expl" || msg == "bombstone") + explode = true; + + if (explode) + replace (it_explosion1); + return Value(); + } + public: + Document() { + set_attrib("text", ""); + } + }; + DEF_TRAITSF(Document, "it-document", it_document, itf_inflammable); +} + + +/* -------------------- Dynamite -------------------- */ +namespace +{ + class Dynamite : public Item { + CLONEOBJ(Dynamite); + DECL_TRAITS; + public: + Dynamite() : state(IDLE) {} + private: + enum State { IDLE, BURNING }; + State state; + + void change_state(State newstate) { + if (newstate==BURNING && state==IDLE) { + state = BURNING; + set_anim("it-dynamite-burning"); + } + } + + void explode () { + GridPos p = get_pos(); + SendExplosionEffect(p, EXPLOSION_DYNAMITE); + sound_event ("dynamite"); + //SetItem(p, it_explosion2); + Floor *fl = GetFloor(p); + string model = fl->get_kind(); + // SetItem(p, it_explosion2) only used by it-dynamite? + // If Yes, the following block could be places in the explosion class: + if (model == "fl-space") { + // In space, an it-dynamite explodes to an it-sherd: + // HOT FIX + //replace(it_sherd); + replace(it_hollow); + } else if (model == "fl-ice") { + // In ice, an it-dynamite explodes to an it-crack2: + replace(it_crack2); + } else { + SetItem(p, it_explosion2); + } + } + + virtual Value message(const string &msg, const Value &/*val*/) { + if (msg == "ignite" || msg == "expl" || msg == "bombstone") + change_state(BURNING); + else if (msg == "explode") // currently unused in c++ code + explode(); + else if (msg == "heat") { // used by fire-system + change_state(BURNING); + return Value(1.0); // caught message -> no fire! + } + return Value(); + } + void animcb() { explode(); } + void on_laserhit(Direction) { + change_state(BURNING); + } + void on_drop(Actor *) { change_state(BURNING); } + bool actor_hit(Actor *a) { + if (state == BURNING) + return false; // don't pick up burning dynamite + + return Item::actor_hit(a); + } + }; + DEF_TRAITSF(Dynamite, "it-dynamite", it_dynamite, + itf_indestructible | itf_fireproof); +} + +// ---------------------------- +// BombBase +// ---------------------------- +// base class for BlackBomb and WhiteBomb + +namespace +{ + class BombBase : public Item { + public: + BombBase (bool burning = false) + : m_burning(burning) + {} + + protected: + virtual Value message(const string &msg, const Value &) { + if (msg == "ignite" || msg == "expl") + burn(); + else if (msg == "explode" ) + explode(); + else if (msg == "heat") { // used by fire-system + burn(); + return Value(1.0); // caught message -> no fire! + } + return Value(); + } + + private: + // Variables + bool m_burning; + + // Private methods + virtual void explode() = 0; + + void init_model() { + if (m_burning) + set_anim(burn_anim()); + else + Item::init_model(); + } + + void burn() { + if (!m_burning) { + m_burning = true; + init_model(); + } + } + + void animcb() { explode (); } + + void on_laserhit(Direction) { + explode(); + } + + void on_stonehit(Stone *st) { + switch (server::GameCompatibility) { + case GAMET_OXYD1: + case GAMET_OXYDMAGNUM: + if (!st->is_kind("st-wood?")) + // st-wood does not cause bombs to explode + explode(); + break; + default : + explode(); + break; + } + } + + virtual const char *burn_anim() const = 0; + }; +} + +/* -------------------- Black Bomb -------------------- */ + +/** \page it-blackbomb Black Bomb + +When black bombs explode, they destroy the floor tile underneath them. + +\image html it-blackbomb.png +*/ + +namespace +{ + class BlackBomb : public BombBase { + CLONEOBJ(BlackBomb); + DECL_TRAITS; + public: + BlackBomb (bool burning=false) + : BombBase(burning) + {} + private: + const char *burn_anim() const { return "it-blackbomb-burning"; } + void explode() { + GridPos p = get_pos(); + sound_event ("blackbomb"); + SendExplosionEffect(p, EXPLOSION_BLACKBOMB); + replace (it_explosion3); + } + }; + DEF_TRAITSF(BlackBomb, "it-blackbomb", it_blackbomb, + itf_static | itf_indestructible | itf_fireproof); + + class BlackBombBurning : public BlackBomb { + CLONEOBJ(BlackBombBurning); + DECL_TRAITS; + public: + BlackBombBurning() : BlackBomb(true) {} + }; + DEF_TRAITSF(BlackBombBurning, "it-blackbomb_burning", + it_blackbomb_burning, + itf_static | itf_indestructible | itf_norespawn | itf_fireproof); +} + + +/* -------------------- White bomb -------------------- */ + +/*! When a white bombs explode, they destroy the floor tile underneath +them and neighboring floors. */ + +namespace +{ + class WhiteBomb : public BombBase { + CLONEOBJ(WhiteBomb); + DECL_TRAITS; + + const char *burn_anim() const { return "it-whitebomb-burning"; } + void explode() { + GridPos p = get_pos(); + sound_event ("whitebomb"); + replace (it_explosion3); + SendExplosionEffect(p, EXPLOSION_WHITEBOMB); + } + + public: + WhiteBomb() + {} + }; + DEF_TRAITSF(WhiteBomb, "it-whitebomb", it_whitebomb, + itf_static | itf_indestructible | itf_fireproof); +} + + +/* -------------------- Trigger -------------------- */ +namespace +{ + class Trigger : public Item { + CLONEOBJ(Trigger); + DECL_TRAITS; + public: + Trigger(); + private: + // Variables + bool m_pressedp; + int m_actorcount; + + // Methods + void update_state(); + + // Item interface + void init_model(); + void actor_enter(Actor *) { m_actorcount += 1; update_state(); } + void actor_leave(Actor *) { m_actorcount -= 1; update_state(); } + void stone_change(Stone *) { update_state(); } + + virtual Value on_message (const Message &m) { + if (m.message == "signal") { + PerformAction (this, to_double (m.value) != 0.0); + } + else if (m.message == "init") { + update_state(); + } + return Value(); + } + }; + + DEF_TRAITSF(Trigger, "it-trigger", it_trigger, + itf_static | itf_indestructible); +} + +Trigger::Trigger() +{ + m_pressedp = false; + m_actorcount = 0; + set_attrib("invisible", 0.0); +} + +void Trigger::init_model() +{ + if (int_attrib("invisible")) + set_model("invisible"); + else if (m_pressedp) + set_model("it-trigger1"); + else + set_model("it-trigger"); +} + +void Trigger::update_state() +{ + Stone *st = GetStone(get_pos()); + // Hack to make tunnel puzzle stones press triggers + bool stone_pressure = st && (!st->is_floating() || st->is_kind ("st-puzzle*")); + bool pressedp = stone_pressure || (m_actorcount > 0); + + if (m_pressedp != pressedp) { + m_pressedp = pressedp; + + init_model(); + if (m_pressedp) { + sound_event ("triggerdown"); + world::PerformAction(this, true); + } else { + sound_event ("triggerup"); + world::PerformAction(this, false); + } + } +} + + +/* -------------------- Seed -------------------- */ +namespace +{ + class Seed : public Item { + bool activep; + + bool actor_hit(Actor *a) { + if (activep) + return false; // do not pickup growing seed + return Item::actor_hit(a); + } + void on_drop (Actor *) {start_growing();} + void on_stonehit (Stone *) {start_growing();} + void on_laserhit (Direction) {start_growing();} + + virtual Value message(const string &msg, const Value &) { + if (msg == "grow" || msg == "signal") { + start_growing(); + } + return Value(); + } + + void start_growing() { + if (!activep) { + activep = true; + sound_event ("seedgrow"); + set_anim("it-seed-growing"); + } + } + + void animcb() { + GridPos p= get_pos(); + if ((server::GameCompatibility == GAMET_OXYDMAGNUM || server::GameCompatibility == GAMET_OXYD1) && + (!strcmp(get_stone_name(),"st-wood-growing") && GetStone(p))) { + string model = GetStone(p)->get_kind(); + if (model == "st-grate1") { + SetFloor(p, MakeFloor("fl-stwood")); + kill(); + return; + } + } + Stone *st = world::MakeStone (get_stone_name()); + TransferObjectName (this, st); + world::SetStone (p, st); + kill(); + } + + virtual const char* get_stone_name() = 0; + public: + Seed () + : activep(false) + {} + }; + + class SeedWood : public Seed { + CLONEOBJ(SeedWood); + DECL_TRAITS; + + const char* get_stone_name() { + return "st-wood-growing"; + } + + public: + SeedWood() + {} + }; + DEF_TRAITSR(SeedWood, "it-seed", it_seed, 0.2f); + + class SeedNowood : public Seed { + CLONEOBJ(SeedNowood); + DECL_TRAITS; + + const char* get_stone_name() { + return "st-greenbrown-growing"; + } + public: + SeedNowood() + {} + }; + DEF_TRAITSR(SeedNowood, "it-seed_nowood", it_seed_nowood, 0.2f); + + class SeedVolcano : public Seed { + CLONEOBJ(SeedVolcano); + DECL_TRAITS; + + const char* get_stone_name() { + return "st-volcano-growing"; + } + public: + SeedVolcano() + {} + }; + DEF_TRAITSR(SeedVolcano, "it-seed_volcano", it_seed_volcano, 0.2f); +} + + +/* -------------------- Shogun dot -------------------- */ + +/** \page it-shogun Shogun Dot + +\subsection shogundota Attributes +- \b size: 1..3 (smallest..largest) +- \b target, \b action: as usual +*/ +namespace +{ + class ShogunDot : public Item { + CLONEOBJ(ShogunDot); + DECL_TRAITS_ARRAY(3, subtype); + public: + static void setup() { + RegisterItem (new ShogunDot(SMALL)); + RegisterItem (new ShogunDot(MEDIUM)); + RegisterItem (new ShogunDot(LARGE)); + } + private: + enum SubType { SMALL, MEDIUM, LARGE }; + ShogunDot(SubType size); + + virtual Value message(const string &str, const Value &v); + void stone_change(Stone *st); + + // Variables. + bool activated; + SubType subtype; + }; + + ItemTraits ShogunDot::traits[3] = { + { "it-shogun-s", it_shogun_s, itf_static, 0.0 }, + { "it-shogun-m", it_shogun_m, itf_static, 0.0 }, + { "it-shogun-l", it_shogun_l, itf_static, 0.0 } + }; +} + +ShogunDot::ShogunDot (SubType size) +: activated(false), subtype(size) +{ +} + +void ShogunDot::stone_change(Stone *st) { + if (activated) { + if (st == 0) { + warning("stone_change: Stone disappeared w/o sending me a proper message!"); + } + } +} + +Value ShogunDot::message(const string &str, const Value &/*v*/) { + if (str == "noshogun") { + if (activated) { + activated = false; + world::PerformAction(this, false); + } + } + else { + const char *s = str.c_str(); + bool size_matches = + (strncmp(s, "shogun", 6) == 0) && + ((s[6]-'1') == subtype) && + (s[7] == 0); + + if (size_matches != activated) { + activated = size_matches; + world::PerformAction(this, activated); + } + } + return Value(); +} + + +/* -------------------- Magnet -------------------- */ +namespace +{ + class Magnet : public OnOffItem { + class Magnet_FF : public world::ForceField { + public: + Magnet_FF() + : m_active(false), strength(30), range(1000) + {} + + void set_pos(GridPos p) { center = p.center(); } + void set_range(double r) { range = r; } + void set_strength(double s) { strength = s; } + + void add_force(Actor *a, V2 &f) { + if (m_active) { + V2 dv = center - a->get_pos(); + double dist = length(dv); + + if (dist >= 0.2 && dist < range) + f += 0.6* strength * dv / (dist*dist); + } + } + + bool m_active; + V2 center; + double strength; + double range; + }; + + CLONEOBJ(Magnet); + DECL_TRAITS_ARRAY(2, is_on()); + public: + Magnet(bool onoff) : OnOffItem (onoff) { + set_attrib ("strength", Value()); // 30.0 + set_attrib ("range", Value()); + } + private: + void on_creation (GridPos p) { + double range = server::MagnetRange; + double_attrib("range", &range); + + double strength = server::MagnetForce; + double_attrib("strength", &strength); + + ff.m_active = is_on(); + ff.set_pos (p); + ff.set_range (range); + ff.set_strength (strength); + + world::AddForceField(&ff); + Item::on_creation (p); + } + void on_removal (GridPos p) { + Item::on_removal(p); + world::RemoveForceField(&ff); + } + + virtual void notify_onoff(bool on) { + ff.m_active = on; + } + + + Magnet_FF ff; + }; + + ItemTraits Magnet::traits[2] = { + { "it-magnet-off", it_magnet_off, itf_static | itf_indestructible, 0.0 }, + { "it-magnet-on", it_magnet_on, itf_static | itf_indestructible, 0.0 }, + }; +} + + +/* -------------------- Wormhole -------------------- */ + +/** \page it-wormhole Worm hole + +Worm holes teleport actors to another place. They have a variable +attracting force field. + +\subsection wormholea Attributes +- \b targetx: X coordinate of the destination +- \b targety: Y coordinate of the destination +- \b strength: Strength of the force field (default: 50) +- \b range: Range of the force field + +\subsection wormholee Example +\verbatim +set_item("it-wormhole", 1,1, {targetx=5.5, targety=10.5, strength=50, range=5}) +\endverbatim +*/ + +namespace +{ + class WormHole_FF : public world::ForceField { + public: + WormHole_FF() : strength(0.6 * 50), rangesquared(1000000) {} + + void set_pos(GridPos p) { center = p.center(); } + void set_range (double r) { rangesquared = r*r; } + void set_strength (double s) { strength = 0.6 * s; } + + void add_force(Actor *a, V2 &f) { + V2 b = center - a->get_pos(); + double bb = square(b); + if (bb < rangesquared && bb>0) + f += (strength/bb)*b; + } + + V2 center; // Center of the force field + double strength; // Strength of the force + double rangesquared; // Range of the force squared + }; + + class WormHole : public OnOffItem, public enigma::TimeHandler { + CLONEOBJ(WormHole); + DECL_TRAITS_ARRAY(2, is_on()); + public: + WormHole(bool onoff_) : OnOffItem(onoff_) { + set_attrib("targetx", Value()); + set_attrib("targety", Value()); + set_attrib("strength", Value()); + set_attrib("range", Value()); + set_attrib("interval", Value()); // see get_interval() for default + state = TELEPORT_IDLE; + justWarping = false; + } + + void init_model(); + bool actor_hit(Actor *a); + void notify_onoff (bool /* onoff */) { set_forcefield(); } + void alarm(); + + protected: + virtual ~WormHole() { + GameTimer.remove_alarm (this); + } + private: + enum State { TELEPORT_IDLE, TELEPORT_WAITING } state; + // Note that there're two notions of on and off for this object: + // The OnOffItem-part is only used to operate the force field, + // whereas the teleport ability is controlled by the state-variable. + // Animation is turned off when either one of them is off. + + void on_creation (GridPos p) { + Item::on_creation (p); + set_forcefield(); + } + void on_removal (GridPos p); + + void set_forcefield() { + if (is_on()) { + ff.set_pos(get_pos()); + double range = server::WormholeRange; + double_attrib("range", &range); + ff.set_range (range); + + double s = server::WormholeForce; + double_attrib("strength", &s); + ff.set_strength (s); + + world::AddForceField(&ff); + } else { + world::RemoveForceField(&ff); + } + } + + V2 vec_to_center (V2 v) {return v-get_pos().center();} + bool near_center_p (Actor *a) { + return (length(vec_to_center(a->get_pos())) < 0.5/4); + } + bool get_target (V2 &targetpos); + + double get_interval() const { + double interval = 0.0; + double_attrib("interval", &interval); + return interval; + } + + // Variables. + WormHole_FF ff; + bool justWarping; // to avoid recursions + }; + + ItemTraits WormHole::traits[2] = { + { "it-wormhole-off", it_wormhole_off, itf_static, 0.0 }, + { "it-wormhole", it_wormhole_on, itf_static, 0.0 } + }; +} + +bool WormHole::get_target(V2 &targetpos) { + V2 t; + if (double_attrib("targetx", &t[0]) && double_attrib("targety", &t[1])) { + targetpos = t; + return true; + } + else { + // no target attributes -> search for signal + GridPos p; + if (GetSignalTargetPos(this, p)) { + targetpos = p.center(); + return true; + } + else { + warning("no target attributes and no signal found"); + return false; + } + } +} + +bool WormHole::actor_hit(Actor *actor) +{ + ASSERT(!justWarping, XLevelRuntime, "WormHole:: Recursion detected!"); + if (state == TELEPORT_IDLE && near_center_p(actor)) { + client::Msg_Sparkle (get_pos().center()); + V2 targetpos; + if (get_target (targetpos)) { + sound_event ("warp"); + if(get_interval() > 0) { + state = TELEPORT_WAITING; + GameTimer.set_alarm(this, get_interval(), false); + init_model(); + } + justWarping = true; + world::WarpActor(actor, targetpos[0], targetpos[1], false); + justWarping = false; + } + } + return false; +} + +void WormHole::alarm() { + state = TELEPORT_IDLE; + init_model(); +} + +void WormHole::init_model() { + if(state == TELEPORT_IDLE) + OnOffItem::init_model(); + else + set_anim("it-wormhole-off"); +} + +void WormHole::on_removal(GridPos p) { + world::RemoveForceField(&ff); + Item::on_removal(p); + ASSERT(!justWarping, XLevelRuntime, "Tried to kill a busy wormhole. Please use another way."); +} + +/* -------------------- Vortex -------------------- */ + +/** \page it-vortex Vortex + +Vortexes teleport actors to another place. + +They may be opened or closed. Is a vortex is closed, the actor cannot enter. + +\subsection vortexa Attributes +- \b targetx: X coordinate of the destination +- \b targety: Y coordinate of the destination + +\subsection vortexe Example +\verbatim +set_item("it-vortex-open", 1,1, {targetx=5.5, targety=10.5}) +set_item("it-vortex-closed", 3,1, {targetx=7.5, targety=10.5}) +\endverbatim + +\subsection vortexm Messages +- \b open opens the vortex +- \b close closes the vortex +- \b trigger, openclose +- \b signal signal value: 1 -> "open"; 0 -> "close" +*/ + +namespace +{ + class Vortex : public Item, public TimeHandler { + CLONEOBJ(Vortex); + DECL_TRAITS_ARRAY(2, is_open()); + public: + Vortex(bool opened); + virtual ~Vortex(); + + private: + static const double RANGE; + + // Item interface + bool actor_hit(Actor*); + void init_model(); + void animcb(); + virtual Value message(const string &msg, const Value &val); + + // TimeHandler interface + void alarm(); + + // Private methods + + V2 vec_to_center (V2 v) { + return v-get_pos().center(); + } + bool near_center_p (Actor *a) { + return length(vec_to_center(a->get_pos())) < RANGE; + } + + void open(); + void close(); + void openclose(); + + void prepare_for_warp (Actor *actor); + void emit_actor(); + + bool get_target_by_index (int idx, V2 &target); + void perform_warp(); // warp swallowed actor(s) + void warp_to(const V2 &target); + + bool is_open() const { return state == OPEN; } + + void on_removal(GridPos p); + + // Variables + enum State { + OPEN, + CLOSED, + OPENING, + CLOSING, + WARPING, + EMITTING, + SWALLOWING, + }; + + State state; + bool close_after_warp; + Actor *m_actor_being_warped; + int m_target_index; + Vortex *m_target_vortex; + }; + + ItemTraits Vortex::traits[2] = { + {"it-vortex-closed", it_vortex_closed, itf_static | itf_fireproof, 0.0}, + {"it-vortex-open", it_vortex_open, itf_static | itf_fireproof, 0.0} + }; + + const double Vortex::RANGE = 0.5/2; +} + +Vortex::Vortex(bool opened) +: state(opened ? OPEN : CLOSED), + close_after_warp(!opened), + m_actor_being_warped (0), + m_target_index (0), + m_target_vortex(0) +{ + set_attrib ("autoclose", Value()); + set_attrib ("targetx", Value()); + set_attrib ("targety", Value()); +} + +Vortex::~Vortex() { + GameTimer.remove_alarm(this); +} + +void Vortex::on_removal(GridPos p) { + Item::on_removal(p); + ASSERT(state != WARPING && state != SWALLOWING && state != EMITTING, + XLevelRuntime, "Tried to kill a busy vortex. Please use another way."); +} + +void Vortex::prepare_for_warp (Actor *actor) +{ + SendMessage(actor, "fallvortex"); + m_target_index = 0; + m_actor_being_warped = actor; + state = SWALLOWING; + + GameTimer.set_alarm(this, 0.4, false); +} + + +bool Vortex::actor_hit (Actor *actor) +{ + if (state == OPEN && near_center_p(actor) && actor->can_be_warped()) + prepare_for_warp (actor); + return false; +} + +Value Vortex::message(const string &msg, const Value &val) +{ + if (msg == "signal") { + int ival = to_int(val); + if (ival != 0) + open(); + else + close(); + } + else if (msg == "openclose" || msg == "trigger") + openclose(); + else if (msg == "open") + open(); + else if (msg == "close" || (msg == "arrival" && close_after_warp)) { + close(); + } + return Value(); +} + +void Vortex::init_model() { + switch(state) { + case WARPING: + case OPEN: + case EMITTING: + case SWALLOWING: + set_model("it-vortex-open"); + break; + case CLOSED: set_model("it-vortex-closed"); break; + case OPENING: set_anim("it-vortex-opening"); break; + case CLOSING: set_anim("it-vortex-closing"); break; + } +} + +void Vortex::animcb() { + if (state == CLOSING) { + state = CLOSED; + init_model(); + } + else if (state == OPENING) { + state = OPEN; + init_model(); + } +} + +void Vortex::open() { + if (state == CLOSING) { + state = OPENING; + get_model()->reverse(); // reverse animation + } + else if (state == CLOSED) { + state = OPENING; + sound_event ("vortexopen"); + init_model(); + } +} + +void Vortex::close() { + if (state == OPENING) { + state = CLOSING; + get_model()->reverse(); // reverse animation + } + else if (state == OPEN) { + state = CLOSING; + sound_event ("vortexclose"); + init_model(); + } +} + +void Vortex::openclose() { + if (state == OPEN || state == OPENING) + close(); + else + open(); +} + +bool Vortex::get_target_by_index (int idx, V2 &target) +{ + GridPos targetpos; + // signals take precedence over targetx, targety attributes + if (world::GetSignalTargetPos(this, targetpos, idx)) { + target = targetpos.center(); + return true; + } + // no signal defined + else if (idx == 0) { + V2 t; + if (double_attrib("targetx", &t[0]) && double_attrib("targety", &t[1])) { + target = t; + return true; + } + } + return false; +} + +void Vortex::alarm() { + if (state == WARPING) { + perform_warp(); + } else if (state == EMITTING) { + emit_actor(); + } else if (state == SWALLOWING) { + state = WARPING; + sound_event ("hitfloor"); + perform_warp(); + } else + ASSERT (0, XLevelRuntime, "Vortex: alarm called with inconsistent state"); +} + +void Vortex::emit_actor () { + V2 v(m_target_vortex->get_pos().center()); + world::WarpActor (m_actor_being_warped, v[0], v[1], false); + SendMessage (m_actor_being_warped, "rise"); + m_actor_being_warped = 0; + + state = OPEN; + if (this != m_target_vortex && close_after_warp) + close(); +} + +void Vortex::warp_to(const V2 &target) { + client::Msg_Sparkle (target); + world::WarpActor (m_actor_being_warped, target[0], target[1], false); + SendMessage (m_actor_being_warped, "appear"); + m_actor_being_warped = 0; + state = OPEN; + if (close_after_warp) + close(); +} + +void Vortex::perform_warp() +{ + if (!m_actor_being_warped) + return; + + ASSERT (state == WARPING, XLevelRuntime, + "Vortex: perform_warp called with inconsistent state"); + + V2 v_target; + + // is another target position defined? + if (get_target_by_index (m_target_index, v_target)) { + GridPos p_target(v_target); + + Vortex *v = dynamic_cast(GetItem(p_target)); + + if (v) { // Destination is also a vortex + Stone *st = GetStone(p_target); + + if (st && !st->is_floating()) { + // is destination vortex blocked? redirect + m_target_index += 1; + client::Msg_Sparkle (v_target); + world::WarpActor (m_actor_being_warped, + v_target[0], v_target[1], false); + GameTimer.set_alarm(this, 0.4, false); + } + else { + m_target_vortex = v; + + switch (v->state) { + case OPEN: + case OPENING: + // destination is open + emit_actor(); + break; + + case CLOSED: + case CLOSING: + // destination is closed + SendMessage(v, "open"); + state = EMITTING; + GameTimer.set_alarm(this, 0.4, false); + break; + case SWALLOWING: + case WARPING: + case EMITTING: + // destination is busy -> don't warp actor, emit + // it where it has started + m_target_vortex = this; + emit_actor(); + } + } + } else { + warp_to (v_target); + } + } + else { + // if no target defined, don't warp actor + m_target_vortex = this; + emit_actor(); + } +} + + +/* -------------------- YinYang item -------------------- */ +namespace +{ + class YinYang : public Item { + CLONEOBJ(YinYang); + DECL_TRAITS; + public: + YinYang() + {} + + string get_inventory_model() { + if (player::CurrentPlayer()==0) + return "it-yinyang"; + else + return "it-yanying"; + } + + ItemAction activate(Actor *, GridPos p) { + // Switch to other marble + player::SwapPlayers(); + // play_sound("switch"); // don't! wrong coordinates! + sound::EmitSoundEvent ("switchplayer", p.center()); + return ITEM_KEEP; + } + }; + DEF_TRAITS(YinYang, "it-yinyang", it_yinyang); +} + + +/* -------------------- Spade -------------------- */ +namespace +{ + class Spade : public Item { + CLONEOBJ(Spade); + DECL_TRAITS; + + ItemAction activate(Actor *, GridPos p) { + if (Item *it=GetItem(p)) { + sound::EmitSoundEvent ("spade", p.center()); + SendMessage(it, "shovel"); + return ITEM_KEEP; + } + return ITEM_DROP; + } + public: + Spade() {} + }; + DEF_TRAITS(Spade, "it-spade", it_spade); +} + + +/* -------------------- Pipes -------------------- */ +namespace +{ + class Pipe : public Item { + CLONEOBJ(Pipe); + int subtype; + DECL_TRAITS_ARRAY(10, subtype); + + Pipe(int stype) : subtype(stype) {} + virtual Value message(const string &msg, const Value &) { + if (msg == "expl") + replace (it_explosion1); + return Value(); + } + public: + static void setup() { + for (int i=0; i<10; ++i) + RegisterItem (new Pipe (i)); + } + }; + + ItemTraits Pipe::traits[] = { + {"it-pipe-e", it_pipe_e, itf_none, 0.0 }, + {"it-pipe-w", it_pipe_w, itf_none, 0.0 }, + {"it-pipe-s", it_pipe_s, itf_none, 0.0 }, + {"it-pipe-n", it_pipe_n, itf_none, 0.0 }, + {"it-pipe-es", it_pipe_es, itf_none, 0.0 }, + {"it-pipe-ne", it_pipe_ne, itf_none, 0.0 }, + {"it-pipe-sw", it_pipe_sw, itf_none, 0.0 }, + {"it-pipe-wn", it_pipe_wn, itf_none, 0.0 }, + {"it-pipe-h", it_pipe_h, itf_none, 0.0 }, + {"it-pipe-v", it_pipe_v, itf_none, 0.0 }, + }; +} + + +/* -------------------- Pullers -------------------- */ +namespace +{ + class Puller : public Item { + CLONEOBJ (Puller); + DECL_TRAITS_ARRAY(4, get_orientation()); + + bool active; + Direction m_direction; + + bool actor_hit(Actor *a) { + if (active) + return false; + return Item::actor_hit(a); + } + + void on_drop(Actor *) { activate(); } + + void activate() { + active=true; + set_anim("it-puller-active"); + sound_event ("puller"); + } + void animcb() { + Direction dir = get_orientation(); + GridPos stonepos = move(get_pos(), reverse(dir)); + + send_impulse(stonepos, dir); + sound_event ("dynamite"); + replace (it_explosion1); + } + + Direction get_orientation() const { + return m_direction; + } + + Puller(Direction dir) + : active(false), m_direction(dir) + { } + public: + static void setup() { + RegisterItem (new Puller(NORTH)); + RegisterItem (new Puller(EAST)); + RegisterItem (new Puller(SOUTH)); + RegisterItem (new Puller(WEST)); + } + }; + + ItemTraits Puller::traits[4] = { + { "it-puller-w", it_puller_w, itf_none, 0.0 }, + { "it-puller-s", it_puller_s, itf_none, 0.0 }, + { "it-puller-e", it_puller_e, itf_none, 0.0 }, + { "it-puller-n", it_puller_n, itf_none, 0.0 }, + }; +} + + +/* -------------------- Cracks -------------------- */ +namespace +{ + class Crack : public Item { + CLONEOBJ(Crack); + DECL_TRAITS_ARRAY(4, get_type()); + + public: + static void setup() { + RegisterItem (new Crack(0)); + RegisterItem (new Crack(1)); + RegisterItem (new Crack(2)); + RegisterItem (new Crack(3)); + } + + private: + Crack(int type=0) + : state(IDLE), anim_end(false) + { + set_attrib("type", type); + set_attrib("fixed", 0.0); + set_attrib("brittleness", Value()); + } + + enum State { IDLE, CRACKING1, CRACKING2 } state; + bool anim_end; + + int get_type() const { + int t = int_attrib("type"); + return ecl::Clamp(t, 0, 4); + } + bool is_fixed() const { return int_attrib("fixed") != 0; } + + void init_model() { + if (int t=get_type()) { + if (t > 3) { + state = CRACKING1; + sound_event ("floordestroy"); + set_anim("it-crack_anim1"); + //SetItem(get_pos(), MakeItem("it-debris")); + }else { + set_model(ecl::strf("it-crack%d", t)); + } + } + else + set_model("invisible"); + } + void animcb() { + if (state == CRACKING2) { + GridPos p= get_pos(); + SetFloor(p, MakeFloor("fl-abyss")); + KillItem(p); + } else { + state = CRACKING2; + set_anim("it-crack_anim2"); + } + } + + void crack(const GridPos &p) { + if (Floor *fl = GetFloor(p)) { + if (fl->is_destructible()) { + if (Item *it = GetItem(p)) + SendMessage (it, "crack"); + else if (do_crack()) + SetItem(p, it_crack0); + } + } + } + + void actor_enter(Actor *a) { + if (a->is_on_floor()) { + SendMessage(this, "crack"); + + if (get_type() <= 3) { + GridPos p = get_pos(); + crack (move(p, NORTH)); + crack (move(p, EAST)); + crack (move(p, SOUTH)); + crack (move(p, WEST)); + } + } + } + bool actor_hit(Actor *a) { + if (anim_end) + SendMessage(a, "fall"); + return false; + } + virtual Value message(const string &msg, const Value &val) { + if (msg == "crack" && state==IDLE && !is_fixed()) { + int type = get_type(); + if ((type == 0 && do_crack()) || (type > 0)) { + set_attrib("type", Value(int_attrib("type") + 1)); + sound_event ("crack"); + init_model(); + } + } + if (msg == "heat") { + sound_event ("crack"); + replace(it_debris); + return Value(1.0); + } + return Value(); + } + + bool do_crack() { + if (!is_fixed()) { + double brittleness = server::Brittleness; + double_attrib ("brittleness", &brittleness); + double rnd = DoubleRand(0, 1); + return rnd < brittleness; + } + else + return false; + } + }; + ItemTraits Crack::traits[4] = { + {"it-crack0", it_crack0, itf_static | itf_indestructible | itf_fireproof, 0.0}, + {"it-crack1", it_crack1, itf_static | itf_indestructible | itf_fireproof, 0.0}, + {"it-crack2", it_crack2, itf_static | itf_indestructible | itf_fireproof, 0.0}, + {"it-crack3", it_crack3, itf_static | itf_indestructible | itf_fireproof, 0.0} + }; +} + +/* -------------------- Debris -------------------- */ +namespace +{ + class Debris : public Item { + CLONEOBJ(Debris); + DECL_TRAITS; + + bool actor_hit(Actor *a) { + SendMessage(a, "fall"); + return false; + } + void animcb() { + GridPos p = get_pos(); + SetFloor(p, MakeFloor("fl-abyss")); + KillItem(p); + } + public: + Debris() {} + }; + DEF_TRAITSF(Debris, "it-debris", it_debris, + itf_static | itf_animation | itf_indestructible | itf_fireproof); +} + + +/* -------------------- Burning floor -------------------- */ + +namespace +{ + /* Used for animations and interfaces of fire. Study */ + /* floors.hh and floors.cc for the fire implementation. */ + + class Burnable : public Item { + CLONEOBJ(Burnable); + DECL_TRAITS_ARRAY(6, state); + public: + static void setup() { + RegisterItem (new Burnable(IDLE)); + RegisterItem (new Burnable(IGNITE)); + RegisterItem (new Burnable(BURNING)); + RegisterItem (new Burnable(FIREPROOF)); + RegisterItem (new Burnable(ASH)); + RegisterItem (new Burnable(OIL)); + } + private: + enum State { IDLE, IGNITE, BURNING, FIREPROOF, ASH, OIL }; + Burnable (State initstate) { + state = initstate; + } + State state; + + virtual Value message (const string &msg, const Value &v); + void animcb(); + bool actor_hit(Actor *a); + void init_model(); + }; + + ItemTraits Burnable::traits[6] = { + {"it-burnable", it_burnable, itf_static, 0.0}, + {"it-burnable_ignited", it_burnable_ignited, + itf_static | itf_animation | itf_fireproof, 0.0}, + {"it-burnable_burning", it_burnable_burning, + itf_static | itf_animation | itf_fireproof, 0.0}, + {"it-burnable_fireproof", it_burnable_fireproof, + itf_static | itf_fireproof, 0.0}, + {"it-burnable_ash", it_burnable_ash, + itf_static | itf_fireproof, 0.0}, + {"it-burnable_oil", it_burnable_oil, itf_static, 0.0}, + }; +} + +Value Burnable::message(const string &msg, const Value &v) +{ + if (msg == "extinguish") { // stop / never start burning + state = FIREPROOF; + init_model(); + } else if (msg == "brush" && (state == ASH || state == FIREPROOF)) { + kill(); // The brush cleans the floor + } else if (Floor *fl = GetFloor(get_pos())) { + if (msg == "trigger" || msg == "ignite" || msg == "expl") + return SendMessage(fl, "ignite"); + } + return Item::message(msg, v); +} + +void Burnable::animcb() { + if(Floor *fl = GetFloor(get_pos())) + fl->on_burnable_animcb(state == IGNITE); +} + +bool Burnable::actor_hit(Actor *a) { + if (state == IGNITE || state == BURNING) + SendMessage(a, "shatter"); + return false; +} + +void Burnable::init_model() { + if(state == OIL) { + string mymodel = "it-burnable_oil"; + int r = IntegerRand(1,4); + mymodel = mymodel + ((string) (r==1?"1":r==2?"2":r==3?"3":"4")); + set_model(mymodel.c_str()); + } else + Item::init_model(); +} + + +/* -------------------- Fire Extinguisher -------------------- */ +namespace +{ + /*! This items can extinguish burning floor. */ + class Extinguisher : public Item { + CLONEOBJ(Extinguisher); + DECL_TRAITS_ARRAY(3, get_load()); + public: + static void setup() { + RegisterItem (new Extinguisher(0)); + RegisterItem (new Extinguisher(1)); + RegisterItem (new Extinguisher(2)); + } + + private: + Extinguisher (int load) { + set_attrib("load", load); + } + + int get_load() const { return ecl::Clamp(int_attrib("load"),0,2); } + void set_load (int load) { set_attrib("load", ecl::Clamp(load,0,2)); } + + void extinguish (GridPos p) { + if (Item *it = GetItem(p)) { + SendMessage (it, "extinguish"); + } else { + SetItem (p, it_burnable_fireproof); + } + } + + /* ---------- Item interface ---------- */ + + ItemAction activate(Actor *a, GridPos p); + }; + + ItemTraits Extinguisher::traits[3] = { + {"it-extinguisher_empty", it_extinguisher_empty, itf_none, 0.0}, + {"it-extinguisher_medium", it_extinguisher_medium, itf_fireproof, 0.0}, + {"it-extinguisher", it_extinguisher, itf_fireproof, 0.0}, + }; +} + +ItemAction Extinguisher::activate(Actor *a, GridPos p) +{ + int load = get_load(); + if (load > 0) { + extinguish (p); + extinguish (move(p, NORTH)); + extinguish (move(p, SOUTH)); + extinguish (move(p, EAST)); + extinguish (move(p, WEST)); + if (load > 1) { + // full extinguisher has a larger range + extinguish (move(p, NORTH, NORTH)); + extinguish (move(p, NORTH, EAST)); + extinguish (move(p, SOUTH, SOUTH)); + extinguish (move(p, SOUTH, WEST)); + extinguish (move(p, EAST, EAST)); + extinguish (move(p, EAST, SOUTH)); + extinguish (move(p, WEST, WEST)); + extinguish (move(p, WEST, NORTH)); + } + set_load(load - 1); + + // Redraw the player's inventory, the visual representation of + // the extinguisher has changed. + player::RedrawInventory(); + } + return ITEM_DROP; +} + + +/* -------------------- Flags -------------------- */ + +namespace +{ + /*! Flags can be used to set a new respawn point for the black or + white marble. */ + class FlagBlack : public Item { + CLONEOBJ(FlagBlack); + DECL_TRAITS; + + void on_drop(Actor *) { + player::SetRespawnPositions(get_pos(), true); + } + void on_pickup(Actor *) { + player::RemoveRespawnPositions(true); + } + + public: + FlagBlack() {} + }; + DEF_TRAITS(FlagBlack, "it-flagblack", it_flagblack); + + class FlagWhite : public Item { + CLONEOBJ(FlagWhite); + DECL_TRAITS; + + void on_drop(Actor *) { + player::SetRespawnPositions(get_pos(), false); + } + void on_pickup(Actor *) { + player::RemoveRespawnPositions(false); + } + + public: + FlagWhite() + {} + }; + + DEF_TRAITS(FlagWhite, "it-flagwhite", it_flagwhite); +} + + + +/* -------------------- Blocker item -------------------- */ + +namespace +{ + /*! If a 'BolderStone' moves over a 'Blocker' the 'Blocker' starts + growing and replaces itself by a BlockerStone. */ + class Blocker : public Item, public TimeHandler { + CLONEOBJ(Blocker); + DECL_TRAITS; + + enum State { + IDLE, // nothing special + SHRINKED, // recently shrinked by a BolderStone (which has not arrived yet) + BOLDERED, // BolderStone has arrived (the one that made me shrink) + COVERED // BolderStone will make me grow (but is on top of me) + // Note: BOLDERED and COVERED are used for all kinds of stones + // (e.g. when a stone is on top and the Blocker receives a "close" message) + } state; + + static const char * const stateName[]; + + void change_state(State new_state); + void on_creation (GridPos p); + void on_removal (GridPos p); + virtual Value message(const string &msg, const Value &val); + void stone_change(Stone *st); + void grow(); + void alarm(); + public: + Blocker(bool shrinked_recently); + ~Blocker(); + }; + DEF_TRAITSF(Blocker, "it-blocker", it_blocker, itf_static); +}; + +const char * const Blocker::stateName[] = { "IDLE", "SHRINKED", "BOLDERED", "COVERED" }; + + +Blocker::Blocker(bool shrinked_recently) +: state(shrinked_recently ? SHRINKED : IDLE) +{} + +Blocker::~Blocker() { + GameTimer.remove_alarm (this); +} + +void Blocker::on_creation (GridPos p) +{ + if (state == SHRINKED) { + GameTimer.set_alarm(this, 0.5, false); + } + Item::on_creation(p); +} + +void Blocker::on_removal(GridPos p) +{ + change_state(IDLE); + Item::on_removal(p); +} + +void Blocker::change_state(State new_state) +{ + if (state != new_state) { + if (state == SHRINKED) + GameTimer.remove_alarm(this); + else if (new_state == SHRINKED) + GameTimer.set_alarm(this, 0.5, false); + state = new_state; + } +} + + +void Blocker::grow() +{ + Stone *st = world::MakeStone("st-blocker-growing"); + world::SetStone(get_pos(), st); + TransferObjectName(this, st); + kill(); +} + +void Blocker::alarm() +{ + if (state == SHRINKED) { // BolderStone did not arrive in time + change_state(IDLE); + } +} + + +Value Blocker::message(const string &msg, const Value &val) +{ + if (msg == "trigger" || msg == "openclose") { + switch (state) { + case IDLE: + case SHRINKED: + grow(); // if no stone on top -> grow + break; + + // if stone on top -> toggle state (has no effect until stone leaves) + case BOLDERED: + change_state(COVERED); + break; + case COVERED: + change_state(BOLDERED); + break; + } + } + else { + int open = -1; + + if (msg == "signal") { + if (val.get_type() == Value::DOUBLE) { + // val: 1 means "shrink", 0 means "grow" + open = static_cast(val.get_double()); +// warning("received signal %i", open); + } + else { + ASSERT(0, XLevelRuntime, "Blocker: message 'signal' with wrong typed value"); + } + } + else if (msg == "open") + open = 1; + else if (msg == "close") + open = 0; + + if (open == 1) { // shrink + if (state == COVERED) + change_state(BOLDERED); + } + else if (open != -1) { // grow + if (state == BOLDERED) + change_state(COVERED); + else if (state == SHRINKED) + change_state(IDLE); // remove alarm + + if (state == IDLE) { + if (Stone *st = GetStone(get_pos())) { + if (st->is_kind("st-bolder")) + change_state(BOLDERED); // occurs in Per.Oxyd #84 + else + change_state(COVERED); + } + else { + grow(); + } + } + } + } + return Value(); +} + +void Blocker::stone_change(Stone *st) +{ + if (st) { + if (st->is_kind("st-bolder")) { // bolder arrived + switch (state) { + case IDLE: + change_state(COVERED); + break; + case SHRINKED: + change_state(BOLDERED); + break; + case COVERED: + case BOLDERED: + // two BolderStones running directly next to each other + // let second pass as well (correct? siegfried says yes) + break; + } + } + else { // any other stone + change_state(BOLDERED); + } + } + else { // stone disappeared + switch (state) { + case BOLDERED: + change_state(IDLE); + break; + case COVERED: + grow(); + break; + case IDLE: + case SHRINKED: + // no action + break; + } + } +} + + +/* -------------------- Ring -------------------- */ +namespace +{ + class Ring : public Item { + CLONEOBJ(Ring); + DECL_TRAITS; + public: + Ring() {} + + ItemAction activate(Actor *a, GridPos) { + if (ExchangeMarbles(a)) { + sound_event ("switchmarbles"); + } + else { + world::RespawnActor(a); + } + return ITEM_DROP; + } + }; + DEF_TRAITS(Ring, "it-ring", it_ring); +} + +//---------------------------------------- +// Bridge item (for Oxyd compatibility) +// +// Floor tiles seem to be static in Oxyd and cannot change dynamically +// or be animated. For this reason, every bridge floor in Oxyd has to +// be combined with a bridge "item" that receives the signals, shows +// the animation and sets or removes the floor. +//---------------------------------------- +namespace +{ + class OxydBridge : public Item { + CLONEOBJ(OxydBridge); + DECL_TRAITS; + + virtual Value message(const string& msg, const Value &val) { + if (msg == "signal") { + int ival = to_int (val); + Floor *floor = GetFloor (get_pos()); + if (ival > 0) + SendMessage (floor, "close"); + else + SendMessage (floor, "open"); + } + return Value(); + } + public: + OxydBridge() {} + }; + DEF_TRAITSF(OxydBridge, "it-bridge-oxyd", it_bridge_oxyd, + itf_static | itf_indestructible | itf_invisible | itf_fireproof); + + class OxydBridgeActive : public OxydBridge { + CLONEOBJ(OxydBridgeActive); + DECL_TRAITS; + + void on_creation (GridPos p) { + Floor *floor = GetFloor (p); + SendMessage (floor, "close"); + } + public: + OxydBridgeActive() {} + }; + DEF_TRAITSF(OxydBridgeActive, "it-bridge-oxyd_active", it_bridge_oxyd_active, + itf_static | itf_indestructible | itf_invisible | itf_fireproof); +} + + +/* -------------------- Sensors -------------------- */ + + +/* Basically behave like regular triggers, but they are invisible and can be + activated only once. */ +namespace +{ + class Sensor : public Item { + CLONEOBJ(Sensor); + DECL_TRAITS; + public: + Sensor() {} + + void actor_enter (Actor *) { + PerformAction (this, true); + } + }; + DEF_TRAITSF(Sensor, "it-sensor", it_sensor, itf_static | itf_invisible); + + class InverseSensor : public Item { + CLONEOBJ(InverseSensor); + DECL_TRAITS; + public: + InverseSensor() {} + + void actor_enter (Actor *) { + PerformAction (this, false); + } + }; + DEF_TRAITSF(InverseSensor, "it-inversesensor", it_inversesensor, + itf_static | itf_invisible); +} + + +/* -------------------- Signal filters -------------------- */ +namespace +{ + class SignalFilterItem : public Item { + CLONEOBJ(SignalFilterItem); + DECL_TRAITS_ARRAY(2, type); + + SignalFilterItem(int type_) : type(type_) { + ASSERT(type >= 0 && type <= 1, XLevelRuntime, "SignalFilterItem: type unknown"); + } + + virtual Value message(const string& m, const Value& val) { + if (m == "signal") { + int value = to_int(val); +// warning("received signal with value %i", value); + if (value) + PerformAction(this, type != 0); + } + return Value(); + } + + // type of signal filter + // 0 : receive 1 -> send 0 + // 1 : receive 1 -> send 1 + int type; + + public: + static void setup() { + RegisterItem (new SignalFilterItem(0)); + RegisterItem (new SignalFilterItem(1)); + } + }; + ItemTraits SignalFilterItem::traits[2] = { + {"it-signalfilter0", it_signalfilter0, + itf_static | itf_invisible | itf_fireproof, 0.0}, + {"it-signalfilter1", it_signalfilter1, + itf_static | itf_invisible | itf_fireproof, 0.0}, + }; +} + + +/* -------------------- EasyKillStone -------------------- */ + +/* +This item is never visible during the game. Its only purpose is to +modify the level if the difficulty mode is set to easy *before* the +game starts. + +In easy game mode this item removes the stone at its position. Then +in both modes it kills itself. + +E.g. it can be used to hide walls in easy game mode. + +\ref st-easymode +*/ + +namespace +{ + class EasyKillStone : public Item { + CLONEOBJ(EasyKillStone); + DECL_TRAITS; + + virtual Value on_message (const Message &); + public: + EasyKillStone() {} + }; + DEF_TRAITSF(EasyKillStone, "it-easykillstone", + it_easykillstone, itf_invisible | itf_fireproof); +} + +Value EasyKillStone::on_message (const Message &m ) +{ + if (m.message == "init") { + // does not work in on_creation() because items are created + // before stones are created. + if (server::GetDifficulty() == DIFFICULTY_EASY) { + if (Stone *st = GetStone (get_pos())) { + if (st->is_kind ("st-death") || + st->is_kind ("st-flash") || + st->is_kind ("st-thief")) + { + SetStone (get_pos(), MakeStone ("st-plain")); + } + else + KillStone(get_pos()); + } + } + kill(); + } + return Value(); +} + +/* -------------------- EasyKeepStone -------------------- */ +namespace +{ + class EasyKeepStone : public Item { + CLONEOBJ(EasyKeepStone); + DECL_TRAITS; + + virtual Value message(const string& m, const Value& ) { + if (m == "init") { + // does not work in on_creation() because items are created + // before stones are created. + if (server::GetDifficulty() == DIFFICULTY_HARD) + KillStone(get_pos()); + kill(); + } + return Value(); + } + public: + EasyKeepStone() {} + }; + DEF_TRAITSF(EasyKeepStone, "it-easykeepstone", it_easykeepstone, + itf_invisible | itf_fireproof); +} + +/* -------------------- SingleKillStone -------------------- */ +namespace +{ + class OnePKillStone : public Item { + CLONEOBJ (OnePKillStone); + DECL_TRAITS; + + virtual Value on_message (const Message &m) { + if (m.message == "init") { + if (server::SingleComputerGame) + KillStone (get_pos()); + kill(); + } + return Value(); + } + public: + OnePKillStone () {} + }; + DEF_TRAITSF(OnePKillStone, "it-1pkillstone", it_1pkillstone, + itf_invisible | itf_fireproof); + + class TwoPKillStone : public Item { + CLONEOBJ (TwoPKillStone); + DECL_TRAITS; + + virtual Value on_message (const Message &m) { + if (m.message == "init") { + if (!server::SingleComputerGame) + KillStone (get_pos()); + kill(); + } + return Value(); + } + public: + TwoPKillStone () {} + }; + DEF_TRAITSF(TwoPKillStone, "it-2pkillstone", it_2pkillstone, + itf_invisible | itf_fireproof); +} + + +/* -------------------- Glasses -------------------- */ +namespace +{ + class Glasses : public Item { + CLONEOBJ(Glasses); + DECL_TRAITS; + + static bool wears_glasses(Actor *a) { + return player::GetInventory(a)->find("it-glasses") != -1; + } + + void on_drop(Actor *a) { + if (!wears_glasses(a)) // 'this' was the only it-glasses + BroadcastMessage("glasses", 0.0, GRID_STONES_BIT); + } + void on_pickup(Actor *a) { + if (!wears_glasses(a)) // no glasses before + BroadcastMessage("glasses", 1.0, GRID_STONES_BIT); + } + void on_stonehit(Stone *) { + sound_event ("shatter"); + replace (it_glasses_broken); + } + public: + Glasses() + {} + }; + DEF_TRAITS(Glasses, "it-glasses", it_glasses); +} + + +/* -------------------- Invisible abyss -------------------- */ +namespace +{ + class InvisibleAbyss : public Item { + CLONEOBJ(InvisibleAbyss); + DECL_TRAITS; + bool actor_hit(Actor *a) { + SendMessage(a, "fall"); + return false; + } + public: + InvisibleAbyss() {} + }; + DEF_TRAITSF(InvisibleAbyss, "it-abyss", it_abyss, + itf_static | itf_invisible | itf_fireproof); +} + + +/* -------------------- Landmine -------------------- */ +namespace +{ + class Landmine : public Item { + CLONEOBJ(Landmine); + DECL_TRAITS; + + void explode() { + sound_event ("landmine"); + replace (it_explosion2); + } + + bool actor_hit (Actor *a) { + const double ITEM_RADIUS = 0.3; + double dist = length(a->get_pos() - get_pos().center()); + if (dist < ITEM_RADIUS) + explode(); + return false; + } + + void on_stonehit(Stone *) { explode(); } + public: + Landmine() + {} + }; + DEF_TRAITSF(Landmine, "it-landmine", it_landmine, itf_static); +} + + +/* -------------------- Cross -------------------- */ +namespace +{ + class Cross : public Item, public TimeHandler { + CLONEOBJ(Cross); + DECL_TRAITS; + + bool m_active; + + void actor_enter(Actor *a) { + if (!m_active && a->get_attrib("player") != 0) { + GameTimer.set_alarm (this, 10); + } + } + + void actor_leave (Actor *) { + if (m_active) { + GameTimer.remove_alarm (this); + m_active = false; + } + } + + void alarm() { + PerformAction (this, true); + } + + virtual Value on_message (const Message &m) { + if (server::GameCompatibility == enigma::GAMET_PEROXYD) { + // Crosses can be used to invert signals in Per.Oxyd + if (m.message == "signal") { + PerformAction (this, to_double (m.value) != 1.0); + } + } else if (enigma_server::GameCompatibility == GAMET_ENIGMA) { + if (m.message == "brush") + KillItem(this->get_pos()); + } + return Value(); + } + + public: + Cross() : m_active(false) { + } + virtual ~Cross(); + }; + DEF_TRAITSF(Cross, "it-cross", it_cross, itf_static); + + Cross::~Cross() { + GameTimer.remove_alarm(this); + } + +} + +/* -------------------- Bag -------------------- */ +namespace +{ + class Bag : public Item, public enigma::ItemHolder { + DECL_TRAITS; + + enum { BAGSIZE = 13 }; + vector m_contents; + + // Item interface + bool actor_hit (Actor *a) { + if (Item::actor_hit(a)) { + if (Inventory *inv = player::MayPickup(a, NULL)) { + std::vector::size_type oldSize = m_contents.size(); + inv->takeItemsFrom(this); + if (oldSize != m_contents.size() && inv->is_full()) { + // some items have been picked up but the bag will not + // be picked up (and cause the following actions) + player::RedrawInventory (inv); + sound_event ("pickup"); + } + return true; + } + } + return false; + } + + public: + virtual Bag * clone() { + ASSERT(is_empty(), XLevelRuntime, "Bag:: Clone of a full bag!"); + return new Bag(*this); + } + + virtual void dispose() { + Item * it = yield_first(); + while (it != NULL) { + DisposeObject(it); + it = yield_first(); + } + delete this; + } + + // ItemHolder interface + virtual bool is_full() const { + return m_contents.size() >= BAGSIZE; + } + virtual void add_item (Item *it) { + // thieves may add items beyond pick up limit BAGSIZE + m_contents.insert (m_contents.begin(), it); + } + + virtual bool is_empty() const { + return m_contents.size() == 0; + } + + virtual Item *yield_first() { + if (m_contents.size() > 0) { + Item *it = m_contents[0]; + m_contents.erase (m_contents.begin()); + return it; + } + return NULL; + } + + Bag() + {} + + ~Bag() { +// ecl::delete_sequence (m_contents.begin(), m_contents.end()); + } + }; + DEF_TRAITS(Bag, "it-bag", it_bag); +} + +/* -------------------- pencil -------------------- */ +namespace +{ + class Pencil : public Item { + CLONEOBJ(Pencil); + DECL_TRAITS; + + ItemAction activate(Actor * a, GridPos p) { + if (enigma_server::GameCompatibility == GAMET_ENIGMA) { + if (Item *it=GetItem(p)) { + return ITEM_KEEP; + } + // If the actor is flying and tries to make a cross, drop the it-pencil + if (a->is_flying()) { + return ITEM_DROP; + } + + Floor *fl = GetFloor(p); + string model = fl->get_kind(); + + /* do not allow markings on this floortypes: + fl-abyss, fl-water, fl-swamp + fl-bridge[{-closed,-open}]? + markings on fl-ice will result as it-crack1 + */ + if (model == "fl-abyss" || model == "fl-water" || model == "fl-swamp") { + return ITEM_KEEP; + } else if (model == "fl-ice") { + SetItem (p, it_crack1); + } else { + SetItem (p, it_cross); + } + return ITEM_KILL; + } + return ITEM_KEEP; + } + + public: + Pencil() {} + }; + + DEF_TRAITS(Pencil, "it-pencil", it_pencil); +} + +/* -------------------- it-death -------------------- */ +namespace +{ + class Death : public Item { + CLONEOBJ(Death); + DECL_TRAITS; + + bool active; + + bool actor_hit(Actor *a) { + ActorInfo &ai = * a->get_actorinfo(); + if (!ai.grabbed) { + SendMessage(a, "shatter"); + if (!active) { + active=true; + set_anim("it-death-anim"); + } + } + return false; + } + + protected: + void animcb() { set_model("it-death"); active=false; } + + public: + Death() : active(false) {} + }; + + DEF_TRAITSF(Death, "it-death", it_death, itf_static | itf_indestructible); +} + +/* -------------------- HStrip and VStrip -------------------- */ +namespace +{ + class HStrip : public Item { + CLONEOBJ(HStrip); + DECL_TRAITS; + public: + HStrip() { + } + bool covers_floor(ecl::V2 pos) const { + if (GridPos(pos) != get_pos()) + return false; + + const double MAXDIST = 6.0/32; + double ycenter = get_pos().y + 0.5; + return (fabs(pos[1] - ycenter) > MAXDIST) ? false : true; + } + }; + DEF_TRAITSF(HStrip, "it-hstrip", it_hstrip, itf_static); + + class VStrip : public Item { + CLONEOBJ(VStrip); + DECL_TRAITS; + public: + VStrip() { + } + bool covers_floor(ecl::V2 pos) const { + if (GridPos(pos) != get_pos()) + return false; + + const double MAXDIST = 5.0/32; + double xcenter = get_pos().x + 0.5; + return (fabs(pos[0] - xcenter) > MAXDIST) ? false : true; + } + }; + DEF_TRAITSF(VStrip, "it-vstrip", it_vstrip, itf_static); + + class SurpriseItem : public Item { + CLONEOBJ(SurpriseItem); + DECL_TRAITS; + + void on_drop (Actor *) { + static ItemID items[] = { + it_umbrella, + it_spring1, + it_dynamite, + it_coffee, + it_hammer + }; + replace (items[enigma::IntegerRand (0, 4)]); + } + public: + SurpriseItem() { + } + }; + DEF_TRAITS(SurpriseItem, "it-surprise", it_surprise); +} + +/* -------------------- ChangeFloorItem -------------------- */ +namespace +{ + class ChangeFloorItem : public Item { + CLONEOBJ(ChangeFloorItem); + DECL_TRAITS; + + void exchange_floor (const char *a, const char *b) { + GridPos p = get_pos(); + if (Floor *fl = GetFloor(p)) { + if (fl->is_kind(a)) + SetFloor (p, MakeFloor(b)); + else if (fl->is_kind(b)) + SetFloor (p, MakeFloor(a)); + } + } + + void actor_leave (Actor *) { + if (server::TwoPlayerGame) { + // two players: black / white tile + exchange_floor ("fl-acwhite", "fl-acblack"); + } else { + // one player: left / right accel +// exchange_floor ("fl- + } + } + + public: + ChangeFloorItem() { + } + }; + DEF_TRAITSF(ChangeFloorItem, "it-changefloor", it_changefloor, + itf_static | itf_invisible); + + class Oxyd5fItem : public Item { + CLONEOBJ(Oxyd5fItem); + DECL_TRAITS; + + virtual Value on_message (const world::Message &) { + PerformAction (this, true); + return Value(); + } + public: + Oxyd5fItem() + {} + }; + DEF_TRAITSF(Oxyd5fItem, "it-oxyd5f", it_oxyd5f, + itf_static | itf_invisible | itf_fireproof); +} + +/* -------------------- Drop -------------------- */ + +namespace +{ + Actor *replace_actor (Actor *olda, Actor *newa) + { + ActorInfo *info = newa->get_actorinfo(); + info->pos = olda->get_pos(); + info->vel = olda->get_vel(); + + int iplayer; + if (olda->int_attrib("player", &iplayer)) { + player::ReplaceActor (iplayer, olda, newa); + } + + world::AddActor (newa); + if (!world::YieldActor (olda)) { + enigma::Log << "Strange: could not remove old actor\n"; + } + olda->hide(); + newa->show(); + return olda; + } + + class DropCallback : public enigma::TimeHandler { + Actor *rotor; + Actor *old; + public: + DropCallback (Actor *rotor_, Actor *old_) + : rotor (rotor_), old (old_) + {} + + // TimerHandler interface + virtual void alarm() + { + replace_actor (rotor, old); + + delete rotor; + delete this; + } + }; + + class Drop : public Item { + CLONEOBJ (Drop); + DECL_TRAITS; + + ItemAction activate(Actor *a, GridPos) + { + const double ROTOR_LIFETIME = 5.0; + + int iplayer = a->int_attrib("player"); + ActorID id = get_id (a); + + if (id == ac_blackball || id == ac_whiteball) { + // Kill ALL rubberbands connected with the actor: + world::KillRubberBands (a); + Actor *rotor = world::MakeActor (ac_rotor); + rotor->set_attrib ("mouseforce", Value (1.0)); + rotor->set_attrib ("controllers", Value (iplayer+1)); + rotor->set_attrib ("player", Value (iplayer)); + rotor->set_attrib ("gohome", Value (0.0)); + rotor->set_attrib ("essential", Value(a->int_attrib("essential"))); + std::string essId; + if (!a->string_attrib ("essential_id", &essId)) + essId = a->get_traits().name; + rotor->set_attrib ("essential_id", Value(essId)); + + replace_actor (a, rotor); + + world::GameTimer.set_alarm (new DropCallback (rotor, a), + ROTOR_LIFETIME, + false); + } + return ITEM_KILL; // remove from inventory + } + + public: + Drop() {} + }; + DEF_TRAITS(Drop, "it-drop", it_drop); +} + +/* -------------------- Rubberband -------------------- */ +namespace +{ + class Rubberband : public Item { + CLONEOBJ(Rubberband); + DECL_TRAITS; + + ItemAction activate(Actor *a, GridPos p) { + // Default values for the rubberband: + double strength = 10.0; + double length = 1.0; + double minlength = 0.0; + double_attrib ("strength", &strength); + double_attrib ("length", &length); + double_attrib ("minlength", &minlength); + + world::RubberBandData rbd; + rbd.strength = strength; + rbd.length = length; + rbd.minlength = minlength; + + // Target to connect to, default: "" + string target = ""; + string_attrib("target",&target); + // TODO: Multiple Targets! + // TODO: Target for black and target for white marble? + // TODO: MultiplayerGame: Defaulttarget is second actor! + + // The mode attribute "scissor" defines, if when using an it-rubberband, + // other rubberbands to the actor will be cut of or not, true means they will. false is default. + enigma::Value const *scissorValue = get_attrib("scissor"); + bool isScissor = (scissorValue == NULL)? false : to_bool(*scissorValue); + + // Get actor or stone with the name, given in "connect_to": + Actor *target_actor = dynamic_cast(GetNamedObject(target)); + Stone *target_stone = dynamic_cast(GetNamedObject(target)); + + // Target does NOT exist, Drop Item + if((!target_actor)&&(!target_stone)) return ITEM_DROP; + + if (isScissor) + world::KillRubberBands (a); + + sound_event ("rubberband"); + if (target_actor) + world::AddRubberBand (a,target_actor,rbd); + else + world::AddRubberBand (a,target_stone,rbd); + + return ITEM_KILL; + } + + public: + Rubberband() {} + }; + DEF_TRAITS(Rubberband, "it-rubberband", it_rubberband); +} + +/* -------------------- Functions -------------------- */ + +void world::InitItems() +{ + RegisterItem (new Bag); + RegisterItem (new Banana); + RegisterItem (new BlackBomb); + RegisterItem (new BlackBombBurning); + Register ("it-blocker-new", new Blocker(true)); + RegisterItem (new Blocker(false)); + RegisterItem (new Booze); + RegisterItem (new Brake); + RegisterItem (new BrokenBooze); + RegisterItem (new Brush); + Burnable::setup(); + RegisterItem (new ChangeFloorItem); + RegisterItem (new Cherry); + RegisterItem (new Coffee); + RegisterItem (new Coin1); + RegisterItem (new Coin2); + RegisterItem (new Coin4); + Crack::setup(); + RegisterItem (new Cross); + RegisterItem (new Death); + RegisterItem (new Debris); + RegisterItem (new Document); + RegisterItem (new Drop); + RegisterItem (new Dummyitem); + RegisterItem (new Dynamite); + RegisterItem (new EasyKillStone); + RegisterItem (new EasyKeepStone); + RegisterItem (new Explosion1); + RegisterItem (new Explosion2); + RegisterItem (new Explosion3); + Extinguisher::setup(); + RegisterItem (new ExtraLife); + RegisterItem (new FlagBlack); + RegisterItem (new FlagWhite); + RegisterItem (new Floppy); + RegisterItem (new Glasses); + RegisterItem (new BrokenGlasses); + RegisterItem (new Hammer); + RegisterItem (new Hill); + RegisterItem (new Hollow); + RegisterItem (new HStrip); + RegisterItem (new InverseSensor); + RegisterItem (new InvisibleAbyss); + Register ("it-key", new Key); + RegisterItem (new Key(Key::KEY1)); + RegisterItem (new Key(Key::KEY2)); + RegisterItem (new Key(Key::KEY3)); + RegisterItem (new Landmine); + RegisterItem (new MagicWand); + Register ("it-magnet", new Magnet (false)); + RegisterItem (new Magnet (true)); + RegisterItem (new Magnet (false)); + RegisterItem (new Odometer); + RegisterItem (new OnePKillStone); + RegisterItem (new OxydBridge); + RegisterItem (new OxydBridgeActive); + RegisterItem (new Oxyd5fItem); + RegisterItem (new Pencil); + RegisterItem (new Pin); + Pipe::setup(); + Puller::setup(); + RegisterItem (new Ring); + RegisterItem (new Rubberband); + RegisterItem (new SeedWood); + RegisterItem (new SeedNowood); + RegisterItem (new SeedVolcano); + RegisterItem (new Sensor); + ShogunDot::setup(); + SignalFilterItem::setup(); + RegisterItem (new Spade); + RegisterItem (new Spoon); + RegisterItem (new Spring1); + RegisterItem (new Spring2); + RegisterItem (new Springboard); + RegisterItem (new Squashed); + RegisterItem (new SurpriseItem); + RegisterItem (new Sword); + RegisterItem (new TinyHill); + RegisterItem (new TinyHollow); + RegisterItem (new Trigger); + RegisterItem (new TwoPKillStone); + RegisterItem (new Umbrella); + RegisterItem (new Vortex(false)); + RegisterItem (new Vortex(true)); + RegisterItem (new VStrip); + RegisterItem (new Weight); + RegisterItem (new WhiteBomb); + RegisterItem (new Wrench); + RegisterItem (new WormHole(false)); + RegisterItem (new WormHole(true)); + RegisterItem (new YinYang); + RegisterItem (new Rubberband); +} diff --git a/project/jni/application/enigma/src/items.hh b/project/jni/application/enigma/src/items.hh new file mode 100644 index 000000000..2bfa224da --- /dev/null +++ b/project/jni/application/enigma/src/items.hh @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ITEMS_HH_INCLUDED +#define ITEMS_HH_INCLUDED + +#include "objects_decl.hh" + +namespace world +{ + enum ItemID { + it_INVALID = -1, + it_FIRST = 0, + it_none = 0, + it_1pkillstone, + it_2pkillstone, + it_abyss, + it_bag, + it_banana, + it_blackbomb, + it_blackbomb_burning, + it_blocker, + it_booze, + it_brake, + it_bridge_oxyd, + it_bridge_oxyd_active, + it_booze_broken, + it_brush, + it_burnable, + it_burnable_fireproof, + it_burnable_ignited, + it_burnable_burning, + it_burnable_ash, + it_burnable_oil, + it_changefloor, + it_cherry, + it_cherry_crushed, + it_coffee, + it_coin1, + it_coin2, + it_coin4, + it_crack0, + it_crack1, + it_crack2, + it_crack3, + it_cross, + it_death, + it_debris, + it_document, + it_drop, + it_dynamite, + it_dummy, + it_easykeepstone, + it_easykillstone, + it_explosion1, + it_explosion2, + it_explosion3, + it_extinguisher, + it_extinguisher_medium, + it_extinguisher_empty, + it_extralife, + it_flagblack, + it_flagwhite, + it_floppy, + it_glasses, + it_glasses_broken, + it_hammer, + it_hill, + it_hollow, + it_hstrip, + it_inversesensor, + it_key_a, + it_key_b, + it_key_c, + it_landmine, + it_laserbeam, + it_magicwand, + it_magnet_off, + it_magnet_on, + it_odometer, + it_oxyd5f, + it_pencil, + it_pin, + it_pipe_e, it_pipe_w, it_pipe_s, it_pipe_n, + it_pipe_es, it_pipe_ne, it_pipe_sw, it_pipe_wn, + it_pipe_h, it_pipe_v, + it_puller_n, + it_puller_e, + it_puller_s, + it_puller_w, + it_ring, + it_rubberband, + it_seed, + it_seed_nowood, + it_seed_volcano, + it_sensor, + it_shogun_s, + it_shogun_m, + it_shogun_l, + it_signalfilter0, + it_signalfilter1, + it_spade, + it_spoon, + it_spring1, + it_spring2, + it_springboard, + it_squashed, + it_surprise, + it_sword, + it_tinyhill, + it_tinyhollow, + it_trigger, + it_umbrella, + it_vortex_open, + it_vortex_closed, + it_vstrip, + it_weight, + it_whitebomb, + it_wormhole_off, + it_wormhole_on, + it_wrench, + it_yinyang, + it_LAST, + it_COUNT + }; + + /*! What may happen to an item _after_ it was activated? */ + enum ItemAction { + ITEM_DROP, //!< Drop it to the floor + ITEM_KILL, //!< Remove it from the inventory and dispose it + ITEM_KEEP, //!< Keep it in the inventory; do nothing further + }; + + enum ItemFlags { + itf_none = 0, + itf_static = 1, //!< Cannot be picked up + itf_indestructible = 2, //!< Cannot be destroyed by explosions etc. + itf_animation = 4, //!< Use set_anim() instead of set_model() + itf_invisible = 8, //!< Item has no visible model + itf_inflammable = 16, //!< Burns when hit by laser beam + itf_norespawn = 32, //!< Don't respawn balls on top of this item + itf_fireproof = 64, //!< This item can't burn by fire + }; + + struct ItemTraits { + const char *name; //!< Name of the item, e.g., "it-hammer" + ItemID id; + int flags; //!< Combination of ItemFlags + float radius; //!< Radius, 0.0 = default + }; + + class Item : public GridObject { + public: + Item(); + + /* ---------- Public methods ---------- */ + void kill(); + void replace (ItemID id); + + /* ---------- Virtual functions ---------- */ + const char *get_kind() const; + void init_model(); + void on_laserhit(Direction); + + /* ---------- Item interface ---------- */ + + virtual Item *clone() = 0; + + virtual const ItemTraits &get_traits() const = 0; + + /*! Return true if item completely covers the floor. In this + case the Floor::actor_contact() will not be called + automatically; this must be done from `Item::actor_hit' (if + at all). */ + virtual bool covers_floor(ecl::V2 pos) const { return false; } + + /*! Return the force an item exerts on actor `a'. This is + used by sloped and hills for force fields that are local to + the current field. For global force fields you have to + register a ForceField in the world. */ + virtual void add_force(Actor *a, ecl::V2 &f); + + virtual bool can_drop_at (GridPos p); + + virtual void drop (Actor *a, GridPos p); + + /*! Called when item is dropped by actor `a' */ + virtual void on_drop(Actor *a); + + /*! Called when item is picked up by actor `a' */ + virtual void on_pickup(Actor *a); + + /*! Called when stone above item changes. */ + virtual void stone_change(Stone *st); + + /*! Called when item is ``hit'' by a moving stone. */ + virtual void on_stonehit(Stone *st); + + /*! Called when item is ``hit'' by an actor. Return true if + the item should be picked up. */ + virtual bool actor_hit(Actor *a); + + /*! The model used for displaying this item in an + inventory. */ + virtual string get_inventory_model(); + + /* Called when item is activated by the owner of `a'. */ + virtual ItemAction activate(Actor* a, GridPos p); + protected: + // GridObject interface + virtual void set_model (const std::string &mname) { + display::SetModel(GridLoc(GRID_ITEMS, get_pos()), mname); + } + + virtual display::Model *get_model () { + return display::GetModel(GridLoc(GRID_ITEMS, get_pos())); + } + + virtual void kill_model (GridPos p) { + display::KillModel (GridLoc (GRID_ITEMS, p)); + } + // replace template method hook + virtual void setup_successor(Item *newitem) {} + }; + +/* -------------------- Inline functions -------------------- */ + + /*! Return unique item type identifier. */ + inline ItemID get_id (Item *it) { + if (it) + return it->get_traits().id; + return it_none; + } + + inline bool has_flags (Item *it, ItemFlags flags) { + return (it->get_traits().flags & flags) == flags; + } + +/* -------------------- Functions -------------------- */ + + void InitItems(); +} + +#endif diff --git a/project/jni/application/enigma/src/laser.cpp b/project/jni/application/enigma/src/laser.cpp new file mode 100644 index 000000000..80f94f7b0 --- /dev/null +++ b/project/jni/application/enigma/src/laser.cpp @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "laser.hh" +#include "sound.hh" +#include "stones_internal.hh" +#include "server.hh" +#include +#include +#include + +using namespace std; +using namespace world; +using namespace lasers; + +using stones::maybe_push_stone; +using ecl::V2; + +namespace +{ + +/* -------------------- LaserBeam -------------------- */ + + class LaserBeam : public Item, public LaserEmitter { + public: + static void emit_from(GridPos p, Direction d); + static void kill_all(); + static void all_emitted(); + + // LaserEmitter interface + DirectionBits emission_directions() const { return directions; } + static ItemTraits traits; + + const ItemTraits &get_traits() const { + return traits; + } + private: + LaserBeam(Direction dir) { + directions = to_bits(dir); + } + + // Item interface. + void on_laserhit(Direction dir); + void on_creation (GridPos p); + void init_model(); + bool actor_hit(Actor *actor); + + Item *clone() { + // new LaserBeams may only created inside `emit_from'. + assert(0); + return 0; + } + void dispose(); + + // Variables + DirectionBits directions; + + static vector instances; + static map old_laser_positions; + }; + ItemTraits LaserBeam::traits = {"it-laserbeam", it_laserbeam, + itf_static | itf_indestructible, 0.0 }; + + +/* -------------------- Laser Stones -------------------- */ + +/** \page st-laser Laser Stone + +These stones emit a laser beam in a specified direction when +activated. They are the only objects in the game that can act as +primary light sources (mirrors can also emit light but they require +an incoming beam). + +\subsection lasera Attributes + +- \b on: 1 if laser in active, 0 if not +- \b dir: the Direction in which light is emitted + (NORTH, EAST, SOUTH, WEST) + +\subsection laserm Messages + +- \b on, \b off, \b onoff: as usual + +\subsection lasersa See also + +\ref st-pmirror, \ref st-3mirror + +*/ + class LaserStone : public LaserEmitter, public stones::OnOffStone { + public: + LaserStone (Direction dir=EAST); + static void reemit_all(); + + private: + +// INSTANCELISTOBJ(LaserStone); + +// We can't use this macro here: g++ can't handle multiple inheritance +// and covariant return types at the same time ("sorry, not +// implemented: ..." first time I ever saw this error message :-) + + typedef std::vector InstanceList; + static InstanceList instances; + Stone *clone() { + LaserStone *o = new LaserStone(*this); + instances.push_back(o); + return o; + } + void dispose() { + instances.erase(find(instances.begin(), instances.end(), this)); + delete this; + } + + // LaserEmitter interface + DirectionBits emission_directions() const; + + // OnOffStone interface. + void notify_onoff(bool on); + + // Private methods. + void emit_light(); + Direction get_dir() const {return Direction(int_attrib("dir"));} + + // Stone interface. + void on_creation (GridPos p); + void init_model(); + }; +} + + +/* -------------------- PhotoCell -------------------- */ + +vector PhotoCell::instances; + +PhotoCell::~PhotoCell() +{ + photo_deactivate(); +} + + +/** + * This function notifies all instances of PhotoCell that a + * recalculation of the laser beams is about to begin by calling + * on_recalc_start() for each instance. + */ +void PhotoCell::notify_start() +{ + for(unsigned i=0; ion_recalc_start(); + } +} + +/** + * This function notifies all instances of PhotoCell that the engine + * has finished recalculating the laser beams by calling + * on_recalc_finish() for each instance. + */ +void PhotoCell::notify_finish() +{ + for(unsigned i=0; ion_recalc_finish(); + } +} + +void PhotoCell::photo_activate() +{ + vector::iterator i = std::find(instances.begin(), instances.end(), this); + if (i != instances.end()) + assert (0 || "Photocell activated twice\n"); + else + instances.push_back (this); +} + +void PhotoCell::photo_deactivate() +{ + vector::iterator i; + i = std::find (instances.begin(), instances.end(), this); + if (i != instances.end()) + instances.erase(i); +} + + +/* -------------------- PhotoStone -------------------- */ + +PhotoStone::PhotoStone(const char *kind) : Stone(kind) +{ + illuminated = false; +} + +void PhotoStone::on_recalc_start() +{} + +void PhotoStone::on_recalc_finish() +{ + GridPos p = get_pos(); + bool illu = (LightFrom(p, NORTH) || LightFrom(p, EAST) + || LightFrom(p, WEST) || LightFrom(p, SOUTH)); + + if (illu != illuminated) { + if (illu) notify_laseron(); + else notify_laseroff(); + illuminated = illu; + } +} + + +/* -------------------- LaserBeam -------------------- */ + +// The implementation of laser beams is a little tricky because, in +// spite of being implemented as Items, lasers aren't localized and +// changes to any part of the beam can affect the beam elsewhere. A +// `change' may be anything from moving a stone in or out of the beam, +// rotating or moving one of the mirrors, to making a stone in the +// beam transparent. +// +// Here are a couple of facts about laser beams in Enigma: +// +// - Laser beams are static. Once calculated they do not change until +// they are completely recalculated +// +// - LaserBeam::emit_from() is the only way to emit laser beams. A new +// beam will propagate automatically and stops only if it comes +// across an item or a stone that returns `false' from +// Stone::is_transparent(). +// +// - `on_laserhit()' is called for objects in the beam *whenever* +// the beam is recalculated. For objects that need to be notified +// when the laser goes on or off, use the `PhotoStone' +// mixin. + +vector LaserBeam::instances; +map LaserBeam::old_laser_positions; + +void LaserBeam::init_model() +{ + if (directions & (EASTBIT | WESTBIT)) { + if (directions & (NORTHBIT | SOUTHBIT)) + set_model("it-laserhv"); + else + set_model("it-laserh"); + } + else if (directions & (NORTHBIT | SOUTHBIT)) + set_model("it-laserv"); +} + +void LaserBeam::on_laserhit(Direction dir) +{ + DirectionBits dirbit = to_bits(dir); + if (!(directions & dirbit)) { + // `dir' not in `directions' ? + directions = DirectionBits(directions | dirbit); + emit_from(get_pos(), dir); + init_model(); + } +} + +void LaserBeam::on_creation (GridPos p) +{ + if (directions & EASTBIT) emit_from(p, EAST); + if (directions & WESTBIT) emit_from(p, WEST); + if (directions &NORTHBIT) emit_from(p, NORTH); + if (directions &SOUTHBIT) emit_from(p, SOUTH); + init_model(); +} + +void LaserBeam::emit_from(GridPos p, Direction dir) +{ + bool may_pass = true; + + p.move(dir); + if (Stone *st = GetStone(p)) { + may_pass = st->is_transparent (dir); + st->on_laserhit (dir); + } + + if (may_pass) { + if (Item *it = GetItem(p)) + it->on_laserhit (dir); + else { + LaserBeam *lb = new LaserBeam (dir); + SetItem(p, lb); + instances.push_back(lb); + } + } +} + +bool LaserBeam::actor_hit(Actor *actor) +{ + double r = get_radius(actor); + V2 p = actor->get_pos(); + GridPos gp = get_pos(); + + // distance of actor from center of the grid + double dx = fabs(p[0] - gp.x - 0.5) - r; + double dy = fabs(p[1] - gp.y - 0.5) - r; + + if ((directions & (EASTBIT | WESTBIT) && dy<-0.1) || + (directions & (NORTHBIT | SOUTHBIT)) && dx<-0.1) + { + SendMessage(actor, "laserhit"); + } + + return false; // laser beams can't be picked up +} + +void LaserBeam::kill_all() +{ + assert(old_laser_positions.empty()); + + while (!instances.empty()) + { + LaserBeam *lb = instances[0]; + GridPos pos = lb->get_pos(); + + old_laser_positions[pos] = static_cast(lb->directions); + world::KillItem(pos); + } +} + +void LaserBeam::all_emitted() +{ + vector::const_iterator end = instances.end(); + map::iterator none = old_laser_positions.end(); + + double x = 0, y = 0; + int count = 0; + + for (vector::const_iterator i = instances.begin(); i != end; ++i) { + LaserBeam *lb = *i; + GridPos pos = lb->get_pos(); + map::iterator found = old_laser_positions.find(pos); + + if (found != none) { + // a beam was at the current position (during last kill_all()) + DirectionBits old_dir = static_cast(found->second); + + if ((old_dir&lb->directions) != lb->directions) { + // a beam has been added here + x += pos.x; + y += pos.y; + ++count; + } + } + else { + // store newly created LaserBeams + x += pos.x; + y += pos.y; + ++count; + } + } + + if (count) { + sound::EmitSoundEvent ("laseron", ecl::V2(x/count+.5, y/count+.5), + getVolume("laseron", NULL)); + } + + old_laser_positions.clear(); +} + +void LaserBeam::dispose() +{ + instances.erase(std::find(instances.begin(), instances.end(), this)); + delete this; +} + + +//---------------------------------------- +// Laser stone +//---------------------------------------- +LaserStone::InstanceList LaserStone::instances; + +LaserStone::LaserStone (Direction dir) +: OnOffStone("st-laser") +{ + set_attrib("dir", Value(dir)); +} + +DirectionBits +LaserStone::emission_directions() const +{ + if (is_on()) { + return to_bits(get_dir()); + } + return NODIRBIT; +} + + +void LaserStone::reemit_all() +{ + for (unsigned i=0; iemit_light(); + } +} + +void LaserStone::notify_onoff(bool /*on*/) +{ + RecalcLight(); +} + +void LaserStone::emit_light() +{ + if (is_on()) + LaserBeam::emit_from(get_pos(), get_dir()); +} + +void LaserStone::on_creation (GridPos p) +{ + if (is_on()) + RecalcLight(); + Stone::on_creation(p); +} + +void LaserStone::init_model() +{ + string mname = is_on() ? "st-laseron" : "st-laser"; + mname += to_suffix(get_dir()); + set_model(mname); +} + + +/* -------------------- MirrorStone -------------------- */ +namespace +{ + class MirrorStone + : public Stone, public LaserEmitter, public PhotoCell + { + protected: + MirrorStone(const char *name, bool movable=false, bool transparent=false); + + bool is_transparent() const { return int_attrib("transparent") != 0; } + bool is_movable() const { return int_attrib("movable") != 0; } + + void set_orientation(int o) { set_attrib("orientation", o); } + int get_orientation() { return int_attrib("orientation"); } + + void emit_light(Direction dir) { + if (!has_dir(outdirs, dir)) + { + outdirs = DirectionBits(outdirs | to_bits(dir)); + LaserBeam::emit_from(get_pos(), dir); + } + } + + void init_model(); + private: + // Object interface. + virtual Value message(const string &m, const Value &); + + // LaserEmitter interface + DirectionBits emission_directions() const { + return outdirs; + } + + // PhotoCell interface + void on_recalc_start() { outdirs = NODIRBIT; } + void on_recalc_finish() {} + + // Stone interface + void actor_hit(const world::StoneContact &sc); + void on_creation (GridPos p); + void on_removal (GridPos p); + bool is_transparent(Direction) const { return is_transparent(); } + + // Private methods + void rotate_right(); + + // Variables + DirectionBits outdirs; + }; +} + +MirrorStone::MirrorStone(const char *name, bool movable, bool transparent) +: Stone(name), outdirs(NODIRBIT) +{ + set_attrib("transparent", transparent); + set_attrib("movable", movable); + set_attrib("orientation", Value(1)); +} + +void MirrorStone::init_model() { + string mname = get_kind(); + mname += is_movable() ? "-m" : "-s"; + mname += is_transparent() ? "t" : "o"; + mname += char('0' + get_orientation()); + set_model(mname); +} + +Value MirrorStone::message(const string &m, const Value &val) { + if (m == "trigger" || m=="turn") { + rotate_right(); + } + else if (m == "signal") { + if (to_double(val) != 0) { + rotate_right(); + } + } + else if (m == "mirror-north") { + set_orientation(3); + init_model(); + MaybeRecalcLight(get_pos()); + } + else if (m == "mirror-east") { + set_orientation(4); + init_model(); + MaybeRecalcLight(get_pos()); + } + else if (m == "mirror-south") { + set_orientation(1); + init_model(); + MaybeRecalcLight(get_pos()); + } + else if (m == "mirror-west") { + set_orientation(2); + init_model(); + MaybeRecalcLight(get_pos()); + } + return Value(); +} + +void MirrorStone::actor_hit(const world::StoneContact &sc) +{ + if (is_movable()) + maybe_push_stone(sc); + rotate_right(); +} + +void MirrorStone::on_creation (GridPos p) +{ + photo_activate(); + Stone::on_creation(p); +} + +void MirrorStone::on_removal(GridPos p) +{ + photo_deactivate(); + Stone::on_removal(p); +} + +void MirrorStone::rotate_right() +{ + set_orientation(1+(get_orientation() % 4)); + init_model(); + MaybeRecalcLight(get_pos()); + sound_event ("mirrorturn"); +} + + + +/* -------------------- Plane Mirror -------------------- */ +namespace +{ + class PlaneMirror : public MirrorStone { + CLONEOBJ(PlaneMirror); + public: + PlaneMirror(char orientation='/', bool movable=false, bool transparent=false) + : MirrorStone("st-pmirror", movable, transparent) + { + SetOrientation(orientation); + } + private: + void SetOrientation(char o) { + const char *a = " -\\|/"; + MirrorStone::set_orientation(int (strchr(a,o)-a)); + } + char GetOrientation() { + const char *a = " -\\|/"; + return a[MirrorStone::get_orientation()]; + } + void on_laserhit(Direction dir); + }; +} + +void PlaneMirror::on_laserhit(Direction dir) +{ + char orientation = GetOrientation(); + bool transparent = is_transparent(); + + switch (orientation) { + case '|': + if (dir==EAST || dir==WEST) { + emit_light(reverse(dir)); + if (transparent) + emit_light(dir); + } + else if ((dir == NORTH || dir == SOUTH) && transparent && + server::GameCompatibility == GAMET_OXYD1) { + emit_light(dir); + } + break; + case '-': + if (dir==NORTH || dir==SOUTH) { + emit_light(reverse(dir)); + if (transparent) + emit_light(dir); + } + else if ((dir == EAST || dir == WEST) && transparent && + server::GameCompatibility == GAMET_OXYD1) { + emit_light(dir); + } + break; + case '/': + switch(dir) { + case EAST: emit_light(NORTH); break; + case SOUTH: emit_light(WEST); break; + case NORTH: emit_light(EAST); break; + case WEST: emit_light(SOUTH); break; + case NODIR: break; + } + if (transparent) + emit_light(dir); + break; + case '\\': + switch(dir) { + case EAST: emit_light(SOUTH); break; + case SOUTH: emit_light(EAST); break; + case NORTH: emit_light(WEST); break; + case WEST: emit_light(NORTH); break; + case NODIR: break; + } + if (transparent) + emit_light(dir); + break; + } +} + + +/* -------------------- TriangleMirror -------------------- */ + +namespace +{ + // The orientations of the TriangleMirror have an unusual definition, + // but we cannot change them w/o changing many levels + // + // Flat side of the triangle + // points to : Orientation : + // + // WEST 4 + // SOUTH 3 + // EAST 2 + // NORTH 1 + + class TriangleMirror : public MirrorStone { + CLONEOBJ(TriangleMirror); + public: + TriangleMirror(char orientation='v', bool movable=false, bool transparent=false) + : MirrorStone("st-3mirror", movable, transparent) + { + SetOrientation (orientation); + } + private: + + void SetOrientation(char o) { + const char *a = " v<^>"; + MirrorStone::set_orientation( int (strchr(a,o)-a)); + } + + Direction GetOrientation() // orientation of the flat side of the mirror + { + const Direction a[] = {NODIR, NORTH, EAST, SOUTH, WEST}; + return a[MirrorStone::get_orientation()]; + } + void on_laserhit (Direction dir); + }; +} + +void TriangleMirror::on_laserhit(Direction beam_dir) + // note: 'beam_dir' is the direction where laserbeam goes to +{ + // direction where flat side of triangle points to + Direction flat_dir = GetOrientation(); + Direction tip_dir = reverse(flat_dir); + + if (beam_dir == tip_dir) // beam hits the flat side + emit_light(flat_dir); + else if (beam_dir == flat_dir) { + // this is the "complicated" case where the light falls + // on the tip of the triangle + switch (beam_dir) { + case SOUTH: case NORTH: + emit_light(EAST); emit_light(WEST); break; + case WEST: case EAST: + emit_light(SOUTH); emit_light(NORTH); break; + case NODIR: break; + } + } else + emit_light(tip_dir); + + if (is_transparent()) + emit_light(beam_dir); +} + +//---------------------------------------------------------------------- +// FUNCTIONS +//---------------------------------------------------------------------- +namespace +{ + /* This flag is true iff all lasers should be recalculated at the + end of the next tick. */ + bool light_recalc_scheduled = false; +} + +void lasers::Init() { + Register (new LaserStone); + Register ("st-laser-n", new LaserStone(NORTH)); + Register ("st-laser-e", new LaserStone(EAST)); + Register ("st-laser-s", new LaserStone(SOUTH)); + Register ("st-laser-w", new LaserStone(WEST)); + + Register (new TriangleMirror); + Register ("st-mirror-3v", new TriangleMirror('v')); + Register ("st-mirror-3<", new TriangleMirror('<')); + Register ("st-mirror-3^", new TriangleMirror('^')); + Register ("st-mirror-3>", new TriangleMirror('>')); + Register ("st-mirror-3vm", new TriangleMirror('v', true)); + Register ("st-mirror-3m", new TriangleMirror('>', true)); + Register ("st-mirror-3vt", new TriangleMirror('v', false, true)); + Register ("st-mirror-3t", new TriangleMirror('>', false, true)); + Register ("st-mirror-3vtm", new TriangleMirror('v', true, true)); + Register ("st-mirror-3tm", new TriangleMirror('>', true, true)); + + Register (new PlaneMirror); + Register ("st-mirror-p|", new PlaneMirror('|')); + Register ("st-mirror-p/", new PlaneMirror('/')); + Register ("st-mirror-p-", new PlaneMirror('-')); + Register ("st-mirror-p\\", new PlaneMirror('\\')); + Register ("st-mirror-p|m", new PlaneMirror('|', true)); + Register ("st-mirror-p/m", new PlaneMirror('/', true)); + Register ("st-mirror-p-m", new PlaneMirror('-', true)); + Register ("st-mirror-p\\m", new PlaneMirror('\\', true)); + Register ("st-mirror-p|t", new PlaneMirror('|', false, true)); + Register ("st-mirror-p/t", new PlaneMirror('/', false, true)); + Register ("st-mirror-p-t", new PlaneMirror('-', false, true)); + Register ("st-mirror-p\\t", new PlaneMirror('\\', false, true)); + Register ("st-mirror-p|tm", new PlaneMirror('|', true, true)); + Register ("st-mirror-p/tm", new PlaneMirror('/', true, true)); + Register ("st-mirror-p-tm", new PlaneMirror('-', true, true)); + Register ("st-mirror-p\\tm", new PlaneMirror('\\', true, true)); +} + + +void lasers::MaybeRecalcLight(GridPos p) { + light_recalc_scheduled |= + (LightFrom(p, NORTH) || LightFrom(p, SOUTH) || + LightFrom(p, WEST) || LightFrom(p, EAST)); +} + +void lasers::RecalcLight() { + light_recalc_scheduled = true; +} + +bool lasers::LightFrom (GridPos p, Direction dir) { + p.move(dir); + if (LaserEmitter *le = dynamic_cast(GetStone(p))) + if (has_dir(le->emission_directions(), reverse(dir))) + return true; + if (LaserEmitter *le = dynamic_cast(GetItem(p))) + return (has_dir(le->emission_directions(), reverse(dir))); + return false; +} + +void lasers::RecalcLightNow() { + if (light_recalc_scheduled) { + PhotoCell::notify_start(); + LaserBeam::kill_all(); + LaserStone::reemit_all(); + PhotoCell::notify_finish(); + LaserBeam::all_emitted(); + light_recalc_scheduled = false; + } +} diff --git a/project/jni/application/enigma/src/laser.hh b/project/jni/application/enigma/src/laser.hh new file mode 100644 index 000000000..72853ddb6 --- /dev/null +++ b/project/jni/application/enigma/src/laser.hh @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef LASER_HH +#define LASER_HH + +/* This file contains the declarations for (almost) everything that + has to do with lasers. */ + +#include "objects.hh" + +namespace world +{ + /** + * This interface must be implemented by all items and stones that + * are capable of emitting light. + */ + class LaserEmitter { + public: + virtual ~LaserEmitter() {} + virtual DirectionBits emission_directions() const = 0; + }; + +/* -------------------- PhotoCell -------------------- */ + + /** + * PhotoCells are objects (not necessarily stones) that are + * sensitive to laser light. Whenever the game engine + * recalculates the laser beams, instances of this class are + * notified about the beginning and the end of a recalculation. + */ + class PhotoCell { + public: + virtual ~PhotoCell(); + + // ---------- Static functions ---------- + static void notify_start(); + static void notify_finish(); + + // ---------- PhotoCell interface ---------- + virtual void on_recalc_start() = 0; + virtual void on_recalc_finish() = 0; + protected: + + /*! Derived classes must call this method to register + themselves for the on_recalc_start() and on_recalc_finish() + events. */ + void photo_activate(); + + /*! Derived classes must call this method to unregister + themselves. It is automatically called by ~PhotoCell(), but + objects may have to call it explicitly if they are not + interested in PhotoCell events. */ + void photo_deactivate(); + private: + static std::vector instances; + }; + +/* -------------------- PhotoStone -------------------- */ + + /*! Most stones are indifferent to laser beams: They either block + the light completely or they let it pass, but they do not change + their internal state when they are hit by light. Certain kinds + of stones need to be notified whenever the `light' goes on or off + -- these can be derived from this class. + + The most prominent example are Oxyd stones -- they open when + they are hit by a laser beam. See the remarks at the beginning + of this file to understand why overriding `on_laserhit' is not + sufficient for a proper implementation of Oxyd stones. + */ + + class PhotoStone : public Stone, public PhotoCell { + protected: + PhotoStone (const char *kind); + + private: + bool illuminated; + + // PhotoCell interface + void on_recalc_start(); + void on_recalc_finish(); + + // PhotoStone interface + virtual void notify_laseron() = 0; + virtual void notify_laseroff() = 0; + }; +} + +/* -------------------- Functions -------------------- */ +namespace lasers +{ + void Init(); + + /*! This function must be called at the end of each tick; it + recalculates the laser beams if necessary. */ + void RecalcLightNow(); + + /*! Force all light beams to be recalculated at the end of the + current tick. So far, this is only used by laser stones and in + world::InitWorld(). */ + void RecalcLight(); + + /*! If position `p' is inside a laser beam, force all laser beams + to be recalculated. This is mainly used when items and stones + are created or removed, but it can be also used for objects + (like doors) that sometimes shut off a light beam (when the door + is closed) and sometimes don't (when the door is open). */ + void MaybeRecalcLight (enigma::GridPos p); + + /*! Return true iff a stone or an item at position `p' it hit by + light coming from direction `dir'. */ + bool LightFrom (enigma::GridPos p, enigma::Direction dir); +} +#endif diff --git a/project/jni/application/enigma/src/lev/Index.cpp b/project/jni/application/enigma/src/lev/Index.cpp new file mode 100644 index 000000000..f32f1d123 --- /dev/null +++ b/project/jni/application/enigma/src/lev/Index.cpp @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "lev/Index.hh" +#include "lev/VolatileIndex.hh" +#include "errors.hh" +#include "main.hh" +#include "options.hh" +#include "sound.hh" +#include "PreferenceManager.hh" +#include "StateManager.hh" +#include "lev/ScoreManager.hh" +#include "lev/RatingManager.hh" + +#include + +namespace enigma { namespace lev { + + std::map Index::indices; + std::map *> Index::indexGroups; + + Index * Index::currentIndex = NULL; + std::string Index::currentGroup; + std::map Index::nullExtensions; + + void Index::initGroups() { + ASSERT(indexGroups.empty(), XFrontend, "Reinitialization of groups"); + std::vector groupNames = getGroupNames(); + for (int i = 0; i < groupNames.size(); i++) { + std::vector *group = new std::vector; + indexGroups.insert(std::make_pair(groupNames[i], group)); + } + currentGroup = app.state->getString("CurrentGroup"); + } + + void Index::registerIndex(Index *anIndex) { + if (anIndex == NULL) + return; + + // check for uniqueness of index name + if (findIndex(anIndex->getName()) != NULL) + return; + + indices.insert(std::make_pair(anIndex->getName(), anIndex)); + + + // register index in state.xml and update current position, first with last values + std::string groupName = ""; + double stateLocation = 0; + app.state->addIndex(anIndex->getName(), groupName, stateLocation, + anIndex->currentPosition, anIndex->screenFirstPosition); + + // user location for index? + if (stateLocation > 0) + anIndex->indexLocation = stateLocation; + + // reset positions that are out of range - this may happen due to + // modified levelpacks (updates, deleted levels in auto, new commandline) + if (anIndex->currentPosition < 0 || anIndex->currentPosition >= anIndex->size()) + anIndex->currentPosition = 0; + + // check user preferences for assigned group + if (!groupName.empty()) + anIndex->indexGroup = groupName; // use users preference + else + groupName = anIndex->indexGroup; // use index default group + + std::vector * group; + + // if no prefs ask for index default group + + // make new group if not existing + if (groupName != INDEX_EVERY_GROUP) { + std::map *>::iterator i = indexGroups.find(groupName); + if (i != indexGroups.end()) { + group = i->second; + } else { + // make the group + group = new std::vector; + indexGroups.insert(std::make_pair(groupName, group)); + app.state->addGroup(groupName, anIndex->getName(), 0); + + // fill group with indices that appear in every group + std::map::iterator iti; + for (iti = indices.begin(); iti != indices.end(); iti++) + if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP) + addIndexToGroup((*iti).second, group); + } + } + + if (groupName != INDEX_EVERY_GROUP) { + // insert according to user prefs or index defaults + addIndexToGroup(anIndex, group); + addIndexToGroup(anIndex, getGroup(INDEX_ALL_PACKS)); + } else { + // add index to all groups inclusive INDEX_ALL_PACKS + std::map *>::iterator itg; + for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++) + addIndexToGroup(anIndex, (*itg).second); + } + return; + } + + void Index::addIndexToGroup(Index *anIndex, std::vector * aGroup) { + std::vector::iterator itg; + for (itg = aGroup->begin(); itg != aGroup->end() && + (*itg)->indexLocation <= anIndex->indexLocation; + itg++) { + } + aGroup->insert(itg, anIndex); + } + + void Index::removeIndexFromGroup(Index *anIndex, std::string groupName) { + std::vector *theGroup = indexGroups[groupName]; + std::vector::iterator itg; + for (itg = theGroup->begin(); itg != theGroup->end(); itg++) { + if ((*itg) == anIndex) { + theGroup->erase(itg); + return; + } + } + } + + Index * Index::findIndex(std::string anIndexName) { + std::string::size_type lastChar = anIndexName.find_last_not_of(" "); + if (lastChar == std::string::npos) + // the name is effectively an empty string + return NULL; + + // stip of trailing and leading spaces + std::string name = anIndexName.substr(0 , lastChar + 1); + name = name.substr(anIndexName.find_first_not_of(" ")); + + std::map::iterator i = indices.find(name); + if (i != indices.end()) + return i->second; + else + return NULL; + } + + std::string Index::getCurrentGroup() { + if (currentIndex == NULL) + // initialize current group + getCurrentIndex(); + return currentGroup; + } + + void Index::setCurrentGroup(std::string groupName) { + // set group - even "All Packs" + app.state->setProperty("CurrentGroup", groupName); + currentGroup = groupName; + + // set current index for desired group + std::string indexName = getGroupSelectedIndex(groupName); + Index * newIndex = findIndex(indexName); + std::string indexGroupName; + + if (newIndex != NULL) + indexGroupName = newIndex->getGroupName(); + + if (newIndex != NULL && (indexGroupName == groupName || + indexGroupName == INDEX_EVERY_GROUP || + groupName == INDEX_ALL_PACKS)) { + // set the groups current index as main current index + setCurrentIndex(indexName); + } else { + // the groups current index is no longer available or did change the + // group -- reset the groups current index + std::vector * group = getGroup(groupName); + if (group->size() > 0) { + setCurrentIndex((*group)[0]->getName()); + } else { + // the group is empty -- delete group current index entry and + // leave the apps current index unchanged + setGroupSelectedIndex(groupName,""); + } + } +// Log << "Index setCurrentGroup: wanted " << groupName << " - got " << currentGroup << " - idxGroup " << indexGroupName <<"\n"; + } + + std::vector Index::getGroupNames() { + std::vector names; + app.state->getGroupNames(&names); + return names; + } + + void Index::deleteGroup(std::string groupName) { + std::vector * theGroup = getGroup(groupName); + if (theGroup != NULL) { + indexGroups.erase(groupName); + delete theGroup; + } + + if (currentGroup == groupName) { + std::vector groups = getGroupNames(); + for (int i = 0; i < groups.size(); i++) { + if (groups[i] == groupName) { + if (i > 0) { + setCurrentGroup(groups[i-1]); + } else { + ASSERT (groups.size() > 1, XFrontend, "Delete of last existing group."); + setCurrentGroup(groups[1]); + } + break; + } + } + } + app.state->deleteGroup(groupName); + } + + void Index::moveGroup(std::string groupName, int newPos) { + std::string indexName = app.state->getGroupSelectedIndex(groupName); + std::string column = app.state->getGroupSelectedColumn(groupName); + app.state->deleteGroup(groupName); + app.state->insertGroup(newPos, groupName, indexName, column); + } + + void Index::renameGroup(std::string oldName, std::string newName) { + // rename state group element + app.state->renameGroup(oldName, newName); + + // rename map of groups + std::vector * group = getGroup(oldName); + indexGroups.erase(oldName); + indexGroups.insert(std::make_pair(newName, group)); + + // rename group name in indices + for (int i = 0; i < group->size(); i++) { + if ((*group)[i]->getGroupName() == oldName) { + (*group)[i]->indexGroup = newName; + // set group as users choice for index in state + app.state->setIndexGroup((*group)[i]->getName(), newName); + } + } + + // handle currentGroup + if (currentGroup == oldName) { + currentGroup = newName; + app.state->setProperty("CurrentGroup", newName); + } + } + + void Index::insertGroup(std::string groupName, int newPos) { + // make the group + std::vector *group = new std::vector; + indexGroups.insert(std::make_pair(groupName, group)); + app.state->insertGroup(newPos, groupName, "", ""); + + // fill group with indices that appear in every group + std::map::iterator iti; + for (iti = indices.begin(); iti != indices.end(); iti++) + if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP) + addIndexToGroup((*iti).second, group); + + setCurrentGroup(groupName); + } + + std::string Index::getGroupSelectedIndex(std::string groupName) { + return app.state->getGroupSelectedIndex(groupName); + } + + int Index::getGroupSelectedColumn(std::string groupName) { + std::string columnString = app.state->getGroupSelectedColumn(groupName); + if (columnString.empty()) + return INDEX_GROUP_COLUMN_UNKNOWN; + else { + int col = INDEX_GROUP_COLUMN_UNKNOWN; + std::sscanf(columnString.c_str(), "%i", &col); + return col; + } + } + + void Index::setGroupSelectedIndex(std::string groupName, std::string indexName) { + app.state->setGroupSelectedIndex(groupName, indexName); + } + + void Index::setGroupSelectedColumn(std::string groupName, int column) { + if (column == INDEX_GROUP_COLUMN_UNKNOWN) + app.state->setGroupSelectedColumn(groupName, ""); + else + app.state->setGroupSelectedColumn(groupName, ecl::strf("%d",column)); + } + + Index * Index::getCurrentIndex() { + if (currentIndex == NULL) { + // first look for user preference + if (setCurrentIndex(app.state->getGroupSelectedIndex( + app.state->getString("CurrentGroup")))) + ; + + // fallback to "Tutorial" pack + else if (setCurrentIndex("Tutorial")) + ; + + // fallback to any pack + else if (!indices.empty()) { + setCurrentIndex(indices.begin()->second->getName()); + } + // add empty pack + else { + std::vector emptyList; + registerIndex(new lev::VolatileIndex("Empty Index", + INDEX_DEFAULT_GROUP, emptyList)); + setCurrentIndex("Empty Index"); + } + } + return currentIndex; + } + + bool Index::setCurrentIndex(std::string anIndexName) { + Index * newIndex = findIndex(anIndexName); + if (newIndex != NULL) { + if (newIndex != currentIndex) { + sound::SetDefaultSoundSet(newIndex->get_default_SoundSet()); + currentIndex = newIndex; + std::string group = currentIndex->getGroupName(); + if (group != INDEX_EVERY_GROUP && + app.state->getString("CurrentGroup") != INDEX_ALL_PACKS) { + app.state->setProperty("CurrentGroup", group); + currentGroup = group; + } + if (getGroupSelectedIndex(currentGroup) != currentIndex->getName()) { + setGroupSelectedIndex(currentGroup, currentIndex->getName()); + setGroupSelectedColumn(currentGroup, INDEX_GROUP_COLUMN_UNKNOWN); + } + } + return true; + } + return false; + } + + Index * Index::nextGroupIndex() { + std::vector * curGroup = getGroup(currentGroup); + ASSERT(curGroup != NULL, XFrontend, ""); + + for (int i = 0; i < curGroup->size() - 1; i++) { + if ((*curGroup)[i] == currentIndex) + return (*curGroup)[i+1]; + } + return currentIndex; + } + + Index * Index::previousGroupIndex() { + std::vector * curGroup = getGroup(currentGroup); + ASSERT(curGroup != NULL, XFrontend, ""); + + for (int i = 1; i < curGroup->size(); i++) { + if ((*curGroup)[i] == currentIndex) + return (*curGroup)[i-1]; + } + return currentIndex; + } + + Proxy * Index::getCurrentProxy() { + return getCurrentIndex()->getCurrent(); + } + + std::vector * Index::getGroup(std::string groupName) { + std::map *>::iterator i = indexGroups.find(groupName); + if (i != indexGroups.end()) + return i->second; + else + return NULL; + } + + double Index::getNextUserLocation() { + double lastUsed = INDEX_USER_PACK_LOCATION; + std::map::iterator iti; + for (iti = indices.begin(); iti != indices.end(); iti++) { + double idxLocation = (*iti).second->indexLocation; + if (idxLocation > lastUsed && idxLocation < INDEX_DEFAULT_PACK_LOCATION) { + lastUsed = idxLocation; + } + } + if (lastUsed + 999 < INDEX_DEFAULT_PACK_LOCATION) + return lastUsed + 100; + else + return 0.9 * lastUsed + INDEX_DEFAULT_PACK_LOCATION / 10; + } + + + Index::Index(std::string anIndexName, std::string aGroupName, double defaultLocation) : + indexName (anIndexName), indexGroup (aGroupName), defaultGroup (aGroupName), + indexLocation (defaultLocation), indexDefaultLocation (defaultLocation), + currentPosition (0), screenFirstPosition (0) { + } + + Index::~Index() {} + + std::string Index::getName() { + return indexName; + } + + std::string Index::getGroupName() { + return indexGroup; + } + + std::string Index::getDefaultGroupName() { + return defaultGroup; + } + + double Index::getLocation() { + return indexLocation; + } + + double Index::getDefaultLocation() { + return indexDefaultLocation; + } + + void Index::setDefaultLocation(double defLocation) { + indexDefaultLocation = defLocation; + } + + void Index::moveToGroup(std::string newGroupName) { + // remove from old group + if (indexGroup != INDEX_EVERY_GROUP) { + // remove index from the unique group + removeIndexFromGroup(this, indexGroup); + removeIndexFromGroup(this, INDEX_ALL_PACKS); + } else { + // remove index from all groups inclusive INDEX_ALL_PACKS + std::vector groupNames = getGroupNames(); + for (int i = 0; i < groupNames.size(); i++) + removeIndexFromGroup(this, groupNames[i]); + // declare this index as not belonging to any group + indexGroup = ""; + } + + // create group if not existing + if (newGroupName != INDEX_EVERY_GROUP) { + std::map *>::iterator i = indexGroups.find(newGroupName); + if (i == indexGroups.end()) { + // make the group + std::vector *group = new std::vector; + indexGroups.insert(std::make_pair(newGroupName, group)); + app.state->addGroup(newGroupName, indexName, 0); + + // fill group with indices that appear in every group + std::map::iterator iti; + for (iti = indices.begin(); iti != indices.end(); iti++) + if ((*iti).second->getGroupName() == INDEX_EVERY_GROUP) + addIndexToGroup((*iti).second, group); + } + } + + + indexGroup = newGroupName; + + // add to new group + if (newGroupName != INDEX_EVERY_GROUP) { + // insert according to user prefs or index defaults + addIndexToGroup(this, getGroup(newGroupName)); + addIndexToGroup(this, getGroup(INDEX_ALL_PACKS)); + } else { + // add index to all groups inclusive INDEX_ALL_PACKS + std::map *>::iterator itg; + for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++) + addIndexToGroup(this, (*itg).second); + } + + // store new group as users state + app.state->setIndexGroup(indexName, + newGroupName == defaultGroup ? "" : newGroupName); + + // select this index with its new group if it is the current Index + if (this == currentIndex) { + if (indexGroup != INDEX_EVERY_GROUP && + app.state->getString("CurrentGroup") != INDEX_ALL_PACKS) { + app.state->setProperty("CurrentGroup", indexGroup); + currentGroup = indexGroup; + } + if (getGroupSelectedIndex(currentGroup) != currentIndex->getName()) { + setGroupSelectedIndex(currentGroup, currentIndex->getName()); + setGroupSelectedColumn(currentGroup, INDEX_GROUP_COLUMN_UNKNOWN); + } + } + } + + int indexLocationCompare(Index * first, Index * second) { + return first->getLocation() < second->getLocation(); + } + + void Index::locateBehind(std::string predName) { + double predLocation = 0; + double succLocation = 0; + double newLocation; + std::string predGroup; + std::string succGroup; + std::vector * allGroup = getGroup(INDEX_ALL_PACKS); + if (predName.empty()) { + succLocation = (*allGroup)[0]->indexLocation; + succGroup = (*allGroup)[0]->indexGroup; + } else { + for (int i = 0; i < allGroup->size(); i++) { + if ((*allGroup)[i]->getName() == predName) { + predLocation = (*allGroup)[i]->indexLocation; + predGroup = (*allGroup)[i]->indexGroup; + int succ = 0; + if ((i+1 < allGroup->size()) && ((*allGroup)[i+1] != this)) { + succ = i + 1; + } else if (i+2 < allGroup->size()) { + succ = i + 2; + } + if (succ > 0) { + succLocation = (*allGroup)[succ]->indexLocation; + succGroup = (*allGroup)[succ]->indexGroup; + } + break; + } + } + } + ASSERT (!(predLocation == 0 && succLocation == 0), XFrontend, ""); + if (predLocation == 0) { + if (succGroup == indexGroup) { + newLocation = succLocation * 0.98; + } else { + newLocation = succLocation * 0.75; + } + } else if (succLocation == 0) { + if (predGroup == indexGroup) { + newLocation = predLocation + 100; + } else { + newLocation = predLocation + 10000; + } + } else if ((predGroup == indexGroup && succGroup == indexGroup) || + (predGroup != indexGroup && succGroup != indexGroup)){ + newLocation = (predLocation + succLocation) / 2; + } else if (predGroup == indexGroup) { + newLocation = 0.95 * predLocation + 0.05 * succLocation; + } else { + newLocation = 0.05 * predLocation + 0.95 * succLocation; + } +// Log << "newLocation " << newLocation << "\n"; + indexLocation = newLocation; + app.state->setIndexLocation(indexName, indexLocation); + + // reorder all groups according to new location + std::map *>::iterator itg; + for (itg = indexGroups.begin(); itg != indexGroups.end(); itg++) + std::sort((*itg).second->begin(), (*itg).second->end(), indexLocationCompare); + } + + void Index::renameIndex(std::string newName) { + indices.erase(indexName); + indices[newName] = this; + app.state->setIndexName(indexName, newName); + indexName = newName; + } + + bool Index::isSource(Proxy *) { + return false; + } + + int Index::getCurrentPosition() { + return currentPosition; + } + + int Index::getCurrentLevel() { + return currentPosition + 1; + } + + Proxy * Index::getCurrent() { + return getProxy(currentPosition); + } + + void Index::setCurrentPosition(int newPos) { + // reset positions that are out of range - this may happen due to + // editable Indices + if (newPos < 0 || newPos > size()) + newPos = 0; + + // + currentPosition = newPos; + app.state->setIndexCurpos(getName(), currentPosition); + } + + int Index::getScreenFirstPosition() { + return screenFirstPosition; + } + + void Index::setScreenFirstPosition(int iFirstPos) { + screenFirstPosition = iFirstPos; + app.state->setIndexCurfirst(getName(), screenFirstPosition); + } + + bool Index::mayPlayLevel(int levelNumber) { + return true; + } + + Proxy * Index::getProxy(int pos) { + if (pos >= 0 && pos < proxies.size()) + return proxies[pos]; + else + return NULL; + } + + bool Index::containsProxy(Proxy * aProxy) { + for (int i = 0; i < proxies.size(); i++) { + if (proxies[i] == aProxy) + return true; + } + return false; + } + + bool Index::hasNormLevelPath(std::string path) { + for (int i = 0; i < proxies.size(); i++) { + if (proxies[i]->getNormLevelPath() == path) + return true; + } + return false; + } + + bool Index::advanceLevel(LevelAdvanceMode advMode) { + NextLevelMode nextMode = static_cast(app.state->getInt("NextLevelMode")); + + switch (advMode) { + case ADVANCE_STRICTLY: + nextMode = NEXT_LEVEL_STRICTLY; + break; + case ADVANCE_UNSOLVED: + nextMode = NEXT_LEVEL_UNSOLVED; + break; + default: + break; + }; + + bool found = false; + const int max = size(); + int newPos = currentPosition; + lev::ScoreManager *scm = lev::ScoreManager::instance(); + lev::RatingManager *ratingMgr = lev::RatingManager::instance(); + int difficulty = app.state->getInt("Difficulty"); + + while (newPos < max - 1 && !found) { + ++newPos; + + if (nextMode == NEXT_LEVEL_UNSOLVED || nextMode == NEXT_LEVEL_NOT_BEST || + nextMode == NEXT_LEVEL_OVER_PAR) { + bool solved = scm->isSolved(proxies[newPos], difficulty); + if (!solved) // always play unsolved levels + found = true; + else { // solved levels + if (nextMode == NEXT_LEVEL_NOT_BEST) { + int par_time = ratingMgr->getBestScore(proxies[newPos], difficulty); + int best_user_time = scm->getBestUserScore(proxies[newPos], difficulty); + if (best_user_time<0 || (par_time>0 && best_user_time>par_time)) + found = true; + } else if (nextMode == NEXT_LEVEL_OVER_PAR) { + int par_time = ratingMgr->getParScore(proxies[newPos], difficulty); + int best_user_time = scm->getBestUserScore(proxies[newPos], difficulty); + if (best_user_time<0 || (par_time>0 && best_user_time>par_time)) + found = true; + } + } + } + else + found = true; + } + if (!found) + newPos = 0; // ? + + currentPosition = newPos; + return found; + } + + int Index::size() const { + return proxies.size(); + } + + void Index::appendProxy(Proxy * newLevel, controlType varCtrl, + scoreUnitType varUnit, std::string varTarget, + std::map varExtensions) { + proxies.push_back(newLevel); + } + + void Index::clear() { +// proxies.clear(); + } + + void Index::updateFromProxies() { + for (int i = 0, l = proxies.size(); i < l; i++) { + try { + proxies[i]->loadMetadata(true); + } catch (XLevelLoading &err) { + // silently ignore errors + } + } + } + + /* ---------- LevelPack interface ---------- */ + + + /*! Return the default SoundSet (see options::SoundSet for meaning) */ + const char* Index::get_default_SoundSet() const { + return "Enigma"; + } + + /*! Returns true if it's a twoplayer levelpack, but has no + it-yinyang (needed to add it-yinyang to inventory if + oxyd-linkgame is played as single-player) */ + bool Index::needs_twoplayers() const { + return false; + } + +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/Index.hh b/project/jni/application/enigma/src/lev/Index.hh new file mode 100644 index 000000000..b08371cac --- /dev/null +++ b/project/jni/application/enigma/src/lev/Index.hh @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LEV_INDEX_HH_INCLUDED +#define LEV_INDEX_HH_INCLUDED + +#include "lev/Proxy.hh" + +#include +#include +#include + +#define INDEX_DEFAULT_GROUP "User" +#define INDEX_EVERY_GROUP "Every Group" +#define INDEX_ALL_PACKS "All Packs" + +#define INDEX_STARTUP_PACK_NAME "Startup Levels" +#define INDEX_AUTO_PACK_NAME "Auto Folder" +#define INDEX_HISTORY_PACK_NAME "History" +#define INDEX_SEARCH_PACK_NAME "Search Result" +#define INDEX_CLIPBOARD_PACK_NAME "Clipboard" + +#define INDEX_STARTUP_PACK_LOCATION 5100 +#define INDEX_AUTO_PACK_LOCATION 5200 +#define INDEX_HISTORY_PACK_LOCATION 5300 +#define INDEX_SEARCH_PACK_LOCATION 5400 +#define INDEX_CLIPBOARD_PACK_LOCATION 5500 + +#define INDEX_USER_PACK_LOCATION 50000 +#define INDEX_DEFAULT_PACK_LOCATION 69000 + +#define INDEX_GROUP_COLUMN_UNKNOWN -1000 + + + +namespace enigma { namespace lev { + enum LevelAdvanceMode { + ADVANCE_NEXT_MODE, // honor NextLevelMode + ADVANCE_STRICTLY, // move to the next level in index + ADVANCE_UNSOLVED // move to next not yet solved level + }; + + enum NextLevelMode { + NEXT_LEVEL_STRICTLY, // move to the next level in index + NEXT_LEVEL_UNSOLVED, // move to next not yet solved level + NEXT_LEVEL_NOT_BEST, // move to next level where player is not best score holder + NEXT_LEVEL_OVER_PAR // move to next level with a score over PAR + }; + + /** + * + */ + class Index { + public: + static void initGroups(); + static void registerIndex(Index *anIndex); + static Index * findIndex(std::string anIndexName); + static Index * getCurrentIndex(); + static bool setCurrentIndex(std::string anIndexName); + static Index * nextGroupIndex(); + static Index * previousGroupIndex(); + static Proxy * getCurrentProxy(); + static std::string getCurrentGroup(); + static void setCurrentGroup(std::string groupName); + static std::vector getGroupNames(); + static std::vector * getGroup(std::string groupName); + static std::string getGroupSelectedIndex(std::string groupName); + static int getGroupSelectedColumn(std::string groupName); + static void setGroupSelectedIndex(std::string groupName, std::string indexName); + static void setGroupSelectedColumn(std::string groupName, int column); + static void deleteGroup(std::string groupName); + static void moveGroup(std::string groupName, int newPos); + static void renameGroup(std::string oldName, std::string newName); + static void insertGroup(std::string groupName, int newPos); + static double getNextUserLocation(); + + /** + * Convention: method names *Level() can take int pos or Proxy as arg. + */ + Index(std::string anIndexName = "Unnamed Pack", + std::string aGroupName = INDEX_DEFAULT_GROUP, + double defaultLocation = INDEX_DEFAULT_PACK_LOCATION); + ~Index(); + + std::string getName(); + std::string getGroupName(); + std::string getDefaultGroupName(); + double getLocation(); + double getDefaultLocation(); + void setDefaultLocation(double defLocation); + void moveToGroup(std::string groupName); + void locateBehind(std::string indexName); + void renameIndex(std::string newName); + virtual bool isSource(Proxy * aProxy); + + int getCurrentPosition(); // 0 .. size-1 + int getCurrentLevel(); // 1 .. size + Proxy * getCurrent(); + void setCurrentPosition(int newPos); + int getScreenFirstPosition(); + void setScreenFirstPosition(int iFirstPos); + virtual bool mayPlayLevel(int levelNumber); + Proxy * getProxy(int pos); + bool containsProxy(Proxy * aProxy); + bool hasNormLevelPath(std::string path); + virtual bool advanceLevel(LevelAdvanceMode advMode); + + /*! Return number of levels */ + virtual int size() const; + virtual void appendProxy(Proxy * newLevel, controlType varCtrl = force, + scoreUnitType varUnit = duration, std::string varTarget = "time", + std::map varExtensions = nullExtensions); + virtual void clear(); + void updateFromProxies(); + + // ---------- LevelPack legacy methods ---to be renamed ------- */ + /*! Return the default SoundSet (see options::SoundSet for meaning) */ + virtual const char* get_default_SoundSet() const; + + /*! Returns true if it's a twoplayer levelpack, but has no + it-yinyang (needed to add it-yinyang to inventory if + oxyd-linkgame is played as single-player) */ + virtual bool needs_twoplayers() const; + + + protected: + std::string indexName; + std::string indexGroup; + double indexLocation; + std::string defaultGroup; + double indexDefaultLocation; + int currentPosition; // 0,... + int screenFirstPosition; // LevelWidget ifirst + std::vector proxies; + static std::map nullExtensions; + + private: + /** + * A map of index names to the indices themselves. + */ + static std::map indices; + + /** + * A map of index group names to vectors of indices. The vectors + * are sorted by the user sequence preference in the index group menu. + * Every index is listed in the group the user asigned it to. + */ + static std::map *> indexGroups; + + /** + * Current active index. This index is selected in the Levelpack menu, + * shown and used in the submenus and stored in the user preferences. + * It's default is the "Tutorial" index. + */ + static Index * currentIndex; + static std::string currentGroup; + + static void addIndexToGroup(Index *anIndex, std::vector * aGroup); + static void removeIndexFromGroup(Index *anIndex, std::string groupName); + + }; + +}} // namespace enigma::lev +#endif diff --git a/project/jni/application/enigma/src/lev/PersistentIndex.cpp b/project/jni/application/enigma/src/lev/PersistentIndex.cpp new file mode 100644 index 000000000..2bf3bb5cf --- /dev/null +++ b/project/jni/application/enigma/src/lev/PersistentIndex.cpp @@ -0,0 +1,1102 @@ +/* + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "lev/PersistentIndex.hh" +#include "lev/Proxy.hh" +#include "lev/RatingManager.hh" +#include "errors.hh" +#include "gui/ErrorMenu.hh" +#include "main.hh" +#include "nls.hh" +#include "file.hh" +#include "options.hh" +#include "oxyd.hh" +#include "LocalToXML.hh" +#include "utilXML.hh" +#include "Utf8ToXML.hh" +#include "XMLtoUtf8.hh" +#include "ecl_system.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#endif + +using namespace std; +XERCES_CPP_NAMESPACE_USE + +namespace enigma { namespace lev { + Variation::Variation(controlType ctrlValue, scoreUnitType unitValue, + std::string targetValue) : ctrl (ctrlValue), + unit (unitValue), target (targetValue) { + } + + bool Variation::operator == (const Variation& otherVar) { + return ctrl == otherVar.ctrl && unit == otherVar.unit && + target == otherVar.target && extensions == otherVar.extensions; + } + + PersistentIndex * PersistentIndex::historyIndex = NULL; + std::vector PersistentIndex::indexCandidates; + + void PersistentIndex::checkCandidate(PersistentIndex * candidate) { + if (candidate->getName().empty() || + candidate->getCompatibility() > ENIGMACOMPATIBITLITY) { + delete candidate; + } else { + // check if new Index is an update of another + for (int i = 0; i < indexCandidates.size(); i++) { + if (indexCandidates[i]->getName() != candidate->getName()) { + continue; + } else if (indexCandidates[i]->getRelease() != candidate->getRelease() || + indexCandidates[i]->getRevision() >= candidate->getRevision()) { + delete candidate; + return; + } else { + // it is an update + delete indexCandidates[i]; + indexCandidates[i] = candidate; + return; + } + } + indexCandidates.push_back(candidate); + } + } + + void PersistentIndex::registerPersistentIndices(bool onlySystemIndices) { + DirIter * dirIter; + DirEntry dirEntry; + + // SysemPath: register dirs and zips with xml-indices + std::vector sysPaths = app.systemFS->getPaths(); + std::set candidates; + std::set candidates2; + for (int i = 0; i < sysPaths.size(); i++) { + dirIter = DirIter::instance(sysPaths[i] + "/levels"); + while (dirIter->get_next(dirEntry)) { + if (dirEntry.is_dir && dirEntry.name != "." && dirEntry.name != ".." && + dirEntry.name != ".svn" && dirEntry.name != "enigma_cross") { + candidates.insert(dirEntry.name); + } + else { + std::string::size_type zipPos = dirEntry.name.rfind(".zip"); + if (zipPos != std::string::npos && zipPos == dirEntry.name.size() - 4) { + candidates.insert(dirEntry.name.substr(0, dirEntry.name.size() - 4)); + } + } + } + delete dirIter; + } + + for (std::set::iterator i = candidates.begin(); + i != candidates.end(); i++) { + // register the index just on the system path even if the + // user has an update on his path. We need to know the release to + // decide if a user copy is an update + PersistentIndex * anIndex = new PersistentIndex(*i, true); + anIndex->isUserOwned = false; + Log << "precheck: " << *i << "\n"; + checkCandidate(anIndex); + } + + //add system cross indices + for (int i = 0; i < sysPaths.size(); i++) { + dirIter = DirIter::instance(sysPaths[i] + "/levels/enigma_cross"); + while (dirIter->get_next(dirEntry)) { + if (!dirEntry.is_dir && dirEntry.name.size() > 4 && + (dirEntry.name.rfind(".xml") == dirEntry.name.size() - 4)) { + PersistentIndex * anIndex = new PersistentIndex("enigma_cross", true, + INDEX_DEFAULT_PACK_LOCATION, "", dirEntry.name); + anIndex->isUserOwned = false; + checkCandidate(anIndex); + } + } + } + delete dirIter; + + if (onlySystemIndices) + return; + + // UserPath: register dirs and zips with xml-indices excl auto + dirIter = DirIter::instance(app.userPath + "/levels"); + while (dirIter->get_next(dirEntry)) { + if (dirEntry.is_dir && dirEntry.name != "." && dirEntry.name != ".." && + dirEntry.name != ".svn" && dirEntry.name != "auto" && + dirEntry.name != "cross" && dirEntry.name != "enigma_cross" && + dirEntry.name != "legacy_dat") { + candidates2.insert(dirEntry.name); + } + else { + std::string::size_type zipPos = dirEntry.name.rfind(".zip"); + if (zipPos != std::string::npos && zipPos == dirEntry.name.size() - 4) { + candidates2.insert(dirEntry.name.substr(0, dirEntry.name.size() - 4)); + } + } + } + delete dirIter; + + candidates2.insert(""); +#ifdef __MINGW32__ + // eliminate logical duplicates as Windows does not distinguish + // upper and lower case filenames but we can have e.g. an uppercase zip + // and a lower case dir + candidates2 = ecl::UniqueFilenameSet(candidates2); +#endif + for (std::set::iterator i = candidates2.begin(); + i != candidates2.end(); i++) { + PersistentIndex * anIndex = new PersistentIndex(*i, false); + checkCandidate(anIndex); + } + + //add user cross indices + dirIter = DirIter::instance(app.userPath + "/levels/cross"); + while (dirIter->get_next(dirEntry)) { + if (!dirEntry.is_dir && dirEntry.name.size() > 4 && + (dirEntry.name.rfind(".xml") == dirEntry.name.size() - 4)) { + PersistentIndex * anIndex = new PersistentIndex("cross", false, + INDEX_DEFAULT_PACK_LOCATION, "", dirEntry.name); + checkCandidate(anIndex); + } + } + delete dirIter; + + for (int i = 0; i < indexCandidates.size(); i++) { + Index::registerIndex(indexCandidates[i]); + } + + // register auto not yet registered new files + PersistentIndex * autoIndex = new PersistentIndex("auto", false, + INDEX_AUTO_PACK_LOCATION, INDEX_AUTO_PACK_NAME); + autoIndex->isEditable = false; + dirIter = DirIter::instance(app.userPath + "/levels/auto"); + while (dirIter->get_next(dirEntry)) { + if( !dirEntry.is_dir) { + if (dirEntry.name.size() > 4 && ( + (dirEntry.name.rfind(".xml") == dirEntry.name.size() - 4) || + (dirEntry.name.rfind(".lua") == dirEntry.name.size() - 4))) { + Proxy * newProxy = Proxy::autoRegisterLevel("auto", + dirEntry.name.substr(0, dirEntry.name.size() - 4)); + if (newProxy != NULL) { + // first check that the proxy is not in the index + // - may occur if the level is stored as .xml and .lua in the folder + if (!autoIndex->containsProxy(newProxy)) { + // it is new, add it + autoIndex->appendProxy(newProxy); + } + } + } + } + } + delete dirIter; + Index::registerIndex(autoIndex); + + // check if history is available - else generate a new index + Index * foundHistory = Index::findIndex("History"); + if ( foundHistory != NULL) { + historyIndex = dynamic_cast(foundHistory); + } else { + historyIndex = new PersistentIndex("cross", false, INDEX_HISTORY_PACK_LOCATION, + INDEX_HISTORY_PACK_NAME, "history.xml"); + Index::registerIndex(historyIndex); + } + historyIndex->isEditable = false; + } + + void PersistentIndex::addCurrentToHistory() { + Variation var; + Proxy * curProxy = Index::getCurrentProxy(); + // remember all but commandline absolute and relative paths + if (curProxy->getNormPathType() != Proxy::pt_absolute) { + PersistentIndex * curIndex = dynamic_cast(Index::getCurrentIndex()); + if (curIndex != NULL) + var = curIndex->getVariation(curIndex->getCurrentPosition()); + historyIndex->insertProxy(0, curProxy, false, var.ctrl, var.unit, + var.target, var.extensions); + if (historyIndex->size() > 100) + historyIndex->erase(historyIndex->size() - 1); + historyIndex->setCurrentPosition(0); // last played is always current in history + } + } + + PersistentIndex::PersistentIndex(std::string thePackPath, bool systemOnly, + double defaultLocation, std::string anIndexName, + std::string theIndexFilename, std::string aGroupName) : + Index(anIndexName, aGroupName, defaultLocation), packPath (thePackPath), + indexFilename(theIndexFilename), isModified (false), + isUserOwned (true), isEditable (true), release (1), revision (1), + compatibility (1.00), doc(NULL) { +// Log << "PersistentIndex AddLevelPack " << thePackPath << " - " << anIndexName << " - " << indexDefaultLocation <<"\n"; + load(systemOnly); + } + + void PersistentIndex::load(bool systemOnly, bool update) { + if (doc != NULL) { + doc->release(); + doc = NULL; + } + // auto and new levelpacks are not loadable + if (packPath == " " || packPath == "auto") + return; // as long as Auto is not editable + + std::auto_ptr isptr; + ByteVec indexCode; + std::string errMessage; + absIndexPath = ""; + std::string relIndexPath = "levels/" + packPath + "/" + indexFilename; + if ((!systemOnly && app.resourceFS->findFile(relIndexPath, absIndexPath, isptr)) || + (systemOnly && app.systemFS->findFile(relIndexPath, absIndexPath, isptr))) { + // preload index file or zipped index + if (isptr.get() != NULL) { + // zipped file + Readfile (*isptr, indexCode); + } else { + // plain file + std::basic_ifstream ifs(absIndexPath.c_str(), ios::binary | ios::in); + Readfile(ifs, indexCode); + } + try { + std::ostringstream errStream; + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToOstream(&errStream); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("index.xsd","index.xsd"); + if (update) { + // local xml file or URL + doc = app.domParser->parseURI(indexUrl.c_str()); + } else { + // preloaded xml or zipped xml +#if _XERCES_VERSION >= 30000 + std::auto_ptr domInputIndexSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(&(indexCode[0])), + indexCode.size(), absIndexPath.c_str(), false))); + doc = app.domParser->parse(domInputIndexSource.get()); +#else + std::auto_ptr domInputIndexSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(&(indexCode[0])), + indexCode.size(), absIndexPath.c_str(), false))); + doc = app.domParser->parse(*domInputIndexSource); +#endif + } + + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + infoElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("info").x_str())->item(0)); + updateElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("update").x_str())->item(0)); + levelsElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("levels").x_str())->item(0)); + } + + if(app.domParserErrorHandler->getSawErrors()) { + errMessage = errStream.str(); + } + app.domParserErrorHandler->reportToNull(); // do not report to errStream any more + } + catch (...) { + errMessage = "Unexpected XML Exception on load of index\n"; + } + if (!errMessage.empty()) { + Log << errMessage; // make long error messages readable + std::string message; + if (update) { + message = _("Error on update of levelpack index: \n"); + message += absIndexPath + "\n\n"; + message += _("Note: the current version will be reloaded!\n\n"); + message += errMessage; + } else { + if (doc != NULL) { + doc->release(); // empty or errornous doc + doc = NULL; + } + message = _("Error on registration of levelpack index: \n"); + message += absIndexPath + "\n\n"; + message += _("Note: the levelpack will not show up!\n\n"); + message += errMessage; + } + gui::ErrorMenu m(message, N_("Continue")); + m.manage(); + if (update) { + load(systemOnly, false); // reload local version + } + return; + } else if (doc != NULL) { + //TODO check if an updated index exists for system packs + loadDoc(); + } + } + } + + void PersistentIndex::loadDoc() { + if (doc != NULL) { + clear(); // allow a reload of an index + indexName = XMLtoUtf8(infoElem->getAttribute( + Utf8ToXML("title").x_str())).c_str(); + indexGroup = XMLtoUtf8(infoElem->getAttribute( + Utf8ToXML("group").x_str())).c_str(); + defaultGroup = indexGroup; + owner = XMLtoUtf8(infoElem->getAttribute( + Utf8ToXML("owner").x_str())).c_str(); + release = XMLString::parseInt(infoElem->getAttribute( + Utf8ToXML("release").x_str())); + revision = XMLString::parseInt(infoElem->getAttribute( + Utf8ToXML("revision").x_str())); + XMLDouble * result = new XMLDouble(infoElem->getAttribute( + Utf8ToXML("enigma").x_str())); + compatibility = result->getValue(); + delete result; + result = new XMLDouble(infoElem->getAttribute( + Utf8ToXML("location").x_str())); + indexDefaultLocation = result->getValue(); + indexLocation = indexDefaultLocation; + delete result; + + if (updateElem != NULL) { + indexUrl = XMLtoUtf8(updateElem->getAttribute( + Utf8ToXML("indexurl").x_str())).c_str(); + } + DOMNodeList *levelList = levelsElem->getElementsByTagName( + Utf8ToXML("level").x_str()); + std::set knownAttributes; + knownAttributes.insert("_seq"); + knownAttributes.insert("_title"); + knownAttributes.insert("_xpath"); + knownAttributes.insert("id"); + knownAttributes.insert("author"); + knownAttributes.insert("score"); + knownAttributes.insert("rel"); + knownAttributes.insert("rev"); + knownAttributes.insert("easy"); + knownAttributes.insert("ctrl"); + knownAttributes.insert("unit"); + knownAttributes.insert("target"); + for (int i = 0, l = levelList->getLength(); i < l; i++) { + DOMElement *levelElem = dynamic_cast(levelList->item(i)); + std::string path = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("_xpath").x_str())).c_str(); + std::string id = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("id").x_str())).c_str(); + std::string title = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("_title").x_str())).c_str(); + std::string author = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("author").x_str())).c_str(); + int scoreVersion = XMLString::parseInt(levelElem->getAttribute( + Utf8ToXML("score").x_str())); + int releaseVersion = XMLString::parseInt(levelElem->getAttribute( + Utf8ToXML("rel").x_str())); + int revisionVersion = XMLString::parseInt(levelElem->getAttribute( + Utf8ToXML("rev").x_str())); + bool hasEasymodeFlag = boolValue(levelElem->getAttribute( + Utf8ToXML("easy").x_str())); + Proxy * newProxy = Proxy::registerLevel(path, packPath, id, title, + author, scoreVersion, releaseVersion, hasEasymodeFlag, + GAMET_ENIGMA, STATUS_RELEASED, revisionVersion); + Variation var; + std::string controlString = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("ctrl").x_str())).c_str(); + if (controlString == "balance") + var.ctrl = balance; + else if (controlString == "key") + var.ctrl = key; + else if (controlString == "other") + var.ctrl = other; + std::string txt = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("unit").x_str())).c_str(); + if (txt == "number") + var.unit = number; + else + // default + var.unit = duration; + var.target = XMLtoUtf8(levelElem->getAttribute( + Utf8ToXML("target").x_str())).c_str(); + DOMNamedNodeMap * attrMap = levelElem->getAttributes(); + for (int j = 0, k = attrMap->getLength(); j < k; j++) { + DOMAttr * levelAttr = dynamic_cast(attrMap->item(j)); + std::string attrName = XMLtoUtf8(levelAttr->getName()).c_str(); + if (knownAttributes.find(attrName) == knownAttributes.end()) { + Log << "PersistentIndex Load unknown Attribut: " << attrName << "\n"; + var.extensions[attrName]= XMLtoUtf8(levelAttr->getValue()).c_str(); + } + + } + appendProxy(newProxy, var.ctrl, var.unit, var.target, var.extensions); + } + } + } + + PersistentIndex::~PersistentIndex() { + if (doc != NULL) + doc->release(); + } + + std::string PersistentIndex::getPackPath() { + return packPath; + } + + bool PersistentIndex::setName(std::string newName) { + if (findIndex(newName) != NULL) + return false; // do not allow duplicate names + + // substitute spaces by underscores + std::string fileName = newName; + std::string::size_type pos = fileName.find_first_of(' ', 0); + while (pos != std::string::npos) { + fileName.replace(pos, 1,"_"); + pos = fileName.find_first_of(' ', pos+1); + } + + if (packPath == " " || (packPath == "cross" && indexFilename == INDEX_STD_FILENAME)) { + // generate usabale path name and check it it usable + if (fileName == "cross" || fileName == "enigma_cross" || + fileName == "legacy_dat" || fileName == "auto" || + fileName == "history") { + return false; + } + // check if the name would conflict with existing files + std::auto_ptr isptr; // dummy + absIndexPath = ""; + std::string relIndexPath1 = "levels/" + fileName + "/" + INDEX_STD_FILENAME; + std::string relIndexPath2 = "levels/cross/" + fileName + ".xml"; + if (app.resourceFS->findFile(relIndexPath1, absIndexPath, isptr) || + app.resourceFS->findFile(relIndexPath2, absIndexPath, isptr)) { + return false; + } + if (packPath == " ") { + packPath = fileName; + indexFilename = INDEX_STD_FILENAME; + } else + indexFilename = fileName + ".xml"; + } + + if (!getName().empty()) + renameIndex(newName); // Index maps + else + // a new unregistered levelpack is named the first time - just set the name + indexName = newName; + return true; + } + + bool PersistentIndex::isUpdatable() { + return (updateElem != NULL) && !indexUrl.empty(); + } + + bool PersistentIndex::isCross() { + return packPath == "cross" || packPath == "enigma_cross"; + } + + void PersistentIndex::markNewAsCross(){ + if (packPath == " ") + packPath = "cross"; + } + + std::string PersistentIndex::getOwner() { + return owner; + } + + void PersistentIndex::setOwner(std::string newOwner) { + owner = newOwner; + } + + int PersistentIndex::getRelease() { + return release; + } + + void PersistentIndex::setRelease(int newRelease) { + release = newRelease; + } + + int PersistentIndex::getRevision() { + return revision; + } + + void PersistentIndex::setRevision(int newRevision) { + revision = newRevision; + } + + double PersistentIndex::getCompatibility() { + return compatibility; + } + + void PersistentIndex::setCompatibility(double newCompatibility) { + compatibility = newCompatibility; + } + + bool PersistentIndex::isUserEditable() { + return isEditable && (isUserOwned || WizardMode); + } + + void PersistentIndex::clear() { + proxies.clear(); + variations.clear(); + currentPosition = 0; + } + + void PersistentIndex::appendProxy(Proxy * newLevel, controlType varCtrl, + scoreUnitType varUnit, std::string varTarget, + std::map varExtensions) { + proxies.push_back(newLevel); + + Variation var(varCtrl, varUnit, varTarget); + var.extensions = varExtensions; + variations.push_back(var); + } + + void PersistentIndex::insertProxy(int pos, Proxy * newLevel, bool allowDuplicates, + controlType varCtrl, scoreUnitType varUnit, std::string varTarget, + std::map varExtensions) { + // TODO Assert pos >= size + + Variation var(varCtrl, varUnit, varTarget); + var.extensions = varExtensions; + + std::vector::iterator itProxy = proxies.begin(); + std::vector::iterator itVar = variations.begin(); + if (!allowDuplicates) { + // delete duplicates + while (itProxy != proxies.end()) { + if (*itProxy == newLevel && *itVar == var) { +// Log << "History Duplicat found\n"; + itProxy = proxies.erase(itProxy); + itVar = variations.erase(itVar); + } + if (itProxy != proxies.end()) + itProxy++; itVar++; + } + } + + itProxy = proxies.begin(); + itVar = variations.begin(); + for (int i = 0; i < pos; i++) { + itProxy++; itVar++; + } + proxies.insert(itProxy, newLevel); + variations.insert(itVar, var); + } + + Variation PersistentIndex::getVariation(int pos) { + // TODO Assert pos + return variations[pos]; + } + + void PersistentIndex::erase(int pos) { + std::vector::iterator itProxy = proxies.begin(); + std::vector::iterator itVar = variations.begin(); + for (int i = 0; i < pos; i++) { + itProxy++; itVar++; + } + proxies.erase(itProxy); + variations.erase(itVar); + } + + void PersistentIndex::exchange(int pos1, int pos2) { + Proxy * proxy = proxies[pos1]; + proxies[pos1] = proxies[pos2]; + proxies[pos2] = proxy; + Variation var = variations[pos1]; + variations[pos1] = variations[pos2]; + variations[pos2] = var; + if (getCurrentPosition() == pos1) + setCurrentPosition(pos2); + else if (getCurrentPosition() == pos2) + setCurrentPosition(pos1); + } + + bool PersistentIndex::isSource(Proxy * aProxy) { + std::string proxyPath = aProxy->getNormLevelPath(); + if (proxyPath[0] == '#') + // Oxyd reference + return false; + else if (packPath.empty()) { + // old levelpack on levels directory + if (proxyPath.find('/') == std::string::npos) + return true; + else + return false; + } else { + if (proxyPath.find(packPath + "/") == 0) + return true; + else + return false; + } + } + + bool PersistentIndex::save(bool allowOverwrite) { + bool result = true; + + if (doc == NULL) { + std::string errMessage; + std::string indexTemplatePath; + if (app.systemFS->findFile( "schemas/index.xml" , indexTemplatePath)) { + try { + std::ostringstream errStream; + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToOstream(&errStream); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("index.xsd","index.xsd"); + doc = app.domParser->parseURI(indexTemplatePath.c_str()); + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + infoElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("info").x_str())->item(0)); + levelsElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("levels").x_str())->item(0)); + } + if(app.domParserErrorHandler->getSawErrors()) { + errMessage = errStream.str(); + } + app.domParserErrorHandler->reportToNull(); // do not report to errStream any more + } + catch (...) { + errMessage = "Unexpected XML Exception on load of index\n"; + } + if (!errMessage.empty()) { + if (doc != NULL) { + doc->release(); // empty or errornous doc + doc = NULL; + } + Log << errMessage; // make long error messages readable + return false; + } + } else + // TODO add error handling + return false; + } + + // + + infoElem->setAttribute( Utf8ToXML("title").x_str(), + Utf8ToXML(&indexName).x_str()); + infoElem->setAttribute( Utf8ToXML("group").x_str(), + Utf8ToXML(&defaultGroup).x_str()); + infoElem->setAttribute( Utf8ToXML("owner").x_str(), + Utf8ToXML(&owner).x_str()); + infoElem->setAttribute( Utf8ToXML("release").x_str(), + Utf8ToXML(ecl::strf("%d",release)).x_str()); + infoElem->setAttribute( Utf8ToXML("revision").x_str(), + Utf8ToXML(ecl::strf("%d",revision)).x_str()); + infoElem->setAttribute( Utf8ToXML("enigma").x_str(), + Utf8ToXML(ecl::strf("%.2f",compatibility)).x_str()); + infoElem->setAttribute( Utf8ToXML("location").x_str(), + Utf8ToXML(ecl::strf("%g",indexDefaultLocation)).x_str()); + DOMNodeList *levelsChildList = levelsElem->getChildNodes(); + int levelsChildCount = levelsChildList->getLength(); + // delete no more used level elements + for (int i = 0; i < levelsChildCount; i++) { + // delete all elements of list, as the list is dynamically updated - + // we can not recycle level elements as they may be reordered and + // their attributes are not identical + levelsElem->removeChild(levelsChildList->item(0)); + } + DOMElement * levelElem; + // add level elements + for (int i = 0; i < size(); i++) { + // add a new element + levelElem = doc->createElement(Utf8ToXML("level").x_str()); + levelsElem->appendChild(levelElem); // insert it at the end + + Proxy * level = getProxy(i); + // convert Proxy normLevelPath to pack local path ./* if possible + std::string xpath = level->getNormLevelPath(); + if (xpath.find(packPath + "/") == 0) + xpath = "." + xpath.substr(packPath.size()); + else if (packPath.empty() && xpath.find("/") == std::string::npos) + xpath = "./" + xpath; + levelElem->setAttribute( Utf8ToXML("_xpath").x_str(), + Utf8ToXML(xpath).x_str()); + levelElem->setAttribute( Utf8ToXML("id").x_str(), + Utf8ToXML(level->getId()).x_str()); + levelElem->setAttribute( Utf8ToXML("_title").x_str(), + Utf8ToXML(level->getTitle()).x_str()); + levelElem->setAttribute( Utf8ToXML("author").x_str(), + Utf8ToXML(level->getAuthor()).x_str()); + levelElem->setAttribute( Utf8ToXML("score").x_str(), + Utf8ToXML(ecl::strf("%d",level->getScoreVersion())).x_str()); + levelElem->setAttribute( Utf8ToXML("rel").x_str(), + Utf8ToXML(ecl::strf("%d",level->getReleaseVersion())).x_str()); + levelElem->setAttribute( Utf8ToXML("rev").x_str(), + Utf8ToXML(ecl::strf("%d",level->getRevisionNumber())).x_str()); + levelElem->setAttribute( Utf8ToXML("easy").x_str(), + Utf8ToXML(level->hasEasymode() ? "true" : "false").x_str()); + std::string control; + switch (variations[i].ctrl) { + case lev::force: + control = "force"; break; + case lev::balance: + control = "balance"; break; + case lev::key: + control = "key"; break; + default: + control = "other"; break; + } + levelElem->setAttribute( Utf8ToXML("ctrl").x_str(), + Utf8ToXML(control).x_str()); + std::string unit; + switch (variations[i].unit) { + case lev::duration: + unit = "duration"; break; + case lev::number: + unit = "number"; break; + } + levelElem->setAttribute( Utf8ToXML("unit").x_str(), + Utf8ToXML(unit).x_str()); + levelElem->setAttribute( Utf8ToXML("target").x_str(), + Utf8ToXML(variations[i].target).x_str()); + for (std::map::iterator j= variations[i].extensions.begin(); + j != variations[i].extensions.end(); j++) { +// Log << "Persistent save extension: " << (*j).first << " - " << (*j).second << "\n"; + levelElem->setAttribute( Utf8ToXML((*j).first).x_str(), + Utf8ToXML((*j).second).x_str()); + } + } + + // update the sequence number of the levels + DOMNodeList *levList = levelsElem->getElementsByTagName( + Utf8ToXML("level").x_str()); + for (int i = 0, l = levList-> getLength(); i < l; i++) { + DOMElement *levElem = dynamic_cast(levList->item(i)); + levElem->setAttribute( Utf8ToXML("_seq").x_str(), + Utf8ToXML(ecl::strf("%d",i+1)).x_str()); + } + + stripIgnorableWhitespace(doc->getDocumentElement()); + + std::string path = app.userPath + "/levels/" + packPath + "/" + indexFilename; + if (allowOverwrite || !ecl::FileExists(path)) { + try { + // auto-create the directory if necessary + std::string directory; + if (ecl::split_path (path, &directory, 0) && !ecl::FolderExists(directory)) { + ecl::FolderCreate (directory); + } + +#if _XERCES_VERSION >= 30000 + result = app.domSer->writeToURI(doc, LocalToXML(& path).x_str()); +#else + XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(path.c_str()); + result = app.domSer->writeNode(myFormTarget, *doc); + delete myFormTarget; // flush +#endif + } + catch (const XMLException& toCatch) { + char* message = XMLString::transcode(toCatch.getMessage()); + cerr << "Exception on save of index: " + << message << "\n"; + XMLString::release(&message); + result = false; + } + catch (const DOMException& toCatch) { + char* message = XMLString::transcode(toCatch.msg); + cerr << "Exception on save of index: " + << message << "\n"; + XMLString::release(&message); + result = false; + } + catch (...) { + cerr << "Unexpected exception on save of index\n" ; + result = false; + } + if (!result) + Log << "Index save fault on " << path << " \n"; + else + Log << "Index save " << path << " o.k.\n"; + } else + result = false; + + return result; + } + + + PersistentIndex::PersistentIndex(std::istream *legacyIndexStream, + std::string thePackPath, bool isZip, std::string anIndexName, + std::string theIndexFilename) : + Index(anIndexName, INDEX_DEFAULT_GROUP, Index::getNextUserLocation()), + indexFilename(theIndexFilename), isModified (false), + isUserOwned (true), isEditable (true), release (1), revision (1), + compatibility (1.00), doc(NULL) { + Log << "PersistentIndex convert 0.92 index " << thePackPath << " - " << anIndexName <<"\n"; + lev::RatingManager *theRatingMgr = lev::RatingManager::instance(); + + // prepare Proxy coding of pack path - + if (thePackPath == "levels") + packPath = ""; + else { + // cut off leading "levels/" + packPath = thePackPath.substr(7); + } + + int linenumber = 0; + try { + std::string line; + while (std::getline(*legacyIndexStream, line)) { + using namespace std; + using namespace ecl; + + string filename = ""; //< Filename of the level (exl. extension) + string indexname = ""; //< The name used in the options file, + // empty string -> use filename entry + string name = ""; //< Complete name of the level + string author = ""; //< Author of the level + int revision = 1; //< Revision # of this level + bool has_easymode = false; //< whether level has an easymode + int par_time_easy = -1 ; //< Best time in seconds (for easy mode) + int par_time_normal = -1; //< Best time in seconds (for normal mode) + string par_time_easy_by = ""; //< player name(s) for 'best_time_easy' + string par_time_normal_by = ""; //< same for 'best_time_normal' + int par_moves = -1; //< Minimum moves to solve level + int intelligence = 0; + int dexterity = 0; + int patience = 0; + int knowledge = 0 ; + int speed = 0; + + ++linenumber; + + const char *wspace = " \t"; + size_t p = line.find_first_not_of(wspace); + + if (p == string::npos || line.at(p) != '{') + continue; + + // found a level description + + int par_time = -1; + string par_time_by; + string par_moves_by; + + ++p; + while (true) { + string tag; + string content; + + // ugly parser code starts here. + // - it parses 'tag = content' or 'tag = "content"' + // - '\"' is allowed in '"content"' + // - whitespace is ignored + // - sets message and breaks while loop in case of error + size_t tag_start = line.find_first_not_of(wspace, p); + Assert (tag_start != string::npos, "Expected tag or '}'"); + if (line.at(tag_start) == '}') break; // line done + + size_t equal = line.find('=', tag_start); + Assert (equal != string::npos, "'=' expected"); + Assert (equal != tag_start, "empty tag"); + + size_t tag_end = line.find_last_not_of(wspace, equal-1); + tag = line.substr(tag_start, tag_end-tag_start+1); + + size_t content_start = line.find_first_not_of(wspace, equal+1); + if (line.at(content_start) == '\"') { // content in "" + size_t oquote = line.find('\"', content_start+1); + bool have_escapes = false; + while (oquote != string::npos && line.at(oquote-1) == '\\') { // step over \" + oquote = line.find('\"', oquote+1); + have_escapes = true; + } + Assert (oquote != string::npos, "unmatched quote"); + content = line.substr(content_start+1, oquote-content_start-1); + if (have_escapes) { + size_t esc; + while ((esc = content.find("\\\"")) != string::npos) + content.replace(esc, 2, "\""); + } + p = oquote+1; + } + else { // content w/o "" + size_t content_end = line.find_first_of(" \t}", content_start); + Assert (content_end != string::npos, "expected space or } behind content"); + content = line.substr(content_start, content_end-content_start); + p = content_end; + } + + if (tag == "file") filename = content; + else if (tag == "indexname")indexname = content; + else if (tag == "name") name = content; + else if (tag == "author") author = content; + else if (tag == "revision") revision = atoi(content.c_str()); + else if (tag == "easymode") has_easymode = (content == "1"); + else if (tag == "int") intelligence = atoi(content.c_str()); + else if (tag == "dex") dexterity = atoi(content.c_str()); + else if (tag == "pat") patience = atoi(content.c_str()); + else if (tag == "kno") knowledge = atoi(content.c_str()); + else if (tag == "spe") speed = atoi(content.c_str()); + else if (tag == "par_time") parsePar(content, par_time, par_time_by); + else if (tag == "par_time_easy") parsePar(content, par_time_easy, par_time_easy_by); + else if (tag == "par_time_normal") parsePar(content, par_time_normal, par_time_normal_by); + else if (tag == "par_moves") parsePar(content, par_moves, par_moves_by); + // else if (tag == "hint1") hint1 = content; + // else if (tag == "hint2") hint2 = content; + else + throw XLevelPackInit(strf("unknown tag '%s'", tag.c_str())); + } + + Assert (filename.length() != 0, + "mandatory tag 'file' missing"); + + if (has_easymode) { + Assert (par_time == -1, + "'par_time' not allowed when easymode=1 " + "(use 'par_time_easy' and 'par_time_normal')"); + } + else { + Assert (par_time_normal==-1 && par_time_easy==-1, + "'par_time_easy' and 'par_time_normal' are not allowed when easymode=0 (use 'par_time')"); + par_time_easy = par_time_normal = par_time; + par_time_easy_by = par_time_normal_by = par_time_by; + } + + // correct filename for zip + if (isZip) { + Log << "Zip " << thePackPath << " - " <registerRating((indexname.empty() ? filename : indexname), + revision, intelligence, dexterity, patience, knowledge, speed, + par_time_easy, par_time_easy_by, par_time_normal, par_time_normal_by); + + } + } catch (const XLevelPackInit &e) { + std::string xerror = ecl::strf("in line %i: %s", linenumber, e.what()); + throw XLevelPackInit (xerror); + } + + // convert to XML + + updateFromProxies(); + + // save but do not overwrite existing index.xml - would be a second conversion, + // but the user may already have modified the index.xml + save(false); + } + + void PersistentIndex::parsePar(const string& par, int& par_value, std::string& par_text) + { + // 'par' is in format "value,text" + // The value is stored in 'par_value' and the text in 'par_text'. + using namespace std; + using namespace ecl; + + size_t comma = par.find(','); + Assert (comma!=string::npos, "Comma expected in par"); + + string value = par.substr(0, comma); + par_text = par.substr(comma+1); + par_value = atoi(value.c_str()); + } + + void AddLevelPack (const char *init_file, const char *indexName) { +// Log << "Index AddLevelPack " << init_file << "\n"; + if (Index::findIndex(indexName) == NULL) { + std::string absPath; + if (app.resourceFS->findFile(init_file, absPath)) { + try { + std::string path = init_file; + std::string dir = ""; + std::string filename = ""; + ecl::split_path(path, &dir, &filename); + std::ifstream is(absPath.c_str()); + + if (!is) + throw XLevelPackInit ("Cannot open index file"); + Index::registerIndex(new PersistentIndex(&is, dir, false, indexName)); + } catch (const XLevelPackInit &e) { + Log << e.get_string() << "\n"; + } + } else { + Log << "Could not find level index file `" << init_file << "'\n"; + } + } else { +// Log << "Ignored index file `" << init_file << "' - already converted to XML\n"; + } + } + + void AddZippedLevelPack (const char *zipfile) { +// Log << "Index AddZippedLevelPack " << zipfile << "\n"; + using namespace std; + using namespace ecl; + string absPath; + if (app.resourceFS->findFile (zipfile, absPath)) { + // the index file as it would be for a unpacked zip + std::string zf = zipfile; + std::string dir = zf.substr(0, zf.rfind('.')); + std::string indexfile = dir + "/index.txt"; + try { + auto_ptr isptr; + std::string dummy; + if(!app.resourceFS->findFile(indexfile, dummy, isptr)) + throw XLevelPackInit ("No index in level pack: "); + + istream &is = *isptr; + + string line; + std::string indexName; + if (getline(is, line)) { + // we read the index in binary mode and have to strip of the \n for + // windows + if (line[line.size()-1] = '\n') { + line.resize(line.size()-1); + } + indexName = line; + + // check if already loaded + + Index::registerIndex(new PersistentIndex(isptr.get(), dir, true, indexName)); + } + else { + throw XLevelPackInit ("Invalid level pack: " + indexName); + } + } + catch (std::exception &) { + throw XLevelPackInit ("Error reading from .zip file: " + indexfile); + } + } else { + enigma::Log << "Could not find zip file `" << zipfile << "'\n"; + } + } + +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/PersistentIndex.hh b/project/jni/application/enigma/src/lev/PersistentIndex.hh new file mode 100644 index 000000000..3c03dbf1e --- /dev/null +++ b/project/jni/application/enigma/src/lev/PersistentIndex.hh @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LEV_PERSISTENTINDEX_HH_INCLUDED +#define LEV_PERSISTENTINDEX_HH_INCLUDED + +#include "lev/Index.hh" + +#include +#include +#include + + +#define INDEX_STD_FILENAME "index.xml" + +namespace enigma { namespace lev { + + struct Variation { + // Constructor + Variation(controlType ctrlValue = force, scoreUnitType unitValue = duration, + std::string targetValue = "time"); + + controlType ctrl; + scoreUnitType unit; + std::string target; + std::map extensions; + + bool operator == (const Variation& otherVar); + }; + + + /** + * + */ + class PersistentIndex : public Index { + public: + static void registerPersistentIndices(bool onlySystemIndices); + static PersistentIndex * historyIndex; + static void addCurrentToHistory(); + + /** + * Convention: method names *Level() can take int pos or Proxy as arg. + */ + /** + * + * thePackPath " " for a new not yet defined path + */ + PersistentIndex(std::string thePackPath, bool systemOnly, + double defaultLocation = INDEX_DEFAULT_PACK_LOCATION, + std::string anIndexName = "", + std::string theIndexFilename = INDEX_STD_FILENAME, + std::string aGroupName = INDEX_DEFAULT_GROUP); + /** + * Legacy 0.92 constructor - called once to convert the index to XML. + * When the index has been stored as XML this constructor will not be + * called again. + */ + PersistentIndex(std::istream *legacyIndex, std::string thePackPath, bool isZip = false, + std::string anIndexName = "", std::string theIndexFilename = INDEX_STD_FILENAME); + ~PersistentIndex(); + void load(bool systemOnly, bool update = false); + void loadDoc(); + std::string getPackPath(); + bool setName(std::string newName); + std::string getOwner(); + void setOwner(std::string newOwner); + int getRelease(); + void setRelease(int newRelease); + int getRevision(); + void setRevision(int newRevision); + double getCompatibility(); + void setCompatibility(double newCompatibility); + bool isUserEditable(); + bool isUpdatable(); + bool isCross(); + void markNewAsCross(); + virtual void clear(); + virtual void appendProxy(Proxy * newLevel, controlType varCtrl = force, + scoreUnitType varUnit = duration, std::string varTarget = "time", + std::map varExtensions = nullExtensions); + void insertProxy(int pos, Proxy * newLevel, bool allowDuplicates = true, + controlType varCtrl = force, scoreUnitType varUnit = duration, + std::string varTarget = "time", + std::map varExtensions = nullExtensions); + Variation getVariation(int pos); + void erase(int pos); + void exchange(int pos1, int pos2); + virtual bool isSource(Proxy * aProxy); + bool save(bool allowOverwrite = true); + protected: + std::string packPath; // "auto", "",... + std::string indexFilename; + std::string owner; + int release; + int revision; + double compatibility; + std::vector variations; + bool isModified; + bool isUserOwned; + bool isEditable; + std::string indexUrl; + private: + static std::vector indexCandidates; + std::string absIndexPath; + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *infoElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *updateElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem; + + static void checkCandidate(PersistentIndex * candidate); + // legacy 0.92 + void parsePar(const string& par, int& par_value, std::string& par_text); + }; + + void AddLevelPack (const char *init_file, const char *name); + void AddZippedLevelPack (const char *zipfile); + +}} // namespace enigma::lev +#endif diff --git a/project/jni/application/enigma/src/lev/Proxy.cpp b/project/jni/application/enigma/src/lev/Proxy.cpp new file mode 100644 index 000000000..ff550c16b --- /dev/null +++ b/project/jni/application/enigma/src/lev/Proxy.cpp @@ -0,0 +1,1172 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "lev/Proxy.hh" + +#include "ecl_system.hh" +#include "errors.hh" +#include "gui/ErrorMenu.hh" +#include "lua.hh" +#include "main.hh" +#include "nls.hh" +#include "oxyd_internal.hh" +#include "server.hh" +#include "utilXML.hh" +#include "Utf8ToXML.hh" +#include "XMLtoUtf8.hh" +#include "lev/Index.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CXXLUA +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} +#else +#include "lua.h" +#include "lauxlib.h" +#endif + + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +namespace enigma { namespace lev { + // http://enigma-game.org/schema/level/1 + const XMLCh Proxy::levelNS[] = { + chLatin_h, chLatin_t, chLatin_t, chLatin_p, chColon, chForwardSlash, + chForwardSlash, chLatin_e, chLatin_n, chLatin_i, chLatin_g, chLatin_m, + chLatin_a, chDash, chLatin_g, chLatin_a, chLatin_m, chLatin_e, + chPeriod, chLatin_o, chLatin_r, chLatin_g, chForwardSlash, + chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_a, + chForwardSlash, chLatin_l, chLatin_e, chLatin_v, chLatin_e, + chLatin_l, chForwardSlash, chDigit_1, chNull + }; + std::map Proxy::cache; + std::vector Proxy::loadedLibs; + std::vector Proxy::registeredLibs; + void Proxy::releaseLibs() { + for (int i = 0; i << loadedLibs.size(); i++) + delete loadedLibs[i]; + loadedLibs.clear(); + for (int i = 0; i << registeredLibs.size(); i++) + delete registeredLibs[i]; + registeredLibs.clear(); + } + + Proxy *Proxy::currentLevel = NULL; + + Proxy *Proxy::loadedLevel() { + return currentLevel; + } + + + Proxy * Proxy::registerLevel(std::string levelPath, std::string indexPath, + std::string levelId, std::string levelTitle, std::string levelAuthor, + int levelScoreVersion, int levelRelease, bool levelHasEasymode, + GameType levelCompatibilty, levelStatusType status, int levelRevision) { + Proxy *theProxy; + pathType thePathType = pt_resource; + std::string theNormLevelPath; + + // Normalize level path + if (indexPath == "#commandline") { + if (levelPath.find("://") != std::string::npos) { + thePathType = pt_url; // http, ftp, https + } + else + thePathType = pt_absolute; + theNormLevelPath = levelPath; + } else { + if (levelPath.find("://") != std::string::npos) { + thePathType = pt_url; // http, ftp, https + theNormLevelPath = levelPath; + } else if (!levelPath.empty() && levelPath[0] == '#'){ + thePathType = pt_oxyd; + theNormLevelPath = levelPath; + } else { + thePathType = pt_resource; + if (levelPath.find("./") == 0) { + assert(indexPath != "#history"); // no relative paths on history + // relative level path + if (indexPath.empty()) + // levelpack on data/levels directory - depreceated + theNormLevelPath = levelPath.substr(2); + else + // a subdirectory or zip + theNormLevelPath = indexPath + levelPath.substr(1); + } else { + theNormLevelPath = levelPath; + } + } + } + + // search for existing proxy + // we use a combined key to support pack indices to reference + // different releases of a level at the same path -- this is an + // inconsistency in the users level collection as different releases + // should have different filenames -- but we have to handle it: + // we generate proxys for each registration and decide on level load + // evaluating the level metadata which proxy was right. + char txt[5]; + snprintf(txt, sizeof(txt), "%d", levelRelease); + std::string cacheKey = theNormLevelPath + levelId + txt; + std::map::iterator i = cache.find(cacheKey); + if (i != cache.end()) { + Proxy * candidate = i->second; + return candidate; + } + + // create new proxy + theProxy = new Proxy(false, thePathType, theNormLevelPath, levelId, levelTitle, + levelAuthor, levelScoreVersion, levelRelease, levelHasEasymode, + levelCompatibilty, status, levelRevision); + cache.insert(std::make_pair(cacheKey, theProxy)); + return theProxy; + } + + Proxy * Proxy::autoRegisterLevel(std::string indexPath, std::string filename) { + Proxy *theProxy = new Proxy(false, pt_resource, indexPath + "/" + filename , "", "", + "unknown", 1, 0, false, GAMET_UNKNOWN, STATUS_UNKNOWN); + try { + theProxy->loadMetadata(true); + } + catch (XLevelLoading &err) { + Log << "autoRegisterLevel load error on '" << indexPath << "/"<< filename << "\n"; + std::string message = _("Error on auto registration of levelfile: "); + message += indexPath + "/" + filename + ".[xml/lua]\n\n"; + message += _("Note: the level will not show up in the \"Auto Folder\" levelpack!\n\n"); + message += err.what(); + gui::ErrorMenu m(message, N_("Continue")); + m.manage(); + } + if (theProxy->getId().empty() || theProxy->isLibraryFlag) { + delete theProxy; + theProxy = NULL; + } else { + // eliminate duplicates and register +// Log << "autoRegisterLevel register '" << indexPath << "/"<< filename << " Title: " << theProxy->getTitle() <<"\n"; + std::string cacheKey = theProxy->getNormLevelPath() + theProxy->getId() + + ecl::strf("%d", theProxy->getReleaseVersion()); + std::map::iterator i = cache.find(cacheKey); + if (i != cache.end()) { + delete theProxy; + theProxy = i->second; + } else { + cache.insert(std::make_pair(cacheKey, theProxy)); + } + } + return theProxy; + } + + + struct LowerCaseString { + std::string low; + LowerCaseString(const std::string& s) : low(s) { + for (std::string::iterator i = low.begin(); i != low.end(); ++i) + *i = tolower(*i); + } + bool containedBy(LowerCaseString other) const { + return other.low.find(low) != string::npos; + } + }; + + Index * searchIndex; + LowerCaseString searchText(""); + void do_search(const std::map::value_type pair) { + Proxy * candidate = pair.second; + if (searchText.containedBy(candidate->getNormLevelPath()) || + searchText.containedBy(candidate->getTitle()) || + searchText.containedBy(candidate->getId()) || + searchText.containedBy(candidate->getAuthor())) { + searchIndex->appendProxy(candidate); +// Log << "Search result: " << pair.first << " - is - " << candidate->getTitle() << "\n"; + } + } + + std::string Proxy::search(std::string text) { + searchIndex = Index::findIndex("Search Result"); + // assert searchIndex + searchIndex->clear(); + searchText = LowerCaseString(text); + std::for_each(cache.begin(), cache.end(), do_search); + return (searchIndex->size() > 0) ? searchIndex->getName() : ""; + } + + void Proxy::countLevels() { + int countProxy = 0; + int countLegacy = 0; + int countStable = 0; + int countReleased = 0; + map ids; + for (std::map::iterator i= cache.begin(); i != cache.end(); i++) { + Proxy * candidate = (*i).second; + countProxy++; + if (candidate->getNormPathType() == pt_oxyd) + countLegacy++; + else if (candidate->getLevelStatus() == STATUS_STABLE) + ids[candidate->getId()]; + else if (candidate->getLevelStatus() == STATUS_RELEASED) + ++ids[candidate->getId()]; + } + for (std::map::iterator i= ids.begin(); i != ids.end(); i++) { + if ((*i).second == 0) + countStable++; + else + countReleased++; + } + + Log << "Proxy count: " << countReleased <<" released, " + << countStable << " stable, " << countLegacy << " legacy, " + << countProxy << " total\n"; + } + + std::set Proxy::getLevelIds(bool withEasy) { + std::set result; + for (std::map::iterator i= cache.begin(); i != cache.end(); i++) { + if ((*i).second->hasEasymode() == withEasy) + result.insert((*i).second->getId()); + } + return result; + } + + std::set Proxy::getProxies() { + std::set result; + for (std::map::iterator i= cache.begin(); i != cache.end(); i++) { + result.insert((*i).second); + } + return result; + } + + Proxy::Proxy(bool proxyIsLibrary, pathType thePathType, std::string theNormLevelPath, + std::string levelId, std::string levelTitle, std::string levelAuthor, + int levelScoreVersion, int levelRelease, bool levelHasEasymode, + GameType levelCompatibilty,levelStatusType status, int levelRevision) : + isLibraryFlag (proxyIsLibrary), normPathType(thePathType), normLevelPath(theNormLevelPath), + id(levelId), title(levelTitle), author(levelAuthor), + scoreVersion(levelScoreVersion), releaseVersion(levelRelease), + revisionNumber(levelRevision), hasEasymodeFlag(levelHasEasymode), + engineCompatibility(levelCompatibilty), levelStatus (status), + scoreUnit (duration), doc(NULL) { + } + + Proxy::~Proxy() { + this->release(); + } + + void Proxy::release() { + if (doc != NULL) { + doc->release(); + doc = NULL; + } + if (this == currentLevel) { + currentLevel = NULL; + releaseLibs(); + } + } + + std::string Proxy::getNormLevelPath() { + return normLevelPath; + } + + std::string Proxy::getLocalSubstitutionLevelPath() { + std::string result = getNormLevelPath(); + std::string::size_type pos; + // substitute url protocol "http://" by "http/" + pos = result.find("://"); + if (pos != std::string::npos) + result.replace(pos, 2,""); + + // substitute all filesystem uncommon chars to '_' + const std::string validChars("\\_-/.~#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + pos = result.find_first_not_of(validChars, 0); + while (pos != std::string::npos) { + result.replace(pos, 1,"~"); + pos = result.find_first_not_of(validChars, pos+1); + } + return result; + } + + Proxy::pathType Proxy::getNormPathType() { + return normPathType; + } + + std::string Proxy::getAbsLevelPath() { + return absLevelPath; + } + + void Proxy::loadLevel() { + load(false, true); + } + + Proxy * Proxy::copy(std::string newBasePath, std::string newPackPath, bool backup) { + bool useFileLoader = false; + bool isXML = true; + std::auto_ptr isptr; + ByteVec levelCode; + std::string absLevelPath = ""; + std::string filename; + std::string filenameBase; // without extension + + if (normPathType == pt_oxyd) { + return NULL; + + // resolve resource path to filepath + } else if (normPathType == pt_absolute || normPathType == pt_url) { + absLevelPath = normLevelPath; + } else if(normPathType == pt_resource) { + if(!app.resourceFS->findFile ("levels/" + normLevelPath + ".xml", + absLevelPath, isptr) && + !app.resourceFS->findFile ("levels/" + normLevelPath + ".lua", + absLevelPath, isptr)) { + return NULL; + } + } else + // error unknown type + return NULL; + + size_t lastSlash = absLevelPath.rfind ('/'); + if (lastSlash == std::string::npos) { + filename = absLevelPath; + } else { + filename = absLevelPath.substr(lastSlash + 1); + } + + // xml or lua + size_t extbegin = filename.rfind('.'); + if (extbegin != string::npos) { + std::string ext = filename.substr(extbegin); + + if ( ext != ".lua" && ext!= ".ell" && ext != ".xml" && ext != ".elx") { + return NULL; + } else { + filenameBase = filename.substr(0, extbegin); + } + } else { + return NULL; + } + + // load + if (normPathType != pt_url) { + // preload plain Lua file or zipped level + if (isptr.get() != NULL) { + // zipped file + Readfile (*isptr, levelCode); + } else { + // plain file + basic_ifstream ifs(absLevelPath.c_str(), ios::binary | ios::in); + Readfile (ifs, levelCode); + } + std::string oPath = newBasePath + "/" + newPackPath + "/" + filename; + if (backup) { + std::remove((oPath + "~").c_str()); + std::rename(oPath.c_str(), (oPath + "~").c_str()); + } + basic_ofstream ofs(oPath.c_str(), ios::binary | ios::out); + for (int i = 0; i < levelCode.size(); i++) { + ofs << levelCode[i]; + } + ofs.close(); + } else { + // load XML via Xerces + return NULL; + } + + // create new proxy + return registerLevel(std::string("./")+filenameBase, newPackPath, id, title, + author, scoreVersion, releaseVersion, hasEasymodeFlag, + engineCompatibility, levelStatus, revisionNumber); + } + + void Proxy::loadMetadata(bool expectLevel) { + load(true, expectLevel); + } + + void Proxy::load(bool onlyMetadata, bool expectLevel) { + if (doc != NULL) { + if (onlyMetadata) + // doc exists - metadata are loaded + return; + if (!isLibraryFlag != expectLevel) + throw XLevelLoading(ecl::strf("Level - Library mismatch on %s", normLevelPath.c_str())); + // doc exists - we can directly load + loadDoc(); + return; + } + + bool useFileLoader = false; + bool isXML = true; + std::auto_ptr isptr; + ByteVec levelCode; + std::string errMessage; + absLevelPath = ""; + + // release current proxy + if (!isLibraryFlag) { + if (currentLevel != NULL) + currentLevel->release(); + currentLevel = this; + } + + // handle oxyd first + if (normPathType == pt_oxyd) { + if(onlyMetadata) + return; + // use oxyd loader + std::string::size_type posSecondHash = normLevelPath.find('#',1); + if (posSecondHash == string::npos) + throw XLevelLoading("Bad filename for oxyd level: " + normLevelPath ); + std::string packName = normLevelPath.substr(1, posSecondHash -1); + std::string levelNumber = normLevelPath.substr(posSecondHash + 1); + if (Index * oxydIndex = Index::findIndex(packName)) { + dynamic_cast(oxydIndex)->load_oxyd_level(atoi(levelNumber.c_str())); + } else { + throw XLevelLoading("Missing oxyd levelpack for: " + normLevelPath); + } + return; + + // resolve resource path to filepath + } else if (normPathType == pt_absolute || normPathType == pt_url) { + absLevelPath = normLevelPath; + } else if(normPathType == pt_resource) { + if(!app.resourceFS->findFile ("levels/" + normLevelPath + ".xml", + absLevelPath, isptr) && + !app.resourceFS->findFile ("levels/" + normLevelPath + ".lua", + absLevelPath, isptr)) { + std::string type = isLibraryFlag ? "library " : "level "; + throw XLevelLoading("Could not find " + type + normLevelPath ); + } + } else + // error unknown type + return; + + + // xml or lua + size_t extbegin = absLevelPath.rfind ('.'); + if (extbegin != string::npos) { + string ext = absLevelPath.substr (extbegin); + + if (normPathType != pt_url && (ext == ".lua" || ext == ".ell")) { + useFileLoader = true; + isXML = false; // preliminary - may still be lua commented xml + } else if (ext == ".xml" || ext == ".elx") { + isXML = true; + // use file loader only for zipped xml files + useFileLoader = (isptr.get() != NULL) ? true : false; + } else { + throw XLevelLoading ("Unknown file extension in " + absLevelPath); + } + } else { + throw XLevelLoading ("Unknown file extension in " + absLevelPath); + } + + // load + + if (useFileLoader) { + // preload plain Lua file or zipped level + if (isptr.get() != NULL) { + // zipped file + Readfile (*isptr, levelCode); + } else { + // plain file + basic_ifstream ifs(absLevelPath.c_str(), ios::binary | ios::in); + Readfile (ifs, levelCode); + } + + if(!isXML) { + if(levelCode.size() >= 8 && std::string("--xml-- ").compare( + 0, 8, &(levelCode[0]), 8) == 0) { + isXML = true; + // delete lua "--xml-- " comments in level code + int s = levelCode.size(); + int i = 0; + int j = 0; + for (i=8, j=0; i < s;) { + char c = levelCode[j++] = levelCode[i++]; + if(c == '\n') { + if(s >= i+8 && std::string("--xml-- ").compare( + 0, 8, &(levelCode[i]), 8) == 0) { + i += 8; + } + } + } + levelCode.resize(j); + } else if (!onlyMetadata){ + // handle pure lua + // load plain lua file + doc = NULL; + const char *buffer = reinterpret_cast(&levelCode[0]); + // add debugging info to lua code + std::string luaCode = "--@" + absLevelPath + "\n" + + buffer; + lua_State *L = lua::LevelState(); + if (luaL_dostring(L, luaCode.c_str() ) != 0) { + lua_setglobal (L, "_LASTERROR"); + throw XLevelLoading(lua::LastError(L)); + } + } else { + // ensure that metadata are consistent - called for all + // new commanline lua levels + if (releaseVersion == 0) + releaseVersion = 1; + } + } + } + if (isXML) { + try { + std::ostringstream errStream; + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToOstream(&errStream); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("level.xsd","level.xsd"); + if (!useFileLoader) { + // local xml file or URL + doc = app.domParser->parseURI(absLevelPath.c_str()); + } else { + // preloaded lua-commented xml or zipped xml +#if _XERCES_VERSION >= 30000 + std::auto_ptr domInputLevelSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(&(levelCode[0])), + levelCode.size(), absLevelPath.c_str(), false))); + doc = app.domParser->parse(domInputLevelSource.get()); +#else + std::auto_ptr domInputLevelSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(&(levelCode[0])), + levelCode.size(), absLevelPath.c_str(), false))); + doc = app.domParser->parse(*domInputLevelSource); +#endif + } + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + infoElem = dynamic_cast(doc->getElementsByTagNameNS( + levelNS, Utf8ToXML("info").x_str())->item(0)); + stringList = doc->getElementsByTagNameNS(levelNS, + Utf8ToXML("string").x_str()); + } + if(app.domParserErrorHandler->getSawErrors()) { + errMessage = errStream.str(); + } + app.domParserErrorHandler->reportToNull(); // do not report to errStream any more + } + catch (...) { + errMessage = "Unexpected XML Exception on load of level\n"; + } + if (!errMessage.empty()) { + release(); // empty or errornous doc + Log << errMessage; // make long error messages readable + throw XLevelLoading (errMessage); +// todo: check metadata, handle shadowed levels + } else { + // check metadata - currently just overwrite + isLibraryFlag = (getType() == "library") ? true : false; + if (!updateReleaseVersion()) { + release(); // avoid load success on a second read attempt + throw XLevelLoading(ecl::strf("Release version mismatch on %s: requested %d\n", normLevelPath.c_str(), releaseVersion)); + } + if (!updateId()) { + release(); // avoid load success on a second read attempt + throw XLevelLoading(ecl::strf("Id mismatch on %s: requested %s\n", normLevelPath.c_str(), id.c_str())); + } + getTitle(); + getScoreVersion(); + getRevisionNumber(); + getLevelStatus(); + getAuthor(); + hasEasymode(); + getScoreUnit(); + getEngineCompatibility(); + if (!onlyMetadata){ + if (!isLibraryFlag != expectLevel) + throw XLevelLoading(ecl::strf("Level - Library mismatch on %s", normLevelPath.c_str())); + loadDoc(); + } + } + } + } + + void Proxy::loadDoc() { + if (getEnigmaCompatibility() > ENIGMACOMPATIBITLITY) + throw XLevelLoading(ecl::strf("Level is incompatible: %s requires Enigma %.2f or above", + absLevelPath.c_str(), getEnigmaCompatibility())); + if (this == currentLevel) { + server::SetCompatibility(this); + } + processDependencies(); + loadLuaCode(); + } + + void Proxy::processDependencies() { + // cleanup on level but not on libs + if (this == currentLevel) { + // cleanup all lib proxies loaded by previous load + releaseLibs(); + } + if (doc != NULL) { + DOMNodeList *depList = infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("dependency").x_str()); + for (int i = 0, l = depList->getLength(); i < l; i++) { + DOMElement *depElem = dynamic_cast(depList->item(i)); + std::string depPath; + std::string depId; + int depRelease; + bool depPreload; + std::string depUrl; + depPath = XMLtoUtf8(depElem->getAttributeNS(levelNS, + Utf8ToXML("path").x_str())).c_str(); + depId = XMLtoUtf8(depElem->getAttributeNS(levelNS, + Utf8ToXML("id").x_str())).c_str(); + depRelease = XMLString::parseInt(depElem->getAttributeNS(levelNS, + Utf8ToXML("release").x_str())); + depPreload = boolValue(depElem->getAttributeNS(levelNS, + Utf8ToXML("preload").x_str())); + depUrl = XMLtoUtf8(depElem->getAttributeNS(levelNS, + Utf8ToXML("url").x_str())).c_str(); +// Log << "Deps: Path="<registerPreloadDependency(depPath, depId, depRelease, + depPreload, depUrl); + } + } + } + + void Proxy::registerPreloadDependency(std::string depPath, std::string depId, + int depRelease, bool depPreload, std::string depUrl) { + // check if lib is already registered + for (int i=0 ; igetId() == depId) + if (loadedLibs[i]->getReleaseVersion() != depRelease) + throw XLevelLoading(ecl::strf("declaration of incompatible library version: '%s' release %d - release %d already loaded", + depId.c_str(), depRelease, loadedLibs[i]->getReleaseVersion())); + else + return; + } + for (int i=0 ; igetId() == depId) + if (registeredLibs[i]->getReleaseVersion() != depRelease) { + throw XLevelLoading(ecl::strf("declaration of incompatible library version: '%s' release %d - release %d already registered", + depId.c_str(), depRelease, registeredLibs[i]->getReleaseVersion())); + } else if (depPreload == true) { + // same lib but now load it directly - delete the no preload entry + registeredLibs[i] = registeredLibs[registeredLibs.size() - 1]; + registeredLibs.pop_back(); + break; // end search and do load lib + } else { + // same lib and again no preload + return; + } + } + + // resolve relative lib paths + if (depPath.find("./") == 0) { + // relative lib path + std::string levelDir; + std::string levelFilename; + if (ecl::split_path(normLevelPath, &levelDir, &levelFilename)) + // the level is on subdirectory or in a zip + depPath = levelDir + "/" + depPath.substr(2); + else + // level on data/levels directory - depreceated + depPath = depPath.substr(2); + } + + // find lib in requested release version without or with release number + // in filename + Proxy * depProxy = new Proxy(true, depPath.empty() ? pt_url : pt_resource, + (depPath.empty() ? depUrl : depPath) + ecl::strf("_%d",depRelease), + depId, "", "", 0, depRelease, false, GAMET_ENIGMA, STATUS_UNKNOWN); + try { + depProxy->loadMetadata(false); + } catch (XLevelLoading &err) { + delete depProxy; + depProxy = new Proxy(true, depPath.empty() ? pt_url : pt_resource, + depPath.empty() ? depUrl : depPath, depId, "", "", 0, depRelease, + false, GAMET_ENIGMA, STATUS_UNKNOWN); + try { + depProxy->loadMetadata(false); + } catch (XLevelLoading &err) { + delete depProxy; + throw err; + } + } + + // load and register lib + if (depPreload) { + loadedLibs.push_back(depProxy); + depProxy->load(false, false); + } else { + registeredLibs.push_back(depProxy); + } + + } + + void Proxy::loadDependency(std::string depId) { + // check if lib is already loaded + for (int i=0 ; igetId() == depId) + // library is already loaded + return; + } + + for (int i=0 ; igetId() == depId) { + // library is registered to be loaded - do it + loadedLibs.push_back(registeredLibs[i]); + registeredLibs[i]->load(false, false); + // delete form not yet loaded list + registeredLibs[i] = registeredLibs[registeredLibs.size() - 1]; + registeredLibs.pop_back(); + return; + } + } + if (doc == NULL) { +// if (true) { +// Log << "loadDependency " << depId << "\n"; + // handling for legacy Lua levels that did not register dependencies + Proxy * depProxy = new Proxy(true, pt_resource, depId, depId, "", "", 0, 1, + false, GAMET_ENIGMA, STATUS_UNKNOWN); + loadedLibs.push_back(depProxy); + depProxy->load(false, false); + return; + } else + // xml levels have to register used libraries as dependencies + throw XLevelLoading("load attempt of undeclared library"); + } + + void Proxy::loadLuaCode() { + lua_State *L = lua::LevelState(); + DOMNodeList * luamainList = doc->getElementsByTagNameNS(levelNS, Utf8ToXML("luamain").x_str()); + if (luamainList->getLength() == 1) { + DOMElement *luamain = dynamic_cast(luamainList->item(0)); + // add debugging info to lua code + std::string luaCode = "--@" + absLevelPath + "\n" + + XMLtoUtf8(luamain->getTextContent()).c_str(); + if (luaL_dostring(L, luaCode.c_str() ) != 0) { + lua_setglobal (L, "_LASTERROR"); + throw XLevelLoading(lua::LastError(L)); + } + } else { + throw XLevelLoading("not implemented"); + } + } + + + std::string Proxy::getLocalizedString(const std::string &key) { + std::string english = key; + std::string translation; + std::string lang = ecl::GetLanguageCode (app.language); + + // add handling for calls besides level run: info from cache + if (doc == NULL) { + if (key == "title") + return title; + else + return key; + } + + if (key == "title" || key == "subtitle") { + // get the english originals from the identity attributes + DOMElement *identityElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("identity").x_str())->item(0)); + english = XMLtoUtf8(identityElem->getAttributeNS(levelNS, + Utf8ToXML(&key).x_str())).c_str(); + if (key == "title") + title = english; // update cash + } + + bool translFound = false; + bool keyFound = false; + bool protectedString = true; + for (int i = 0, l = stringList-> getLength(); i < l && !translFound; i++) { + DOMElement *stringElem = dynamic_cast(stringList->item(i)); + if (key == XMLtoUtf8(stringElem->getAttributeNS(levelNS, + Utf8ToXML("key").x_str())).c_str()) { + keyFound = true; + // 2 strings with matching key may be found: + // first the protected, then the public one + if (protectedString) { + // resolve english text from string->english subelement + DOMElement *englishElem = + dynamic_cast(stringElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("english").x_str())->item(0)); + std::string tmp = XMLtoUtf8(englishElem->getTextContent()).c_str(); + if (!tmp.empty()) { + // overwrite the english key only if text is provided + english = tmp; + } + // we got the final english string + if (lang == "en") { + translation = english; + translFound = true; + } + } + if (!translFound) { + // protected and public translations + DOMNodeList *translList = stringElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("translation").x_str()); + for (int i = 0, l = translList-> getLength(); i < l && !translFound; i++) { + DOMElement *translElem = dynamic_cast(translList->item(i)); + if (lang == XMLtoUtf8(translElem->getAttributeNS(levelNS, + Utf8ToXML("lang").x_str())).c_str()) { + translation = XMLtoUtf8(translElem->getTextContent()).c_str(); + translFound = true; + } + } + } + if (!translFound && protectedString) { + // gettext + std::string tmp = _(english.c_str()); + if (tmp != english) { + translation = tmp; + translFound = true; + } + } + protectedString = false; // the next matching string is public + } + } + if (!keyFound && !english.empty()) { + // string may originate from a lib - still try gettext + std::string tmp = _(english.c_str()); + if (tmp != english) { + translation = tmp; + translFound = true; + } + } + return (translFound ? translation : english); + } + + std::string Proxy::getType() { + if (doc != NULL) { + return XMLtoUtf8(infoElem->getAttributeNS(levelNS, + Utf8ToXML("type").x_str())).c_str(); + } else + return ""; + } + + bool Proxy::updateId() { + if (doc != NULL) { + DOMElement *identityElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("identity").x_str())->item(0)); + std::string docId = XMLtoUtf8(identityElem->getAttributeNS(levelNS, + Utf8ToXML("id").x_str())).c_str(); + if (id.empty() || id[0] == '_') { + // set yet undetermined id (auto folder, command line,...) + id = docId; + return true; + } else if (id != docId) + return false; + } + return true; + } + + std::string Proxy::getId() { + return id; + } + + int Proxy::getScoreVersion() { + if (doc != NULL) { + DOMElement *versionElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("version").x_str())->item(0)); + scoreVersion = XMLString::parseInt(versionElem->getAttributeNS(levelNS, + Utf8ToXML("score").x_str())); + } + return scoreVersion; + } + + bool Proxy::updateReleaseVersion() { + if (doc != NULL) { + DOMElement *versionElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("version").x_str())->item(0)); + int docRelease = XMLString::parseInt(versionElem->getAttributeNS(levelNS, + Utf8ToXML("release").x_str())); + if (releaseVersion == 0) { + // set yet undetermined version + releaseVersion = docRelease; + return true; + } else if (releaseVersion != docRelease) { + return false; + } + } + return true; + } + + int Proxy::getReleaseVersion() { + return releaseVersion; + } + + int Proxy::getRevisionNumber() { + if (doc != NULL) { + DOMElement *versionElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("version").x_str())->item(0)); + const XMLCh * revision = versionElem->getAttributeNS(levelNS, + Utf8ToXML("revision").x_str()); + unsigned int len = XMLString::stringLen(revision); + int start = -1; + int end = -1; + for (int i = 0; i(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("version").x_str())->item(0)); + std::string status = XMLtoUtf8(versionElem->getAttributeNS(levelNS, + Utf8ToXML("status").x_str())).c_str(); + if (status == "released") + levelStatus = STATUS_RELEASED; + else if (status == "stable") + levelStatus = STATUS_STABLE; + else if (status == "test") + levelStatus = STATUS_TEST; + else if (status == "experimental") + levelStatus = STATUS_EXPERIMENTAL; + } + return levelStatus; + } + + std::string Proxy::getAuthor() { + if (doc != NULL) { + DOMElement *authorElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("author").x_str())->item(0)); + author = XMLtoUtf8(authorElem->getAttributeNS(levelNS, + Utf8ToXML("name").x_str())).c_str(); + } + return author; + } + + std::string Proxy::getTitle() { + if (doc != NULL) { + DOMElement *identityElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("identity").x_str())->item(0)); + title = XMLtoUtf8(identityElem->getAttributeNS(levelNS, + Utf8ToXML("title").x_str())).c_str(); + } + return title; + } + + bool Proxy::hasEasymode() { + if (doc != NULL) { + DOMElement *modesElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("modes").x_str())->item(0)); + hasEasymodeFlag = boolValue(modesElem->getAttributeNS(levelNS, + Utf8ToXML("easy").x_str())); + } + return hasEasymodeFlag; + } + + std::string Proxy::getContact() { + std::string contact; + if (doc != NULL) { + DOMElement *authorElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("author").x_str())->item(0)); + contact = XMLtoUtf8(authorElem->getAttributeNS(levelNS, + Utf8ToXML("email").x_str())).c_str(); + } + return contact; + } + + std::string Proxy::getHomepage() { + std::string homepage; + if (doc != NULL) { + DOMElement *authorElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("author").x_str())->item(0)); + homepage = XMLtoUtf8(authorElem->getAttributeNS(levelNS, + Utf8ToXML("homepage").x_str())).c_str(); + } + return homepage; + } + + double Proxy::getEnigmaCompatibility() { + double value = 0.92; + if (doc != NULL) { + DOMElement *compatibilityElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("compatibility").x_str())->item(0)); + XMLDouble * result = new XMLDouble(compatibilityElem->getAttributeNS(levelNS, + Utf8ToXML("enigma").x_str())); + value = result->getValue(); + delete result; + } + return value; + } + + GameType Proxy::getEngineCompatibility() { + if (doc != NULL) { + DOMElement *authorElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("compatibility").x_str())->item(0)); + engineCompatibility = GetGameType(XMLtoUtf8(authorElem->getAttributeNS(levelNS, + Utf8ToXML("engine").x_str())).c_str()); + } + return engineCompatibility; + } + + + controlType Proxy::getControl() { + controlType control = force; + if (doc != NULL) { + DOMElement *authorElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("modes").x_str())->item(0)); + std::string controlString = XMLtoUtf8(authorElem->getAttributeNS(levelNS, + Utf8ToXML("control").x_str())).c_str(); + if (controlString == "balance") + control = balance; + else if (controlString == "key") + control = key; + else if (controlString == "other") + control = other; + } + return control; + } + + scoreUnitType Proxy::getScoreUnit() { + if (doc != NULL) { + DOMElement *modesElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("modes").x_str())->item(0)); + std::string txt = XMLtoUtf8(modesElem->getAttributeNS(levelNS, + Utf8ToXML("scoreunit").x_str())).c_str(); + if (txt == "number") + scoreUnit = number; + else + // default + scoreUnit = duration; + } + return scoreUnit; + } + + std::string Proxy::getScoreTarget() { + std::string title = "time"; + if (doc != NULL) { + DOMElement *modesElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("modes").x_str())->item(0)); + title = XMLtoUtf8(modesElem->getAttributeNS(levelNS, + Utf8ToXML("scoretarget").x_str())).c_str(); + } + return title; + } + + std::string Proxy::getCredits(bool infoUsage) { + std::string credits; + std::string attribute = infoUsage ? "showinfo" : "showstart"; + if (doc != NULL) { + DOMElement *creditsElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("credits").x_str())->item(0)); + if (creditsElem != NULL) // element is optional + if (boolValue(creditsElem->getAttributeNS(levelNS, + Utf8ToXML(attribute.c_str()).x_str()))) + credits = XMLtoUtf8(creditsElem->getTextContent()).c_str(); + } + return credits; + } + + std::string Proxy::getDedication(bool infoUsage) { + std::string dedication; + std::string attribute = infoUsage ? "showinfo" : "showstart"; + if (doc != NULL) { + DOMElement *dedicationElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("dedication").x_str())->item(0)); + if (dedicationElem != NULL) // element is optional + if (boolValue(dedicationElem->getAttributeNS(levelNS, + Utf8ToXML(attribute.c_str()).x_str()))) + dedication = XMLtoUtf8(dedicationElem->getTextContent()).c_str(); + } + return dedication; + } + int Proxy::getEasyScore() { + int score = -1; + if (doc != NULL) { + DOMElement *versionElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("score").x_str())->item(0)); + score = scoreText2Int(XMLtoUtf8(versionElem->getAttributeNS(levelNS, + Utf8ToXML("easy").x_str())).c_str()); + } + return score; + } + + int Proxy::getDifficultScore() { + int score = -1; + if (doc != NULL) { + DOMElement *versionElem = + dynamic_cast(infoElem->getElementsByTagNameNS( + levelNS, Utf8ToXML("score").x_str())->item(0)); + score = scoreText2Int(XMLtoUtf8(versionElem->getAttributeNS(levelNS, + Utf8ToXML("difficult").x_str())).c_str()); + } + return score; + } + + int Proxy::scoreText2Int(std::string text) { + if (text == "-") + return -1; + else { + std::string::size_type colon = text.find(':'); + if (colon == std::string::npos) + return atoi(text.c_str()); + else + return atoi(text.substr(0,colon).c_str()) * 60 + + atoi(text.substr(colon+1).c_str()); + } + } +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/Proxy.hh b/project/jni/application/enigma/src/lev/Proxy.hh new file mode 100644 index 000000000..acfc1d248 --- /dev/null +++ b/project/jni/application/enigma/src/lev/Proxy.hh @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LEV_PROXY_HH_INCLUDED +#define LEV_PROXY_HH_INCLUDED + +#include "enigma.hh" + +#include +#include +#include +#include + +namespace enigma { namespace lev { + enum controlType {force, balance, key, other}; + enum scoreUnitType {duration, number}; + enum scoreTargetType {time, pushes, moves, callback}; + enum levelStatusType { + STATUS_RELEASED, + STATUS_STABLE, + STATUS_TEST, + STATUS_EXPERIMENTAL, + STATUS_UNKNOWN + }; + + /** + * A standin for an addressable level file and its level metadata. + * Every level index and the commandline register their levels with the + * known level metadata. The unique proxy is henceforth used to access + * level data and to load the level. The level metadata are updated on + * level load and can be updated without a complete load of the level. + * + * The supplied level address is analysed and normalized to a unique + * schema. Urls, Enigma resource path addresses, absolute paths and + * Oxyd level addresses are supported. + * + * Mismatches of levels to requested levels are detected on level load. + * Old level revisions on the users Enigma home directory shadowing new + * revisions of a new Enigma installation will be handled, too. + */ + class Proxy { + public: + enum pathType { pt_url, pt_resource, pt_absolute, pt_oxyd}; + static const XMLCh levelNS[]; // the XML namespace + static Proxy *loadedLevel(); // tmp ? + + /** + * The registration of a level. + * @arg levelPath as stored in indices or entered on the commandline. + * valid formats are: + * welcome, ./firefox, stable/welcome:, ftp://..., #oxyd#17 + * @arg indexPath a path identifier of the index in strict standard form: + * stable, "", #commandline, #history, #oxyd, http://... + * resource path level packs use the subdirectory name below + * levels or "", zipped packs the filename without suffix + * @arg levelId the version independent level id as used for scoring + * @arg levelTitle the english title of the level + * @arg levelAuthor the name of the author + * @arg levelScoreVersion the score version + * @arg levelRelease the level compatibility release version + * @arg levelHasEasymode support of an easy mode + */ + static Proxy *registerLevel(std::string levelPath, std::string indexPath, + std::string levelId, std::string levelTitle, std::string levelAuthor, + int levelScoreVersion, int levelRelease, bool levelHasEasymode, + GameType levelCompatibilty, levelStatusType status =STATUS_RELEASED, + int levelRevision = 0); + + static Proxy *autoRegisterLevel(std::string indexPath, std::string filename); + + static std::string search(std::string text); + static void countLevels(); + static std::set getLevelIds(bool withEasy); + static std::set getProxies(); + + void loadLevel(); + void loadMetadata(bool expectLevel); + Proxy * copy(std::string newBasePath, std::string newPackPath, bool backup = true); + + /** + * Retrieve and translate a level string. The key may be "title", + * "subtitle" or any level specific string key. The priorities for + * translation are as follows: protected translation - gettext + * translation - public translation - protected english - key + * @arg key the key for the search string + * @return the translation of the string + */ + std::string getLocalizedString(const std::string &key); + + std::string getId(); + int getScoreVersion(); + int getReleaseVersion(); + int getRevisionNumber(); + levelStatusType getLevelStatus(); + std::string getAuthor(); + std::string getTitle(); // english title + bool hasEasymode(); + std::string getContact(); + std::string getHomepage(); + controlType getControl(); + scoreUnitType getScoreUnit(); + std::string getScoreTarget(); + std::string getCredits(bool infoUsage); + std::string getDedication(bool infoUsage); + int getEasyScore(); + int getDifficultScore(); + GameType getEngineCompatibility(); + double getEnigmaCompatibility(); + + /** + * the level address that can be used independent of a level pack + * as a crossreference. (stable/welcome, #oxyd#17, http://..., ~/test) + */ + std::string getNormLevelPath(); + + /** + * The normalized level path with all critical characters substituted + * by '~' to allow url's to be used and to make generated paths portable. + * Url protocols like "http://" are substituted by "http/". The returned + * path can be used as a local path element. + */ + std::string getLocalSubstitutionLevelPath(); + + /** + * the type of the level address + */ + Proxy::pathType getNormPathType(); + std::string getAbsLevelPath(); + void loadDependency(std::string depId); + void release(); + private: + static Proxy *currentLevel; + static std::map cache; + static std::vector loadedLibs; + static std::vector registeredLibs; + static void releaseLibs(); + + bool isLibraryFlag; + pathType normPathType; + std::string normLevelPath; // stable/welcome, #oxyd#17, http://..., ~/test + std::string absLevelPath; + std::string id; // level id - old filename or indexname + std::string title; // old name + std::string author; + int scoreVersion; + int releaseVersion; + int revisionNumber; + levelStatusType levelStatus; + bool hasEasymodeFlag; + scoreUnitType scoreUnit; + /** + * The compatibility that needs to be preset on level load. + * Usually level set the compatibility themselves on load. But heritage + * levels loaded from DAT-files do not. Thus we need to keep the type + * info in the Proxy as an exception. + */ + GameType engineCompatibility; + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *infoElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList *stringList; + + Proxy(bool proxyIsLibrary, pathType thePathType, std::string theNormLevelPath, + std::string levelId, std::string levelTitle, std::string levelAuthor, + int levelScoreVersion, int levelRelease, bool levelHasEasymode, + GameType levelCompatibilty, levelStatusType status, int levelRevision = 0); + ~Proxy(); + void load(bool onlyMetadata, bool expectLevel); + void loadDoc(); + void loadLuaCode(); + void processDependencies(); + void registerPreloadDependency(std::string depPath, std::string depId, + int depRelease, bool depPreload, std::string depUrl); + std::string getType(); + bool updateId(); + bool updateReleaseVersion(); + int scoreText2Int(std::string text); + }; +}} // namespace enigma::lev +#endif + diff --git a/project/jni/application/enigma/src/lev/RatingManager.cpp b/project/jni/application/enigma/src/lev/RatingManager.cpp new file mode 100644 index 000000000..a95d681f2 --- /dev/null +++ b/project/jni/application/enigma/src/lev/RatingManager.cpp @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "lev/RatingManager.hh" + +#include "main.hh" +#include "ecl_util.hh" +#include "errors.hh" +#include "LocalToXML.hh" +#include "Utf8ToXML.hh" +#include "XMLtoUtf8.hh" + +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#endif + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + + +namespace enigma { namespace lev { + std::time_t timeValue(const std::string &timeString) { + std::tm time; + time.tm_year = atoi(timeString.substr(0,4).c_str()) - 1900; + time.tm_mon = atoi(timeString.substr(5,2).c_str()) - 1; + time.tm_mday = atoi(timeString.substr(8,2).c_str()); + time.tm_hour = atoi(timeString.substr(11,2).c_str()); + time.tm_min = atoi(timeString.substr(14,2).c_str()); + time.tm_sec = atoi(timeString.substr(17,2).c_str()); + time.tm_isdst = false; + return std::mktime(&time); + } + + std::time_t timeValue(const XMLCh * const string) { + std::string timeString = XMLtoUtf8(string).c_str(); + return timeValue(timeString); + } + + Rating::Rating() + { + intelligence = 0; + dexterity = 0; + patience = 0; + knowledge = 0; + speed = 0; + bestScoreEasy = -1; //DEFAULT_TIME; + bestScoreDifficult = -1; //DEFAULT_TIME; + parScoreEasy = -1; + parScoreDifficult = -1; + numSolvedEasy = 0; + numSolvedDifficult = 0; + pdmSolvedEasy = 0; + pdmSolvedDifficult = 0; + averageRating = -1; + } + + + RatingManager *RatingManager::theSingleton = 0; + + RatingManager* RatingManager::instance() { + if (theSingleton == 0) { + theSingleton = new RatingManager(); + } + return theSingleton; + } + + + + RatingManager::RatingManager() : didAddRatings (false), didEditRatings (false) { + std::tm time; + time.tm_year = 2006 - 1900; // the past - all ratings are newer as this + time.tm_mon = 02; // is the date it was coded + time.tm_mday = 30; + time.tm_hour = 20; + time.tm_min = 10; + time.tm_sec = 00; + time.tm_isdst = false; + ratingVersion = std::mktime(&time); + if(app.state->getString("RatingsUpdateTime").empty()) { + // guarantee usable "RatingsUpdateTime" + char str[22]; + std::strftime(str, 22, "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&ratingVersion)); + std::string currentTimeString = str; + app.state->setProperty("RatingsUpdateTime", currentTimeString); + } + std::string sysRatingPath; + std::string userRatingPath; + if (app.systemFS->findFile(RATINGSFILENAME, sysRatingPath)) { + // preload cache with application distributed ratings + loadURI(sysRatingPath); + } + if (app.resourceFS->findFile(RATINGSFILENAME, userRatingPath) && + userRatingPath != sysRatingPath) { + // update with user saved ratings - ignore cache changes as there + // is no reason to store the cache. + loadURI(userRatingPath); + } + if (app.prefs->getBool("RatingsAutoUpdate") == true) + update(); + } + + RatingManager::~RatingManager() { + } + + RatingManager::loadResult RatingManager::loadURI(std::string path) { + RatingManager::loadResult status = checked; + try { + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToErr(); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("ratings.xsd","ratings.xsd"); + DOMDocument *doc = app.domParser->parseURI(path.c_str()); + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + DOMElement *updateElem = + dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("update").x_str())->item(0)); + std::time_t newVersion = timeValue(updateElem->getAttribute(Utf8ToXML("date").x_str())); + bool isUpdate = (std::difftime(newVersion, ratingVersion) > 0) ? true : false; + if (isUpdate) { + ratingVersion = newVersion; + ratingVersionString = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("date").x_str())).c_str(); + urlFullUpdate = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("urlFull").x_str())).c_str(); + urlIncrementalUpdate = XMLtoUtf8(updateElem->getAttribute(Utf8ToXML("urlIncremental").x_str())).c_str(); + updateMinDelay = XMLString::parseInt(updateElem->getAttribute(Utf8ToXML("delay").x_str())); + } + DOMNodeList *levelList = doc->getElementsByTagName( + Utf8ToXML("level").x_str()); + for (int i = 0, l = levelList-> getLength(); i < l; i++) { + DOMElement *levelElem = dynamic_cast(levelList->item(i)); + const XMLCh * attr = levelElem->getAttribute(Utf8ToXML("id").x_str()); + std::string id = XMLtoUtf8(attr).c_str(); + attr = levelElem->getAttribute(Utf8ToXML("sv").x_str()); + short sv = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + std::string cacheKey = id + "#" + ecl::strf("%d", sv); + std::map::iterator it = cache.find(cacheKey); + Rating * theRating; + bool isNewRating = false; + if (it != cache.end()) { + if (isUpdate) + // update the outdated entry + theRating = it->second; + else + // keep the newer entry + continue; + } else { + // add not existing entries in all cases + theRating = new Rating(); + isNewRating = true; + } + status = updated; + attr = levelElem->getAttribute(Utf8ToXML("int").x_str()); + theRating->intelligence = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("dex").x_str()); + theRating->dexterity = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("pat").x_str()); + theRating->patience = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("kno").x_str()); + theRating->knowledge = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("spe").x_str()); + theRating->speed = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("bse").x_str()); + theRating->bestScoreEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1; + attr = levelElem->getAttribute(Utf8ToXML("bseh").x_str()); + theRating->bestScoreEasyHolder = XMLtoUtf8(attr).c_str(); + attr = levelElem->getAttribute(Utf8ToXML("bsd").x_str()); + theRating->bestScoreDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1; + attr = levelElem->getAttribute(Utf8ToXML("bsdh").x_str()); + theRating->bestScoreDifficultHolder = XMLtoUtf8(attr).c_str(); + attr = levelElem->getAttribute(Utf8ToXML("pare").x_str()); + theRating->parScoreEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1; + attr = levelElem->getAttribute(Utf8ToXML("pard").x_str()); + theRating->parScoreDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1; + attr = levelElem->getAttribute(Utf8ToXML("solvne").x_str()); + theRating->numSolvedEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("solvpe").x_str()); + theRating->pdmSolvedEasy = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("solvnd").x_str()); + theRating->numSolvedDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("solvpd").x_str()); + theRating->pdmSolvedDifficult = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : 0; + attr = levelElem->getAttribute(Utf8ToXML("avgur").x_str()); + theRating->averageRating = XMLString::stringLen(attr) > 0 ? XMLString::parseInt(attr) : -1; + if (isNewRating) + cache.insert(std::make_pair(cacheKey, theRating)); + } + doc->release(); + } else { + status = error; + } + + } + catch (const XMLException& toCatch) { + char* message = XMLString::transcode(toCatch.getMessage()); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + return error; + } + catch (const DOMException& toCatch) { + char* message = XMLString::transcode(toCatch.msg); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + return error; + } + catch (...) { + cerr << "Unexpected Exception on load of preferences\n" ; + return error; + } + return status; + } + + void RatingManager::update() { + std::time_t currentTime = std::time(NULL); + + // check if an update is allowed + std::time_t currentWeirdTime = std::mktime(gmtime(¤tTime)); + double updateDelay = std::difftime(currentWeirdTime, + timeValue(app.state->getString("RatingsUpdateTime"))); + if (updateDelay/86400 < updateMinDelay) { + Log << "Rating update not yet allowed: " < 3 * updateMinDelay) { + result = loadURI(urlFullUpdate); + } + } + + if (result != error) { + // save current time as rating update time + char str[22]; + std::strftime(str, 22, "%Y-%m-%dT%H:%M:%SZ", std::gmtime(¤tTime)); + std::string currentTimeString = str; + app.state->setProperty("RatingsUpdateTime", currentTimeString); + } + + if (didUpdate) + didAddRatings = true; // we need to save the cache + else if (result == checked) + ; // the update had no new ratings + + } + + // the following local vars should be ivars or arguments to doit - but + // as is anti OO it does not allow to transfer a context. + DOMDocument *saveDoc; + DOMElement *levelsElem; + void saveLevelRating(const std::map::value_type info) { + DOMElement *level = saveDoc->createElement(Utf8ToXML("level").x_str()); + std::string id = info.first.substr(0, info.first.rfind('#')); + std::string sv = info.first.substr(info.first.rfind('#')+1); + level->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(id.c_str()).x_str()); + level->setAttribute(Utf8ToXML("sv").x_str(), Utf8ToXML(sv.c_str()).x_str()); + level->setAttribute(Utf8ToXML("int").x_str(), Utf8ToXML(ecl::strf("%d",info.second->intelligence).c_str()).x_str()); + level->setAttribute(Utf8ToXML("dex").x_str(), Utf8ToXML(ecl::strf("%d",info.second->dexterity).c_str()).x_str()); + level->setAttribute(Utf8ToXML("pat").x_str(), Utf8ToXML(ecl::strf("%d",info.second->patience).c_str()).x_str()); + level->setAttribute(Utf8ToXML("kno").x_str(), Utf8ToXML(ecl::strf("%d",info.second->knowledge).c_str()).x_str()); + level->setAttribute(Utf8ToXML("spe").x_str(), Utf8ToXML(ecl::strf("%d",info.second->speed).c_str()).x_str()); + level->setAttribute(Utf8ToXML("bse").x_str(), Utf8ToXML(ecl::strf("%d",info.second->bestScoreEasy).c_str()).x_str()); + level->setAttribute(Utf8ToXML("bsd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->bestScoreDifficult).c_str()).x_str()); + level->setAttribute(Utf8ToXML("bseh").x_str(), Utf8ToXML(info.second->bestScoreEasyHolder.c_str()).x_str()); + level->setAttribute(Utf8ToXML("bsdh").x_str(), Utf8ToXML(info.second->bestScoreDifficultHolder.c_str()).x_str()); + level->setAttribute(Utf8ToXML("pare").x_str(), Utf8ToXML(ecl::strf("%d",info.second->parScoreEasy).c_str()).x_str()); + level->setAttribute(Utf8ToXML("pard").x_str(), Utf8ToXML(ecl::strf("%d",info.second->parScoreDifficult).c_str()).x_str()); + level->setAttribute(Utf8ToXML("solvne").x_str(), Utf8ToXML(ecl::strf("%d",info.second->numSolvedEasy).c_str()).x_str()); + level->setAttribute(Utf8ToXML("solvpe").x_str(), Utf8ToXML(ecl::strf("%d",info.second->pdmSolvedEasy).c_str()).x_str()); + level->setAttribute(Utf8ToXML("solvnd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->numSolvedDifficult).c_str()).x_str()); + level->setAttribute(Utf8ToXML("solvpd").x_str(), Utf8ToXML(ecl::strf("%d",info.second->pdmSolvedDifficult).c_str()).x_str()); + level->setAttribute(Utf8ToXML("avgur").x_str(), Utf8ToXML(ecl::strf("%d",info.second->averageRating).c_str()).x_str()); + levelsElem->appendChild(level); + } + + void RatingManager::save() { + if (!(didAddRatings || didEditRatings)) + return; + if (didEditRatings) { + // set the rating version to the current time + char date[25]; + std::time_t current = std::time(NULL); + strftime(date, 25, "%Y-%m-%dT%H:%M:%SZ",gmtime(¤t)); + ratingVersionString = date; + } + std::string ratTemplatePath; + if (!app.systemFS->findFile("schemas/ratings.xml" , ratTemplatePath)) { + cerr << "Ratings: no template found\n"; + return; + } + try { + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToErr(); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("ratings.xsd","ratings.xsd"); + saveDoc = app.domParser->parseURI(ratTemplatePath.c_str()); + if (saveDoc != NULL && !app.domParserErrorHandler->getSawErrors()) { + DOMElement * updateElem = dynamic_cast(saveDoc->getElementsByTagName( + Utf8ToXML("update").x_str())->item(0)); + updateElem->setAttribute(Utf8ToXML("date").x_str(), Utf8ToXML(ratingVersionString.c_str()).x_str()); + updateElem->setAttribute(Utf8ToXML("urlFull").x_str(), Utf8ToXML(urlFullUpdate.c_str()).x_str()); + updateElem->setAttribute(Utf8ToXML("urlIncremental").x_str(), Utf8ToXML(urlIncrementalUpdate.c_str()).x_str()); + updateElem->setAttribute(Utf8ToXML("delay").x_str(), Utf8ToXML(ecl::strf("%d",updateMinDelay).c_str()).x_str()); + levelsElem = dynamic_cast(saveDoc->getElementsByTagName( + Utf8ToXML("levels").x_str())->item(0)); + std::for_each(cache.begin(), cache.end(), saveLevelRating); + std::string writePath = app.userPath + "/" + RATINGSFILENAME; +#if _XERCES_VERSION >= 30000 + app.domSer->writeToURI(saveDoc, LocalToXML(writePath.c_str()).x_str()); +#else + XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(writePath.c_str()); + app.domSer->writeNode(myFormTarget, *saveDoc); +#endif + saveDoc->release(); + } + } + catch (const XMLException& toCatch) { + char* message = XMLString::transcode(toCatch.getMessage()); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + return; + } + catch (const DOMException& toCatch) { + char* message = XMLString::transcode(toCatch.msg); + cerr << "Exception on load of preferences: " + << message << "\n"; + XMLString::release(&message); + return; + } + catch (...) { + cerr << "Unexpected Exception on load of preferences\n" ; + return; + } + + } + + void RatingManager::registerRating(std::string levelId, short levelScoreVersion, + short intelligence, short dexterity, short patience, + short knowledge, short speed, short bestScoreEasy, + std::string bestScoreEasyHolder, short bestScoreDifficult, + std::string bestScoreDifficultHolder) { + std::string cacheKey = levelId + "#" + ecl::strf("%d", levelScoreVersion); + std::map::iterator i = cache.find(cacheKey); + if (i != cache.end()) { +// Log << "Rating cache hit for " << levelId <<"\n"; + return; + } + Rating *theRating = new Rating(); + theRating->intelligence = intelligence; + theRating->dexterity = dexterity; + theRating->patience = patience; + theRating->knowledge = knowledge; + theRating->speed = speed; + theRating->bestScoreEasy = bestScoreEasy; + theRating->bestScoreEasyHolder = bestScoreEasyHolder; + theRating->bestScoreDifficult = bestScoreDifficult; + theRating->bestScoreDifficultHolder = bestScoreDifficultHolder; + cache.insert(std::make_pair(cacheKey, theRating)); + didAddRatings = true; + return; + } + + Rating * RatingManager::registerNewRating(Proxy * levelProxy) { + std::string cacheKey = levelProxy->getId() + "#" + + ecl::strf("%d", levelProxy->getScoreVersion()); + std::map::iterator it = cache.find(cacheKey); + if (it != cache.end()) { + return it->second; + } + Rating *theRating = new Rating(); + cache.insert(std::make_pair(cacheKey, theRating)); + didAddRatings = true; + return theRating; + } + + Rating * RatingManager::findRating(Proxy * levelProxy) { + std::string cacheKey = levelProxy->getId() + "#" + + ecl::strf("%d", levelProxy->getScoreVersion()); + std::map::iterator it = cache.find(cacheKey); + if (it != cache.end()) { + Rating * theRating = it->second; + return theRating; + } else { + return NULL; + } + } + + short RatingManager::getIntelligence(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->intelligence; + } + return 0; + } + + void RatingManager::setIntelligence(Proxy *levelProxy, short intelligence) { + Rating * theRating = registerNewRating(levelProxy); + if (theRating != NULL) { + theRating->intelligence = intelligence; + didEditRatings = true; + } + } + + short RatingManager::getDexterity(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->dexterity; + } + return 0; + } + + void RatingManager::setDexterity(Proxy *levelProxy, short dexterity) { + Rating * theRating = registerNewRating(levelProxy); + if (theRating != NULL) { + theRating->dexterity = dexterity; + didEditRatings = true; + } + } + + short RatingManager::getPatience(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->patience; + } + + return 0; + } + + void RatingManager::setPatience(Proxy *levelProxy, short patience) { + Rating * theRating = registerNewRating(levelProxy); + if (theRating != NULL) { + theRating->patience = patience; + didEditRatings = true; + } + } + + short RatingManager::getKnowledge(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->knowledge; + } + + return 0; + } + + void RatingManager::setKnowledge(Proxy *levelProxy, short knowledge) { + Rating * theRating = registerNewRating(levelProxy); + if (theRating != NULL) { + theRating->knowledge = knowledge; + didEditRatings = true; + } + } + + short RatingManager::getSpeed(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->speed; + } + + return 0; + } + + void RatingManager::setSpeed(Proxy *levelProxy, short speed) { + Rating * theRating = registerNewRating(levelProxy); + if (theRating != NULL) { + theRating->speed = speed; + didEditRatings = true; + } + } + + short RatingManager::getDifficulty(Proxy *levelProxy) { + int difficulty = 7*getIntelligence(levelProxy) + + 6*getDexterity(levelProxy) + + 4*getPatience(levelProxy) + + 3*getKnowledge(levelProxy) + + 4*getSpeed(levelProxy) - 23; + return difficulty > 0 ? difficulty : 0; + } + + short RatingManager::getBestScore(Proxy *levelProxy, int difficulty) { + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + switch (difficulty) { + case DIFFICULTY_EASY: + return getBestScoreEasy(levelProxy); + case DIFFICULTY_HARD: + return getBestScoreDifficult(levelProxy); + default: + ecl::Assert (false, "RatingManager::getBestScore illegal difficulty"); + } + return -1; + } + + std::string RatingManager::cutHolders(std::string org, int factor) { + if (factor <= 0) + return org; + + int others = 0; + if (org.rfind(" others") == org.length() - 7) { + // ratings string is already cut + std::string::size_type lastPlus = org.rfind('+'); + std::istringstream othersString(org.substr(lastPlus + 1)); + othersString >> std::dec >> others; + org = org.substr(0, lastPlus); + } + std::string::size_type cutPlus; + for (int i=0; i< factor; i++) { + cutPlus = org.rfind('+'); + if (cutPlus == std::string::npos) + return ""; + org = org.substr(0, cutPlus); + others++; + } + return ecl::strf("%s+%d others", org.c_str(), others); + } + + std::string RatingManager::getBestScoreHolder(Proxy *levelProxy, int difficulty, int cut) { + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + switch (difficulty) { + case DIFFICULTY_EASY: + return getBestScoreEasyHolder(levelProxy, cut); + case DIFFICULTY_HARD: + return getBestScoreDifficultHolder(levelProxy, cut); + default: + ecl::Assert (false, "RatingManager::getBestScoreHolder illegal difficulty"); + } + return ""; + } + + short RatingManager::getBestScoreEasy(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->bestScoreEasy; + } + return -1; + } + + std::string RatingManager::getBestScoreEasyHolder(Proxy *levelProxy, int cut) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return cutHolders(theRating->bestScoreEasyHolder, cut); + } + return ""; + } + + short RatingManager::getBestScoreDifficult(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->bestScoreDifficult; + } + return -1; + } + + std::string RatingManager::getBestScoreDifficultHolder(Proxy *levelProxy, int cut) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return cutHolders(theRating->bestScoreDifficultHolder, cut); + } + return ""; + } + + + short RatingManager::getParScore(Proxy *levelProxy, int difficulty) { + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + switch (difficulty) { + case DIFFICULTY_EASY: + return getParScoreEasy(levelProxy); + case DIFFICULTY_HARD: + return getParScoreDifficult(levelProxy); + default: + ecl::Assert (false, "RatingManager::getParScore illegal difficulty"); + } + return -1; + } + + short RatingManager::getParScoreEasy(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->parScoreEasy; + } + return -1; + } + + short RatingManager::getParScoreDifficult(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->parScoreDifficult; + } + return -1; + } + + short RatingManager::getNumSolvedEasy(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->numSolvedEasy; + } + return 0; + } + + short RatingManager::getNumSolvedDifficult(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->numSolvedDifficult; + } + return 0; + } + + short RatingManager::getPdmSolvedEasy(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->pdmSolvedEasy; + } + return 0; + } + + std::string RatingManager::getPcSolvedEasy(Proxy *levelProxy) { + std::string s = ecl::strf("%5.3d", getPdmSolvedEasy(levelProxy)); + s.insert(3,1,'.'); + return s; + } + + short RatingManager::getPdmSolvedDifficult(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->pdmSolvedDifficult; + } + return 0; + } + + std::string RatingManager::getPcSolvedDifficult(Proxy *levelProxy) { + std::string s = ecl::strf("%5.3d", getPdmSolvedDifficult(levelProxy)); + s.insert(3,1,'.'); + return s; + } + + short RatingManager::getDAverageRating(Proxy *levelProxy) { + Rating * theRating = findRating(levelProxy); + if (theRating != NULL) { + return theRating->averageRating; + } + return -1; + } + + std::string RatingManager::getAverageRating(Proxy *levelProxy) { + short rat = getDAverageRating(levelProxy); + std::string s; + if ( rat >= 0 ) { + s = ecl::strf("%3.2d", rat); + s.insert(2,1,'.'); + } else { + s = "-"; + } + return s; + } +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/RatingManager.hh b/project/jni/application/enigma/src/lev/RatingManager.hh new file mode 100644 index 000000000..1ccfb579a --- /dev/null +++ b/project/jni/application/enigma/src/lev/RatingManager.hh @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2006, 2007 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef RATINGMGR_HH_INCLUDED +#define RATINGMGR_HH_INCLUDED + +#include "lev/Proxy.hh" +#include "enigma.hh" + +#include +#include +#include + +namespace enigma { namespace lev { + /** + * The public rating info is currently just a data structure as the data + * have no behaviour. No public direct access to the data should occur. + */ + struct Rating + { + // Constructor + Rating(); + + // Constants + enum { DEFAULT_TIME = 99*60+59 }; + + // Variables. + short intelligence; + short dexterity; + short patience; + short knowledge; + short speed; + short bestScoreEasy; // Best time in seconds or Minimum moves to solve level(for easy mode) + short bestScoreDifficult; // Best time in seconds or Minimum moves to solve level(for normal mode) + std::string bestScoreEasyHolder; // player name(s) for 'best_time_easy' + std::string bestScoreDifficultHolder; // same for 'best_time_normal' + short parScoreEasy; + short parScoreDifficult; + int numSolvedEasy; + int numSolvedDifficult; + short pdmSolvedEasy; // per deca mille - 1/10000 + short pdmSolvedDifficult; + short averageRating; // rating * 10 + }; + + /** + * A singelton manager for public ratings and score statistics + * The singelton loads the ratings on initialization and caches them. An + * update methods allows to online update the ratings and a save method that + * should be called on game exit saves the cached ratings if they have been + * modified. + * + * The key to rating access is the Proxy. A rating is bound to a level id + * together with its scoring version. + * An up to date rating file is distributed with the application. These + * ratings are filled up by existing ratings and stored on the users enigma + * path. On an update which can be incremental or full the downloaded ratings + * replace their predecessors. All other ratings are kept. + * + * Only the rating administrator can and should edit ratings by copying + * the current offical rating file to his user enigma path, editing + * the ratings and leaving Enigma what saves the ratings with the current + * time stamp. The rating administrator has to ensure that he plays + * a clean Enigma version without any additional level packs on his + * resource path! + * + * Note: the time handling is a little bit weird due to limitations in the + * C++ standard libs. As no convertion for GMT to time_t exists and we + * would have to know the daylight saving offsets for a correct localtime + * handling we cheat with our time handling: We save correct GMT time values. + * But we read these values and convert them to local times without daylight + * saving correctures. This does not harm as we use these values only for + * comparisons and nothing more! + */ + class RatingManager { + public: + static RatingManager *instance(); + ~RatingManager(); + + /** + * Online updates the ratings from the update URLs. Online updates + * are only processed if the delay period after the last successful + * update is over. This feature reduced network access and server + * load. + * Unsuccessful online updates do not harm. + */ + void update(); + + /** + * Save the ratings if changes are pending. + */ + void save(); + + /** + * Registers an 0.92 index.txt rating in the cache. This rating will be + * used and stored in the new users ratings.xml only if no newer rating + * exists. + */ + void registerRating(std::string levelId, short levelScoreVersion, + short intelligence, short dexterity, short patience, + short knowledge, short speed, short bestScoreEasy, + std::string bestScoreEasyHolder, short bestScoreDifficult, + std::string bestScoreDifficultHolder); + short getIntelligence(Proxy *levelProxy); + void setIntelligence(Proxy *levelProxy, short intelligence); + short getDexterity(Proxy *levelProxy); + void setDexterity(Proxy *levelProxy, short dexterity); + short getPatience(Proxy *levelProxy); + void setPatience(Proxy *levelProxy, short patience); + short getKnowledge(Proxy *levelProxy); + void setKnowledge(Proxy *levelProxy, short knowledge); + short getSpeed(Proxy *levelProxy); + void setSpeed(Proxy *levelProxy, short speed); + short getDifficulty(Proxy *levelProxy); + short getBestScore(Proxy *levelProxy, int difficulty); + std::string getBestScoreHolder(Proxy *levelProxy, int difficulty, int cut = 0); + short getBestScoreEasy(Proxy *levelProxy); + std::string getBestScoreEasyHolder(Proxy *levelProxy, int cut = 0); + short getBestScoreDifficult(Proxy *levelProxy); + std::string getBestScoreDifficultHolder(Proxy *levelProxy, int cut = 0); + short getParScore(Proxy *levelProxy, int difficulty); + short getParScoreEasy(Proxy *levelProxy); + short getParScoreDifficult(Proxy *levelProxy); + short getNumSolvedEasy(Proxy *levelProxy); + short getNumSolvedDifficult(Proxy *levelProxy); + short getPdmSolvedEasy(Proxy *levelProxy); + std::string getPcSolvedEasy(Proxy *levelProxy); + short getPdmSolvedDifficult(Proxy *levelProxy); + std::string getPcSolvedDifficult(Proxy *levelProxy); + short getDAverageRating(Proxy *levelProxy); + std::string getAverageRating(Proxy *levelProxy); + protected: + RatingManager(); + private: + enum loadResult { updated, checked, error}; + static RatingManager *theSingleton; + std::map cache; + std::time_t ratingVersion; + std::string ratingVersionString; + std::string urlFullUpdate; + std::string urlIncrementalUpdate; + short updateMinDelay; + Rating * findRating(Proxy * levelProxy); + Rating * registerNewRating(Proxy * levelProxy); + std::string cutHolders(std::string org, int factor); + + /** + * Loads the ratings from a given URI and updates the cached ratings. + * Not existing ratings are always added, existing ratings are updated + * only if the rating version is newer than the cached version. + * @arg path a local filepath or a URL + * @return ratings cache updated, no updates but file checked, or error + */ + loadResult loadURI(std::string path); + bool didAddRatings; + bool didEditRatings; + }; +}} // namespace enigma::lev + +#endif diff --git a/project/jni/application/enigma/src/lev/ScoreManager.cpp b/project/jni/application/enigma/src/lev/ScoreManager.cpp new file mode 100644 index 000000000..9a021289c --- /dev/null +++ b/project/jni/application/enigma/src/lev/ScoreManager.cpp @@ -0,0 +1,1152 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "lev/ScoreManager.hh" +#include "enigma.hh" +#include "errors.hh" +#include "DOMErrorReporter.hh" +#include "DOMSchemaResolver.hh" +#include "LocalToXML.hh" +#include "Utf8ToXML.hh" +#include "utilXML.hh" +#include "XMLtoLocal.hh" +#include "XMLtoUtf8.hh" +#include "ecl_system.hh" +#include "gui/ErrorMenu.hh" +#include "nls.hh" +#include "file.hh" + +#include "main.hh" +#include "options.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if _XERCES_VERSION < 30000 +#include +#include +#endif + + +using namespace std; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +namespace { +#if _XERCES_VERSION >= 30000 + class ScoreDomSerFilter : public DOMLSSerializerFilter { + public: + virtual DOMNodeFilter::FilterAction acceptNode(const DOMNode *node) const; +#else + class ScoreDomSerFilter : public DOMWriterFilter { + public: + virtual short acceptNode(const DOMNode *node) const; +#endif + virtual unsigned long getWhatToShow () const { + return DOMNodeFilter::SHOW_ALL; + } + virtual void setWhatToShow (unsigned long toShow) {} + }; + +#if _XERCES_VERSION >= 30000 + DOMNodeFilter::FilterAction ScoreDomSerFilter::acceptNode(const DOMNode *node) const { +#else + short ScoreDomSerFilter::acceptNode(const DOMNode *node) const { +#endif + if (node->getNodeType () == DOMNode::ELEMENT_NODE && + std::string(XMLtoUtf8(node->getNodeName()).c_str()) == "level") { + const DOMElement *e = dynamic_cast(node); + std::string id = XMLtoUtf8(e->getAttribute(Utf8ToXML("id").x_str())).c_str(); + if (id.find("Import ") == 0) { + // reject scores for levels imported from dat files + return DOMNodeFilter::FILTER_REJECT; + } + } + return DOMNodeFilter::FILTER_ACCEPT; + } +} + +namespace enigma { namespace lev { + ScoreManager *ScoreManager::theSingleton = 0; + unsigned ScoreManager::ctab[256]; + unsigned ScoreManager::pol = 0x1021; + + ScoreManager* ScoreManager::instance() { + if (theSingleton == 0) { + for (unsigned i = 0; i < 256; i++) { + unsigned r = i << 8; + for (int j = 0; j < 8; j++) { + bool bit = (r & 0x8000) != 0; + r <<= 1; + if (bit) + r ^= pol; + } + ctab[i] = r & 0xFFFF; + } + theSingleton = new ScoreManager(); + } + return theSingleton; + } + + ScoreManager::ScoreManager() : hasValidUserId (false), isModified (false) { + sec(""); + ratingMgr = lev::RatingManager::instance(); + std::string scorePath; + std::string errMessage; + bool isTemplate = false; + bool hasValidStateUserId = false; + std::string stateUserId = app.state->getString("UserId"); + + if (stateUserId.length() == 16 && + stateUserId.find_first_not_of("0123456789ABCDEF") == std::string::npos) { + unsigned i1, i2, i3, i4; + std::istringstream s1(stateUserId.substr(0, 4)); + std::istringstream s2(stateUserId.substr(4, 4)); + std::istringstream s3(stateUserId.substr(8, 4)); + std::istringstream s4(stateUserId.substr(12, 4)); + s1 >> std::hex >> i1; + s2 >> std::hex >> i2; + s3 >> std::hex >> i3; + s4 >> std::hex >> i4; + if ((i4 == (i1 ^ i2 ^ i3)) && stateUserId != "0000000000000000") { + hasValidStateUserId = true; + Log << "User id '" << stateUserId << "'\n"; + } else + Log << "Bad user id '" << ecl::strf("%.4lX %.4lX %.4lX %.4lX",i1, i2,i3, i4) << "'\n"; + + } + + if (!app.resourceFS->findFile( "enigma.score" , scorePath)) { + isTemplate = true; + if (!app.systemFS->findFile( "schemas/score.xml" , scorePath)) { + throw XFrontend("Cannot load application score template!"); + } + } + + try { + std::ostringstream errStream; + app.domParserErrorHandler->resetErrors(); + app.domParserErrorHandler->reportToOstream(&errStream); + app.domParserSchemaResolver->resetResolver(); + app.domParserSchemaResolver->addSchemaId("score.xsd","score.xsd"); + + if (isTemplate) + doc = app.domParser->parseURI(scorePath.c_str()); + else { + std::basic_ifstream ifs(scorePath.c_str(), ios::binary | ios::in); + std::string zipFile; + char c; + while (ifs.get(c)) + zipFile += (char)(c ^ 0xE5); + std::istringstream zipStream(zipFile); + std::ostringstream content; + readFromZipStream(zipStream, content); + std::string score = content.str(); + #if _XERCES_VERSION >= 30000 + std::auto_ptr domInputScoreSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(score.c_str()), + score.size(), "", false))); + doc = app.domParser->parse(domInputScoreSource.get()); +#else + std::auto_ptr domInputScoreSource ( new Wrapper4InputSource( + new MemBufInputSource(reinterpret_cast(score.c_str()), + score.size(), "", false))); + doc = app.domParser->parse(*domInputScoreSource); +#endif + + } + if (doc != NULL && !app.domParserErrorHandler->getSawErrors()) { + propertiesElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("properties").x_str())->item(0)); + levelsElem = dynamic_cast(doc->getElementsByTagName( + Utf8ToXML("levels").x_str())->item(0)); + levelList = levelsElem->getElementsByTagName(Utf8ToXML("level").x_str()); + + userId = getString("UserId"); + if (hasValidStateUserId && userId == stateUserId) { + hasValidUserId = true; + for (int i = 0, l = levelList->getLength(); i < l; i++) { + DOMElement * levelElem = dynamic_cast(levelList->item(i)); + std::string levelId = XMLtoUtf8(levelElem->getAttribute(Utf8ToXML("id").x_str())).c_str(); + std::string scoreVersion = XMLtoUtf8(levelElem->getAttribute(Utf8ToXML("version").x_str())).c_str(); + DOMNamedNodeMap * attrXMap = levelElem->getAttributes(); + std::map attrMap; + for (int j = 0, k = attrXMap->getLength(); j < k; j++) { + DOMAttr * levelAttr = dynamic_cast(attrXMap->item(j)); + std::string attrName = XMLtoUtf8(levelAttr->getName()).c_str(); + if (attrName != "sec" && levelAttr->getSpecified()) + attrMap[attrName] = XMLtoUtf8(levelAttr->getValue()).c_str(); + } + std::string target; + std::map::iterator it; + for (it = attrMap.begin(); it != attrMap.end(); it++) + target += (*it).second; + target += userId; + if (sec(target) != XMLtoUtf8(levelElem->getAttribute(Utf8ToXML("sec").x_str())).c_str()) { + Log << "Faulty score entry deletion: " << levelId << " - version: " << scoreVersion << "\n"; + levelsElem->removeChild(levelElem); + --i; --l; + continue; + } + + std::string cacheKey = levelId + "#" + scoreVersion; + allLevelScores[cacheKey] = levelElem; + std::map::iterator its; + its = curLevelScores.find(levelId); + if (its == curLevelScores.end()) { + curLevelScores[levelId] = levelElem; + } else { + // another scoreversion for this level exists - compare versions + int previousVersion = XMLString::parseInt(its->second->getAttribute( + Utf8ToXML("version").x_str())); + int thisVersion = XMLString::parseInt(levelElem->getAttribute( + Utf8ToXML("version").x_str())); + if (thisVersion > previousVersion) + curLevelScores[levelId] = levelElem; + } + } + } else if (isTemplate && userId.empty()) { + isModified = true; + if (hasValidStateUserId) { + userId = stateUserId; + setProperty("UserId", userId); + hasValidUserId = true; + } else { + // create first part of user id based on time stamp + unsigned id; + if (RAND_MAX > 0x10000) { + id = std::rand() & 0xFFFFFFFF; + } else { + // MinGW 3.4.2 and maybe other configs + unsigned t = std::time(NULL); + std::srand(t >> 15); + id = (std::rand() << 16) ^ (t & 0xFFFF); + enigma::Randomize(); + } + userId = ecl::strf("%.8lX",id); + // we need a second random part as 2 users may start Enigma + // within the same second - we postpone this part till we save + } + // do not allow existing level entries + int levelCount = levelList->getLength(); + for (int i = 0; i < levelCount; i++) { + levelsElem->removeChild(levelList->item(0)); + } + } else { + doc->release(); + // rename faulty score file as backup and give Enigma a chance + // to recreate a new good score file + std::string scoreBasePath = app.userPath + "/enigma.score"; + std::string backupPath = scoreBasePath + "~s1"; + int i = 1; + while (ecl::FileExists(backupPath)) { + backupPath = scoreBasePath + ecl::strf("~s%d", ++i); + } + std::rename(scoreBasePath.c_str(), backupPath.c_str()); + + errMessage = "Mismatch of state.xml and enigma.score.\n"; + errMessage += "Your current faulty score file is backed up to:\n"; + errMessage += backupPath + "\n"; + errMessage += "Restore both from your backup or just restart\n"; + errMessage += "Enigma to retry with an empty score file\n"; + throw XFrontend(""); + } + // update from 0.92 + + // check if already updated + didUpgrade = (getString("Upgrade") == "true") || + (!isTemplate && (upgradeSum() != getString("Upgrade"))); + if (!didUpgrade) { + if (!hasValidUserId) { + unsigned id3 = idFromLegacyScore(); + if (id3 <= 0xFFFF) { + Log << "idFromLegacyScore\n"; + finishUserId(id3); + } + } + if (hasValidUserId) + if (upgradeLegacy()) { + isModified = true; + didUpgrade = true; + } + } + + } + if(app.domParserErrorHandler->getSawErrors()) { + errMessage = errStream.str(); + } + app.domParserErrorHandler->reportToNull(); // do not report to errStream any more + } + catch (...) { + if (errMessage.empty()) + errMessage = "Unexpected XML Exception on load of score\n"; + } + if (!errMessage.empty()) { + throw XFrontend("Cannot load application score file: " + scorePath + + "\nError: " + errMessage); + } + + + } + + ScoreManager::~ScoreManager() { + if (doc != NULL) + shutdown(); + } + + void ScoreManager::markModified() { + isModified = true; + } + + void ScoreManager::finishUserId(unsigned id3) { + unsigned i1, i2, i3, i4; + std::istringstream s1(userId.substr(0, 4)); + std::istringstream s2(userId.substr(4, 4)); + s1 >> std::hex >> i1; + s2 >> std::hex >> i2; + i3 = id3 & 0xFFFF; + i4 = (i1 ^ i2 ^ i3); + userId += ecl::strf("%.4lX%.4lX",i3, i4); + app.state->setProperty("UserId", userId); + setProperty("UserId", userId); + hasValidUserId = true; + } + + std::string ScoreManager::sec(std::string target) { + int len = target.size(); + unsigned r = 0; + const char *p = target.c_str(); + + while (len--) + r = (r<<8 & 0xFFFF) ^ ctab[(r >> 8) ^ *p++]; + return ecl::strf("%.4lX", r); + } + + bool ScoreManager::save() { + bool result = true; + std::string errMessage; + + if (doc == NULL || !isModified) + return true; + + int count = getInt("Count"); + setProperty("Count", ++count); + + setProperty("UserName", app.state->getString("UserName")); + + if (!hasValidUserId) { + finishUserId(std::time(NULL) & 0xFFFF); + } + + if (userId.find("0000") == 0) { + Log << "ReId Windows 1.00 User Id: " << userId << "\n"; + setProperty("UserId1.00", userId); + app.state->setProperty("UserId1.00", userId); + unsigned id1 = std::rand() & 0xFFFF; + userId.replace(0, 4, ecl::strf("%.4lX",id1)); + unsigned id2, id3, id4; + std::istringstream s2(userId.substr(4, 4)); + std::istringstream s3(userId.substr(8, 4)); + s2 >> std::hex >> id2; + s3 >> std::hex >> id3; + id4 = (id1 ^ id2 ^ id3); + userId.replace(12, 4, ecl::strf("%.4lX",id4)); + app.state->setProperty("UserId", userId); + setProperty("UserId", userId); + Log << "new id: " << userId << "\n"; + } + + for (int i = 0, l = levelList->getLength(); i < l; i++) { + DOMElement * levelElem = dynamic_cast(levelList->item(i)); + DOMNamedNodeMap * attrXMap = levelElem->getAttributes(); + std::map attrMap; + for (int j = 0, k = attrXMap->getLength(); j < k; j++) { + DOMAttr * levelAttr = dynamic_cast(attrXMap->item(j)); + std::string attrName = XMLtoUtf8(levelAttr->getName()).c_str(); + if (attrName != "sec" && levelAttr->getSpecified()) + attrMap[attrName] = XMLtoUtf8(levelAttr->getValue()).c_str(); + } + std::string target; + std::map::iterator it; + for (it = attrMap.begin(); it != attrMap.end(); it++) + target += (*it).second; + target += userId; + levelElem->setAttribute(Utf8ToXML("sec").x_str(), + Utf8ToXML(sec(target)).x_str()); + } + + if (!didUpgrade) + setProperty("Upgrade", upgradeSum()); + else + setProperty("Upgrade", std::string("true")); + + stripIgnorableWhitespace(doc->getDocumentElement()); +// std::string path = app.userPath + "/score.xml"; + std::string zipPath = app.userPath + "/enigma.score"; + std::string zipPathNoDat = app.userPath + "/enigma_nodat.score"; + std::string zipPathBackup = app.userPath + "/backup/enigma.score"; + + // backup score every 10th save as we save after each level completion once + if (count%10 == 0) { + std::remove((zipPathBackup + "~2").c_str()); + std::remove((zipPath + "~2").c_str()); // 1.00 bakups + if (ecl::FileExists(zipPath + "~1")) { + if (std::difftime(ecl::FileModTime(zipPath + "~1"), + ecl::FileModTime(zipPathBackup + "~1")) > 0) { + // backup 1 from 1.00 is newer than backup 1 on backup path + if (Copyfile(zipPath + "~1", zipPathBackup + "~2")) + std::remove((zipPath + "~1").c_str()); // 1.00 bakup + } else { + // just in case off previous copy failure + std::rename((zipPathBackup + "~1").c_str(), (zipPathBackup + "~2").c_str()); + std::remove((zipPath + "~1").c_str()); // 1.00 bakup + } + } else { + std::rename((zipPathBackup + "~1").c_str(), (zipPathBackup + "~2").c_str()); + } + Copyfile(zipPath, zipPathBackup + "~1"); + } + + try { + ScoreDomSerFilter serialFilter; + for (int j=0; j < 2; j++) { // save twice: first all, then without dat scores +#if _XERCES_VERSION >= 30000 +// result = app.domSer->writeToURI(doc, LocalToXML(& path).x_str()); + if (j==1) + (app.domSer)->setFilter(&serialFilter); + std::string contents(XMLtoUtf8(app.domSer->writeToString(doc)).c_str()); + if (j==1) + (app.domSer)->setFilter(NULL); + contents.replace(contents.find("UTF-16"), 6, "UTF-8"); // adapt encoding info +#else +// XMLFormatTarget *myFormTarget = new LocalFileFormatTarget(path.c_str()); +// result = app.domSer->writeNode(myFormTarget, *doc); +// delete myFormTarget; // flush + + MemBufFormatTarget *memFormTarget = new MemBufFormatTarget(); + if (j==1) + app.domSer->setFilter(&serialFilter); + result = app.domSer->writeNode(memFormTarget, *doc); + if (j==1) + app.domSer->setFilter(NULL); + std::string contents( + reinterpret_cast(memFormTarget->getRawBuffer()), + memFormTarget->getLen()); + delete memFormTarget; +#endif + std::istringstream contentStream(contents); + std::ostringstream zippedStream; + writeToZip(zippedStream, "score.xml", contents.size(), contentStream); + std::ofstream of( j==0 ? zipPath.c_str() : zipPathNoDat.c_str(), + ios::out | ios::binary ); + std::string zipScore = zippedStream.str(); + + // patch zipios++ malformed output + // assumptions: just one file named "score.xml" (9 chars) + if ((zipScore[0x06] & 0x08) == 0) { + Log << "Fixing Zipios++ output\n"; + unsigned cdirOffset = zipScore.size() - 22 - 46 - 9; + unsigned compressedSize = cdirOffset - 30 - 9; + zipScore.replace(cdirOffset + 20, 1, 1, (char)(compressedSize & 0xFF)); + zipScore.replace(cdirOffset + 21, 1, 1, (char)((compressedSize & 0xFF00) >> 8)); + zipScore.replace(cdirOffset + 22, 1, 1, (char)((compressedSize & 0xFF0000) >> 16)); + zipScore.replace(cdirOffset + 23, 1, 1, (char)((compressedSize & 0xFF000000) >> 24)); + std::string dataDescr = "\x50\x4B\x07\x08" + zipScore.substr(cdirOffset + 16, 12); + zipScore.replace(6, 1 , 1, '\x08'); // general purpose bit flag + zipScore.replace(14, 12, 12, '\x00'); + zipScore.replace(cdirOffset + 8, 1 , 1, '\x08'); // general purpose bit flag + zipScore.replace(cdirOffset + 38, 8, 8, '\x00'); // external file attr, offset local header + zipScore.insert(cdirOffset, dataDescr); + cdirOffset += 16; + zipScore.replace(zipScore.size() - 6, 1, 1, (char)(cdirOffset & 0xFF)); + zipScore.replace(zipScore.size() - 5, 1, 1, (char)((cdirOffset & 0xFF00) >> 8)); + zipScore.replace(zipScore.size() - 4, 1, 1, (char)((cdirOffset & 0xFF0000) >> 16)); + zipScore.replace(zipScore.size() - 3, 1, 1, (char)((cdirOffset & 0xFF000000) >> 24)); + } + + for (int i=0; irelease(); + doc = NULL; + } + + + bool ScoreManager::isSolved(lev::Proxy *levelProxy, int difficulty) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_ANY, "ScoreManager::isSolved illegal difficulty"); + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + DOMElement * level = getLevel(levelProxy); + if (level != NULL) { + const XMLCh *attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + return score != SCORE_UNSOLVED; + } else + return false; + } + + bool ScoreManager::isOutdated(lev::Proxy *levelProxy, int difficulty) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_HARD, "ScoreManager::isOutdated illegal difficulty"); + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + DOMElement * level = getLevel(levelProxy); + if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + const XMLCh *attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + return score == SCORE_SOLVED; + } else if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) != levelProxy->getScoreVersion()){ + return true; + } else + return false; + } + + int ScoreManager::getBestUserScore(lev::Proxy *levelProxy, int difficulty) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_HARD, "ScoreManager::getBestUserScore illegal difficulty"); + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + DOMElement * level = getLevel(levelProxy); + if (level == NULL || XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) != levelProxy->getScoreVersion()) { + return SCORE_UNSOLVED; + } + const XMLCh *attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + return (score < 0) ? SCORE_UNSOLVED : score; + } + + void ScoreManager::updateUserScore(lev::Proxy *levelProxy, int difficulty, + int score, double enigmaRelease) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_HARD, "ScoreManager::updateUserScore illegal difficulty"); + if (levelProxy->getLevelStatus() != lev::STATUS_RELEASED) + return; + + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + if (score > SCORE_MAX) + score = SCORE_MAX; // distinguish from SCORE_SOLVED levels + + if (!hasValidUserId) { + finishUserId(std::time(NULL) & 0xFFFF); + } + + DOMElement * level = getCreateLevel(levelProxy); + + // get the current newest scoreversion of the level - it exists now + DOMElement * curLevel = curLevelScores[levelProxy->getId()]; + if (curLevel != level) { + // this levelversion is older than the current one + // update the current one concerning the solved status + const XMLCh * attr = curLevel->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int curScore1 = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (curScore1 == SCORE_UNSOLVED) { + isModified = true; + curLevel->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str(), + Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + curLevel->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1rel" : "easy1rel").x_str(), + Utf8ToXML(ecl::strf("%.2f",enigmaRelease)).x_str()); + } + } + // update this levelversion + const XMLCh *attr; + // read attributes - for new elements the XML defaults are not yet given + attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score1 = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff2" : "easy2").x_str()); + int score2 = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1rel" : "easy1rel").x_str()); + double rel1 = 0.92; // default + if (XMLString::stringLen(attr) > 0) { + XMLDouble * result = new XMLDouble(attr); + rel1 = result->getValue(); + delete result; + } + attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff2rel" : "easy2rel").x_str()); + double rel2 = 0.92; // default + if (XMLString::stringLen(attr) > 0) { + XMLDouble * result = new XMLDouble(attr); + rel2 = result->getValue(); + delete result; + } + + bool score1mod = false; + bool score2mod = false; + if (score1 < 0) { + // the level has not yet been solved in this scoreversion + score1 = score; + rel1 = enigmaRelease; + score1mod = true; + } else if (score < score1) { + // new best score + if (rel1 > rel2 && rel1 > enigmaRelease) { + // remember old best score if it is played with newest Enigma version + score2 = score1; + rel2 = rel1; + score2mod = true; + } + score1 = score; + rel1 = enigmaRelease; + score1mod = true; + } else if (score == score1) { + if (rel1 < enigmaRelease) { + // update the Enigma release version for top score + rel1 = enigmaRelease; + score1mod = true; + } + } else if (rel1 < enigmaRelease) { + // score > score1 but played with a more recent Enigma version + // check if we should save it as score2 + if (score2 < 0) { + // second best score and no valid score2 yet + score2 = score; + rel2 = enigmaRelease; + score2mod = true; + } else if (score < score2) { + // score better than previous second best score + if (rel2 <= enigmaRelease) { + score2 = score; + rel2 = enigmaRelease; + score2mod = true; + } + } else if (rel2 < enigmaRelease) { + // we remember the second best score played with the most recent + // Enigma version + score2 = score; + rel2 = enigmaRelease; + score2mod = true; + } + } + if (score1mod) { + isModified = true; + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str(), + Utf8ToXML(ecl::strf("%d",score1)).x_str()); + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1rel" : "easy1rel").x_str(), + Utf8ToXML(ecl::strf("%.2f",rel1)).x_str()); + } + if (score2mod) { + isModified = true; + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff2" : "easy2").x_str(), + Utf8ToXML(ecl::strf("%d",score2)).x_str()); + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff2rel" : "easy2rel").x_str(), + Utf8ToXML(ecl::strf("%.2f",rel2)).x_str()); + } + + } + + bool ScoreManager::bestScoreReached(lev::Proxy *levelProxy, int difficulty) { + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + int bestUserScore = getBestUserScore(levelProxy, difficulty); + int bestScore = ratingMgr->getBestScore(levelProxy, difficulty); + return bestUserScore>=0 && (bestScore<0 || bestUserScore <= bestScore); + } + + bool ScoreManager::parScoreReached(lev::Proxy *levelProxy, int difficulty) { + if (difficulty == DIFFICULTY_EASY && !levelProxy->hasEasymode()) + difficulty = DIFFICULTY_HARD; + + int bestUserScore = getBestUserScore(levelProxy, difficulty); + int parScore = ratingMgr->getParScore(levelProxy, difficulty); + return bestUserScore>=0 && (parScore<0 || bestUserScore <= parScore); + } + + void ScoreManager::markUnsolved(lev::Proxy *levelProxy, int difficulty) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_ANY, "ScoreManager::markUnsolved illegal difficulty"); + DOMElement * level = getLevel(levelProxy); + if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + const XMLCh *attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (score != SCORE_UNSOLVED) { + isModified = true; + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str(), + Utf8ToXML(ecl::strf("%d",SCORE_UNSOLVED)).x_str()); + } + } + } + + void ScoreManager::markSolved(lev::Proxy *levelProxy, int difficulty) { + ecl::Assert (difficulty >= DIFFICULTY_EASY && + difficulty <= DIFFICULTY_ANY, "ScoreManager::markSolved illegal difficulty"); + if (enigma::WizardMode) { + if (!hasValidUserId) { + finishUserId(std::time(NULL) & 0xFFFF); + } + DOMElement * level = getLevel(levelProxy); + if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + const XMLCh *attr = level->getAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (score != SCORE_UNSOLVED) + return; // avoid deleting existing scores + } + // reset the score to solved but no score value + updateUserScore(levelProxy, difficulty, 99*60+59); // store max possible score + level = getLevel(levelProxy); + // check if score is created - it may be NULL if level is not released + if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + isModified = true; + level->setAttribute(Utf8ToXML((difficulty == DIFFICULTY_HARD) ? "diff1" : "easy1").x_str(), + Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + } + } + } + + int ScoreManager::countSolved(lev::Index *ind, int difficulty) { + int i; + int size = ind->size(); + int num = 0; + for (i=0 ; i < size; i++) { + if (isSolved(ind->getProxy(i),difficulty)) + num++; + } + return num; + } + + int ScoreManager::countBestScore(lev::Index *ind, int difficulty) { + int i; + int size = ind->size(); + int num = 0; + for (i=0 ; i < size; i++) { + if (bestScoreReached(ind->getProxy(i),difficulty)) + num++; + } + return num; + } + + int ScoreManager::countParScore(lev::Index *ind, int difficulty) { + int i; + int size = ind->size(); + int num = 0; + for (i=0 ; i < size; i++) { + if (parScoreReached(ind->getProxy(i),difficulty)) + num++; + } + return num; + } + + double ScoreManager::calcHCP(lev::Index *ind, int difficulty) { + static double log_2 = log(2.0); + int i; + int size = ind->size(); + double hcp = 0; + for (i=0 ; i < size; i++) { + double score = getBestUserScore(ind->getProxy(i), difficulty); + double par = ratingMgr->getParScore(ind->getProxy(i), difficulty); + double dhcp = 0; + if (score == SCORE_UNSOLVED) { + dhcp = 1; + } else if (score == SCORE_SOLVED) { + dhcp = 0.7; + } else if (score >= par && par > 0) { + dhcp = log10(score/par); + if (dhcp > 0.7) + dhcp = 0.7; + } else if (score < par && par > 0) { + dhcp = log(score/par) / log_2; + if (dhcp < -3) + dhcp = -3; + } else { // par <= 0 no par + dhcp = -3; + } + hcp += dhcp; + } + return hcp*100.0/size; + } + + void ScoreManager::setRating(lev::Proxy *levelProxy, int rating) { + if (!hasValidUserId) { + finishUserId(std::time(NULL) & 0xFFFF); + } + if (rating == -1) { + DOMElement *level = getLevel(levelProxy); + if (level == NULL) + // no level score entry for this level - -1 is default anyway + return; + else if (XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + const XMLCh *attr = level->getAttribute(Utf8ToXML("rating").x_str()); + int oldRating = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (oldRating != -1) { + isModified = true; + level->setAttribute(Utf8ToXML("rating").x_str(), + Utf8ToXML(ecl::strf("%d",rating)).x_str()); + } + DOMAttr *irAttr = level->getAttributeNode(Utf8ToXML("rating-inherited").x_str()); + if ((irAttr != NULL) && irAttr->getSpecified()) { + // delete any inherited rating that may shadow a default of -1 + level->removeAttribute(Utf8ToXML("rating-inherited").x_str()); + isModified = true; + } + return; + } else { + // no level score entry for this score version exists - the user + // did set the rating to undefined. + DOMElement *level = getCreateLevel(levelProxy); + isModified = true; + level->setAttribute(Utf8ToXML("rating").x_str(), + Utf8ToXML(ecl::strf("%d",rating)).x_str()); + level->removeAttribute(Utf8ToXML("rating-inherited").x_str()); + return; + } + } else if (rating != getRating(levelProxy)) { + DOMElement *level = getCreateLevel(levelProxy); + isModified = true; + level->setAttribute(Utf8ToXML("rating").x_str(), + Utf8ToXML(ecl::strf("%d",rating)).x_str()); + level->removeAttribute(Utf8ToXML("rating-inherited").x_str()); + return; + } + } + + int ScoreManager::getRating(lev::Proxy *levelProxy) { + DOMElement * level = getLevel(levelProxy); + if (level == NULL) + return -1; + else { + const XMLCh *attr = level->getAttribute(Utf8ToXML("rating").x_str()); + int rating = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (rating == -1) { + attr = level->getAttribute(Utf8ToXML("rating-inherited").x_str()); + int ratingInherited = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (ratingInherited >= 0) + rating = ratingInherited; + } + return rating; + } + } + + bool ScoreManager::isRatingInherited(lev::Proxy *levelProxy) { + DOMElement * level = getLevel(levelProxy); + if (level == NULL) + return false; + if (XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) == levelProxy->getScoreVersion()) { + const XMLCh *attr = level->getAttribute(Utf8ToXML("rating").x_str()); + int rating = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (rating == -1) { + attr = level->getAttribute(Utf8ToXML("rating-inherited").x_str()); + int ratingInherited = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (ratingInherited >= 0) + return true; + } + return false; + } else { + const XMLCh *attr = level->getAttribute(Utf8ToXML("rating").x_str()); + int rating = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (rating == -1) + return false; + else + return true; + } + } + + DOMElement * ScoreManager::getLevel(lev::Proxy *levelProxy) { + std::map::iterator it; + std::string cacheKey = levelProxy->getId() + "#" + ecl::strf("%d", levelProxy->getScoreVersion()); + it = allLevelScores.find(cacheKey); + if (it != allLevelScores.end()) { + return it->second; + } else { + it = curLevelScores.find(levelProxy->getId()); + if (it != curLevelScores.end()) { + return it->second; + } else { + return NULL; + } + } + } + + DOMElement * ScoreManager::getCreateLevel(lev::Proxy *levelProxy) { + DOMElement * level = getLevel(levelProxy); + if (level == NULL || XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) != levelProxy->getScoreVersion()) { + // no level score entry for this scoreversion exists - create it + isModified = true; + DOMElement * newLevel = doc->createElement (Utf8ToXML("level").x_str()); + newLevel->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(levelProxy->getId()).x_str()); + newLevel->setAttribute(Utf8ToXML("version").x_str(), Utf8ToXML(ecl::strf("%d",levelProxy->getScoreVersion())).x_str()); + newLevel->setAttribute(Utf8ToXML("sec").x_str(), Utf8ToXML("crc").x_str()); + + levelsElem->appendChild(newLevel); + std::string cacheKey = levelProxy->getId() + "#" + ecl::strf("%d", levelProxy->getScoreVersion()); + allLevelScores[cacheKey] = newLevel; + if (level != NULL && XMLString::parseInt(level->getAttribute( + Utf8ToXML("version").x_str())) < levelProxy->getScoreVersion()) { + // this new levelversion is newer than the previous current one + // update solved status form previous current one + const XMLCh * attr = level->getAttribute(Utf8ToXML("diff1").x_str()); + int scoreDiff = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (scoreDiff != SCORE_UNSOLVED) { + newLevel->setAttribute(Utf8ToXML("diff1").x_str(), Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + newLevel->setAttribute(Utf8ToXML("diff1rel").x_str(), + level->getAttribute(Utf8ToXML("diff1rel").x_str())); + } + attr = level->getAttribute(Utf8ToXML("easy1").x_str()); + int scoreEasy = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (scoreEasy != SCORE_UNSOLVED) { + newLevel->setAttribute(Utf8ToXML("easy1").x_str(), Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + newLevel->setAttribute(Utf8ToXML("easy1rel").x_str(), + level->getAttribute(Utf8ToXML("easy1rel").x_str())); + } + attr = level->getAttribute(Utf8ToXML("rating").x_str()); + int oldRating = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (oldRating != -1) { + newLevel->setAttribute(Utf8ToXML("rating-inherited").x_str(), + Utf8ToXML(ecl::strf("%d",oldRating)).x_str()); + } + curLevelScores[levelProxy->getId()] = newLevel; + } else if (level == NULL) { + curLevelScores[levelProxy->getId()] = newLevel; + } + level = newLevel; + } + return level; + } + + // Legacy 0.92 + unsigned ScoreManager::idFromLegacyScore() { + unsigned id3 = 0; + int bits = 0; + bool hasLevels = false; + for (int mode = 0; mode<2; mode++) { + bool withEasy = (mode == 0) ? false : true; + std::set levelIds = Proxy::getLevelIds(withEasy); + for (std::set::iterator it = levelIds.begin(); it != levelIds.end(); it++) { + options::LevelStatus levelstat; + if (options::GetLevelStatus (*it, levelstat)) { + hasLevels = true; + int diffScore = levelstat.time_hard; + if (diffScore > 0) { + id3 <<= 1; + id3 |= (diffScore & 1); + bits++; + if (bits >= 16) + return id3 & 0xFFFF; + } + } + } + } + if (hasLevels) + return id3 & 0xFFFF; + else + return 0x10000; + } + + std::string ScoreManager::upgradeSum() { + std::string scores; + for (int i = 0, l = levelList->getLength(); i < l; i++) { + DOMElement * level = dynamic_cast(levelList->item(i)); + const XMLCh *attr = level->getAttribute(Utf8ToXML("diff1").x_str()); + int score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (score >= 0) + scores += ecl::strf("%d", score); + attr = level->getAttribute(Utf8ToXML("easy1").x_str()); + score = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (score >= 0) + scores += ecl::strf("%d", score); + } + return sec(scores); + } + + bool ScoreManager::upgradeLegacy() { + bool result = false; + // it is likely that the user has tested 1.00 and solved a few levels + // before he updates from his enigma.rc2 + for (int mode = 0; mode<2; mode++) { + // first levels without easy, then those with easy + bool withEasy = (mode == 0) ? false : true; + std::set levelIds = Proxy::getLevelIds(withEasy); + for (std::set::iterator it = levelIds.begin(); it != levelIds.end(); it++) { + options::LevelStatus levelstat; + if (options::GetLevelStatus (*it, levelstat)) { + result = true; + int diffScore = levelstat.time_hard; + if (!withEasy) { + // 0.92 scores could be saved at either place + if (levelstat.time_easy > 0 && (levelstat.time_easy < diffScore || diffScore<0)) + diffScore = levelstat.time_easy; + } + bool solvedDiff = ((levelstat.finished & DIFFICULTY_HARD) || + (!withEasy && levelstat.finished)) && + diffScore != SCORE_UNSOLVED; + int easyScore = levelstat.time_easy; + bool solvedEasy = (levelstat.finished & DIFFICULTY_EASY) && + easyScore != SCORE_UNSOLVED; + + // limit scores to 99*60+59 - they need to fit in a short for XML + if (diffScore > SCORE_MAX) + diffScore = SCORE_MAX; + if (easyScore > SCORE_MAX) + easyScore = SCORE_MAX; + + std::map::iterator itx; + // do we have to update a score entry for a higher version ? + itx = curLevelScores.find(*it); + bool isMostRecent = true; + if (itx != curLevelScores.end() && + levelstat.solved_revision < XMLString::parseInt( + (*itx).second->getAttribute(Utf8ToXML("version").x_str()))) { + isMostRecent = false; + if (solvedDiff) { + const XMLCh *attr = (*itx).second->getAttribute(Utf8ToXML("diff1").x_str()); + int curDiff = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (curDiff == SCORE_UNSOLVED) { + (*itx).second->setAttribute(Utf8ToXML("diff1").x_str(), + Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("diff1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + } + if (withEasy && solvedEasy) { + const XMLCh *attr = (*itx).second->getAttribute(Utf8ToXML("easy1").x_str()); + int curEasy = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (curEasy == SCORE_UNSOLVED) { + (*itx).second->setAttribute(Utf8ToXML("easy1").x_str(), + Utf8ToXML(ecl::strf("%d",SCORE_SOLVED)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("easy1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + } + } + // do we have already an score entry for this version? + std::string cacheKey = *it + "#" + ecl::strf("%d", levelstat.solved_revision); + itx = allLevelScores.find(cacheKey); + if (itx != allLevelScores.end() && + levelstat.solved_revision == XMLString::parseInt( + (*itx).second->getAttribute(Utf8ToXML("version").x_str()))) { + // check if old score is better than current one + if (solvedDiff) { + const XMLCh *attr = (*itx).second->getAttribute(Utf8ToXML("diff1").x_str()); + int curDiff = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (diffScore < curDiff || curDiff < 0) { + if (curDiff >= 0) { + // backup current best score as second + (*itx).second->setAttribute(Utf8ToXML("diff2").x_str(), + Utf8ToXML(ecl::strf("%d",curDiff)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("diff2rel").x_str(), + (*itx).second->getAttribute(Utf8ToXML("diff1rel").x_str())); + } + (*itx).second->setAttribute(Utf8ToXML("diff1").x_str(), + Utf8ToXML(ecl::strf("%d",diffScore)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("diff1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + } + if (withEasy && solvedEasy) { + const XMLCh *attr = (*itx).second->getAttribute(Utf8ToXML("easy1").x_str()); + int curEasy = (XMLString::stringLen(attr) > 0) ? XMLString::parseInt(attr) : -1; + if (easyScore < curEasy || curEasy < 0) { + if (curEasy >= 0) { + // backup current best score as second + (*itx).second->setAttribute(Utf8ToXML("easy2").x_str(), + Utf8ToXML(ecl::strf("%d",curEasy)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("easy2rel").x_str(), + (*itx).second->getAttribute(Utf8ToXML("easy1rel").x_str())); + } + (*itx).second->setAttribute(Utf8ToXML("easy1").x_str(), + Utf8ToXML(ecl::strf("%d",easyScore)).x_str()); + (*itx).second->setAttribute(Utf8ToXML("easy1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + } + } else if (solvedDiff || (withEasy && solvedEasy)) { + // we need a new entry for this version + DOMElement * newLevel = doc->createElement (Utf8ToXML("level").x_str()); + newLevel->setAttribute(Utf8ToXML("id").x_str(), Utf8ToXML(*it).x_str()); + newLevel->setAttribute(Utf8ToXML("version").x_str(), Utf8ToXML(ecl::strf("%d",levelstat.solved_revision)).x_str()); + if (solvedDiff) { + newLevel->setAttribute(Utf8ToXML("diff1").x_str(), + Utf8ToXML(ecl::strf("%d",diffScore)).x_str()); + newLevel->setAttribute(Utf8ToXML("diff1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + if (withEasy && solvedEasy) { + newLevel->setAttribute(Utf8ToXML("easy1").x_str(), + Utf8ToXML(ecl::strf("%d",easyScore)).x_str()); + newLevel->setAttribute(Utf8ToXML("easy1rel").x_str(), + Utf8ToXML("0.92").x_str()); + } + newLevel->setAttribute(Utf8ToXML("sec").x_str(), Utf8ToXML("crc").x_str()); + levelsElem->appendChild(newLevel); + allLevelScores[cacheKey] = newLevel; + if (isMostRecent) + curLevelScores[*it] = newLevel; + } + } + } + } + return result; + } +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/ScoreManager.hh b/project/jni/application/enigma/src/lev/ScoreManager.hh new file mode 100644 index 000000000..d2a0e700b --- /dev/null +++ b/project/jni/application/enigma/src/lev/ScoreManager.hh @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SCOREMGR_HH_INCLUDED +#define SCOREMGR_HH_INCLUDED + +#include "PropertyManager.hh" +#include "lev/Proxy.hh" +#include "lev/RatingManager.hh" +#include "lev/Index.hh" +#include "main.hh" + +#include +#include +#include + +namespace enigma { namespace lev { + // Constants + enum { + SCORE_MAX = 99*60+59, + SCORE_UNSOLVED = -1, + SCORE_SOLVED = -2 + }; + + /** + * A singelton manager for user scores. + * + * There are 2 small paradigm shifts in 0.92 scores and 1.00 scores:

+ * There is no longer a separate "finished" flag but levels that have + * been solved without a score value are marked as "SCORE_SOLVED".

+ * Easy mode scores for levels that have no separate easy mode are stored + * as difficult mode scores. All requests for easy mode scores return the + * difficult mode scores in this case. + */ + class ScoreManager : public PropertyManager{ + public: + static ScoreManager *instance(); + ~ScoreManager(); + void markModified(); + virtual bool save(); + void shutdown(); + + /** + * Returns true if the level has been solved for the given difficulty + * in any score version. + * @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD, DIFFICULTY_ANY + */ + bool isSolved(lev::Proxy *levelProxy, int difficulty); + /** + * Returns true if the level has only been solved for the given difficulty + * in an outdated score version. + * @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD + */ + bool isOutdated(lev::Proxy *levelProxy, int difficulty); + /** + * Returns the best score that the user has reached for the given + * difficulty so far or -1 for not yet solved in the current score + * version. + * @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD + */ + int getBestUserScore(lev::Proxy *levelProxy, int difficulty); + /** + * + */ + void updateUserScore(lev::Proxy *levelProxy, int difficulty, int score, + double enigmaRelease = ENIGMACOMPATIBITLITY); + /** + * Returns true if the users best score is equal or better than the + * official best score for the given difficulty + * @arg difficulty DIFFICULTY_EASY, DIFFICULTY_HARD + */ + bool bestScoreReached(lev::Proxy *levelProxy, int difficulty); + bool parScoreReached(lev::Proxy *levelProxy, int difficulty); + /** + * Resets the user score status to unsolved for the given diffculty. + * If the level has no easy mode the score for the difficult mode + * will be reset to unsolved. + */ + void markUnsolved(lev::Proxy *levelProxy, int difficulty); + /** + * Resets the score value and marks the level as solved for the given + * difficulty. If the level has no easy mode a request will mark the + * difficult mode score as solved. This action is restriced to + * wizards. + */ + void markSolved(lev::Proxy *levelProxy, int difficulty); + int countSolved(lev::Index *ind, int difficulty); + int countBestScore(lev::Index *ind, int difficulty); + int countParScore(lev::Index *ind, int difficulty); + double calcHCP(lev::Index *ind, int difficulty); + void setRating(lev::Proxy *levelProxy, int rating); + int getRating(lev::Proxy *levelProxy); + bool isRatingInherited(lev::Proxy *levelProxy); + protected: + ScoreManager(); + private: + static ScoreManager *theSingleton; + static unsigned ctab[256]; + static unsigned pol; + RatingManager *ratingMgr; + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement *levelsElem; + XERCES_CPP_NAMESPACE_QUALIFIER DOMNodeList * levelList; + std::map allLevelScores; // all scoreversions for each level + std::map curLevelScores; // most current scoreversion for each level + std::string userId; + bool hasValidUserId; + bool didUpgrade; + bool isModified; + + void finishUserId(unsigned id3); + std::string sec(std::string target); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getLevel(lev::Proxy *levelProxy); + XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * getCreateLevel(lev::Proxy *levelProxy); + + unsigned idFromLegacyScore(); + std::string upgradeSum(); + bool upgradeLegacy(); + }; +}} // namespace enigma::lev + +#endif diff --git a/project/jni/application/enigma/src/lev/VolatileIndex.cpp b/project/jni/application/enigma/src/lev/VolatileIndex.cpp new file mode 100644 index 000000000..d7d3fe8f2 --- /dev/null +++ b/project/jni/application/enigma/src/lev/VolatileIndex.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "lev/VolatileIndex.hh" +#include "errors.hh" +#include "main.hh" + + + +namespace enigma { namespace lev { + + int VolatileIndex::levelCount = 0; + + VolatileIndex::VolatileIndex(std::string anIndexName, std::string aGroupName, + const std::vector levelpaths, double defaultLocation) : + Index(anIndexName, aGroupName, defaultLocation) { + for (unsigned i=0; iloadMetadata(true); + } + catch (XLevelLoading &err) { + Log << "Level load error on '" << levelpaths[i] << "\n"; + } + } + } + + VolatileIndex::~VolatileIndex() {} + + void VolatileIndex::clear() { + proxies.clear(); + currentPosition = 0; + } +}} // namespace enigma::lev diff --git a/project/jni/application/enigma/src/lev/VolatileIndex.hh b/project/jni/application/enigma/src/lev/VolatileIndex.hh new file mode 100644 index 000000000..0066383ca --- /dev/null +++ b/project/jni/application/enigma/src/lev/VolatileIndex.hh @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef LEV_VOLATILEINDEX_HH_INCLUDED +#define LEV_VOLATILEINDEX_HH_INCLUDED + +#include "lev/Index.hh" + +#include +#include + +namespace enigma { namespace lev { + /** + * + */ + class VolatileIndex : public Index { + public: + /** + * Convention: method names *Level() can take int pos or Proxy as arg. + */ + VolatileIndex(std::string anIndexName, std::string aGroupName, + const std::vector levelpaths, + double defaultLocation = INDEX_DEFAULT_PACK_LOCATION); + ~VolatileIndex(); + virtual void clear(); + private: + static int levelCount; // used for volatile level ids - necessary for lua levels + }; + +}} // namespace enigma::lev +#endif diff --git a/project/jni/application/enigma/src/lua-display.cpp b/project/jni/application/enigma/src/lua-display.cpp new file mode 100644 index 000000000..eda762aa2 --- /dev/null +++ b/project/jni/application/enigma/src/lua-display.cpp @@ -0,0 +1,580 @@ +/* +** Lua binding: display +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_display_open (lua_State* tolua_S); + +#include "display.hh" +#include "d_models.hh" +using namespace display; +using ecl::Rect; +using ecl::Surface; + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"Rect"); + tolua_usertype(tolua_S,"Surface"); +} + +/* function: GetSurface */ +#ifndef TOLUA_DISABLE_tolua_display_GetSurface00 +static int tolua_display_GetSurface00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Surface* tolua_ret = (Surface*) GetSurface(name); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetSurface'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: CropSurface */ +#ifndef TOLUA_DISABLE_tolua_display_CropSurface00 +static int tolua_display_CropSurface00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Surface",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Surface* s = ((const Surface*) tolua_tousertype(tolua_S,1,0)); + Rect r = *((Rect*) tolua_tousertype(tolua_S,2,0)); + { + Surface* tolua_ret = (Surface*) CropSurface(s,r); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'CropSurface'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineImageModel */ +#ifndef TOLUA_DISABLE_tolua_display_DefineImageModel00 +static int tolua_display_DefineImageModel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + Surface* s = ((Surface*) tolua_tousertype(tolua_S,2,0)); + { + DefineImageModel(name,s); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineImageModel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: SetFollowMode */ +#ifndef TOLUA_DISABLE_tolua_display_display_SetFollowMode00 +static int tolua_display_display_SetFollowMode00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + FollowMode m = ((FollowMode) (int) tolua_tonumber(tolua_S,1,0)); + { + SetFollowMode(m); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetFollowMode'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: SetScrollBoundary */ +#ifndef TOLUA_DISABLE_tolua_display_display_SetScrollBoundary00 +static int tolua_display_display_SetScrollBoundary00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + double boundary = ((double) tolua_tonumber(tolua_S,1,0)); + { + SetScrollBoundary(boundary); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetScrollBoundary'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ResizeGameArea */ +#ifndef TOLUA_DISABLE_tolua_display_display_ResizeGameArea00 +static int tolua_display_display_ResizeGameArea00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int w = ((int) tolua_tonumber(tolua_S,1,0)); + int h = ((int) tolua_tonumber(tolua_S,2,0)); + { + ResizeGameArea(w,h); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ResizeGameArea'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineImage */ +#ifndef TOLUA_DISABLE_tolua_display_display_DefineImage00 +static int tolua_display_display_DefineImage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* fname = ((const char*) tolua_tostring(tolua_S,2,0)); + int xoff = ((int) tolua_tonumber(tolua_S,3,0)); + int yoff = ((int) tolua_tonumber(tolua_S,4,0)); + int padding = ((int) tolua_tonumber(tolua_S,5,0)); + { + int tolua_ret = (int) DefineImage(name,fname,xoff,yoff,padding); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineImage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineSubImage */ +#ifndef TOLUA_DISABLE_tolua_display_display_DefineSubImage00 +static int tolua_display_display_DefineSubImage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isusertype(tolua_S,5,"Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* fname = ((const char*) tolua_tostring(tolua_S,2,0)); + int xoff = ((int) tolua_tonumber(tolua_S,3,0)); + int yoff = ((int) tolua_tonumber(tolua_S,4,0)); + Rect r = *((Rect*) tolua_tousertype(tolua_S,5,0)); + { + int tolua_ret = (int) DefineSubImage(name,fname,xoff,yoff,r); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineSubImage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineRandModel */ +#ifndef TOLUA_DISABLE_tolua_display_display_DefineRandModel00 +static int tolua_display_display_DefineRandModel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_istable(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + int n = ((int) tolua_tonumber(tolua_S,2,0)); +#ifdef __cplusplus + char** names = new char*[n]; +#else + char** names = (char**) malloc((n)*sizeof(char*)); +#endif + { +#ifndef TOLUA_RELEASE + if (!tolua_isstringarray(tolua_S,3,n,0,&tolua_err)) + goto tolua_lerror; + else +#endif + { + int i; + for(i=0; i= 501 + TOLUA_API int luaopen_display (lua_State* tolua_S) { + return tolua_display_open(tolua_S); +}; +#endif + diff --git a/project/jni/application/enigma/src/lua-display.hh b/project/jni/application/enigma/src/lua-display.hh new file mode 100644 index 000000000..3a63dd79a --- /dev/null +++ b/project/jni/application/enigma/src/lua-display.hh @@ -0,0 +1,8 @@ +/* +** Lua binding: display +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +/* Exported function */ +TOLUA_API int tolua_display_open (lua_State* tolua_S); + diff --git a/project/jni/application/enigma/src/lua-ecl.cpp b/project/jni/application/enigma/src/lua-ecl.cpp new file mode 100644 index 000000000..09367affa --- /dev/null +++ b/project/jni/application/enigma/src/lua-ecl.cpp @@ -0,0 +1,1645 @@ +/* +** Lua binding: px +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_px_open (lua_State* tolua_S); + +#include "SDL.h" +#include "ecl.hh" +using namespace ecl; + +/* function to release collected object via destructor */ +#ifdef __cplusplus + +static int tolua_collect_Rect (lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} + +static int tolua_collect_GS (lua_State* tolua_S) +{ + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} + +static int tolua_collect_Font (lua_State* tolua_S) +{ + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} + +static int tolua_collect_Surface (lua_State* tolua_S) +{ + Surface* self = (Surface*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} + +static int tolua_collect_Drawable (lua_State* tolua_S) +{ + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} + +static int tolua_collect_V2 (lua_State* tolua_S) +{ + V2* self = (V2*) tolua_tousertype(tolua_S,1,0); + delete self; + return 0; +} +#endif + + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"Rect"); + tolua_usertype(tolua_S,"GS"); + tolua_usertype(tolua_S,"Font"); + tolua_usertype(tolua_S,"Drawable"); + tolua_usertype(tolua_S,"Surface"); + tolua_usertype(tolua_S,"Screen"); + tolua_usertype(tolua_S,"V2"); +} + +/* get function: x of class Rect */ +#ifndef TOLUA_DISABLE_tolua_get_Rect_x +static int tolua_get_Rect_x(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->x); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: x of class Rect */ +#ifndef TOLUA_DISABLE_tolua_set_Rect_x +static int tolua_set_Rect_x(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'x'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->x = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: y of class Rect */ +#ifndef TOLUA_DISABLE_tolua_get_Rect_y +static int tolua_get_Rect_y(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->y); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: y of class Rect */ +#ifndef TOLUA_DISABLE_tolua_set_Rect_y +static int tolua_set_Rect_y(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'y'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->y = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: w of class Rect */ +#ifndef TOLUA_DISABLE_tolua_get_Rect_w +static int tolua_get_Rect_w(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'w'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->w); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: w of class Rect */ +#ifndef TOLUA_DISABLE_tolua_set_Rect_w +static int tolua_set_Rect_w(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'w'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->w = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: h of class Rect */ +#ifndef TOLUA_DISABLE_tolua_get_Rect_h +static int tolua_get_Rect_h(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'h'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->h); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: h of class Rect */ +#ifndef TOLUA_DISABLE_tolua_set_Rect_h +static int tolua_set_Rect_h(lua_State* tolua_S) +{ + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'h'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->h = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class Rect */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Rect_new00 +static int tolua_px_ecl_Rect_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Rect",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int xx = ((int) tolua_tonumber(tolua_S,2,0)); + int yy = ((int) tolua_tonumber(tolua_S,3,0)); + int ww = ((int) tolua_tonumber(tolua_S,4,0)); + int hh = ((int) tolua_tonumber(tolua_S,5,0)); + { + Rect* tolua_ret = (Rect*) new Rect(xx,yy,ww,hh); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Rect"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class Rect */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Rect_new00_local +static int tolua_px_ecl_Rect_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"Rect",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int xx = ((int) tolua_tonumber(tolua_S,2,0)); + int yy = ((int) tolua_tonumber(tolua_S,3,0)); + int ww = ((int) tolua_tonumber(tolua_S,4,0)); + int hh = ((int) tolua_tonumber(tolua_S,5,0)); + { + Rect* tolua_ret = (Rect*) new Rect(xx,yy,ww,hh); + tolua_pushusertype_and_takeownership(tolua_S,(void *)tolua_ret,"Rect"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class Rect */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Rect_delete00 +static int tolua_px_ecl_Rect_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Rect* self = (Rect*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'",NULL); +#endif + delete self; + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class V2 */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_V2_new00 +static int tolua_px_ecl_V2_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"V2",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + V2* tolua_ret = (V2*) new V2(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"V2"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class V2 */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_V2_new00_local +static int tolua_px_ecl_V2_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"V2",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + V2* tolua_ret = (V2*) new V2(); + tolua_pushusertype_and_takeownership(tolua_S,(void *)tolua_ret,"V2"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class V2 */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_V2_new01 +static int tolua_px_ecl_V2_new01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"V2",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + double x = ((double) tolua_tonumber(tolua_S,2,0)); + double y = ((double) tolua_tonumber(tolua_S,3,0)); + { + V2* tolua_ret = (V2*) new V2(x,y); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"V2"); + } + } + return 1; +tolua_lerror: + return tolua_px_ecl_V2_new00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class V2 */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_V2_new01_local +static int tolua_px_ecl_V2_new01_local(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"V2",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else + { + double x = ((double) tolua_tonumber(tolua_S,2,0)); + double y = ((double) tolua_tonumber(tolua_S,3,0)); + { + V2* tolua_ret = (V2*) new V2(x,y); + tolua_pushusertype_and_takeownership(tolua_S,(void *)tolua_ret,"V2"); + } + } + return 1; +tolua_lerror: + return tolua_px_ecl_V2_new00_local(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: operator[] of class V2 */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_V2__geti00 +static int tolua_px_ecl_V2__geti00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"V2",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + V2* self = (V2*) tolua_tousertype(tolua_S,1,0); + int idx = ((int) tolua_tonumber(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'operator[]'",NULL); +#endif + { + double tolua_ret = (double) self->operator[](idx); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function '.geti'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new of class GS */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_GS_new00 +static int tolua_px_ecl_GS_new00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"GS",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Rect* clipr = ((const Rect*) tolua_tousertype(tolua_S,2,0)); + { + GS* tolua_ret = (GS*) new GS(*clipr); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"GS"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: new_local of class GS */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_GS_new00_local +static int tolua_px_ecl_GS_new00_local(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"GS",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Rect* clipr = ((const Rect*) tolua_tousertype(tolua_S,2,0)); + { + GS* tolua_ret = (GS*) new GS(*clipr); + tolua_pushusertype_and_takeownership(tolua_S,(void *)tolua_ret,"GS"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'new'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class GS */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_GS_delete00 +static int tolua_px_ecl_GS_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"GS",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'",NULL); +#endif + delete self; + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: cliprect of class GS */ +#ifndef TOLUA_DISABLE_tolua_get_GS_cliprect +static int tolua_get_GS_cliprect(lua_State* tolua_S) +{ + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'cliprect'",NULL); +#endif + tolua_pushusertype(tolua_S,(void*)&self->cliprect,"Rect"); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: cliprect of class GS */ +#ifndef TOLUA_DISABLE_tolua_set_GS_cliprect +static int tolua_set_GS_cliprect(lua_State* tolua_S) +{ + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'cliprect'",NULL); + if (!tolua_isusertype(tolua_S,2,"Rect",0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->cliprect = *((Rect*) tolua_tousertype(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: pcolor of class GS */ +#ifndef TOLUA_DISABLE_tolua_get_GS_unsigned_pcolor +static int tolua_get_GS_unsigned_pcolor(lua_State* tolua_S) +{ + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'pcolor'",NULL); +#endif + tolua_pushnumber(tolua_S,(lua_Number)self->pcolor); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: pcolor of class GS */ +#ifndef TOLUA_DISABLE_tolua_set_GS_unsigned_pcolor +static int tolua_set_GS_unsigned_pcolor(lua_State* tolua_S) +{ + GS* self = (GS*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!self) tolua_error(tolua_S,"invalid 'self' in accessing variable 'pcolor'",NULL); + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + self->pcolor = (( unsigned int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_delete00 +static int tolua_px_ecl_Drawable_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'",NULL); +#endif + delete self; + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: map_color of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_map_color00 +static int tolua_px_ecl_Drawable_map_color00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + int r = ((int) tolua_tonumber(tolua_S,2,0)); + int g = ((int) tolua_tonumber(tolua_S,3,0)); + int b = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'map_color'",NULL); +#endif + { + unsigned int tolua_ret = ( unsigned int) self->map_color(r,g,b); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'map_color'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: map_color of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_map_color01 +static int tolua_px_ecl_Drawable_map_color01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + int r = ((int) tolua_tonumber(tolua_S,2,0)); + int g = ((int) tolua_tonumber(tolua_S,3,0)); + int b = ((int) tolua_tonumber(tolua_S,4,0)); + int a = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'map_color'",NULL); +#endif + { + unsigned int tolua_ret = ( unsigned int) self->map_color(r,g,b,a); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +tolua_lerror: + return tolua_px_ecl_Drawable_map_color00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: blit of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_blit00 +static int tolua_px_ecl_Drawable_blit00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isusertype(tolua_S,5,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + Surface* s = ((Surface*) tolua_tousertype(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'blit'",NULL); +#endif + { + self->blit(*gs,x,y,s); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'blit'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: blit of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_blit01 +static int tolua_px_ecl_Drawable_blit01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isusertype(tolua_S,5,"Surface",0,&tolua_err) || + !tolua_isusertype(tolua_S,6,"const Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + Surface* s = ((Surface*) tolua_tousertype(tolua_S,5,0)); + const Rect* r = ((const Rect*) tolua_tousertype(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'blit'",NULL); +#endif + { + self->blit(*gs,x,y,s,*r); + } + } + return 0; +tolua_lerror: + return tolua_px_ecl_Drawable_blit00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* method: get_pixel of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_get_pixel00 +static int tolua_px_ecl_Drawable_get_pixel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + int x = ((int) tolua_tonumber(tolua_S,2,0)); + int y = ((int) tolua_tonumber(tolua_S,3,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'get_pixel'",NULL); +#endif + { + unsigned int tolua_ret = ( unsigned int) self->get_pixel(x,y); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'get_pixel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: set_pixel of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_set_pixel00 +static int tolua_px_ecl_Drawable_set_pixel00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'set_pixel'",NULL); +#endif + { + self->set_pixel(*gs,x,y); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'set_pixel'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: set_pixels of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_set_pixels00 +static int tolua_px_ecl_Drawable_set_pixels00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int n = ((int) tolua_tonumber(tolua_S,3,0)); + const int x = ((const int) tolua_tonumber(tolua_S,4,0)); + const int y = ((const int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'set_pixels'",NULL); +#endif + { + self->set_pixels(*gs,n,&x,&y); + tolua_pushnumber(tolua_S,(lua_Number)x); + tolua_pushnumber(tolua_S,(lua_Number)y); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'set_pixels'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: hline of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_hline00 +static int tolua_px_ecl_Drawable_hline00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + int w = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'hline'",NULL); +#endif + { + self->hline(*gs,x,y,w); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'hline'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: vline of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_vline00 +static int tolua_px_ecl_Drawable_vline00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + int h = ((int) tolua_tonumber(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'vline'",NULL); +#endif + { + self->vline(*gs,x,y,h); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'vline'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: line of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_line00 +static int tolua_px_ecl_Drawable_line00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x1 = ((int) tolua_tonumber(tolua_S,3,0)); + int y1 = ((int) tolua_tonumber(tolua_S,4,0)); + int x2 = ((int) tolua_tonumber(tolua_S,5,0)); + int y2 = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'line'",NULL); +#endif + { + self->line(*gs,x1,y1,x2,y2); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'line'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: box of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_box00 +static int tolua_px_ecl_Drawable_box00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Drawable",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const GS",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,0,&tolua_err) || + !tolua_isnumber(tolua_S,6,0,&tolua_err) || + !tolua_isnoobj(tolua_S,7,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Drawable* self = (Drawable*) tolua_tousertype(tolua_S,1,0); + const GS* gs = ((const GS*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + int w = ((int) tolua_tonumber(tolua_S,5,0)); + int h = ((int) tolua_tonumber(tolua_S,6,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'box'",NULL); +#endif + { + self->box(*gs,x,y,w,h); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'box'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: size of class Drawable */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Drawable_size00 +static int tolua_px_ecl_Drawable_size00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const Drawable",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const Drawable* self = (const Drawable*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'size'",NULL); +#endif + { + Rect tolua_ret = (Rect) self->size(); + { +#ifdef __cplusplus + void* tolua_obj = new Rect(tolua_ret); + tolua_pushusertype_and_takeownership(tolua_S,tolua_obj,"Rect"); +#else + void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(Rect)); + tolua_pushusertype_and_takeownership(tolua_S,tolua_obj,"Rect"); +#endif + } + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'size'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class Surface */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Surface_delete00 +static int tolua_px_ecl_Surface_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Surface* self = (Surface*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'",NULL); +#endif + delete self; + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: width of class Surface */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Surface_width00 +static int tolua_px_ecl_Surface_width00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Surface* self = (Surface*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'width'",NULL); +#endif + { + int tolua_ret = (int) self->width(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'width'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: height of class Surface */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Surface_height00 +static int tolua_px_ecl_Surface_height00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Surface* self = (Surface*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'height'",NULL); +#endif + { + int tolua_ret = (int) self->height(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'height'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: get_surface of class Screen */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Screen_get_surface00 +static int tolua_px_ecl_Screen_get_surface00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Screen",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Screen* self = (Screen*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'get_surface'",NULL); +#endif + { + Surface* tolua_ret = (Surface*) self->get_surface(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'get_surface'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: update_all of class Screen */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Screen_update_all00 +static int tolua_px_ecl_Screen_update_all00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Screen",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Screen* self = (Screen*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'update_all'",NULL); +#endif + { + self->update_all(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'update_all'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: update_rect of class Screen */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Screen_update_rect00 +static int tolua_px_ecl_Screen_update_rect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Screen",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"const Rect",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Screen* self = (Screen*) tolua_tousertype(tolua_S,1,0); + const Rect* r = ((const Rect*) tolua_tousertype(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'update_rect'",NULL); +#endif + { + self->update_rect(*r); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'update_rect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: flush_updates of class Screen */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Screen_flush_updates00 +static int tolua_px_ecl_Screen_flush_updates00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Screen",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Screen* self = (Screen*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'flush_updates'",NULL); +#endif + { + self->flush_updates(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'flush_updates'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: set_caption of class Screen */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Screen_set_caption00 +static int tolua_px_ecl_Screen_set_caption00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Screen",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Screen* self = (Screen*) tolua_tousertype(tolua_S,1,0); + const char* str = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'set_caption'",NULL); +#endif + { + self->set_caption(str); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'set_caption'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: delete of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_delete00 +static int tolua_px_ecl_Font_delete00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'delete'",NULL); +#endif + delete self; + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'delete'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: get_lineskip of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_get_lineskip00 +static int tolua_px_ecl_Font_get_lineskip00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'get_lineskip'",NULL); +#endif + { + int tolua_ret = (int) self->get_lineskip(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'get_lineskip'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: get_height of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_get_height00 +static int tolua_px_ecl_Font_get_height00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'get_height'",NULL); +#endif + { + int tolua_ret = (int) self->get_height(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'get_height'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: get_width of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_get_width00 +static int tolua_px_ecl_Font_get_width00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); + const char* str = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'get_width'",NULL); +#endif + { + int tolua_ret = (int) self->get_width(str); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'get_width'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: render of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_render00 +static int tolua_px_ecl_Font_render00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); + const char* str = ((const char*) tolua_tostring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'render'",NULL); +#endif + { + Surface* tolua_ret = (Surface*) self->render(str); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'render'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: render of class Font */ +#ifndef TOLUA_DISABLE_tolua_px_ecl_Font_render01 +static int tolua_px_ecl_Font_render01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"Font",0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"Surface",0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else + { + Font* self = (Font*) tolua_tousertype(tolua_S,1,0); + Surface* s = ((Surface*) tolua_tousertype(tolua_S,2,0)); + int x = ((int) tolua_tonumber(tolua_S,3,0)); + int y = ((int) tolua_tonumber(tolua_S,4,0)); + const char* str = ((const char*) tolua_tostring(tolua_S,5,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'render'",NULL); +#endif + { + self->render(s,x,y,str); + } + } + return 0; +tolua_lerror: + return tolua_px_ecl_Font_render00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* Open function */ +TOLUA_API int tolua_px_open (lua_State* tolua_S) +{ + tolua_open(tolua_S); + tolua_reg_types(tolua_S); + tolua_module(tolua_S,NULL,0); + tolua_beginmodule(tolua_S,NULL); + tolua_module(tolua_S,"ecl",0); + tolua_beginmodule(tolua_S,"ecl"); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Rect","Rect","",tolua_collect_Rect); + #else + tolua_cclass(tolua_S,"Rect","Rect","",NULL); + #endif + tolua_beginmodule(tolua_S,"Rect"); + tolua_variable(tolua_S,"x",tolua_get_Rect_x,tolua_set_Rect_x); + tolua_variable(tolua_S,"y",tolua_get_Rect_y,tolua_set_Rect_y); + tolua_variable(tolua_S,"w",tolua_get_Rect_w,tolua_set_Rect_w); + tolua_variable(tolua_S,"h",tolua_get_Rect_h,tolua_set_Rect_h); + tolua_function(tolua_S,"new",tolua_px_ecl_Rect_new00); + tolua_function(tolua_S,"new_local",tolua_px_ecl_Rect_new00_local); + tolua_function(tolua_S,".call",tolua_px_ecl_Rect_new00_local); + tolua_function(tolua_S,"delete",tolua_px_ecl_Rect_delete00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"V2","V2","",tolua_collect_V2); + #else + tolua_cclass(tolua_S,"V2","V2","",NULL); + #endif + tolua_beginmodule(tolua_S,"V2"); + tolua_function(tolua_S,"new",tolua_px_ecl_V2_new00); + tolua_function(tolua_S,"new_local",tolua_px_ecl_V2_new00_local); + tolua_function(tolua_S,".call",tolua_px_ecl_V2_new00_local); + tolua_function(tolua_S,"new",tolua_px_ecl_V2_new01); + tolua_function(tolua_S,"new_local",tolua_px_ecl_V2_new01_local); + tolua_function(tolua_S,".call",tolua_px_ecl_V2_new01_local); + tolua_function(tolua_S,".geti",tolua_px_ecl_V2__geti00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"GS","GS","",tolua_collect_GS); + #else + tolua_cclass(tolua_S,"GS","GS","",NULL); + #endif + tolua_beginmodule(tolua_S,"GS"); + tolua_function(tolua_S,"new",tolua_px_ecl_GS_new00); + tolua_function(tolua_S,"new_local",tolua_px_ecl_GS_new00_local); + tolua_function(tolua_S,".call",tolua_px_ecl_GS_new00_local); + tolua_function(tolua_S,"delete",tolua_px_ecl_GS_delete00); + tolua_variable(tolua_S,"cliprect",tolua_get_GS_cliprect,tolua_set_GS_cliprect); + tolua_variable(tolua_S,"pcolor",tolua_get_GS_unsigned_pcolor,tolua_set_GS_unsigned_pcolor); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Drawable","Drawable","",tolua_collect_Drawable); + #else + tolua_cclass(tolua_S,"Drawable","Drawable","",NULL); + #endif + tolua_beginmodule(tolua_S,"Drawable"); + tolua_function(tolua_S,"delete",tolua_px_ecl_Drawable_delete00); + tolua_function(tolua_S,"map_color",tolua_px_ecl_Drawable_map_color00); + tolua_function(tolua_S,"map_color",tolua_px_ecl_Drawable_map_color01); + tolua_function(tolua_S,"blit",tolua_px_ecl_Drawable_blit00); + tolua_function(tolua_S,"blit",tolua_px_ecl_Drawable_blit01); + tolua_function(tolua_S,"get_pixel",tolua_px_ecl_Drawable_get_pixel00); + tolua_function(tolua_S,"set_pixel",tolua_px_ecl_Drawable_set_pixel00); + tolua_function(tolua_S,"set_pixels",tolua_px_ecl_Drawable_set_pixels00); + tolua_function(tolua_S,"hline",tolua_px_ecl_Drawable_hline00); + tolua_function(tolua_S,"vline",tolua_px_ecl_Drawable_vline00); + tolua_function(tolua_S,"line",tolua_px_ecl_Drawable_line00); + tolua_function(tolua_S,"box",tolua_px_ecl_Drawable_box00); + tolua_function(tolua_S,"size",tolua_px_ecl_Drawable_size00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Surface","Surface","Drawable",tolua_collect_Surface); + #else + tolua_cclass(tolua_S,"Surface","Surface","Drawable",NULL); + #endif + tolua_beginmodule(tolua_S,"Surface"); + tolua_function(tolua_S,"delete",tolua_px_ecl_Surface_delete00); + tolua_function(tolua_S,"width",tolua_px_ecl_Surface_width00); + tolua_function(tolua_S,"height",tolua_px_ecl_Surface_height00); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"Screen","Screen","",NULL); + tolua_beginmodule(tolua_S,"Screen"); + tolua_function(tolua_S,"get_surface",tolua_px_ecl_Screen_get_surface00); + tolua_function(tolua_S,"update_all",tolua_px_ecl_Screen_update_all00); + tolua_function(tolua_S,"update_rect",tolua_px_ecl_Screen_update_rect00); + tolua_function(tolua_S,"flush_updates",tolua_px_ecl_Screen_flush_updates00); + tolua_function(tolua_S,"set_caption",tolua_px_ecl_Screen_set_caption00); + tolua_endmodule(tolua_S); + #ifdef __cplusplus + tolua_cclass(tolua_S,"Font","Font","",tolua_collect_Font); + #else + tolua_cclass(tolua_S,"Font","Font","",NULL); + #endif + tolua_beginmodule(tolua_S,"Font"); + tolua_function(tolua_S,"delete",tolua_px_ecl_Font_delete00); + tolua_function(tolua_S,"get_lineskip",tolua_px_ecl_Font_get_lineskip00); + tolua_function(tolua_S,"get_height",tolua_px_ecl_Font_get_height00); + tolua_function(tolua_S,"get_width",tolua_px_ecl_Font_get_width00); + tolua_function(tolua_S,"render",tolua_px_ecl_Font_render00); + tolua_function(tolua_S,"render",tolua_px_ecl_Font_render01); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); + return 1; +} + + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 + TOLUA_API int luaopen_px (lua_State* tolua_S) { + return tolua_px_open(tolua_S); +}; +#endif + diff --git a/project/jni/application/enigma/src/lua-ecl.hh b/project/jni/application/enigma/src/lua-ecl.hh new file mode 100644 index 000000000..af738f5c0 --- /dev/null +++ b/project/jni/application/enigma/src/lua-ecl.hh @@ -0,0 +1,8 @@ +/* +** Lua binding: px +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +/* Exported function */ +TOLUA_API int tolua_px_open (lua_State* tolua_S); + diff --git a/project/jni/application/enigma/src/lua-editor.cpp b/project/jni/application/enigma/src/lua-editor.cpp new file mode 100644 index 000000000..673e32a06 --- /dev/null +++ b/project/jni/application/enigma/src/lua-editor.cpp @@ -0,0 +1,212 @@ +/* +** Lua binding: editor +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_editor_open (lua_State* tolua_S); + +#include "editor.hh" +using namespace editor; + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ +} + +/* function: DefineFloorGroup */ +#ifndef TOLUA_DISABLE_tolua_editor_editor_DefineFloorGroup00 +static int tolua_editor_editor_DefineFloorGroup00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_istable(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* descr = ((const char*) tolua_tostring(tolua_S,2,0)); + int nentries = ((int) tolua_tonumber(tolua_S,3,0)); +#ifdef __cplusplus + char** entries = new char*[nentries]; +#else + char** entries = (char**) malloc((nentries)*sizeof(char*)); +#endif + { +#ifndef TOLUA_RELEASE + if (!tolua_isstringarray(tolua_S,4,nentries,0,&tolua_err)) + goto tolua_lerror; + else +#endif + { + int i; + for(i=0; i= 501 + TOLUA_API int luaopen_editor (lua_State* tolua_S) { + return tolua_editor_open(tolua_S); +}; +#endif + diff --git a/project/jni/application/enigma/src/lua-editor.hh b/project/jni/application/enigma/src/lua-editor.hh new file mode 100644 index 000000000..d1c9f7430 --- /dev/null +++ b/project/jni/application/enigma/src/lua-editor.hh @@ -0,0 +1,8 @@ +/* +** Lua binding: editor +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +/* Exported function */ +TOLUA_API int tolua_editor_open (lua_State* tolua_S); + diff --git a/project/jni/application/enigma/src/lua-enigma.cpp b/project/jni/application/enigma/src/lua-enigma.cpp new file mode 100644 index 000000000..d4b0681e2 --- /dev/null +++ b/project/jni/application/enigma/src/lua-enigma.cpp @@ -0,0 +1,1310 @@ +/* +** Lua binding: enigma +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_enigma_open (lua_State* tolua_S); + +#include "server.hh" +#include "world.hh" +#include "lev/PersistentIndex.hh" +using namespace enigma; +using ecl::Surface; +using ecl::Font; +using namespace server; +using namespace lev; +using namespace world; +#include "video.hh" +using namespace video; +using ecl::Screen; +#include "sound.hh" +using namespace sound; + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"Stone"); + tolua_usertype(tolua_S,"Font"); + tolua_usertype(tolua_S,"Object"); + tolua_usertype(tolua_S,"Floor"); + tolua_usertype(tolua_S,"Item"); + tolua_usertype(tolua_S,"Screen"); + tolua_usertype(tolua_S,"Surface"); +} + +/* function: GetImage */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_GetImage00 +static int tolua_enigma_enigma_GetImage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Surface* tolua_ret = (Surface*) GetImage(name); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetImage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetImage */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_GetImage01 +static int tolua_enigma_enigma_GetImage01(lua_State* tolua_S) +{ + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* extension = ((const char*) tolua_tostring(tolua_S,2,0)); + { + Surface* tolua_ret = (Surface*) GetImage(name,extension); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +tolua_lerror: + return tolua_enigma_enigma_GetImage00(tolua_S); +} +#endif //#ifndef TOLUA_DISABLE + +/* function: LoadImage */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_LoadImage00 +static int tolua_enigma_enigma_LoadImage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Surface* tolua_ret = (Surface*) LoadImage(name); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'LoadImage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: RegisterImage */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_RegisterImage00 +static int tolua_enigma_enigma_RegisterImage00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isusertype(tolua_S,2,"Surface",0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + Surface* s = ((Surface*) tolua_tousertype(tolua_S,2,0)); + { + Surface* tolua_ret = (Surface*) RegisterImage(name,s); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Surface"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'RegisterImage'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetFont */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_GetFont00 +static int tolua_enigma_enigma_GetFont00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Font* tolua_ret = (Font*) GetFont(name); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Font"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetFont'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: date */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_date00 +static int tolua_enigma_enigma_date00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* format = ((const char*) tolua_tostring(tolua_S,1,0)); + { + const char* tolua_ret = (const char*) date(format); + tolua_pushstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'date'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: TwoPlayerGame */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_TwoPlayerGame +static int tolua_get_enigma_TwoPlayerGame(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)TwoPlayerGame); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: TwoPlayerGame */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_TwoPlayerGame +static int tolua_set_enigma_TwoPlayerGame(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + TwoPlayerGame = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: SingleComputerGame */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_SingleComputerGame +static int tolua_get_enigma_SingleComputerGame(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)SingleComputerGame); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: SingleComputerGame */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_SingleComputerGame +static int tolua_set_enigma_SingleComputerGame(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + SingleComputerGame = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: ConserveLevel */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_ConserveLevel +static int tolua_get_enigma_ConserveLevel(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)ConserveLevel); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: ConserveLevel */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_ConserveLevel +static int tolua_set_enigma_ConserveLevel(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + ConserveLevel = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: AllowTogglePlayer */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_AllowTogglePlayer +static int tolua_get_enigma_AllowTogglePlayer(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)AllowTogglePlayer); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: AllowTogglePlayer */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_AllowTogglePlayer +static int tolua_set_enigma_AllowTogglePlayer(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + AllowTogglePlayer = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: ShowMoves */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_ShowMoves +static int tolua_get_enigma_ShowMoves(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)ShowMoves); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: ShowMoves */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_ShowMoves +static int tolua_set_enigma_ShowMoves(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + ShowMoves = ((int) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: Brittleness */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_Brittleness +static int tolua_get_enigma_Brittleness(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)Brittleness); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: Brittleness */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_Brittleness +static int tolua_set_enigma_Brittleness(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + Brittleness = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: SlopeForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_SlopeForce +static int tolua_get_enigma_SlopeForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)SlopeForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: SlopeForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_SlopeForce +static int tolua_set_enigma_SlopeForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + SlopeForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: FlatForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_FlatForce +static int tolua_get_enigma_FlatForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)FlatForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: FlatForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_FlatForce +static int tolua_set_enigma_FlatForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + FlatForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: FrictionFactor */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_FrictionFactor +static int tolua_get_enigma_FrictionFactor(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)FrictionFactor); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: FrictionFactor */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_FrictionFactor +static int tolua_set_enigma_FrictionFactor(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + FrictionFactor = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: IceFriction */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_IceFriction +static int tolua_get_enigma_IceFriction(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)IceFriction); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: IceFriction */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_IceFriction +static int tolua_set_enigma_IceFriction(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + IceFriction = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: ElectricForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_ElectricForce +static int tolua_get_enigma_ElectricForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)ElectricForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: ElectricForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_ElectricForce +static int tolua_set_enigma_ElectricForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + ElectricForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: BumperForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_BumperForce +static int tolua_get_enigma_BumperForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)BumperForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: BumperForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_BumperForce +static int tolua_set_enigma_BumperForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + BumperForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: MagnetForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_MagnetForce +static int tolua_get_enigma_MagnetForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)MagnetForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: MagnetForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_MagnetForce +static int tolua_set_enigma_MagnetForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + MagnetForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: MagnetRange */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_MagnetRange +static int tolua_get_enigma_MagnetRange(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)MagnetRange); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: MagnetRange */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_MagnetRange +static int tolua_set_enigma_MagnetRange(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + MagnetRange = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: WaterSinkSpeed */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_WaterSinkSpeed +static int tolua_get_enigma_WaterSinkSpeed(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)WaterSinkSpeed); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: WaterSinkSpeed */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_WaterSinkSpeed +static int tolua_set_enigma_WaterSinkSpeed(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + WaterSinkSpeed = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: SwampSinkSpeed */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_SwampSinkSpeed +static int tolua_get_enigma_SwampSinkSpeed(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)SwampSinkSpeed); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: SwampSinkSpeed */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_SwampSinkSpeed +static int tolua_set_enigma_SwampSinkSpeed(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + SwampSinkSpeed = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: WormholeForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_WormholeForce +static int tolua_get_enigma_WormholeForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)WormholeForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: WormholeForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_WormholeForce +static int tolua_set_enigma_WormholeForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + WormholeForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: WormholeRange */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_WormholeRange +static int tolua_get_enigma_WormholeRange(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)WormholeRange); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: WormholeRange */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_WormholeRange +static int tolua_set_enigma_WormholeRange(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + WormholeRange = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: HoleForce */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_HoleForce +static int tolua_get_enigma_HoleForce(lua_State* tolua_S) +{ + tolua_pushnumber(tolua_S,(lua_Number)HoleForce); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: HoleForce */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_HoleForce +static int tolua_set_enigma_HoleForce(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isnumber(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + HoleForce = ((double) tolua_tonumber(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* get function: CreatingPreview */ +#ifndef TOLUA_DISABLE_tolua_get_enigma_CreatingPreview +static int tolua_get_enigma_CreatingPreview(lua_State* tolua_S) +{ + tolua_pushboolean(tolua_S,(bool)CreatingPreview); + return 1; +} +#endif //#ifndef TOLUA_DISABLE + +/* set function: CreatingPreview */ +#ifndef TOLUA_DISABLE_tolua_set_enigma_CreatingPreview +static int tolua_set_enigma_CreatingPreview(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if (!tolua_isboolean(tolua_S,2,0,&tolua_err)) + tolua_error(tolua_S,"#vinvalid type in variable assignment.",&tolua_err); +#endif + CreatingPreview = ((bool) tolua_toboolean(tolua_S,2,0)) +; + return 0; +} +#endif //#ifndef TOLUA_DISABLE + +/* function: AddLevelPack */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_AddLevelPack00 +static int tolua_enigma_enigma_AddLevelPack00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* init_file = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* name = ((const char*) tolua_tostring(tolua_S,2,0)); + { + AddLevelPack(init_file,name); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddLevelPack'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: AddZippedLevelPack */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_AddZippedLevelPack00 +static int tolua_enigma_enigma_AddZippedLevelPack00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* zipfile = ((const char*) tolua_tostring(tolua_S,1,0)); + { + AddZippedLevelPack(zipfile); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'AddZippedLevelPack'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: SetCompatibility */ +#ifndef TOLUA_DISABLE_tolua_enigma_enigma_SetCompatibility00 +static int tolua_enigma_enigma_SetCompatibility00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* version = ((const char*) tolua_tostring(tolua_S,1,0)); + { + SetCompatibility(version); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetCompatibility'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: MakeObject */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_MakeObject00 +static int tolua_enigma_world_MakeObject00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Object* tolua_ret = (Object*) MakeObject(kind); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Object"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeObject'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: MakeFloor */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_MakeFloor00 +static int tolua_enigma_world_MakeFloor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Floor* tolua_ret = (Floor*) MakeFloor(kind); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Floor"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeFloor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: MakeItem */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_MakeItem00 +static int tolua_enigma_world_MakeItem00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Item* tolua_ret = (Item*) MakeItem(kind); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Item"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeItem'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: MakeStone */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_MakeStone00 +static int tolua_enigma_world_MakeStone00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Stone* tolua_ret = (Stone*) MakeStone(kind); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Stone"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'MakeStone'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetNamedObject */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_GetNamedObject00 +static int tolua_enigma_world_GetNamedObject00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + { + Object* tolua_ret = (Object*) GetNamedObject(name); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Object"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetNamedObject'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: Resize */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_Resize00 +static int tolua_enigma_world_Resize00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnumber(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int w = ((int) tolua_tonumber(tolua_S,1,0)); + int h = ((int) tolua_tonumber(tolua_S,2,0)); + { + Resize(w,h); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'Resize'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineSimpleStone */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_DefineSimpleStone00 +static int tolua_enigma_world_DefineSimpleStone00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isnoobj(tolua_S,5,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* sound = ((const char*) tolua_tostring(tolua_S,2,0)); + int hollow = ((int) tolua_tonumber(tolua_S,3,0)); + int glass = ((int) tolua_tonumber(tolua_S,4,0)); + { + DefineSimpleStone(kind,sound,hollow,glass); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineSimpleStone'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineSimpleStoneMovable */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_DefineSimpleStoneMovable00 +static int tolua_enigma_world_DefineSimpleStoneMovable00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* sound = ((const char*) tolua_tostring(tolua_S,2,0)); + int glass = ((int) tolua_tonumber(tolua_S,3,0)); + { + DefineSimpleStoneMovable(kind,sound,glass); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineSimpleStoneMovable'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineSimpleFloor */ +#ifndef TOLUA_DISABLE_tolua_enigma_world_DefineSimpleFloor00 +static int tolua_enigma_world_DefineSimpleFloor00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isboolean(tolua_S,4,0,&tolua_err) || + !tolua_isstring(tolua_S,5,0,&tolua_err) || + !tolua_isnoobj(tolua_S,6,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* kind = ((const char*) tolua_tostring(tolua_S,1,0)); + double friction = ((double) tolua_tonumber(tolua_S,2,0)); + double mousefactor = ((double) tolua_tonumber(tolua_S,3,0)); + bool burnable = ((bool) tolua_toboolean(tolua_S,4,0)); + const char* firetransform = ((const char*) tolua_tostring(tolua_S,5,0)); + { + DefineSimpleFloor(kind,friction,mousefactor,burnable,firetransform); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineSimpleFloor'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: GetScreen */ +#ifndef TOLUA_DISABLE_tolua_enigma_video_GetScreen00 +static int tolua_enigma_video_GetScreen00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnoobj(tolua_S,1,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + Screen* tolua_ret = (Screen*) GetScreen(); + tolua_pushusertype(tolua_S,(void*)tolua_ret,"Screen"); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetScreen'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: HideMouse */ +#ifndef TOLUA_DISABLE_tolua_enigma_video_HideMouse00 +static int tolua_enigma_video_HideMouse00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnoobj(tolua_S,1,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + HideMouse(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'HideMouse'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: ShowMouse */ +#ifndef TOLUA_DISABLE_tolua_enigma_video_ShowMouse00 +static int tolua_enigma_video_ShowMouse00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isnoobj(tolua_S,1,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + { + ShowMouse(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'ShowMouse'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: DefineSoundEffect */ +#ifndef TOLUA_DISABLE_tolua_enigma_sound_DefineSoundEffect00 +static int tolua_enigma_sound_DefineSoundEffect00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,3,0,&tolua_err) || + !tolua_isnumber(tolua_S,4,0,&tolua_err) || + !tolua_isboolean(tolua_S,5,0,&tolua_err) || + !tolua_isboolean(tolua_S,6,0,&tolua_err) || + !tolua_isnumber(tolua_S,7,0,&tolua_err) || + !tolua_isnumber(tolua_S,8,0,&tolua_err) || + !tolua_isnumber(tolua_S,9,0,&tolua_err) || + !tolua_isnumber(tolua_S,10,0,&tolua_err) || + !tolua_isnumber(tolua_S,11,0,&tolua_err) || + !tolua_isnumber(tolua_S,12,0,&tolua_err) || + !tolua_iscppstring(tolua_S,13,0,&tolua_err) || + !tolua_isnoobj(tolua_S,14,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + string table = ((string) tolua_tocppstring(tolua_S,1,0)); + string name = ((string) tolua_tocppstring(tolua_S,2,0)); + string filename = ((string) tolua_tocppstring(tolua_S,3,0)); + double volume = ((double) tolua_tonumber(tolua_S,4,0)); + bool loop = ((bool) tolua_toboolean(tolua_S,5,0)); + bool global = ((bool) tolua_toboolean(tolua_S,6,0)); + int priority = ((int) tolua_tonumber(tolua_S,7,0)); + double damp_max = ((double) tolua_tonumber(tolua_S,8,0)); + double damp_inc = ((double) tolua_tonumber(tolua_S,9,0)); + double damp_mult = ((double) tolua_tonumber(tolua_S,10,0)); + double damp_min = ((double) tolua_tonumber(tolua_S,11,0)); + double damp_tick = ((double) tolua_tonumber(tolua_S,12,0)); + string silence_string = ((string) tolua_tocppstring(tolua_S,13,0)); + { + DefineSoundEffect(table,name,filename,volume,loop,global,priority,damp_max,damp_inc,damp_mult,damp_min,damp_tick,silence_string); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineSoundEffect'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* function: SetActiveSoundSet */ +#ifndef TOLUA_DISABLE_tolua_enigma_sound_SetActiveSoundSet00 +static int tolua_enigma_sound_SetActiveSoundSet00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_iscppstring(tolua_S,1,0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + string table = ((string) tolua_tocppstring(tolua_S,1,0)); + { + SetActiveSoundSet(table); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SetActiveSoundSet'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* Open function */ +TOLUA_API int tolua_enigma_open (lua_State* tolua_S) +{ + tolua_open(tolua_S); + tolua_reg_types(tolua_S); + tolua_module(tolua_S,NULL,0); + tolua_beginmodule(tolua_S,NULL); + tolua_module(tolua_S,"enigma",0); + tolua_beginmodule(tolua_S,"enigma"); + tolua_constant(tolua_S,"NODIR",NODIR); + tolua_constant(tolua_S,"NORTH",NORTH); + tolua_constant(tolua_S,"EAST",EAST); + tolua_constant(tolua_S,"SOUTH",SOUTH); + tolua_constant(tolua_S,"WEST",WEST); + tolua_function(tolua_S,"GetImage",tolua_enigma_enigma_GetImage00); + tolua_function(tolua_S,"GetImage",tolua_enigma_enigma_GetImage01); + tolua_function(tolua_S,"LoadImage",tolua_enigma_enigma_LoadImage00); + tolua_function(tolua_S,"RegisterImage",tolua_enigma_enigma_RegisterImage00); + tolua_function(tolua_S,"GetFont",tolua_enigma_enigma_GetFont00); + tolua_function(tolua_S,"date",tolua_enigma_enigma_date00); + tolua_endmodule(tolua_S); + tolua_module(tolua_S,"enigma",1); + tolua_beginmodule(tolua_S,"enigma"); + tolua_variable(tolua_S,"TwoPlayerGame",tolua_get_enigma_TwoPlayerGame,tolua_set_enigma_TwoPlayerGame); + tolua_variable(tolua_S,"SingleComputerGame",tolua_get_enigma_SingleComputerGame,tolua_set_enigma_SingleComputerGame); + tolua_variable(tolua_S,"ConserveLevel",tolua_get_enigma_ConserveLevel,tolua_set_enigma_ConserveLevel); + tolua_variable(tolua_S,"AllowTogglePlayer",tolua_get_enigma_AllowTogglePlayer,tolua_set_enigma_AllowTogglePlayer); + tolua_variable(tolua_S,"ShowMoves",tolua_get_enigma_ShowMoves,tolua_set_enigma_ShowMoves); + tolua_variable(tolua_S,"Brittleness",tolua_get_enigma_Brittleness,tolua_set_enigma_Brittleness); + tolua_variable(tolua_S,"SlopeForce",tolua_get_enigma_SlopeForce,tolua_set_enigma_SlopeForce); + tolua_variable(tolua_S,"FlatForce",tolua_get_enigma_FlatForce,tolua_set_enigma_FlatForce); + tolua_variable(tolua_S,"FrictionFactor",tolua_get_enigma_FrictionFactor,tolua_set_enigma_FrictionFactor); + tolua_variable(tolua_S,"IceFriction",tolua_get_enigma_IceFriction,tolua_set_enigma_IceFriction); + tolua_variable(tolua_S,"ElectricForce",tolua_get_enigma_ElectricForce,tolua_set_enigma_ElectricForce); + tolua_variable(tolua_S,"BumperForce",tolua_get_enigma_BumperForce,tolua_set_enigma_BumperForce); + tolua_variable(tolua_S,"MagnetForce",tolua_get_enigma_MagnetForce,tolua_set_enigma_MagnetForce); + tolua_variable(tolua_S,"MagnetRange",tolua_get_enigma_MagnetRange,tolua_set_enigma_MagnetRange); + tolua_variable(tolua_S,"WaterSinkSpeed",tolua_get_enigma_WaterSinkSpeed,tolua_set_enigma_WaterSinkSpeed); + tolua_variable(tolua_S,"SwampSinkSpeed",tolua_get_enigma_SwampSinkSpeed,tolua_set_enigma_SwampSinkSpeed); + tolua_variable(tolua_S,"WormholeForce",tolua_get_enigma_WormholeForce,tolua_set_enigma_WormholeForce); + tolua_variable(tolua_S,"WormholeRange",tolua_get_enigma_WormholeRange,tolua_set_enigma_WormholeRange); + tolua_variable(tolua_S,"HoleForce",tolua_get_enigma_HoleForce,tolua_set_enigma_HoleForce); + tolua_variable(tolua_S,"CreatingPreview",tolua_get_enigma_CreatingPreview,tolua_set_enigma_CreatingPreview); + tolua_function(tolua_S,"AddLevelPack",tolua_enigma_enigma_AddLevelPack00); + tolua_function(tolua_S,"AddZippedLevelPack",tolua_enigma_enigma_AddZippedLevelPack00); + tolua_function(tolua_S,"SetCompatibility",tolua_enigma_enigma_SetCompatibility00); + tolua_endmodule(tolua_S); + tolua_module(tolua_S,"world",0); + tolua_beginmodule(tolua_S,"world"); + tolua_cclass(tolua_S,"Object","Object","",NULL); + tolua_beginmodule(tolua_S,"Object"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"Floor","Floor","Object",NULL); + tolua_beginmodule(tolua_S,"Floor"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"Item","Item","Object",NULL); + tolua_beginmodule(tolua_S,"Item"); + tolua_endmodule(tolua_S); + tolua_cclass(tolua_S,"Stone","Stone","Object",NULL); + tolua_beginmodule(tolua_S,"Stone"); + tolua_endmodule(tolua_S); + tolua_function(tolua_S,"MakeObject",tolua_enigma_world_MakeObject00); + tolua_function(tolua_S,"MakeFloor",tolua_enigma_world_MakeFloor00); + tolua_function(tolua_S,"MakeItem",tolua_enigma_world_MakeItem00); + tolua_function(tolua_S,"MakeStone",tolua_enigma_world_MakeStone00); + tolua_function(tolua_S,"GetNamedObject",tolua_enigma_world_GetNamedObject00); + tolua_function(tolua_S,"Resize",tolua_enigma_world_Resize00); + tolua_function(tolua_S,"DefineSimpleStone",tolua_enigma_world_DefineSimpleStone00); + tolua_function(tolua_S,"DefineSimpleStoneMovable",tolua_enigma_world_DefineSimpleStoneMovable00); + tolua_function(tolua_S,"DefineSimpleFloor",tolua_enigma_world_DefineSimpleFloor00); + tolua_endmodule(tolua_S); + tolua_module(tolua_S,"video",0); + tolua_beginmodule(tolua_S,"video"); + tolua_function(tolua_S,"GetScreen",tolua_enigma_video_GetScreen00); + tolua_function(tolua_S,"HideMouse",tolua_enigma_video_HideMouse00); + tolua_function(tolua_S,"ShowMouse",tolua_enigma_video_ShowMouse00); + tolua_endmodule(tolua_S); + tolua_module(tolua_S,"sound",0); + tolua_beginmodule(tolua_S,"sound"); + tolua_function(tolua_S,"DefineSoundEffect",tolua_enigma_sound_DefineSoundEffect00); + tolua_function(tolua_S,"SetActiveSoundSet",tolua_enigma_sound_SetActiveSoundSet00); + tolua_endmodule(tolua_S); + tolua_endmodule(tolua_S); + return 1; +} + + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 + TOLUA_API int luaopen_enigma (lua_State* tolua_S) { + return tolua_enigma_open(tolua_S); +}; +#endif + diff --git a/project/jni/application/enigma/src/lua-enigma.hh b/project/jni/application/enigma/src/lua-enigma.hh new file mode 100644 index 000000000..03a589fe2 --- /dev/null +++ b/project/jni/application/enigma/src/lua-enigma.hh @@ -0,0 +1,8 @@ +/* +** Lua binding: enigma +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +/* Exported function */ +TOLUA_API int tolua_enigma_open (lua_State* tolua_S); + diff --git a/project/jni/application/enigma/src/lua-global.cpp b/project/jni/application/enigma/src/lua-global.cpp new file mode 100644 index 000000000..9529bc480 --- /dev/null +++ b/project/jni/application/enigma/src/lua-global.cpp @@ -0,0 +1,82 @@ +/* +** Lua binding: global +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +#ifndef __cplusplus +#include "stdlib.h" +#endif +#include "string.h" + +#include "tolua++.h" + +/* Exported function */ +TOLUA_API int tolua_global_open (lua_State* tolua_S); + +#include "enigma.hh" +using namespace enigma; + +/* function to register type */ +static void tolua_reg_types (lua_State* tolua_S) +{ +} + +/* function: DefineFont */ +#ifndef TOLUA_DISABLE_tolua_global_DefineFont00 +static int tolua_global_DefineFont00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnumber(tolua_S,3,0,&tolua_err) || + !tolua_isstring(tolua_S,4,0,&tolua_err) || + !tolua_isnumber(tolua_S,5,1,&tolua_err) || + !tolua_isnumber(tolua_S,6,1,&tolua_err) || + !tolua_isnumber(tolua_S,7,1,&tolua_err) || + !tolua_isnoobj(tolua_S,8,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* name = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* ttf_name = ((const char*) tolua_tostring(tolua_S,2,0)); + int ttf_size = ((int) tolua_tonumber(tolua_S,3,0)); + const char* bmf_name = ((const char*) tolua_tostring(tolua_S,4,0)); + int r = ((int) tolua_tonumber(tolua_S,5,255)); + int g = ((int) tolua_tonumber(tolua_S,6,255)); + int b = ((int) tolua_tonumber(tolua_S,7,255)); + { + DefineFont(name,ttf_name,ttf_size,bmf_name,r,g,b); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'DefineFont'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* Open function */ +TOLUA_API int tolua_global_open (lua_State* tolua_S) +{ + tolua_open(tolua_S); + tolua_reg_types(tolua_S); + tolua_module(tolua_S,NULL,0); + tolua_beginmodule(tolua_S,NULL); + tolua_function(tolua_S,"DefineFont",tolua_global_DefineFont00); + tolua_endmodule(tolua_S); + return 1; +} + + +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501 + TOLUA_API int luaopen_global (lua_State* tolua_S) { + return tolua_global_open(tolua_S); +}; +#endif + diff --git a/project/jni/application/enigma/src/lua-global.hh b/project/jni/application/enigma/src/lua-global.hh new file mode 100644 index 000000000..cd4d92648 --- /dev/null +++ b/project/jni/application/enigma/src/lua-global.hh @@ -0,0 +1,8 @@ +/* +** Lua binding: global +** Generated automatically by tolua++-1.0.92 on Sun Dec 13 15:12:49 2009. +*/ + +/* Exported function */ +TOLUA_API int tolua_global_open (lua_State* tolua_S); + diff --git a/project/jni/application/enigma/src/lua.cpp b/project/jni/application/enigma/src/lua.cpp new file mode 100644 index 000000000..27c346eaf --- /dev/null +++ b/project/jni/application/enigma/src/lua.cpp @@ -0,0 +1,928 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "lua.hh" +#include "errors.hh" +#include "main.hh" +#include "world.hh" +#include "config.h" +#include "video.hh" +#include "server.hh" +#include "sound.hh" +#include "options.hh" +#include "lev/Index.hh" +#include "lev/Proxy.hh" + +#ifndef CXXLUA +extern "C" { +#include "lualib.h" +#include "tolua++.h" +} +#else +#include "lualib.h" +#include "tolua++.h" +#endif + + +#include "lua-display.hh" +#include "lua-enigma.hh" +#include "lua-ecl.hh" + +#include "ecl.hh" +#include + +#include "nls.hh" + +using namespace std; +using namespace enigma; +using namespace lua; + +using ecl::round_down; +using ecl::strf; + +using enigma::GridPos; +using world::Object; +using world::GridObject; +using world::ForceField; + +namespace lua +{ + int EmitSound (lua_State *L); + int EmitSoundGlobal (lua_State *L); + int MakeObject (lua_State *L); +} + +namespace +{ + lua_State *level_state = 0; // level-local state + lua_State *global_state = 0; // global Lua state + + lua::Error _lua_err_code (int i) + { + switch (i) { + case 0: return NO_LUAERROR; + case LUA_ERRRUN: return ERRRUN; + case LUA_ERRFILE: return ERRFILE; + case LUA_ERRSYNTAX: return ERRSYNTAX; + case LUA_ERRMEM: return ERRMEM; + case LUA_ERRERR: return ERRERR; + } + assert (!"Should never get there!"); + } + + void throwLuaError(lua_State * L, const char * message) { + std::string backtrace = message; + backtrace += "\nBacktrace:\n"; + lua_Debug dbgInfo; + int frame = 0; + while (lua_getstack(L, frame, &dbgInfo)) { + lua_getinfo(L, "Sl", &dbgInfo); + if (dbgInfo.source[0] == '@') + // lua code loaded from file + backtrace += ecl::strf("#%d %s: %d\n", frame, dbgInfo.source, + dbgInfo.currentline); + else if (dbgInfo.source[0] == '-' && dbgInfo.source[1] == '-' && + dbgInfo.source[2] == '@') { + // lua code loaded from string + std::string code = dbgInfo.source; + std::string::size_type slashPosFilenameEnd = code.find('\n'); + std::string::size_type slashPosLineStart; + std::string::size_type slashPosLineEnd = slashPosFilenameEnd; + for (int i = 1; i < dbgInfo.currentline; i++) { + slashPosLineStart = slashPosLineEnd; + slashPosLineEnd = code.find('\n', slashPosLineStart + 1); + } + backtrace += ecl::strf("#%d %s: %d\n (%s)\n", frame, + code.substr(2, slashPosFilenameEnd - 2).c_str(), + dbgInfo.currentline - 1, + code.substr(slashPosLineStart + 1, slashPosLineEnd - + slashPosLineStart - 1).c_str()); + } + frame++; + } + luaL_error(L, backtrace.c_str()); + + } +} + +/* -------------------- Helper routines -------------------- */ + +using enigma::Value; + +void lua::SetTableVar (lua_State *L, + const char *tablename, + const char *name, + double value) +{ + lua_getglobal (L, tablename); + lua_pushstring (L, name); + lua_pushnumber (L, value); + lua_rawset (L, -3); + lua_pop (L, 1); +} + +static void +push_value(lua_State *L, const Value &val) +{ + switch (val.get_type()) { + case Value::NIL: lua_pushnil(L); break; + case Value::DOUBLE: lua_pushnumber(L, to_double(val)); break; + case Value::STRING: lua_pushstring(L, to_string(val)); break; + } +} + +static Value +to_value(lua_State *L, int idx) +{ + switch (lua_type(L, idx)) { + case LUA_TNIL: return Value(); + case LUA_TNUMBER: return Value(lua_tonumber(L,idx)); + case LUA_TSTRING: return Value(lua_tostring(L,idx)); + case LUA_TBOOLEAN: return (lua_toboolean(L,idx)) ? Value(1) : Value(); + default: throwLuaError(L,"Cannot convert type to Value."); + } + return Value(); +} + +static bool +is_object(lua_State *L, int idx) +{ + return lua_isuserdata(L,idx) && luaL_checkudata(L,idx,"_ENIGMAOBJECT"); +} + +static Object * +to_object(lua_State *L, int idx) +{ + if (lua_isnil(L,idx)) + return 0; + + if (!is_object(L,idx)) { + throwLuaError(L, "Cannot convert type to an Object"); + return 0; + } + return static_cast(*(static_cast(lua_touserdata(L,idx)))); +} + +static void +pushobject (lua_State *L, Object *obj) +{ + Object **udata; + /* Lua does not allow NULL pointers in userdata variables, so + convert them manually to `nil' values. */ + if (obj == 0) + lua_pushnil(L); + else + { + udata=(Object**)lua_newuserdata(L,sizeof(int)); + *udata=obj; + luaL_getmetatable(L, "_ENIGMAOBJECT"); + lua_setmetatable(L, -2); + } +} + + +/* -------------------- Interface routines -------------------- */ + +int lua::MakeObject (lua_State *L) +{ + const char *name = lua_tostring(L, 1); + if (!name) { + throwLuaError(L, "MakeObject: string expected as argument"); + } + Object *obj = world::MakeObject(name); + if (obj == NULL) + throwLuaError(L, ecl::strf("MakeObject: unknown object name '%s'", name).c_str()); + pushobject(L, obj); + return 1; +} + +static int +en_get_object_template(lua_State *L) +{ + Object *obj = world::GetObjectTemplate(lua_tostring(L, 1)); + pushobject(L, obj); + return 1; +} + +static int +en_set_attrib(lua_State *L) +{ + Object *obj = to_object(L,1); + const char *key = lua_tostring(L,2); + if (obj && key) + obj->set_attrib(key, to_value(L, 3)); + else + throwLuaError(L, strf("SetAttrib: invalid object or attribute name '%s'", key).c_str()); + return 0; +} + +static int +en_get_attrib(lua_State *L) +{ + Object *obj = to_object(L,1); + const char *key = lua_tostring(L,2); + + if (!obj) { + throwLuaError(L, "GetAttrib: invalid object"); + return 0; + } + if (!key) { + throwLuaError(L, "GetAttrib: invalid key"); + return 0; + } + + if (0 == strcmp(key, "kind")) { + throwLuaError(L, "GetAttrib: illegal attribute, use GetKind()"); + return 0; + } + + const Value *v = obj->get_attrib(key); + if (!v) { + // unknown attribute + lua_pushnil(L); + } + else + push_value(L, *v); + return 1; +} + +static int +en_get_kind(lua_State *L) +{ + Object *obj = to_object(L,1); + + if (!obj) { + throwLuaError(L, "GetKind: invalid object"); + return 0; + } + + push_value(L, Value(obj->get_kind())); + return 1; +} + +static int +en_is_same_object(lua_State *L) +{ + Object *obj1 = to_object(L,1); + Object *obj2 = to_object(L,2); + + lua_pushboolean(L, obj1 == obj2); + return 1; +} + +static int +en_set_floor(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Floor *fl=0; + + if (lua_isnil(L, 3)) + fl = 0; + else if (is_object(L,3)) { + fl = static_cast(*(static_cast (lua_touserdata(L,3)))); + if( ! fl) + throwLuaError(L, "object is no valid floor"); + } else + throwLuaError(L, "argument 3 must be an Object or nil"); + world::SetFloor(GridPos(x,y), fl); + return 0; +} + +static int +en_set_item(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Item *it = dynamic_cast(to_object(L, 3)); + if( ! it) { + throwLuaError(L, "object is no valid item"); + } + world::SetItem(GridPos(x,y), it); + return 0; +} + +static int +en_set_stone(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Stone *st = dynamic_cast(to_object(L, 3)); + if( ! st) + throwLuaError(L, "object is no valid stone"); + world::SetStone(GridPos(x,y), st); + return 0; +} + +static int en_kill_stone(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + world::KillStone(GridPos(x,y)); + return 0; +} + +static int en_kill_item(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + world::KillItem(GridPos(x,y)); + return 0; +} + +static int +en_set_actor(lua_State *L) +{ + double x = lua_tonumber(L,1); + double y = lua_tonumber(L,2); + Actor *ac = dynamic_cast(to_object(L, 3)); + if( ! ac) + throwLuaError(L, "object is no valid actor"); + if (world::IsInsideLevel(GridPos(round_down(x), round_down(y)))) + world::AddActor(x, y, ac); + else + throwLuaError(L, "position is outside of world"); + return 0; +} + + +static int +en_send_message(lua_State *L) +{ + Object *obj = to_object(L, 1); + const char *msg = lua_tostring(L, 2); + Value v; + if (!msg) + throwLuaError(L,"Illegal message"); + else if (obj) { + try { + v = world::SendMessage (obj, msg, to_value(L, 3)); + } + catch (const XLevelRuntime &e) { + throwLuaError (L, e.what()); + } + catch (...) { + throwLuaError (L, "uncaught exception"); + } + } + push_value(L, v); + return 0; +} + +int lua::EmitSound (lua_State *L) +{ + Object *obj = to_object(L, 1); + const char *soundname = lua_tostring(L, 2); + double vol = 1.0; + + if (lua_isnumber(L, 3)) + vol = lua_tonumber(L, 3); + if (!soundname) + throwLuaError(L,"Illegal sound"); + else if (obj) { + GridObject *gobj = dynamic_cast(obj); + if (gobj) { + if (!gobj->sound_event (soundname, vol)) { + //throwLuaError(L, strf("Can't find sound '%s'", soundname).c_str()); + // Don't throw an error when no sound file was found. + // Remember that user sound sets might be incomplete, and + // absolutely correct levels could throw an error here. + // Instead, write the "silence string" to the command line: + sound::WriteSilenceString(soundname); + } + } + } + else + throwLuaError(L, "EmitSound: invalid object"); + + return 0; +} + +int lua::EmitSoundGlobal (lua_State *L) +{ + const char *soundname = lua_tostring(L, 1); + double vol = 1.0; + + if (lua_isnumber(L, 3)) + vol = lua_tonumber(L, 3); + if (!soundname) + throwLuaError(L,"Illegal sound"); + else + sound::EmitSoundEventGlobal(soundname, vol); + + return 0; +} + +static int +en_name_object(lua_State *L) +{ + Object *obj = to_object(L, 1); + const char *name = lua_tostring(L, 2); + + if (!obj) + throwLuaError(L, "NameObject: Illegal object"); + else if (!name) + throwLuaError(L, "NameObject: Illegal name"); + else + world::NameObject(obj, name); + + return 0; +} + +static int +en_get_named_object(lua_State *L) +{ + Object *o = world::GetNamedObject(lua_tostring(L,1)); + pushobject(L, o); + return 1; +} + +static int +en_get_floor(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Object *o = world::GetFloor(GridPos(x, y)); + pushobject(L, o); + return 1; +} +static int +en_get_item(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Object *o = world::GetItem(GridPos(x, y)); + pushobject(L, o); + return 1; +} +static int +en_get_stone(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + Object *o = world::GetStone(GridPos(x, y)); + pushobject(L, o); + return 1; +} + +static int +en_get_pos(lua_State *L) +{ + Object *obj = to_object(L, 1); + GridPos p; + + if (!obj) { + throwLuaError(L, "GetPos: Illegal object"); + return 0; + } + + if (GridObject *gobj = dynamic_cast(obj)) + p = gobj->get_pos(); + else if (Actor *a = dynamic_cast(obj)) { + p = GridPos(a->get_pos()); + } + else + p = GridPos(-1, -1); + + lua_pushnumber(L, double(p.x)); + lua_pushnumber(L, double(p.y)); + return 2; +} + +static int en_add_constant_force(lua_State *L) { + ecl::V2 v; + v[0] = lua_tonumber(L, 1); + v[1] = lua_tonumber(L, 2); + world::SetConstantForce (v); + return 0; +} + +static int +en_add_rubber_band (lua_State *L) +{ + Actor *a1 = dynamic_cast (to_object(L, 1)); + Object *o2 = to_object(L, 2); + Actor *a2 = dynamic_cast(o2); + Stone *st = dynamic_cast(o2); + world::RubberBandData d; + d.strength = lua_tonumber (L, 3); + d.length = lua_tonumber (L, 4); + d.minlength = lua_tonumber (L, 5); + + if (!a1) + throwLuaError(L, "AddRubberBand: First argument must be an actor\n"); + else { + if (a2) + world::AddRubberBand (a1, a2, d); + else if (st) + world::AddRubberBand (a1, st, d); + else + throwLuaError(L, "AddRubberBand: Second argument must be actor or stone\n"); + } + return 0; +} + +static int +en_get_ticks(lua_State *L) +{ + lua_pushnumber(L, SDL_GetTicks()); + return 1; +} + +static int +en_is_solved(lua_State *L) +{ + // Function depreceated + // - filename is no longer a useful reference for levels + // - levels should not depend on external data for reasons of + // network compatibility and level journaling + throwLuaError(L, "Usage of depreceated function \"IsSolved()\""); + +// const char *levelname = lua_tostring(L,1); +// int solved = 0; +// +// levels::Level level (0, 0); +// if (levels::FindLevel (levelname, level)) { +// solved = level.is_solved(options::GetInt("Difficulty")); +// } else +// if (solved) +// lua_pushnumber(L, solved); +// else +// lua_pushnil(L); + return 1; +} + +static int +en_add_scramble(lua_State *L) +{ + int x = round_down(lua_tonumber(L, 1)); + int y = round_down(lua_tonumber(L, 2)); + const char *dir = lua_tostring(L,3); + const char *allowed = "wsen"; + const char *found = strchr(allowed, dir[0]); + + if (found && found[0]) + world::AddScramble(GridPos(x,y), enigma::Direction(found-allowed)); + else + throwLuaError(L, "AddScramble: Third argument must be one character of \"wsen\""); + + return 0; +} + +static int +en_set_scramble_intensity(lua_State *L) +{ + world::SetScrambleIntensity(int(lua_tonumber(L, 1))); + return 0; +} + +static int +en_add_signal (lua_State *L) { + const char *sourcestr = lua_tostring (L, 1); + const char *targetstr = lua_tostring (L, 2); + const char *msg = lua_tostring (L, 3); + + using namespace enigma; + GridLoc source, target; + + if (sourcestr == 0 || !to_gridloc(sourcestr, source)) + throwLuaError (L, "AddSignal: invalid source descriptor"); + if (targetstr == 0 || !to_gridloc(targetstr, target)) + throwLuaError (L, "AddSignal: invalid target descriptor"); + if (msg == 0) + msg = "signal"; + + world::AddSignal (source, target, msg); + return 0; +} + +int loadLib(lua_State *L) +{ + const char *id = lua_tostring(L, 1); + lev::Proxy * curProxy = lev::Index::getCurrentProxy(); + try { + curProxy->loadDependency(id); + } catch (XLevelLoading &err) { + throwLuaError(L, err.what()); + } + return 1; +} + +static CFunction globalfuncs[] = { + {FindDataFile, "FindDataFile"}, +// {lua::PlaySoundGlobal, "PlaySoundGlobal"}, +// {lua::PlaySound, "PlaySound"}, + {en_get_ticks, "GetTicks"}, + {0,0} +}; + +static CFunction levelfuncs[] = { + + // internal functions + + {FindDataFile, "FindDataFile"}, + {loadLib, "LoadLib"}, + {en_get_object_template,"GetObjectTemplate"}, + {lua::MakeObject, "MakeObject"}, + {en_set_actor, "SetActor"}, + + // finding objects + + {en_get_named_object, "GetNamedObject"}, + + {en_get_floor, "GetFloor"}, + {en_get_item, "GetItem"}, + {en_get_stone, "GetStone"}, + + // information from objects + + {en_get_pos, "GetPos"}, + {en_get_attrib, "GetAttrib"}, + {en_get_kind, "GetKind"}, + {en_is_same_object, "IsSameObject"}, + + // manipulating objects + + {en_set_attrib, "SetAttrib"}, + {en_send_message, "SendMessage"}, + {en_name_object, "NameObject"}, + + // sound effects + + {lua::EmitSound, "EmitSound"}, + + // manipulating level + + {en_set_floor, "SetFloor"}, + {en_set_item, "SetItem"}, + {en_set_stone, "SetStone"}, + + {en_kill_stone, "KillStone"}, + {en_kill_item, "KillItem"}, + + + // signals + {en_add_signal, "AddSignal"}, + + // access/modify global data + + {en_get_ticks, "GetTicks"}, + {en_is_solved, "IsSolved"}, + + {en_add_constant_force, "AddConstantForce"}, + {en_add_rubber_band, "AddRubberBand"}, + {en_add_scramble, "AddScramble"}, + {en_set_scramble_intensity, "SetScrambleIntensity"}, + + + {0,0} +}; + + +/* -------------------- lua:: functions -------------------- */ + +int lua::FindDataFile (lua_State *L) +{ + const char *filename = lua_tostring(L, 1); + string absfile; + if (app.resourceFS->findFile(filename, absfile)) + lua_pushstring(L, absfile.c_str()); + else + lua_pushnil (L); + return 1; +} + +void lua::RegisterFuncs(lua_State *L, CFunction *funcs) +{ + lua_getglobal(L, "enigma"); + for (unsigned i=0; funcs[i].func; ++i) { + lua_pushstring(L, funcs[i].name); + lua_pushcfunction(L, funcs[i].func); + lua_settable(L, -3); + } + lua_pop(L, 1); +} + +Error lua::CallFunc(lua_State *L, const char *funcname, const Value& arg, world::Object *obj) { + int retval; + lua_getglobal(L, funcname); + push_value(L, arg); + pushobject(L, obj); + retval=lua_pcall(L,2,0,0); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + return _lua_err_code(retval); +} + +Error lua::CallFunc(lua_State *L, const char *funcname, const ByteVec& arg) { + int retval; + lua_getglobal(L, funcname); + lua_pushlstring (L, &arg[0], arg.size()); + retval=lua_pcall(L,1,0,0); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + return _lua_err_code(retval); +} + +Error lua::DoAbsoluteFile(lua_State *L, const string &filename) +{ + int oldtop = lua_gettop(L); + int retval = luaL_loadfile(L, filename.c_str()); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + else + { + retval= lua_pcall(L, 0, 0, 0); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + } + lua_settop(L, oldtop); + return _lua_err_code(retval); +} + +Error lua::DoGeneralFile(lua_State *L, GameFS * fs, const string &filename) +{ + string completefn; + if (fs->findFile(filename, completefn)) { + return lua::DoAbsoluteFile(L, completefn); + } + else { + return _lua_err_code(LUA_ERRFILE); + } +} + +Error lua::Dofile(lua_State *L, const string &filename) +{ + return lua::DoGeneralFile(L, app.resourceFS, filename); +} + +Error lua::DoSysFile(lua_State *L, const string &filename) +{ + return lua::DoGeneralFile(L, app.systemFS, filename); +} + +void lua::CheckedDoFile (lua_State *L, GameFS * fs, std::string const& fname) +{ + string completefn; + if (!fs->findFile(fname, completefn)) + { + fprintf(stderr, _("Cannot find '%s'.\n"), fname.c_str()); + fprintf(stderr, _("Your installation may be incomplete or invalid.\n")); + exit (1); + } + + lua::Error status = lua::DoAbsoluteFile(L, completefn); + if (status != lua::NO_LUAERROR) { + fprintf(stderr, _("There was an error loading '%s'.\n"), completefn.c_str()); + fprintf(stderr, _("Your installation may be incomplete or invalid.\n")); + fprintf(stderr, _("Error: '%s'\n"), lua::LastError(L).c_str()); + exit (1); + } +} + + +Error lua::Dobuffer (lua_State *L, const ByteVec &luacode) { + int retval; + const char *buffer = reinterpret_cast(&luacode[0]); + + retval=luaL_loadbuffer(L, buffer, luacode.size(), "buffer"); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + else + { + retval= lua_pcall(L, 0, 0, 0); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + } + return _lua_err_code(retval); +} + +string lua::LastError (lua_State *L) +{ + lua_getglobal (L, "_LASTERROR"); + if (lua_isstring(L,-1)){ + return string (lua_tostring (L, -1)); + } + else { + return "Lua Error. No error message available."; + } +} + + +Error lua::DoSubfolderfile(lua_State *L, const string &basefolder, const string &filename) { + std::list fnames = app.resourceFS->findSubfolderFiles(basefolder, filename); + int retval = 0; + while (fnames.size() > 0) { + int oldtop = lua_gettop(L); + string fname = fnames.front(); + retval = luaL_loadfile(L, fname.c_str()); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + else + { + retval= lua_pcall(L, 0, 0, 0); + if (retval!=0) // error + { + lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message + } + } + fnames.pop_front(); + lua_settop(L, oldtop); + } + return _lua_err_code(retval); +} + +lua_State *lua::GlobalState() +{ + if (global_state == 0) { + lua_State *L = global_state = lua_open(); + + luaL_openlibs(L); + CheckedDoFile(L, app.systemFS, "compat.lua"); + + tolua_open(L); + tolua_enigma_open(L); + + RegisterFuncs(L, globalfuncs); + } + return global_state; +} + +void lua::ShutdownGlobal() +{ + assert (global_state); + lua_close(global_state); + global_state = 0; +} + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + + +lua_State *lua::InitLevel() +{ + char buffer[255]; + + lua_State *L = level_state = lua_open(); + luaL_dostring (L, "options={}"); + snprintf (buffer, sizeof(buffer), "options.Difficulty = %d", + server::GetDifficulty()); + luaL_dostring (L, buffer); + + luaL_openlibs(L); + + tolua_open(L); + tolua_enigma_open(L); + tolua_px_open(L); + tolua_display_open(L); + + RegisterFuncs(L, levelfuncs); + + // Create a new metatable for world::Object objects + luaL_newmetatable(L,"_ENIGMAOBJECT"); + return L; +} + +lua_State *lua::LevelState () +{ + return level_state; +} + +void lua::ShutdownLevel() { + if (level_state) { + lua_close(level_state); + level_state = 0; + } +} + diff --git a/project/jni/application/enigma/src/lua.hh b/project/jni/application/enigma/src/lua.hh new file mode 100644 index 000000000..17d9a8699 --- /dev/null +++ b/project/jni/application/enigma/src/lua.hh @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef LUA_HH +#define LUA_HH + +#include "enigma.hh" +#include "ecl_geom.hh" +#include "objects_decl.hh" + +#ifdef CXXLUA +struct lua_State; +#else +extern "C" struct lua_State; +#endif + + +namespace lua +{ + using namespace enigma; + +/* -------------------- Data structures -------------------- */ + + struct CFunction { + int (*func) (lua_State *); // lua_CFunction func; + const char *name; + }; + + enum Error { + NO_LUAERROR = 0, + ERRRUN, + ERRFILE, + ERRSYNTAX, + ERRMEM, + ERRERR + }; + +/* -------------------- Lua states for Enigma -------------------- */ + + lua_State *GlobalState(); + void ShutdownGlobal(); + + /*! Return the Lua state that is active while the game is running. + This state is used to execute the Lua code that builds the level + and to execute signal callbacks and event handlers while the + game is running. */ + lua_State *LevelState(); + + /*! Initialize the ingame Lua state. */ + lua_State *InitLevel(); + + /*! Close the ingame Lua state. */ + void ShutdownLevel(); + + +/* -------------------- Enigma/Lua bindings -------------------- */ + + int FindDataFile (lua_State *L); + +/* -------------------- Helper routines -------------------- */ + + /*! Register the C functions in `funcs'. The end of the array is + denoted by an entry with func==0. */ + void RegisterFuncs (lua_State *L, CFunction funcs[]); + + /*! Set the value of entry `name' in the global table `tablename'. */ + void SetTableVar (lua_State *L, const char *tablename, const char *name, double value); + + /*! Call a Lua function with one argument. This is mainly used + for callbacks during the game. */ + Error CallFunc(lua_State *L, const char *funcname, const enigma::Value& arg, world::Object *obj); + + /*! Call a Lua function with a (large) byte vector as the sole + argument. Currently only used for loading XML levels. */ + Error CallFunc (lua_State *L, const char *funcname, const ByteVec &arg); + + /** + * Run a Lua script using a given absolute path. + */ + Error DoAbsoluteFile (lua_State *L, const std::string & filename); + + /*! Find a Lua script using enigma::FindFile and run it. */ + Error Dofile (lua_State *L, const std::string & filename); + + /*! Find a system Lua script using enigma::FindFile and run it. */ + Error DoSysFile (lua_State *L, const std::string & filename); + + /*! Find a Lua script in given filesystem using enigma::FindFile and run it. */ + Error DoGeneralFile(lua_State *L, GameFS * fs, const string &filename); + + /*! Run the Lua code contained in `luacode'. */ + Error Dobuffer (lua_State *L, const ByteVec &luacode); + + /*! Try to run given file on given filesystem. If something + fails, provide generic error message and exit enigma.*/ + void CheckedDoFile (lua_State *L, GameFS * fs, const string &filename); + + /*! Return the text of the last error message. */ + std::string LastError (lua_State *L); + + + Error DoSubfolderfile(lua_State *L, + const std::string & basefolder, + const std::string & filename); +} +#endif + diff --git a/project/jni/application/enigma/src/main.cpp b/project/jni/application/enigma/src/main.cpp new file mode 100644 index 000000000..e5f5da540 --- /dev/null +++ b/project/jni/application/enigma/src/main.cpp @@ -0,0 +1,850 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "main.hh" +#include "display.hh" +#include "lua.hh" +#include "gui/MainMenu.hh" +#include "gui/ErrorMenu.hh" +#include "gui/LevelPreviewCache.hh" +#include "options.hh" +#include "oxyd.hh" +#include "sound.hh" +#include "video.hh" +#include "ecl_argp.hh" +#include "ecl_system.hh" +#include "errors.hh" +#include "world.hh" +#include "nls.hh" +#include "LocalToXML.hh" +#include "PreferenceManager.hh" +#include "Utf8ToXML.hh" +#include "XMLtoUtf8.hh" +#include "XMLtoLocal.hh" +#include "lev/RatingManager.hh" +#include "lev/VolatileIndex.hh" +#include "lev/PersistentIndex.hh" +#include "lev/ScoreManager.hh" + +#include "enet/enet.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MACOSX +// for search paths +#include "NSSystemDirectories.h" +#endif + +using namespace std; +using namespace ecl; +using namespace enigma; +XERCES_CPP_NAMESPACE_USE + +#ifdef WIN32 +// LoadImage is a Syscall on Windows, which gets defined to LoadImageA +// or LoadImageW in winuser.h so we simply undefine it to use this +// name for one of the methods +#undef LoadImage +#endif + +namespace +{ + class Nulbuf : public std::streambuf {}; + Nulbuf* nullbuffer = new Nulbuf; +} + +/* -------------------- Variables -------------------- */ + +namespace enigma +{ + Application app; + + bool noAssert = true; // block expensive assert evaluations by default + + bool WizardMode = false; + + //! If true, do not ``grab'' the mouse and keyboard + bool Nograb = false; +} + +/*! The stream object that is used for logging messages. As defined + here, it is not connected to any file or buffer. + + (Note: I think writing to a stream without a streambuffer *should* + be a no-op, but it leads to crashes with g++ 2.95. to circumvent + this, Log is initialized with a dummy streambuf in init(). ) */ +std::ostream enigma::Log(0); + +/*! This is the stream object that is used when logging to a file. In + this case, enigma::Log uses this object's streambuffer. */ +static std::fstream logfile; + +/* -------------------- Functions -------------------- */ + +static void usage() +{ + printf("Usage: %s [options] [level files]\n\n" + "Available options :\n\n" + " --nosound Disable music and sound\n" + " --nomusic Disable music\n" + " --window -w Run in a window; do not enter fullscreen mode\n" + " --help -h Show this help\n" + " --version Print the executable's version number\n" + " --nograb Do not use exclusive mouse/keyboard access\n" + " --data -d path Load data from additional directory\n" + " --lang -l lang Set game language\n" + " --pref -p file Use filename or dirname for preferences\n" + "\n", + app.progCallPath.c_str() + ); +} + +namespace +{ + struct AP : public ecl::ArgParser { + public: + // Constructor + AP(); + + // Variables. + bool nosound, nomusic, show_help, show_version, do_log, do_assert, force_window; + bool dumpinfo, makepreview; + string gamename; + string datapath; + string preffilename; + std::vector levelnames; + + private: + enum { + OPT_WINDOW, OPT_GAME, OPT_DATA, OPT_LANG, OPT_PREF + }; + + // ArgParser interface. + void on_error (ErrorType t, const string &option) { + cout << errormsg(t, option) << endl; + show_help = true; + } + + void on_option (int id, const string ¶m); + void on_argument (const string &arg); + + }; +} + +AP::AP() : ArgParser (app.args.begin(), app.args.end()) +{ + nosound = nomusic = show_help = show_version = do_log = do_assert = force_window = false; + dumpinfo = makepreview = false; + gamename = ""; + datapath = ""; + preffilename = PREFFILENAME; + + def (&nosound, "nosound"); + def (&nomusic, "nomusic"); + def (&show_version, "version"); + def (&show_help, "help", 'h'); + def (&WizardMode, "wizard"); + def (&Nograb, "nograb"); + def (&do_log, "log"); + def (&do_assert, "assert"); + def (&dumpinfo, "dumpinfo"); + def (&makepreview, "makepreview"); + def (&force_window, "window", 'w'); + def (OPT_GAME, "game", true); + def (OPT_DATA, "data", 'd', true); + def (OPT_LANG, "lang", 'l', true); + def (OPT_PREF, "pref", 'p', true); +} + +void AP::on_option (int id, const string ¶m) +{ + switch (id) { + case OPT_GAME: + gamename = param; + break; + case OPT_DATA: +// we should be able to add several paths -- file.cc has does not yet support this +// if (datapath.empty()) + datapath = param; +// else +// datapath = param + ":" + datapath; + break; + case OPT_LANG: + app.argumentLanguage = param; + break; + case OPT_PREF: + preffilename = param; + break; + } +} + +void AP::on_argument (const string &arg) +{ + levelnames.push_back (arg); +} + + + +/*! Initialize enough of the game to be able to show error messages in + the window, not on the console. */ + + + +/* -------------------- Application -------------------- */ + +Application::Application() : wizard_mode (false), nograb (false), language (""), + defaultLanguage (""), argumentLanguage (""), errorInit (false), + isMakePreviews (false) { +} + + +void Application::init(int argc, char **argv) +{ + progCallPath = argv[0]; + copy(argv+1, argv+argc, back_inserter(args)); + + // parse commandline arguments -- needs args + AP ap; + ap.parse(); + + // ----- Evaluate command line arguments. + // start with simple actions that do not need further initialization + if (ap.show_help || ap.show_version) { + printf("Enigma %s\n", getVersionInfo().c_str()); + if (ap.show_help) usage(); + exit(0); + } + + // + if (ap.makepreview) { + ap.force_window = true; + ap.nosound = true; + ap.nomusic = true; + isMakePreviews = true; + } + + // initialize logfile -- needs ap + if (ap.do_log) + enigma::Log.rdbuf(cout.rdbuf()); + else + enigma::Log.rdbuf(::nullbuffer); + + // initialize assertion stop flag + if (ap.do_assert) + enigma::noAssert = false; + + // initialize system datapaths -- needs ap, log + systemCmdDataPath = ap.datapath; + initSysDatapaths(ap.preffilename); + + // initialize XML -- needs log, datapaths + initXerces(); + + // initialize LUA - Run initialization scripts + lua_State *L = lua::GlobalState(); + lua::CheckedDoFile(L, app.systemFS, "startup.lua"); + + // initialize preferences -- needs LUA, XML + if (!options::Load()) { + fprintf(stderr, _("Error in configuration file.\n")); + fprintf(stderr, lua::LastError (lua::GlobalState()).c_str()); + } + prefs = PreferenceManager::instance(); + + if (ap.force_window) { + options::SetOption("FullScreen", false); + } + if (isMakePreviews) { + options::SetOption("VideoMode", 0); + } + + // initialize user data paths -- needs preferences, system datapaths + initUserDatapaths(); + + // set message language + init_i18n(); + + // ----- Initialize object repositories + world::Init(); + if (ap.dumpinfo) { + world::DumpObjectInfo(); + exit(0); + } + + // ----- Initialize SDL library +#ifdef WIN32 +// SDL_putenv("SDL_VIDEODRIVER=directx"); //needed for SDL 1.2.12 that favors GDI which crashes on SetGamma +#endif + int sdl_flags = SDL_INIT_VIDEO; + if (enigma::WizardMode) + sdl_flags |= SDL_INIT_NOPARACHUTE; + if (SDL_Init(sdl_flags) < 0) { + fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + SDL_EnableUNICODE(1); + const SDL_version* vi = SDL_Linked_Version(); + Log << ecl::strf("SDL Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); + + vi = TTF_Linked_Version(); + Log << ecl::strf("SDL_ttf Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); + if(TTF_Init() == -1) { + fprintf(stderr, "Couldn't initialize SDL_ttf: %s\n", TTF_GetError()); + exit(1); + } + + vi = IMG_Linked_Version(); + Log << ecl::strf("SDL_image Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); +#ifdef SDL_IMG_INIT + int img_flags = IMG_INIT_PNG | IMG_INIT_JPG; + if (IMG_Init(img_flags) & img_flags != img_flags) { + fprintf(stderr, "Couldn't initialize SDL_image: %s\n", IMG_GetError()); + exit(1); + } +#endif + + // ----- Initialize video subsystem + video::Init(); + video::SetCaption ("Enigma v" PACKAGE_VERSION); + video::SetMouseCursor(enigma::LoadImage("cur-magic"), 4, 4); + video::ShowMouse(); + SDL_ShowCursor(0); + errorInit = true; + + + // ----- Initialize sound subsystem + sound::Init(!ap.nomusic, !ap.nosound); + lua::CheckedDoFile (L, app.systemFS, "sound-defaults.lua"); + lua::DoSubfolderfile (L, "soundsets", "soundset.lua"); + + // ----- Initialize network layer + if (enet_initialize () != 0) { + fprintf (stderr, "An error occurred while initializing ENet.\n"); + exit (1); + } + + // ----- Load models + display::Init(); + + // initialize application state + state = StateManager::instance(); + + // ----- Load level packs -- needs state + lev::Index::initGroups(); + oxyd::Init(!isMakePreviews); // Load oxyd data files - must be first to create correct proxies + lev::PersistentIndex::registerPersistentIndices(ap.makepreview); + if (!isMakePreviews) { + lua::Dofile(L, "levels/index.lua"); + lua::DoSubfolderfile(L, "levels", "index.lua"); + lua::Dofile(L, "levels/index_user.lua"); + if (!ap.levelnames.empty()) { + lev::Index::registerIndex(new lev::VolatileIndex(INDEX_STARTUP_PACK_NAME, + INDEX_EVERY_GROUP, ap.levelnames, INDEX_STARTUP_PACK_LOCATION)); + lev::Index::setCurrentIndex(INDEX_STARTUP_PACK_NAME); + } + std::vector emptyList; + lev::Index::registerIndex(new lev::VolatileIndex(INDEX_SEARCH_PACK_NAME, + INDEX_DEFAULT_GROUP, emptyList, INDEX_SEARCH_PACK_LOCATION)); + } + + lev::Proxy::countLevels(); + + // ----- Initialize sound tables -- needs sound, oxyd, video (error messages!) + sound::InitSoundSets(); + +#if MACOSX + updateMac1_00(); +#endif + + // initialize random + enigma::Randomize(); + + // + if (isMakePreviews) { + std::set proxies = lev::Proxy::getProxies(); + int size = proxies.size(); + std::set::iterator it; + std::string message = ecl::strf("Make %d previews on system path '%s'", + size, systemAppDataPath.c_str()); + Log << message; + + Screen *scr = video::GetScreen(); + GC gc (scr->get_surface()); + Font *f = enigma::GetFont("menufont"); + f->render (gc, 80, 240, message.c_str()); + set_color(gc, 200,200,200); + hline(gc, 170, 280, 300); + hline(gc, 170, 300, 300); + vline(gc, 170, 280, 20); + vline(gc, 470, 280, 20); + scr->update_all (); + scr->flush_updates(); + + int i = 0; + for (it = proxies.begin(); it != proxies.end(); it++, i++) { + gui::LevelPreviewCache::makeSystemPreview(*it, systemAppDataPath); + vline(gc, 170 + i*300 / size, 280, 20); + scr->update_all (); + scr->flush_updates(); + } + return; + } + + // initialize score -- needs random init + lev::ScoreManager::instance(); +} + +std::string Application::getVersionInfo() { + std::string versionInfo; + double enigmaVersion; + sscanf(PACKAGE_VERSION,"%4lf",&enigmaVersion); + if (enigmaVersion >= ENIGMACOMPATIBITLITY) + versionInfo = "v" PACKAGE_VERSION; + else { + versionInfo = "v" PACKAGE_VERSION + " (development version - v" + + ecl::strf("%.2f",ENIGMACOMPATIBITLITY) + " compatibility branch)"; + } + return versionInfo; +} + +void Application::initSysDatapaths(const std::string &prefFilename) +{ + std::string progDir; // directory path part of args[0] + std::string progName; // filename part of args[0] + bool progDirExists = split_path(progCallPath, &progDir, &progName); + std::string systemPath = SYSTEM_DATA_DIR; // substituted by configure.ac + bool haveHome = (getenv("HOME") != 0); +#ifdef __MINGW32__ + // windows standard user specific application data directory path + std::string winAppDataPath = ecl::ApplicationDataPath() + "/Enigma"; +#endif + + // systemFS + systemFS = new GameFS(); +#ifdef __MINGW32__ + if (!progDirExists) { + // filename only -- working dir should be on enigma as enigma + // should never be located on exec path on windows + systemAppDataPath = "./data"; + } else { + // a call from elsewhere -- absolute or relative path does not matter. + // this enables calls from a commandline. + systemAppDataPath = progDir + "/data"; + } +#elif MACOSX + // Mac OS X applications are self-contained bundles, + // i.e., directories like "Enigma.app". Resources are + // placed in those bundles under "Enigma.app/Contents/Resources", + // the main executable would be "Enigma.app/Contents/MacOS/enigma". + // Here, we get the executable name, clip off the last bit, chdir into it, + // then chdir to ../Resources. The original SDL implementation chdirs to + // "../../..", i.e. the directory the bundle is placed in. This breaks + // the self-containedness. + + systemAppDataPath = progDir + "/../Resources/data"; + +#else + // Unix -- we get our data path from the installation + systemAppDataPath = systemPath; +#endif + systemFS->append_dir(systemAppDataPath); + if (!systemCmdDataPath.empty()) + systemFS->prepend_dir(systemCmdDataPath); + Log << "systemFS = \"" << systemFS->getDataPath() << "\"\n"; + + // l10nPath + l10nPath = LOCALEDIR; // defined in src/Makefile.am +#ifdef __MINGW32__ + if (progDirExists) { + l10nPath = progDir + "/" + l10nPath; + } +#elif MACOSX + l10nPath = progDir + "/../Resources/locale"; +#endif + Log << "l10nPath = \"" << l10nPath << "\"\n"; + + + // prefPath + if (prefFilename.find_first_of(ecl::PathSeparators) != std::string::npos) { + // pref is a path - absolute or home relative + prefPath = ecl::ExpandPath(prefFilename); + if (!ecl::FolderExists(prefPath)) + if(!ecl::FolderCreate(prefPath)) { + fprintf(stderr, ("Error cannot create pref directory.\n")); + exit(1); + } + userStdPath = prefPath; // default if pref is a path + prefPath = prefPath + ecl::PathSeparator + "." + PREFFILENAME; // include pref in user data path + } else if (haveHome) { + prefPath = ecl::ExpandPath("~"); + if (!ecl::FolderExists(prefPath)) + // may happen on Windows + if(!ecl::FolderCreate(prefPath)) { + fprintf(stderr, _("Error Home directory does not exist.\n")); + exit(1); + } +#ifdef MACOSX + userStdPathMac1_00 = prefPath + "/.enigma"; + userStdPath = prefPath + "/Library/Application Support/Enigma"; +#else + userStdPath = prefPath + "/.enigma"; +#endif + prefPath = prefPath + ecl::PathSeparator + "." + prefFilename; +#ifdef __MINGW32__ + } else if (!winAppDataPath.empty()) { + if (!ecl::FolderExists(winAppDataPath)) + // may happen on Windows + if(!ecl::FolderCreate(winAppDataPath)) { + fprintf(stderr, _("Error Application Data directory does not exist.\n")); + exit(1); + } + Log << "winAppDataPath " << winAppDataPath << "\n"; + userStdPath = winAppDataPath; + prefPath = winAppDataPath + ecl::PathSeparator + "." + prefFilename; +#endif + } else { + fprintf(stderr, _("Error Home directory does not exist.\n")); + exit(1); + } + Log << "prefPath = \"" << prefPath << "\"\n"; +} + +void Application::initXerces() { + // init XML + try { + // Initialize to en_US - we don't know the user prefs yet + // If more than the error messages should be influenced we would + // have to terminate and reinit after reading the user prefs. + XMLPlatformUtils::Initialize(); + + // Initialize transcoding service for XML <-> utf8 + XMLTransService::Codes initResult; + xmlUtf8Transcoder = XMLPlatformUtils::fgTransService-> + makeNewTranscoderFor(XMLRecognizer::UTF_8, initResult, + 4096); // the block size is irrelevant for utf-8 + if (initResult != XMLTransService::Ok) { + fprintf(stderr, _("Error in XML initialization.\n")); + exit(1); + } + + static const XMLCh ls[] = { chLatin_L, chLatin_S, chNull }; + static const XMLCh core[] = { chLatin_C, chLatin_O, chLatin_R, chLatin_E, chNull }; + domImplementationLS = (DOMImplementationLS*) + (DOMImplementationRegistry::getDOMImplementation(ls)); + domImplementationCore = (DOMImplementation*) + (DOMImplementationRegistry::getDOMImplementation(core)); + domParserErrorHandler = new DOMErrorReporter(&Log); + domParserSchemaResolver = new DOMSchemaResolver(); + domSerErrorHandler = new DOMErrorReporter(&Log); + +#if _XERCES_VERSION >= 30000 + domParser = domImplementationLS->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0); + DOMConfiguration *config = domParser->getDomConfig(); + + config->setParameter(XMLUni::fgDOMNamespaces, true); + config->setParameter(XMLUni::fgXercesSchema, true); + config->setParameter(XMLUni::fgXercesSchemaFullChecking, true); + config->setParameter(XMLUni::fgDOMValidate, true); + config->setParameter(XMLUni::fgDOMDatatypeNormalization, true); + config->setParameter(XMLUni::fgDOMErrorHandler, domParserErrorHandler); + config->setParameter(XMLUni::fgDOMResourceResolver, domParserSchemaResolver); + + domSer = domImplementationLS->createLSSerializer(); + config = domSer->getDomConfig(); + + config->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true); + config->setParameter(XMLUni::fgDOMErrorHandler, domSerErrorHandler); + +#else + domParser = domImplementationLS->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); + + domParser->setFeature(XMLUni::fgDOMNamespaces, true); + domParser->setFeature(XMLUni::fgXercesSchema, true); + domParser->setFeature(XMLUni::fgXercesSchemaFullChecking, true); + domParser->setFeature(XMLUni::fgDOMValidation, true); + domParser->setFeature(XMLUni::fgDOMDatatypeNormalization, true); + domParser->setErrorHandler(domParserErrorHandler); + domParser->setEntityResolver(domParserSchemaResolver); + + domSer = domImplementationLS->createDOMWriter(); + domSer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true); + domSer->setErrorHandler(domSerErrorHandler); +#endif + } + catch (...) { + fprintf(stderr, _("Error in XML initialization.\n")); + exit(1); + } +} + +void Application::initUserDatapaths() { + // userPath + userPath = prefs->getString("UserPath"); + if (userPath.empty()) { +#ifdef MACOSX + if (prefs->getInt("_MacUpdate1.00") != 1) + userPath = userStdPathMac1_00; // empty prefs user path is 1.00 std user path + else { + // first installation of Enigma on a Mac + userPath = userStdPath; // use the new path + prefs->setProperty("UserPath", std::string(XMLtoUtf8(LocalToXML(&userPath).x_str()).c_str())); + prefs->setProperty("UserImagePath", std::string(XMLtoUtf8(LocalToXML(&userPath).x_str()).c_str())); + prefs->setProperty("_MacUpdate1.00", 2); + } +#else + userPath = userStdPath; +#endif + } else { + userPath = XMLtoLocal(Utf8ToXML(userPath.c_str()).x_str()).c_str(); + } + Log << "userPath = \"" << userPath << "\"\n"; + + // userImagePath + userImagePath = prefs->getString("UserImagePath"); + if (userImagePath.empty()) { +#ifdef MACOSX + userImagePath = userStdPathMac1_00; // empty prefs user path is 1.00 std user path +#else + userImagePath = userStdPath; +#endif + } else { + userImagePath = XMLtoLocal(Utf8ToXML(userImagePath.c_str()).x_str()).c_str(); + } + Log << "userImagePath = \"" << userImagePath << "\"\n"; + + // resourceFS + resourceFS = new GameFS(); + resourceFS->append_dir(systemAppDataPath); +#ifdef MACOSX + // set user-visible data paths -- use it for resource paths + NSSearchPathEnumerationState cur=NSStartSearchPathEnumeration(NSLibraryDirectory, NSAllDomainsMask); + char path[PATH_MAX]; + + while(cur) { + cur=NSGetNextSearchPathEnumeration(cur, path); + resourceFS->prepend_dir(std::string(path)+"/Application Support/Enigma"); + } +#endif + if (!systemCmdDataPath.empty()) + resourceFS->prepend_dir(systemCmdDataPath); + resourceFS->prepend_dir(userPath); + if (userImagePath != userPath) + resourceFS->prepend_dir(userImagePath); + Log << "resourceFS = \"" << resourceFS->getDataPath() << "\"\n"; + + // create levels/auto, levels/cross, levels/legacy_dat on userPath + if (!ecl::FolderExists(userPath + "/levels/auto")) + ecl::FolderCreate (userPath + "/levels/auto"); + if (!ecl::FolderExists(userPath + "/levels/cross")) + ecl::FolderCreate (userPath + "/levels/cross"); + if (!ecl::FolderExists(userPath + "/levels/legacy_dat")) + ecl::FolderCreate (userPath + "/levels/legacy_dat"); + if (!ecl::FolderExists(userPath + "/backup")) + ecl::FolderCreate (userPath + "/backup"); +} + +#ifdef MACOSX +void Application::updateMac1_00() { + if (prefs->getInt("_MacUpdate1.00") == 0 && + prefs->getString("UserPath").empty() && + prefs->getString("UserImagePath").empty()) { + gui::ErrorMenu m(ecl::strf(N_("Mac OS X upgrade from Enigma 1.00\n\nThe default user data path has changed from\n %s \n\nto the visible data path\n %s \n\nIf ok Enigma will move your data to this new location.\nNote that you have to restart Enigma once for completion of this update."), userStdPathMac1_00.c_str(), userStdPath.c_str()), + N_("OK"), N_("Never"), N_("Remind")); + m.manage(); + if (m.isRejectQuit()) { + prefs->setProperty("_MacUpdate1.00", 2); + } else if (m.isLaterQuit()) { + prefs->setProperty("_MacUpdate1.00", 0); + } else { // OK move now + Log << "Mac update\n"; + // move + std::system(ecl::strf("mkdir '%s' && cd ~/.enigma && tar -cf - * | (cd '%s' && tar -xf -) && cd ~ && rm -rf ~/.enigma", userStdPath.c_str(), userStdPath.c_str()).c_str()); + setUserPath(""); + setUserImagePath(""); + prefs->setProperty("_MacUpdate1.00", 2); + prefs->shutdown(); + exit(0); + } + } +} +#endif + +void Application::init_i18n() +{ + // Initialize the internationalization subsystem + + // priorities: + // language: command-line --- user option --- system (environment) + // defaultLanguage: command-line --- system (environment) + app.language = app.argumentLanguage; + app.defaultLanguage = app.argumentLanguage; + if (app.language == "") { + options::GetOption("Language", app.language); + } + if (app.defaultLanguage == "") { + app.defaultLanguage = ecl::SysMessageLocaleName(); + if (app.language == "") { + app.language = app.defaultLanguage; + } + } + +#if defined(ENABLE_NLS) + + nls::SetMessageLocale (app.language); + + bindtextdomain (PACKAGE_NAME, app.l10nPath.c_str()); + + // SDL_ttf does not handle arbitrary encodings, so use UTF-8 + bind_textdomain_codeset (PACKAGE_NAME, "utf-8"); + textdomain (PACKAGE_NAME); +#endif + +} + +void Application::setLanguage(std::string newLanguage) +{ + if (newLanguage == "") { + language = defaultLanguage; + } + else { + language = newLanguage; + } + nls::SetMessageLocale(language); +} + +void Application::setUserPath(std::string newPath) { + std::string prefUserPath = (newPath == userStdPath) ? "" : newPath; + if ((prefUserPath.empty() && userPath != userStdPath) || (!prefUserPath.empty() && prefUserPath != userPath)) { + // set the new userPath - used for saves + if (prefUserPath.empty()) + userPath = userStdPath; + else + userPath = prefUserPath; + + // load all resources primarily from the new path but keep the old user path + // because the user could not yet copy his user data to the new location + resourceFS->prepend_dir(userPath); + + // set the new path as the users preference - the standard path is saved as "" +#ifdef MACOSX + // 1.00 uses "" as "~/.enigma" - we have to store the complete path + if (prefUserPath.empty()) prefUserPath = userStdPath; +#endif + prefs->setProperty("UserPath", std::string(XMLtoUtf8(LocalToXML(&prefUserPath).x_str()).c_str())); + } +} + +void Application::setUserImagePath(std::string newPath) { + std::string prefUserImagePath = (newPath == userStdPath) ? "" : newPath; + if ((prefUserImagePath.empty() && userImagePath != userStdPath) || (!prefUserImagePath.empty() && prefUserImagePath != userImagePath)) { + // set the new userImagePath - used for saves + if (prefUserImagePath.empty()) + userImagePath = userStdPath; + else + userImagePath = prefUserImagePath; + + // load all resources primarily from the new path but keep the old user path + // because the user could not yet copy his user data to the new location + if (userImagePath != userPath) + resourceFS->prepend_dir(userImagePath); + + // set the new path as the users preference - the standard path is saved as "" +#ifdef MACOSX + // 1.00 uses "" as "~/.enigma" - we have to store the complete path + if (prefUserImagePath.empty()) prefUserImagePath = userStdPath; +#endif + prefs->setProperty("UserImagePath", std::string(XMLtoUtf8(LocalToXML(&prefUserImagePath).x_str()).c_str())); + } +} + +/* -------------------- Functions -------------------- */ + +void Application::shutdown() +{ + oxyd::Shutdown(); + world::Shutdown(); + display::Shutdown(); + if (!isMakePreviews) { // avoid saves on preview generation + lev::RatingManager::instance()->save(); + if (lev::PersistentIndex::historyIndex != NULL) + lev::PersistentIndex::historyIndex->save(); + lev::ScoreManager::instance()->shutdown(); + app.state->shutdown(); + app.prefs->shutdown(); + } + // now we shutdown SDL - no error reports will be possible! + app.errorInit = false; + video::Shutdown(); + sound::Shutdown(); + enet_deinitialize(); + lua::ShutdownGlobal(); + XMLPlatformUtils::Terminate(); +#ifdef SDL_IMG_INIT + IMG_Quit(); +#endif + ClearFontCache(); + TTF_Quit(); + ClearImageCache(); + delete ::nullbuffer; +} + +int main(int argc, char** argv) +{ + try { + app.init(argc,argv); + if (!app.isMakePreviews) + gui::ShowMainMenu(); + app.shutdown(); + return 0; + } + catch (XFrontend &e) { + cerr << "Error: " << e.what() << endl; + std::string message = _("Fatal Error that causes the application to quit:\n\n"); + if (app.errorInit) { + gui::ErrorMenu m(message + e.what(), N_("Quit")); + m.manage(); + } + } + catch (ecl::XGeneric &e) { + cerr << "Error: " << e.what() << endl; + } + catch (std::exception &e) { + cerr << "Error: " << e.what() << endl; + } + catch (...) { + cerr << "Uncaught exception...\n"; + } + return 1; +} diff --git a/project/jni/application/enigma/src/main.hh b/project/jni/application/enigma/src/main.hh new file mode 100644 index 000000000..ee715fd7b --- /dev/null +++ b/project/jni/application/enigma/src/main.hh @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ENIGMA_MAIN_HH +#define ENIGMA_MAIN_HH + +#define ENIGMACOMPATIBITLITY 1.01 +#define PREFFILENAME "enigmarc.xml" +#define RATINGSFILENAME "ratings.xml" + +#include "file.hh" +#include "PreferenceManager.hh" +#include "StateManager.hh" +#include "DOMErrorReporter.hh" +#include "DOMSchemaResolver.hh" +#include +#include +#include +#include +#include +#if _XERCES_VERSION >= 30000 +#include +#else +#include +#endif +#include + +namespace enigma +{ +/* -------------------- Application services -------------------- */ + + class ImageManager; // in resources.hh + class FontManager; // in resources.hh + + /** + * Main object for initialization and resource access. Even though this + * class is a Highlander, it is not implemented as a singleton. Instead a + * global variable "Application app" is provided for fast access to the + * unique instance. All major resources are directly accessible via public + * ivars. They should be read only. We do without set/get-methods to gain + * simple and fast access. + *

The Application provides a set of path and pathlist (GameFs) + * ivars. They are initialized OS and installation dependent. For consistency + * every file access to a relative path should be resolved via the appropriate + * path ivars. All paths are provided in local code pages.

+ */ + class Application { + public: + static Application *get_instance(); + + Application(); + + void init(int argc, char **argv); + void shutdown(); + std::string getVersionInfo(); + void setLanguage(std::string newLanguage); + /** + * Define a new user path. Files stored to user path use the new path + * at once and the path is added to the resource path for file lookups. + * But existing files on the previous user path are not copied or moved + * to the new location! This remains a manual task for the user. The new + * user path is added to the preferences. + * @param newPath local encoded new user path or empty string for default + */ + void setUserPath(std::string newPath); + + /** + * Define a new user image path. Images are stored to new path + * at once and the path is added to the resource path for images lookups. + * But existing images on the previous user images path are not copied or moved + * to the new location! This remains a manual task for the user. The new + * user images path is added to the preferences. + * @param newPath local encoded new path or empty string for default + */ + void setUserImagePath(std::string newPath); + + //---------- Variables ----------// + + std::vector args; // List of command line arguments + std::string progCallPath; // callpath with name of this program + + bool wizard_mode; + bool nograb; // Do not grab mouse cursor during game + std::string language; // Language to use + std::string defaultLanguage; + std::string argumentLanguage; + + /** + * pathlist for enigma release dependent system files. Files like + * XML schema definitions, Lua initialization code should be resolved + * via this pathlist. The pathlist is set to the enigma release dependent + * data directory preceeded by an optional commandline data path (used + * by developers to run enigma without installation). + */ + GameFS *systemFS; + + /** + * pathlist for resources that are either version independent or include + * versioning in their filename schema. Used for read access to files like + * sounds, fonts, images and versioned levelindices and levels. The pathlist + * is set to the enigma release dependent data directory preceeded by + * OS library resource paths, preceeded by the optional commandline data path, + * preceeded by the user data path. + */ + GameFS *resourceFS; // other r data sounds,..., inidices, levels + + /** + * the path to the user preferences file. The directory is OS dependent + * with priority of the "HOME" environment. The filename itself is + * standard, but can be overriden by command line option (main usage for + * developers to start enigma in test configuration). + */ + std::string prefPath; + + /** + * the path to the gettext l10n data. + */ + std::string l10nPath; + + /** + * the path to user data. The base directory for files like score, user + * levelindices, levels, etc.. The directory is OS dependent + * with priority of the "HOME" environment, but can be overriden by a + * user preference. This allows a user to store his data on USB sticks + * and shared partitions. + */ + std::string userPath; + + /** + * the path for large image data. Files like thumbs and screenshots + * that the user may not want to be stored on the userPath. The main + * reason for this separation of user data should be limited space on + * userPath. The userImagePath defaults to userPath, but can be overriden + * by a user preference. + */ + std::string userImagePath; + + /** + * the singleton instance of our preference manager. + */ + PreferenceManager * prefs; + + /** + * the singleton instance of our state manager. + */ + StateManager * state; + + /** + * A xerces transcoder for utf-8. + */ + XERCES_CPP_NAMESPACE_QUALIFIER XMLTranscoder *xmlUtf8Transcoder; + + /** + * The implementation of DOM Core. + */ + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation *domImplementationCore; + + /** + * The implementation of DOM Load and Save. + */ + XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationLS *domImplementationLS; + + /** + * A configured DOM parser for reusage. + */ +#if _XERCES_VERSION >= 30000 + XERCES_CPP_NAMESPACE_QUALIFIER DOMLSParser *domParser; +#else + XERCES_CPP_NAMESPACE_QUALIFIER DOMBuilder *domParser; +#endif + /** + * The error handler attached to the domParser + */ + DOMErrorReporter *domParserErrorHandler; + + /** + * The schema resolver attached to the domParser + */ + DOMSchemaResolver *domParserSchemaResolver; + + /** + * A configured DOM serializer for reusage. + */ +#if _XERCES_VERSION >= 30000 + XERCES_CPP_NAMESPACE_QUALIFIER DOMLSSerializer *domSer; +#else + XERCES_CPP_NAMESPACE_QUALIFIER DOMWriter *domSer; +#endif + /** + * The error handler attached to the domSer + */ + DOMErrorReporter *domSerErrorHandler; + bool errorInit; + bool isMakePreviews; + + private: + void initSysDatapaths(const std::string &prefFilename); + void initXerces(); + void initUserDatapaths(); + void updateMac1_00(); + std::string systemAppDataPath; // dir path to the apps data + std::string systemCmdDataPath; // commandline override of systemAppDataPath + std::string userStdPath; // standard user data path + std::string userStdPathMac1_00; // standard user data path as of Mac 1.00 + + void init_i18n(); + }; + +#define APP enigma::Application::get_instance() + + extern Application app; + + //---------- Logging ----------// + extern std::ostream Log; + + /** + * Flag to use in expensive asserts as first operand in a logical or + * statement to allow the assertion to be blocked in favour of speed. + * F.e. ASSERT(noAssert || long_lasting_check(), XLevelRuntime, ""); + */ + extern bool noAssert; + + //---------- Command line options ----------// + extern bool WizardMode; + extern bool Nograb; +} + +#endif diff --git a/project/jni/application/enigma/src/netgame.cpp b/project/jni/application/enigma/src/netgame.cpp new file mode 100644 index 000000000..02d4b2ee9 --- /dev/null +++ b/project/jni/application/enigma/src/netgame.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "errors.hh" +#include "client.hh" +#include "main.hh" +#include "netgame.hh" +#include "network.hh" +#include "options.hh" +#include "server.hh" + +#include "enet/enet.h" + +#include "SDL.h" +#include + +using namespace enigma; + +#include "client_internal.hh" +#include "server_internal.hh" + + +//====================================================================== +// SERVER +//====================================================================== + +void handle_client_packet (Buffer &b, int player_no) +{ + using namespace enigma_server; + Uint8 code; + while (b >> code) { + switch (code) { + case SVMSG_NOOP: + break; // no nothing + +// not yet used -- rewrite to index/proxy usage +// case SVMSG_LOADLEVEL: { +// Uint16 levelno; +// string levelpack; +// if (b >> levelno >> levelpack) { +// printf ("SV: Loading level %d from levelpack %s\n", int(levelno), +// levelpack.c_str()); +// server::Msg_SetLevelPack (levelpack); +// server::Msg_LoadLevel (levelno); +// } +// +// break; +// } + + case SVMSG_MOUSEFORCE: { + printf ("mouse force\n"); + float dx, dy; + if (b >> dx >> dy) { + printf ("-- yei!\n"); + server::Msg_MouseForce (ecl::V2(dx, dy)); + } + break; + } + + case SVMSG_ACTIVATEITEM: { + server::Msg_ActivateItem (); + break; + } + + default: + enigma::Log << "SV: received undefined packet: " << int(code) << "\n"; + } + } +} + +namespace +{ + Uint32 last_tick_time; +} + + +void server_loop (Peer *m_peer) +{ + printf ("SV: Entered server loop\n"); + server::InitNewGame(); + Buffer buf; + buf << Cl_LevelLoaded (); + m_peer->send_reliable (buf, 1); + + double dtime = 0; + while (!client::AbortGameP() && m_peer->is_connected()) { + last_tick_time=SDL_GetTicks(); + + + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type && e.key.keysym.sym == SDLK_ESCAPE) + goto done; + } + + try { + client::Tick (dtime); + server::Tick (dtime); + } + catch (XLevelRuntime& err) { + client::Msg_Error (string("Server Error: level runtime error:\n") + + err.what()); + server::Msg_Panic(true); + } + + Buffer buf; + int player_no; + while (m_peer->poll_message (buf, player_no)) { + printf ("SV: Received message from client %d\n", player_no); + handle_client_packet (buf, player_no); + } + + + int sleeptime = 10 - (SDL_GetTicks()-last_tick_time); + if (sleeptime >= 3) // only sleep if relatively idle + SDL_Delay(sleeptime); + dtime = (SDL_GetTicks()-last_tick_time)/1000.0; + if (fabs(1-dtime/0.01) < 0.2) { + // less than 20% deviation from desired frame time? + dtime = 0.01; + } + + if (dtime > 500.0) /* Time has done something strange, perhaps + run backwards */ + dtime = 0.0; + + } + + done: + return; +} + +void netgame::Start () +{ + + // ---------- Create network host ---------- + ENetHost *network_host; + ENetAddress network_address; + + network_address.host = ENET_HOST_ANY; + network_address.port = 12345; + + network_host = enet_host_create (&network_address, 1, 0, 0); + if (network_host == NULL) { + fprintf (stderr, + "SV: An error occurred while trying to create an ENet server host.\n"); + return; + } + + // ---------- Wait for client(s) ---------- + ENetEvent event; + Peer *m_peer = 0; + printf ("SV: Waiting for client...\n"); + + while (!m_peer) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type && e.key.keysym.sym == SDLK_ESCAPE) + return; + } + + while (enet_host_service (network_host, & event, 0) > 0) { + if (event.type == ENET_EVENT_TYPE_CONNECT) { + printf ("SV: Connected to client\n"); + m_peer = new Peer_Enet (network_host, event.peer, 2); + } + } + SDL_Delay (10); + } + + server_loop (m_peer); + + m_peer->disconnect(); + delete m_peer; +} + +//====================================================================== +// CLIENT +//====================================================================== + +namespace +{ + struct MovementCommand { + float time_stamp; + float force_x; + float force_y; + }; + + typedef std::list MovementList; + + MovementList movement_list; + + + Peer *server_peer; +} + +void handle_server_packet (Buffer &buf) +{ + if (buf.size() > 0) { + Uint8 code; + buf >> code; + switch (code) { + case CLMSG_LEVEL_LOADED: + printf ("cl_level_loaded\n"); + break; + + } + } + + Buffer obuf; + obuf << Uint8(enigma_server::SVMSG_LOADLEVEL); + obuf << Uint16(84); + obuf << string("Enigma"); + server_peer->send_reliable (obuf, 1); + printf ("CL: sending message %u\n", obuf.size()); +} + +void netgame::Join (std::string hostname, int port) +{ + printf ("CL: trying to join remote game\n"); + + + + // ---------- Create network host ---------- + ENetHost *m_network_host; + m_network_host = enet_host_create (NULL, + 1 /* only allow 1 outgoing connection */, + 57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */, + 14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */); + + if (m_network_host == NULL) { + fprintf (stderr, "CL: An error occurred while trying to create an ENet client host.\n"); + return; + } + + + // ---------- Connect to server ---------- + + ENetAddress sv_address; + ENetPeer *m_server; + + /* Connect to some.server.net:1234. */ + enet_address_set_host (&sv_address, hostname.c_str()); + sv_address.port = port; + + /* Initiate the connection, allocating the two channels 0 and 1. */ + int numchannels = 2; + m_server = enet_host_connect (m_network_host, &sv_address, numchannels); + + if (m_server == NULL) { + fprintf (stderr, + "CL: No available peers for initiating an ENet connection.\n"); + return; + } + + + server_peer = 0; + ENetEvent event; + if (enet_host_service (m_network_host, &event, 5000) > 0 && + event.type == ENET_EVENT_TYPE_CONNECT) + { + fprintf (stderr, "CL: Connection to some.server.net:12345 succeeded.\n"); + if (m_server != event.peer) + printf ("CL: peers differ!?!\n"); + server_peer = new Peer_Enet (m_network_host, m_server, 0); + } + else + return; + + + while (server_peer->is_connected()) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type && e.key.keysym.sym == SDLK_ESCAPE) + goto done; + else if (e.type ==SDL_MOUSEMOTION) { + Buffer buf; + float mouseforce = options::GetDouble("MouseSpeed"); + buf << Uint8 (enigma_server::SVMSG_MOUSEFORCE) + << float (e.motion.xrel * mouseforce) + << float (e.motion.yrel * mouseforce); + server_peer->send_reliable (buf,1); + } + } + + Buffer buf; + int peerno; + while (server_peer->poll_message (buf, peerno)) { + handle_server_packet (buf); + } + SDL_Delay (10); + } + + done: + server_peer->disconnect(); + delete server_peer; + server_peer = 0; + return; + +} diff --git a/project/jni/application/enigma/src/netgame.hh b/project/jni/application/enigma/src/netgame.hh new file mode 100644 index 000000000..35d1bf94e --- /dev/null +++ b/project/jni/application/enigma/src/netgame.hh @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NETGAME_HH_INCLUDED +#define NETGAME_HH_INCLUDED + +#include + +namespace netgame +{ + void Start (); + + void Join (std::string hostname, int port); +} + +#endif diff --git a/project/jni/application/enigma/src/network.cpp b/project/jni/application/enigma/src/network.cpp new file mode 100644 index 000000000..953d916c2 --- /dev/null +++ b/project/jni/application/enigma/src/network.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2002,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "network.hh" +#include "ecl_buffer.hh" + +#include "enet/enet.h" + +#include +#include +#include +#include + +#include "SDL_types.h" + + +using namespace std; +using namespace enigma; +using ecl::Buffer; + +/* -------------------- Peer_Enet implementation -------------------- */ + +Peer_Enet::Peer_Enet (ENetHost *host, ENetPeer *peer, int playerno) + : m_host (host), + m_peer (peer), + m_playerno (playerno), + m_connected (true) +{ +} + + +Peer_Enet::~Peer_Enet() +{ + disconnect(0); +} + + +void Peer_Enet::disconnect(int timeout = 1000) +{ + ENetEvent event; + + enet_peer_disconnect (m_peer, 0); + + while (enet_host_service (m_host, & event, timeout) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + enet_packet_destroy (event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("Disconnection succeeded.\n"); + return; + case ENET_EVENT_TYPE_NONE: + case ENET_EVENT_TYPE_CONNECT: + break; + } + } + + enet_peer_reset (m_peer); + m_connected = false; +} + +bool Peer_Enet::is_connected() const +{ + return m_connected; +} + +bool Peer_Enet::poll_message (Buffer &b, int &player_no) +{ + if (!m_connected) + return false; + + ENetEvent event; + if (enet_host_service (m_host, & event, 0) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: { + b.assign (reinterpret_cast (event.packet->data), + event.packet->dataLength); + player_no = m_playerno; + enet_packet_destroy (event.packet); + return true; + } + + case ENET_EVENT_TYPE_DISCONNECT: + m_connected = false; + event.peer -> data = NULL; + break; + + default: + break; + } + } + return false; +} + +void Peer_Enet::send_message (const Buffer &b, int channel) +{ + ENetPacket *packet = enet_packet_create (b.data(), b.size(), 0); + enet_peer_send (m_peer, channel, packet); +} + +void Peer_Enet::send_reliable (const Buffer &b, int channel) +{ + ENetPacket *packet = enet_packet_create (b.data(), b.size(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send (m_peer, channel, packet); +} + + + + +/* -------------------- Peer_Local implementation -------------------- */ + +bool Peer_Local::poll_message (Buffer &b, int &player_no) +{ + if (!m_queue.empty()) { + + } + return false; +} + +void Peer_Local::send_message (const Buffer &b, int channel) +{ +} + +void Peer_Local::send_reliable (const Buffer &b, int channel) +{ + send_message (b, channel); +} + diff --git a/project/jni/application/enigma/src/network.hh b/project/jni/application/enigma/src/network.hh new file mode 100644 index 000000000..74564a6ab --- /dev/null +++ b/project/jni/application/enigma/src/network.hh @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2002,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef NETWORK_HH_INCLUDED +#define NETWORK_HH_INCLUDED + +#include "ecl_fwd.hh" + +#include "SDL_types.h" +#include "enet/enet.h" +#include +#include + +namespace enigma +{ + class Peer { + public: + virtual ~Peer() {} + virtual void send_message (const ecl::Buffer &b, int channel) = 0; + virtual void send_reliable (const ecl::Buffer &b, int channel) = 0; + virtual bool poll_message (ecl::Buffer &b, int &player_no) = 0; + virtual void disconnect(int timeout=1000) = 0; + virtual bool is_connected() const = 0; + }; + + class Peer_Enet : public Peer { + public: + Peer_Enet (ENetHost *host, ENetPeer *peer, int playerno); + ~Peer_Enet(); + + private: + // Peer interface + virtual void send_message (const ecl::Buffer &b, int channel); + virtual void send_reliable (const ecl::Buffer &b, int channel); + virtual bool poll_message (ecl::Buffer &b, int &player_no); + virtual void disconnect(int timeout); + virtual bool is_connected() const; + + private: + // Variables + ENetHost *m_host; + ENetPeer *m_peer; + int m_playerno; + bool m_connected; + }; + + class Peer_Local : public Peer { + public: + virtual void send_message (const ecl::Buffer &b, int channel) ; + virtual void send_reliable (const ecl::Buffer &b, int channel); + virtual bool poll_message (ecl::Buffer &b, int &player_no); + private: + // Variables + std::list m_queue; + }; + + +} + +#endif diff --git a/project/jni/application/enigma/src/nls.cpp b/project/jni/application/enigma/src/nls.cpp new file mode 100644 index 000000000..b75f8ba2b --- /dev/null +++ b/project/jni/application/enigma/src/nls.cpp @@ -0,0 +1,43 @@ +#include "nls.hh" +#include "main.hh" +#include "ecl_system.hh" + +#include +#include +#include + +#include + +using namespace std; + +#if !defined (HAVE_SETENV) && defined (HAVE_PUTENV) +static char lang_env[256]; +#endif + +static void my_setenv (const std::string &var, const std::string &val) +{ +#if defined (HAVE_SETENV) + setenv(var.c_str(), val.c_str(), 1); +#elif defined (HAVE_PUTENV) + snprintf (lang_env, sizeof(lang_env), "%s=%s", var.c_str(), val.c_str()); + putenv(lang_env); +#endif +} + +void nls::SetMessageLocale (const std::string &language) +{ + if (language != "") + my_setenv ("LANG", language); + +#if defined(ENABLE_NLS) && defined(HAVE_LC_MESSAGES) + // Hack to fool libintl into changing the message locale more than + // once + setlocale (LC_MESSAGES, "C"); + setlocale (LC_MESSAGES, ""); //language.c_str()); +#endif + + std::string li = ecl::SysMessageLocaleName(); + enigma::Log << "locale name: " << li << endl; + enigma::Log << "language code: " << ecl::GetLanguageCode (li) << endl; +} + diff --git a/project/jni/application/enigma/src/nls.hh b/project/jni/application/enigma/src/nls.hh new file mode 100644 index 000000000..7bd8687e8 --- /dev/null +++ b/project/jni/application/enigma/src/nls.hh @@ -0,0 +1,22 @@ +#ifndef NLS_HH_INCLUDED +#define NLS_HH_INCLUDED +#include "config.h" + +#if defined(ENABLE_NLS) +# include +# define _(String) gettext(String) +# define gettext_noop(String) (String) +# define N_(String) gettext_noop(String) +#else // !defined(ENABLE_NLS) +# define _(String) (String) +# define N_(String) (String) +#endif + +#include + +namespace nls +{ + void SetMessageLocale (const std::string &language); +} + +#endif diff --git a/project/jni/application/enigma/src/objects.cpp b/project/jni/application/enigma/src/objects.cpp new file mode 100644 index 000000000..2f747c734 --- /dev/null +++ b/project/jni/application/enigma/src/objects.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "game.hh" +#include "sound.hh" +#include "world.hh" + +#include "ecl_util.hh" +#include "ecl_dict.hh" + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace enigma; +using namespace world; + +// remove comment from define below to switch on verbose messaging +// note: VERBOSE_MESSAGES is defined in multiple source files! +// #define VERBOSE_MESSAGES + + +/* -------------------- Helper routines -------------------- */ + +namespace +{ + // string_match accepts simple wildcards + // '?' means 'any character' + // '*' means '0 or more characters' + bool string_match(const char *str, const char *templ) { + while (true) { + char t = *templ++; + char s = *str++; + + if (t == s) { + if (!t) return true; + continue; + } + else { // mismatch + if (t == '?') continue; + if (t != '*') break; + + t = *templ++; + if (!t) return true; // '*' at EOS + + while (1) { + if (!s) break; + if (s == t) { + if (string_match(str, templ)) + return true; + } + s = *str++; + } + } + } + return false; + } +} + + +/* -------------------- Object implementation -------------------- */ + +Object::Object(const char *kind) { + set_attrib("kind", Value(kind)); +} + + +Value Object::on_message (const world::Message &m) +{ + return message (m.message, m.value); +} + +Value Object::message(const string& /*msg*/, const Value &/*val*/) +{ + return Value(); +} + +void Object::on_levelinit() { +} + + +const char * +Object::get_kind() const +{ + const Value *v = get_attrib("kind"); + ASSERT(v && v->get_type()==Value::STRING, XLevelRuntime, + "Object: attribute kind is not of type string (found in get_kind)"); + return v->get_string(); +} + +// check kind of object +// kind_templ may contain wildcards ( ? and * ) +bool Object::is_kind(const char *kind_templ) const { + return string_match(get_kind(), kind_templ); +} + +bool Object::is_kind(const string& kind_templ) const { + return string_match(get_kind(), kind_templ.c_str()); +} + +void Object::set_attrib(const string& key, const Value& val) { + attribs[key] = val;//.insert (key, val); +} + +const Value* Object::get_attrib(const string& key) const { + AttribMap::const_iterator i = attribs.find(key); + if (i == attribs.end()) + return 0; + else + return &i->second; +} + +bool Object::string_attrib(const string &name, string *val) const { + if (const Value *v = get_attrib(name)) { + if (v->get_type() != Value::NIL) { + const char *s = to_string(*v); + if (s != 0) { + *val = s; + return true; + } + } + } + return false; +} + +int Object::int_attrib(const string &name) const { + if (const Value *v = get_attrib(name)) + return to_int(*v); + return 0; +} + +bool Object::int_attrib(const string &name, int *val) const { + if (const Value *v = get_attrib(name)) { + *val = to_int(*v); + return true; + } + return false; +} + + +bool Object::double_attrib(const string &name, double *val) const { + if (const Value *v = get_attrib(name)) { + if (v->get_type() != Value::NIL) { + *val = to_double(*v); + return true; + } + } + return false; +} + +/* Send an impulse to position 'dest' into direction dir. If 'dest' + contains a stone, on_impulse() is called for that stone */ +void Object::send_impulse(const GridPos& dest, Direction dir) +{ + if (Stone *st = GetStone(dest)) { + Impulse impulse(this, dest, dir); + st->on_impulse(impulse); + } +} + +/* Like variant above, but the _result_ of the impulse is delayed. + */ + +void +Object::send_impulse(const GridPos& dest, Direction dir, double delay) +{ + if (Stone *st = GetStone(dest)) { + addDelayedImpulse(Impulse(this, dest, dir), delay, st); + } +} + +void Object::warning(const char *format, ...) const { + va_list arg_ptr; + + va_start(arg_ptr, format); + + fprintf(stderr, "%p non-grid-\"%s\": ", this, get_kind()); + vfprintf(stderr, format, arg_ptr); + fputc('\n', stderr); + + va_end(arg_ptr); +} + + +/* -------------------- GridObject implementation -------------------- */ + +display::Model *GridObject::set_anim (const std::string &mname) +{ + set_model (mname); + display::Model *m = get_model(); + m->set_callback(this); + return m; +} + +bool GridObject::sound_event (const char *name, double vol) +{ + return sound::EmitSoundEvent (name, get_pos().center(), getVolume(name, this, vol)); +} + +void GridObject::warning(const char *format, ...) const +{ + va_list arg_ptr; + const GridPos& position = get_pos(); + + va_start(arg_ptr, format); + + fprintf(stderr, "%p \"%s\" at %i/%i: ", this, get_kind(), position.x, position.y); + vfprintf(stderr, format, arg_ptr); + fputc('\n', stderr); + + va_end(arg_ptr); +} + diff --git a/project/jni/application/enigma/src/objects.hh b/project/jni/application/enigma/src/objects.hh new file mode 100644 index 000000000..5f02ff698 --- /dev/null +++ b/project/jni/application/enigma/src/objects.hh @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef OBJECTS_HH +#define OBJECTS_HH + +#include "objects_decl.hh" +#include "items.hh" +#include "floors.hh" +#include "stones.hh" +#include "actors.hh" + +namespace enigma +{ + using world::Item; + using world::Stone; + using world::Actor; + using world::Floor; + using world::Object; +} + +#endif diff --git a/project/jni/application/enigma/src/objects_decl.hh b/project/jni/application/enigma/src/objects_decl.hh new file mode 100644 index 000000000..3c8e63ae1 --- /dev/null +++ b/project/jni/application/enigma/src/objects_decl.hh @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef OBJECTS_DECL_HH +#define OBJECTS_DECL_HH + +#include "display.hh" +#include "ecl_alist.hh" + +namespace world +{ + using std::string; + using namespace enigma; + + +/* -------------------- Objects -------------------- */ + + /*! Object is the base class for all ``objects'' in the world. + The most important facilities this class provides are: + + (1) A way to clone() and dispose() objects. This is mainly used + in function MakeObject() to create new objects of a given + type. + + (2) A way to pass messages between unrelated objects via message(). + This allows us to send messages to objects from Lua and to + decouple objects types as much as possible. + + (3) A way to get and set attributes. These attributes are quite + similar to instance variables, but they can be easily modified + from Lua. This makes it possible to modify certain object + parameters (such as the text on a piece of paper or the color + of an oxyd stone) in level descriptions. + */ + class Object { + public: + Object() {} + Object(const char *kind); + virtual ~Object() {} + + /* ---------- Attributes ---------- */ + + typedef ecl::AssocList AttribMap; + + bool string_attrib (const string &name, string *val) const; + int int_attrib (const string &name) const; + bool int_attrib (const string &name, int *val) const; + bool double_attrib (const string &name, double *val) const; + + bool is_kind(const char *kind) const; + bool is_kind(const string& kind) const; + + const AttribMap &get_attribs() const { return attribs; } + + /* ---------- Helper routines ---------- */ + + void send_impulse(const GridPos& dest, Direction dir); + void send_impulse(const GridPos& dest, Direction dir, double delay); + + /* ---------- Object interface ---------- */ + + virtual const char *get_kind() const; + + virtual Value on_message (const Message &m); + virtual Value message(const string& msg, const Value &val); + virtual void set_attrib(const string& key, const Value &val); + virtual const Value* get_attrib(const string& key) const; + + virtual Object *clone()=0; + virtual void dispose()=0; + + virtual void on_levelinit(); + + virtual void warning(const char *format, ...) const; + + private: + AttribMap attribs; + }; + + +/* -------------------- GridObject -------------------- */ + + /*! GridObject is the base class for everything that can only be + placed on "The Grid", i.e., for floor tiles, items, and + stones. */ + + class GridObject : public Object, public display::ModelCallback { + public: + GridObject() {} + GridObject(const char * kind) : Object(kind) {} + + void creation(GridPos p) { + pos = p; + on_creation (p); + } + void removal(GridPos p) { on_removal(p); } + + // GridObject interface + virtual void on_laserhit (Direction) {} + virtual void actor_enter (Actor *) {} + virtual void actor_leave (Actor *) {} + + GridPos get_pos() const { return pos; } + + void warning(const char *format, ...) const; + + // Helper functions + bool sound_event (const char *name, double vol = 1.0); + display::Model *set_anim (const std::string &mname); + + protected: + // GridObject interface + virtual void set_model (const std::string &mname) = 0; + virtual display::Model *get_model () = 0; + virtual void kill_model (GridPos p) = 0; + + virtual void init_model() + { set_model(get_kind()); } + + virtual void on_creation (GridPos) + { init_model(); } + + virtual void on_removal (GridPos p) + { kill_model (p); } + + private: + // ModelCallback interface. + void animcb() {} + + + // Variables + GridPos pos; + }; +} + + +#define CLONEOBJ(TYPE) \ + TYPE* clone() { return new TYPE(*this); } \ + void dispose() { delete this; } + +#define CLONEACTOR(TYPE) \ + TYPE* clone() { TYPE *o=new TYPE(*this); o->init(); return o; } \ + void dispose() { delete this; } + + +// #define SINGLETONOBJ(TYPE) \ +// TYPE* clone() { return this; } \ +// void dispose() {} + + +#define INSTANCELISTOBJ(TYPE) \ + typedef std::vector InstanceList; \ + static InstanceList instances; \ + TYPE *clone() { TYPE *o = new TYPE(*this); instances.push_back(o); return o;} \ + void dispose() { \ + instances.erase(find(instances.begin(), instances.end(), this)); \ + delete this; \ + } + +#endif diff --git a/project/jni/application/enigma/src/options.cpp b/project/jni/application/enigma/src/options.cpp new file mode 100644 index 000000000..2c10bf91c --- /dev/null +++ b/project/jni/application/enigma/src/options.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2002,2003,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "errors.hh" +#include "lua.hh" +#include "main.hh" +#include "options.hh" +#include "PreferenceManager.hh" +#include "sound.hh" +#include "ecl_system.hh" + +#include +#include +#include +#include + +#ifndef CXXLUA +extern "C" { +#include +#include +} +#else +#include +#include +#endif + +#ifdef __MINGW32__ +# include +#endif + +using namespace enigma; + + +/* -------------------- LevelStatus implementation -------------------- */ + +namespace enigma_options +{ + LevelStatus::LevelStatus(int easy, int hard, int finished_, int solved_rev) + : time_easy (easy), + time_hard (hard), + finished (finished_), + solved_revision (solved_rev) { + } + + bool LevelStatus::operator == (const LevelStatus& other) const { + return (time_easy == other.time_easy && + time_hard == other.time_hard && + finished == other.finished && + solved_revision == other.solved_revision); + } +} + +/* -------------------- Variables -------------------- */ + +namespace enigma_options +{ + bool LevelStatusChanged = false; +// bool MustRestart = false; +// bool MustRestartLevel = false; +} + + +/* -------------------- Functions -------------------- */ + +bool options::HasOption (const char *name, std::string &value) { + bool hasOption; + const char * result; + lua_State *L = lua::GlobalState(); + lua_getglobal (L, "options"); + lua_pushstring (L, name); + lua_rawget (L, -2); + result = lua_tostring (L, -1); + if (result != NULL) { + hasOption = true; + value = result; + } else { + hasOption = false; + } + lua_pop (L, 2); + return hasOption; +} + +void options::SetOption (const char *name, double value) +{ + app.prefs->setProperty(name, value); +} + +void options::SetOption (const char *name, const std::string &value) +{ + app.prefs->setProperty(name, value); +} + +void options::GetOption (const char *name, double &value) +{ + app.prefs->getProperty(name, value); +} + +void options::GetOption (const char *name, std::string &value) { + app.prefs->getProperty(name, value); +} + +bool options::GetBool (const char *name) { + double val = 0; + GetOption (name, val); + return val != 0; +} + +double options::GetDouble (const char *name) { + double val = 0; + GetOption (name, val); + return val; +} + +int options::GetInt (const char *name) { + double val = 0; + GetOption (name, val); + return static_cast(val); +} + + +double options::SetMouseSpeed (double speed) +{ + double oldspeed = GetMouseSpeed(); + double newspeed = ecl::Clamp(speed, MIN_MouseSpeed, MAX_MouseSpeed); + SetOption("MouseSpeed", newspeed); + return oldspeed; +} + +double options::GetMouseSpeed () +{ + return GetDouble ("MouseSpeed"); +} + +void options::UpdateVolume() +{ + sound::SetSoundVolume (GetDouble("SoundVolume")); + sound::SetMusicVolume (GetDouble("MusicVolume")); +} + +std::string options::GetString (const char *name) +{ + std::string val; + GetOption (name, val); + return val; +} + +static void UpdateLevelStatus(const std::string &levelname, + const options::LevelStatus &stat) +{ + lua_State *L = lua::GlobalState(); + + int oldtop = lua_gettop(L); + + lua_getglobal(L, "stats"); + lua_pushstring (L, levelname.c_str()); + lua_newtable (L); + lua_pushnumber (L, stat.time_easy); lua_rawseti (L, -2, 1); + lua_pushnumber (L, stat.time_hard); lua_rawseti (L, -2, 2); + lua_pushnumber (L, stat.finished); lua_rawseti (L, -2, 3); + lua_pushnumber (L, stat.solved_revision); lua_rawseti (L, -2, 4); + lua_rawset (L, -3); // stats[levename] = + + lua_settop(L, oldtop); +} + +bool options::GetLevelStatus (const std::string &levelname, + LevelStatus &stat) +{ + lua_State *L = lua::GlobalState(); + + int oldtop = lua_gettop(L); + + lua_getglobal(L, "stats"); + lua_pushstring (L, levelname.c_str()); + lua_rawget (L, -2); + if (!lua_istable(L, -1) || luaL_getn(L, -1)!=4) { + lua_settop(L, oldtop); + return false; + } + + lua_rawgeti (L, -1, 1); stat.time_easy = (int) lua_tonumber (L, -1); lua_pop(L, 1); + lua_rawgeti (L, -1, 2); stat.time_hard = (int) lua_tonumber (L, -1); lua_pop(L, 1); + lua_rawgeti (L, -1, 3); stat.finished = (int) lua_tonumber (L, -1); lua_pop(L, 1); + lua_rawgeti (L, -1, 4); stat.solved_revision = (int) lua_tonumber (L, -1); + + lua_settop(L, oldtop); + + if (stat.solved_revision == 0 && stat.finished>0) { + // revision 0 is an error (lowest allowed revision number is 1) + printf("Auto-increasing revision number of '%s'\n", levelname.c_str()); + stat.solved_revision = 1; + UpdateLevelStatus(levelname, stat); // update this correction + } + + return true; +} + + +/* Determine name of the user's personal configuration file. */ +static std::string +Personal_ConfigurationFileName() +{ + std::string fname = "enigmarc.lua"; + + if (getenv ("HOME") != 0) + fname = ecl::ExpandPath ("~/.enigmarc"); + + return fname; +} + +static std::string +System_ConfigurationFileName() +{ + return app.systemFS->findFile ("enigma_conf.lua"); +} + +#ifdef __MINGW32__ + +static std::string +Windows_ConfigurationFileName() +{ + std::string result = ecl::ApplicationDataPath(); + + if (!result.empty()) + return result + "/enigmarc.lua"; + else + return Personal_ConfigurationFileName(); + +} + +#endif + + +static bool +load_from_file (const std::string &fname) +{ + lua::Error errcode = lua::DoAbsoluteFile(lua::GlobalState(), fname.c_str()); + bool success = (errcode == 0 || errcode == lua::ERRFILE); + if (!success) { + enigma::Log << "error in file `" << fname <<"'\n"; + } + return success; +} + +static bool +load_options (const std::string &fname) +{ + bool success1 = load_from_file (fname); + bool success2 = load_from_file (fname + "2"); + return success1 && success2; +} + + +bool options::Load () +{ + std::string fname; + bool success = true; + + fname = System_ConfigurationFileName(); + success &= load_options (fname); + +#ifdef __MINGW32__ + fname = Windows_ConfigurationFileName(); + success &= load_options (fname); +#endif + + // personal config (in $HOME or current directory) overides all! + fname = Personal_ConfigurationFileName(); + success &= load_options (fname); + + LevelStatusChanged = false; + + return success; +} + diff --git a/project/jni/application/enigma/src/options.hh b/project/jni/application/enigma/src/options.hh new file mode 100644 index 000000000..14967ee74 --- /dev/null +++ b/project/jni/application/enigma/src/options.hh @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2002, 2003 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef OPTIONS_HH +#define OPTIONS_HH + +/* +** Options are ``persistent'' settings that can saved to a Lua file or +** loaded from it. This file defined the C++ interface to options; +** loading and saving are handled by Lua functions defined in +** `startup.lua'. +*/ + +#include + +namespace enigma_options +{ + using namespace enigma; + +/* -------------------- LevelStatus -------------------- */ + + /*! + * This class stores information about the user's progress with a + * particular level. This is the information that is stored in + * the .enigmarc files. See for options.cc and startup.lua for + * the relevant I/O code. + */ + struct LevelStatus { + LevelStatus(int easy=-1, int hard=-1, int fin=0, int solved_rev = 0); + bool operator == (const LevelStatus& other) const; + + // Variables. + + int time_easy; // user's best time in seconds (-1: NA) + int time_hard; // user's best time in seconds (-1: NA) + int finished; // Level solved? 0 = no,1=easy,2=hard,3=easy&hard + int solved_revision; // Revision # that was solved + }; + +/* -------------------- Constants -------------------- */ + + const int MIN_MouseSpeed = 1; + const int MAX_MouseSpeed = 15; + +/* -------------------- Variables -------------------- */ + + /*! An option was changed that will not take effect until Enigma is + restarted. */ +// extern bool MustRestart; + + /*! An option was changed that makes it necessary to restart the + current level (e.g. Difficulty changed during the game). When + 'MustRestartLevel' is set to true, the current level will + restart w/o any further questions. */ +// extern bool MustRestartLevel; + +/* -------------------- Functions -------------------- */ + + bool HasOption (const char *name, std::string &value); + void SetOption (const char *name, double value); + void SetOption (const char *name, const std::string &value); + void GetOption (const char *name, double &value); + void GetOption (const char *name, std::string &value); + + bool GetBool (const char *name); + double GetDouble (const char *name); + int GetInt (const char *name); + std::string GetString (const char *name); + + + double SetMouseSpeed (double speed); + double GetMouseSpeed (); + + + /*! Get the status of a particular level. + Returns false if no record for this level exists. */ + bool GetLevelStatus (const std::string &levelname, + LevelStatus &stat); + + + /*! Set the sound and music volume to the values in SoundVolume + and MusicVolume. */ + void UpdateVolume(); + + /*! Try to load the user's configuration file. Returns true if + successful. */ + bool Load (); +} + +namespace enigma +{ + namespace options = enigma_options; +} +#endif diff --git a/project/jni/application/enigma/src/ox_extra.cpp b/project/jni/application/enigma/src/ox_extra.cpp new file mode 100644 index 000000000..bf6aac606 --- /dev/null +++ b/project/jni/application/enigma/src/ox_extra.cpp @@ -0,0 +1,385 @@ +/* + * Copyright (C) May 2003 by Ralf Westram + * Copyright (C) 2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if defined(PLAIN_SPEC_ONLY) +// This is used from lib-src/oxydlib/EnigmaNames.cpp. +// +// defining PLAIN_SPEC_ONLY only shows xxx_floor_map, xxx_stone_map and xxx_item_map +// Note: the xxx_item_map changes it's type (ItemID -> const char *) + +#define ITEMSPEC(i) #i +#define ITEMMAPTYPE const char * + +#else +// Standard enigma section : + +#define ITEMSPEC(i) i +#define ITEMMAPTYPE world::ItemID + +#include "oxyd_internal.hh" +using namespace world; + +#endif // PLAIN_SPEC_ONLY +// only tables following! + +const char *oxyd::oxydextra_floor_map[256] = { + "fl-abyss", // OxydExtra floor 0x00 + "fl-gray", // OxydExtra floor 0x01 + "fl-metal", // OxydExtra floor 0x02 + UNUSED, // OxydExtra floor 0x03 + UNUSED, // OxydExtra floor 0x04 + UNUSED, // OxydExtra floor 0x05 + UNUSED, // OxydExtra floor 0x06 + "fl-light", // OxydExtra floor 0x07 + "fl-rough-red", // OxydExtra floor 0x08 + "fl-rough-blue", // OxydExtra floor 0x09 + "fl-rough-blue", // OxydExtra floor 0x0a + "fl-rough-red", // OxydExtra floor 0x0b + "fl-inverse", // OxydExtra floor 0x0c + "fl-black", // OxydExtra floor 0x0d + UNUSED, // OxydExtra floor 0x0e + UNUSED, // OxydExtra floor 0x0f + UNUSED, // OxydExtra floor 0x10 + UNUSED, // OxydExtra floor 0x11 + UNUSED, // OxydExtra floor 0x12 + "fl-water", // OxydExtra floor 0x13 + UNUSED, // OxydExtra floor 0x14 + UNUSED, // OxydExtra floor 0x15 + UNUSED, // OxydExtra floor 0x16 + "fl-ice", // OxydExtra floor 0x17 + UNUSED, // OxydExtra floor 0x18 + UNUSED, // OxydExtra floor 0x19 + UNUSED, // OxydExtra floor 0x1a + UNUSED, // OxydExtra floor 0x1b + "fl-space", // OxydExtra floor 0x1c + UNUSED, // OxydExtra floor 0x1d + UNUSED, // OxydExtra floor 0x1e + UNUSED, // OxydExtra floor 0x1f + UNUSED, // OxydExtra floor 0x20 + UNUSED, // OxydExtra floor 0x21 + UNUSED, // OxydExtra floor 0x22 + UNUSED, // OxydExtra floor 0x23 + UNUSED, // OxydExtra floor 0x24 + "fl-gradient3", // OxydExtra floor 0x25 + "fl-gradient4", // OxydExtra floor 0x26 + UNUSED, // OxydExtra floor 0x27 + UNUSED, // OxydExtra floor 0x28 + UNUSED, // OxydExtra floor 0x29 + UNUSED, // OxydExtra floor 0x2a + UNUSED, // OxydExtra floor 0x2b + UNUSED, // OxydExtra floor 0x2c + UNUSED, // OxydExtra floor 0x2d + UNUSED, // OxydExtra floor 0x2e + UNUSED, // OxydExtra floor 0x2f + UNUSED, // OxydExtra floor 0x30 + "fl-gradient15", // OxydExtra floor 0x31 + "fl-gradient16", // OxydExtra floor 0x32 + UNUSED, // OxydExtra floor 0x33 + "fl-rough", // OxydExtra floor 0x34 + UNUSED, // OxydExtra floor 0x35 + UNUSED, // OxydExtra floor 0x36 + UNUSED, // OxydExtra floor 0x37 + "fl-mortar", // OxydExtra floor 0x38 + UNUSED, // OxydExtra floor 0x39 + UNUSED, // OxydExtra floor 0x3a + UNUSED, // OxydExtra floor 0x3b + "fl-samba", // OxydExtra floor 0x3c + UNUSED, // OxydExtra floor 0x3d + UNUSED, // OxydExtra floor 0x3e + UNUSED, // OxydExtra floor 0x3f + UNUSED, // OxydExtra floor 0x40 + "fl-samba", // OxydExtra floor 0x41 + UNUSED, // OxydExtra floor 0x42 + "fl-bumps", // OxydExtra floor 0x43 + UNUSED, // OxydExtra floor 0x44 + UNUSED, // OxydExtra floor 0x45 + UNUSED, // OxydExtra floor 0x46 + "fl-bumps", // OxydExtra floor 0x47 + "fl-rock", // OxydExtra floor 0x48 + UNUSED, // OxydExtra floor 0x49 + UNUSED, // OxydExtra floor 0x4a + UNUSED, // OxydExtra floor 0x4b + "fl-bridge", // OxydExtra floor 0x4c + UNUSED, // OxydExtra floor 0x4d + UNUSED, // OxydExtra floor 0x4e + UNUSED, // OxydExtra floor 0x4f + UNUSED, // OxydExtra floor 0x50 + UNUSED, // OxydExtra floor 0x51 + UNUSED, // OxydExtra floor 0x52 + UNUSED, // OxydExtra floor 0x53 + "fl-gray", // OxydExtra floor 0x54 + "fl-plank", // OxydExtra floor 0x55 + UNUSED, // OxydExtra floor 0x56 + UNUSED, // OxydExtra floor 0x57 + UNUSED, // OxydExtra floor 0x58 + "fl-trigger", // OxydExtra floor 0x59 + UNUSED, // OxydExtra floor 0x5a + "fl-gravel", // OxydExtra floor 0x5b + // codes >= 0x5c are unused +}; + +const char *oxyd::oxydextra_stone_map[256] = { + 0, // no stone + 0, 0, 0, 0, 0, 0, 0, 0, // oxyd stones + 0, 0, 0, 0, 0, 0, 0, 0, // oxyd stones + "st-fakeoxyd", // OxydExtra stone 0x11 + "st-plain", // OxydExtra stone 0x12 + "st-rock4", // OxydExtra stone 0x13 + UNUSED, // OxydExtra stone 0x14 + "st-rock5", // OxydExtra stone 0x15 + "st-rock7", // OxydExtra stone 0x16 + UNUSED, // OxydExtra stone 0x17 + "st-grate1", // OxydExtra stone 0x18 + UNUSED, // OxydExtra stone 0x19 + UNUSED, // OxydExtra stone 0x1a + UNUSED, // OxydExtra stone 0x1b + "st-marble", // OxydExtra stone 0x1c + UNUSED, // OxydExtra stone 0x1d + UNUSED, // OxydExtra stone 0x1e + "st-rock5", // OxydExtra stone 0x1f + "st-stone2", // OxydExtra stone 0x20 + "st-rock7", // OxydExtra stone 0x21 + "st-glass1", // OxydExtra stone 0x22 + UNUSED, // OxydExtra stone 0x23 + "st-plain_break", // OxydExtra stone 0x24 + "st-plain_hole", // OxydExtra stone 0x25 + "st-plain_move", // OxydExtra stone 0x26 + "st-wood", // OxydExtra stone 0x27 + "st-switch", // OxydExtra stone 0x28 + UNUSED, // OxydExtra stone 0x29 + UNUSED, // OxydExtra stone 0x2a + UNUSED, // OxydExtra stone 0x2b + UNUSED, // OxydExtra stone 0x2c + "st-floppy", // OxydExtra stone 0x2d + UNUSED, // OxydExtra stone 0x2e + "st-death", // OxydExtra stone 0x2f + "st-death_invisible", // OxydExtra stone 0x30 + "st-oneway_black-w", // OxydExtra stone 0x31 + UNUSED, // OxydExtra stone 0x32 + "st-oneway_black-n", // OxydExtra stone 0x33 + "st-oneway_black-s", // OxydExtra stone 0x34 + UNUSED, // OxydExtra stone 0x35 + UNUSED, // OxydExtra stone 0x36 + UNUSED, // OxydExtra stone 0x37 + UNUSED, // OxydExtra stone 0x38 + UNUSED, // OxydExtra stone 0x39 + UNUSED, // OxydExtra stone 0x3a + UNUSED, // OxydExtra stone 0x3b + "st-actorimpulse", // OxydExtra stone 0x3c + UNUSED, // OxydExtra stone 0x3d + "st-laser-1", // OxydExtra stone 0x3e The laser-names are fake names! + "st-laser-2", // OxydExtra stone 0x3f Direction and state are generated by Enigma. + "st-laser-3", // OxydExtra stone 0x40 + UNUSED, // OxydExtra stone 0x41 + UNUSED, // OxydExtra stone 0x42 + "st-mirror-p-", // OxydExtra stone 0x43 + "st-mirror-p\\", // OxydExtra stone 0x44 + UNUSED, // OxydExtra stone 0x45 + "st-mirror-p/", // OxydExtra stone 0x46 + UNUSED, // OxydExtra stone 0x47 + UNUSED, // OxydExtra stone 0x48 + UNUSED, // OxydExtra stone 0x49 + UNUSED, // OxydExtra stone 0x4a + "st-mirror-p-t", // OxydExtra stone 0x4b + UNUSED, // OxydExtra stone 0x4c + "st-mirror-3>", // OxydExtra stone 0x4d + "st-mirror-3^", // OxydExtra stone 0x4e + "st-mirror-3<", // OxydExtra stone 0x4f + "st-mirror-3v", // OxydExtra stone 0x50 + UNUSED, // OxydExtra stone 0x51 + UNUSED, // OxydExtra stone 0x52 + UNUSED, // OxydExtra stone 0x53 + UNUSED, // OxydExtra stone 0x54 + UNUSED, // OxydExtra stone 0x55 + UNUSED, // OxydExtra stone 0x56 + UNUSED, // OxydExtra stone 0x57 + UNUSED, // OxydExtra stone 0x58 + UNUSED, // OxydExtra stone 0x59 + UNUSED, // OxydExtra stone 0x5a + UNUSED, // OxydExtra stone 0x5b + UNUSED, // OxydExtra stone 0x5c + "st-stone_break", // OxydExtra stone 0x5d + UNUSED, // OxydExtra stone 0x5e + UNUSED, // OxydExtra stone 0x5f + UNUSED, // OxydExtra stone 0x60 + UNUSED, // OxydExtra stone 0x61 + UNUSED, // OxydExtra stone 0x62 + UNUSED, // OxydExtra stone 0x63 + "st-coinslot", // OxydExtra stone 0x64 + "st-thief", // OxydExtra stone 0x65 + "st-shogun-s", // OxydExtra stone 0x66 + UNUSED, // OxydExtra stone 0x67 + UNUSED, // OxydExtra stone 0x68 + UNUSED, // OxydExtra stone 0x69 + UNUSED, // OxydExtra stone 0x6a + UNUSED, // OxydExtra stone 0x6b + UNUSED, // OxydExtra stone 0x6c + "st-stoneimpulse", // OxydExtra stone 0x6d + UNUSED, // OxydExtra stone 0x6e + UNUSED, // OxydExtra stone 0x6f + UNUSED, // OxydExtra stone 0x70 + UNUSED, // OxydExtra stone 0x71 + UNUSED, // OxydExtra stone 0x72 + "st-door-h", // OxydExtra stone 0x73 + "st-door-v", // OxydExtra stone 0x74 + UNUSED, // OxydExtra stone 0x75 + "st-invisible", // OxydExtra stone 0x76 + UNUSED, // OxydExtra stone 0x77 + UNUSED, // OxydExtra stone 0x78 + UNUSED, // OxydExtra stone 0x79 + UNUSED, // OxydExtra stone 0x7a + UNUSED, // OxydExtra stone 0x7b + UNUSED, // OxydExtra stone 0x7c + UNUSED, // OxydExtra stone 0x7d + UNUSED, // OxydExtra stone 0x7e + UNUSED, // OxydExtra stone 0x7f + UNUSED, // OxydExtra stone 0x80 + UNUSED, // OxydExtra stone 0x81 + UNUSED, // OxydExtra stone 0x82 + UNUSED, // OxydExtra stone 0x83 + UNUSED, // OxydExtra stone 0x84 + UNUSED, // OxydExtra stone 0x85 + UNUSED, // OxydExtra stone 0x86 + UNUSED, // OxydExtra stone 0x87 + UNUSED, // OxydExtra stone 0x88 + UNUSED, // OxydExtra stone 0x89 + "st-blue-sand", // OxydExtra stone 0x8a + "st-bluegray", // OxydExtra stone 0x8b + UNUSED, // OxydExtra stone 0x8c + UNUSED, // OxydExtra stone 0x8d + UNUSED, // OxydExtra stone 0x8e + UNUSED, // OxydExtra stone 0x8f + UNUSED, // OxydExtra stone 0x90 + UNUSED, // OxydExtra stone 0x91 + UNUSED, // OxydExtra stone 0x92 + UNUSED, // OxydExtra stone 0x93 + UNUSED, // OxydExtra stone 0x94 + UNUSED, // OxydExtra stone 0x95 + UNUSED, // OxydExtra stone 0x96 + UNUSED, // OxydExtra stone 0x97 + UNUSED, // OxydExtra stone 0x98 + UNUSED, // OxydExtra stone 0x99 + UNUSED, // OxydExtra stone 0x9a + UNUSED, // OxydExtra stone 0x9b + UNUSED, // OxydExtra stone 0x9c + UNUSED, // OxydExtra stone 0x9d + "st-turnstile", // OxydExtra stone 0x9e + "st-turnstile-n", // OxydExtra stone 0x9f + "st-turnstile-s", // OxydExtra stone 0xa0 + "st-turnstile-w", // OxydExtra stone 0xa1 + "st-turnstile-e", // OxydExtra stone 0xa2 + UNUSED, // OxydExtra stone 0xa3 + "st-fakeoxyd", // OxydExtra stone 0xa4 + UNUSED, // OxydExtra stone 0xa5 + UNUSED, // OxydExtra stone 0xa6 + UNUSED, // OxydExtra stone 0xa7 + UNUSED, // OxydExtra stone 0xa8 + UNUSED, // OxydExtra stone 0xa9 + UNUSED, // OxydExtra stone 0xaa + UNUSED, // OxydExtra stone 0xab + UNUSED, // OxydExtra stone 0xac + UNUSED, // OxydExtra stone 0xad + UNUSED, // OxydExtra stone 0xae + UNUSED, // OxydExtra stone 0xaf + UNUSED, // OxydExtra stone 0xb0 + UNUSED, // OxydExtra stone 0xb1 + UNUSED, // OxydExtra stone 0xb2 + UNUSED, // OxydExtra stone 0xb3 + UNUSED, // OxydExtra stone 0xb4 + UNUSED, // OxydExtra stone 0xb5 + UNUSED, // OxydExtra stone 0xb6 + UNUSED, // OxydExtra stone 0xb7 + UNUSED, // OxydExtra stone 0xb8 + UNUSED, // OxydExtra stone 0xb9 + UNUSED, // OxydExtra stone 0xba + "st-stoneimpulse-hollow", // OxydExtra stone 0xbb + // codes >= 0xbc are unused +}; + +ITEMMAPTYPE oxyd::oxydextra_item_map[256] = { + ITEMSPEC(it_none), // OxydExtra item 0x00 + ITEMSPEC(it_extralife), // OxydExtra item 0x01 + ITEMSPEC(it_document), // OxydExtra item 0x02 + ITEMSPEC(it_document), // OxydExtra item 0x03 + ITEMSPEC(it_hammer), // OxydExtra item 0x04 + ITEMSPEC(it_coffee), // OxydExtra item 0x05 + ITEMSPEC(it_cherry), // OxydExtra item 0x06 + ITEMSPEC(it_umbrella), // OxydExtra item 0x07 + ITEMSPEC(it_MISSING), // OxydExtra item 0x08 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x09 + ITEMSPEC(it_dynamite), // OxydExtra item 0x0a + ITEMSPEC(it_UNUSED), // OxydExtra item 0x0b + ITEMSPEC(it_UNUSED), // OxydExtra item 0x0c + ITEMSPEC(it_crack0), // OxydExtra item 0x0d + ITEMSPEC(it_crack1), // OxydExtra item 0x0e + ITEMSPEC(it_crack2), // OxydExtra item 0x0f + ITEMSPEC(it_crack3), // OxydExtra item 0x10 + ITEMSPEC(it_coin1), // OxydExtra item 0x11 + ITEMSPEC(it_coin2), // OxydExtra item 0x12 + ITEMSPEC(it_coin4), // OxydExtra item 0x13 + ITEMSPEC(it_MISSING), // OxydExtra item 0x14 + ITEMSPEC(it_MISSING), // OxydExtra item 0x15 + ITEMSPEC(it_key_a), // OxydExtra item 0x16 + ITEMSPEC(it_floppy), // OxydExtra item 0x17 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x18 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x19 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1a + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1b + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1c + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1d + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1e + ITEMSPEC(it_UNUSED), // OxydExtra item 0x1f + ITEMSPEC(it_UNUSED), // OxydExtra item 0x20 + ITEMSPEC(it_spade), // OxydExtra item 0x21 + ITEMSPEC(it_surprise), // OxydExtra item 0x22 + ITEMSPEC(it_pin), // OxydExtra item 0x23 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x24 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x25 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x26 + ITEMSPEC(it_bag), // OxydExtra item 0x27 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x28 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x29 + ITEMSPEC(it_sensor), // OxydExtra item 0x2a + ITEMSPEC(it_shogun_s), // OxydExtra item 0x2b + ITEMSPEC(it_UNUSED), // OxydExtra item 0x2c + ITEMSPEC(it_vortex_open), // OxydExtra item 0x2d + ITEMSPEC(it_UNUSED), // OxydExtra item 0x2e + ITEMSPEC(it_wormhole_on), // OxydExtra item 0x2f + ITEMSPEC(it_hill), // OxydExtra item 0x30 + ITEMSPEC(it_tinyhill), // OxydExtra item 0x31 + ITEMSPEC(it_hollow), // OxydExtra item 0x32 + ITEMSPEC(it_tinyhollow), // OxydExtra item 0x33 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x34 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x35 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x36 + ITEMSPEC(it_bridge_oxyd), // OxydExtra item 0x37 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x38 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x39 + ITEMSPEC(it_UNUSED), // OxydExtra item 0x3a + ITEMSPEC(it_UNUSED), // OxydExtra item 0x3b + ITEMSPEC(it_MISSING), // OxydExtra item 0x3c + ITEMSPEC(it_UNUSED), // OxydExtra item 0x3d + ITEMSPEC(it_UNUSED), // OxydExtra item 0x3e + ITEMSPEC(it_UNUSED), // OxydExtra item 0x3f + ITEMSPEC(it_trigger), // OxydExtra item 0x40 + ITEMSPEC(it_brush), // OxydExtra item 0x41 + ITEMSPEC(it_MISSING), // OxydExtra item 0x42 + // codes >= 0x43 are unused +}; diff --git a/project/jni/application/enigma/src/ox_magnum.cpp b/project/jni/application/enigma/src/ox_magnum.cpp new file mode 100644 index 000000000..2153b0bdc --- /dev/null +++ b/project/jni/application/enigma/src/ox_magnum.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) May 2003 by Ralf Westram + * Copyright (C) 2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if defined(PLAIN_SPEC_ONLY) +// This is used from lib-src/oxydlib/EnigmaNames.cpp. +// +// defining PLAIN_SPEC_ONLY only shows xxx_floor_map, xxx_stone_map and xxx_item_map +// Note: the xxx_item_map changes it's type (ItemID -> const char *) + +#define ITEMSPEC(i) #i +#define ITEMMAPTYPE const char * + +#else +// Standard enigma section : + +#define ITEMSPEC(i) i +#define ITEMMAPTYPE world::ItemID + +#include "oxyd_internal.hh" +using namespace world; + +#endif // PLAIN_SPEC_ONLY +// only tables following! + +const char *oxyd::oxydmag_floor_map[256] = { + "fl-abyss", // OxydMagnum floor 0x00 + "fl-gray", // OxydMagnum floor 0x01 (common was 'fl-gray') + "fl-metal", // OxydMagnum floor 0x02 (This should be "fl-stwood" for #112) + "fl-metal3", // OxydMagnum floor 0x03 + "fl-metal4", // OxydMagnum floor 0x04 + 0, // OxydMagnum floor 0x05 (common was 'fl-metal')(level 121 ?) + "fl-metal5", // OxydMagnum floor 0x06 + "fl-normal", // OxydMagnum floor 0x07 (common was 'fl-metal') + "fl-tigris", // OxydMagnum floor 0x08 (common was 'fl-rough') + "fl-tigris", // OxydMagnum floor 0x09 (common was 'fl-normal') + "fl-tigris", // OxydMagnum floor 0x0a (common was 'fl-normal') + "fl-tigris", // OxydMagnum floor 0x0b (common was 'fl-stone') + "fl-inverse", // OxydMagnum floor 0x0c (common was 'fl-inverse') + "fl-acblack", // OxydMagnum floor 0x0d + "fl-acwhite", // OxydMagnum floor 0x0e + "fl-swamp", // OxydMagnum floor 0x0f + UNUSED, // OxydMagnum floor 0x10 + UNUSED, // OxydMagnum floor 0x11 + UNUSED, // OxydMagnum floor 0x12 + "fl-water", // OxydMagnum floor 0x13 + UNUSED, // OxydMagnum floor 0x14 + UNUSED, // OxydMagnum floor 0x15 + UNUSED, // OxydMagnum floor 0x16 + "fl-ice", // OxydMagnum floor 0x17 + UNUSED, // OxydMagnum floor 0x18 + UNUSED, // OxydMagnum floor 0x19 + UNUSED, // OxydMagnum floor 0x1a + UNUSED, // OxydMagnum floor 0x1b + "fl-space", // OxydMagnum floor 0x1c + UNUSED, // OxydMagnum floor 0x1d + UNUSED, // OxydMagnum floor 0x1e + UNUSED, // OxydMagnum floor 0x1f + UNUSED, // OxydMagnum floor 0x20 + UNUSED, // OxydMagnum floor 0x21 + UNUSED, // OxydMagnum floor 0x22 + "fl-gradient1", // OxydMagnum floor 0x23 + "fl-gradient2", // OxydMagnum floor 0x24 + "fl-gradient3", // OxydMagnum floor 0x25 + "fl-gradient4", // OxydMagnum floor 0x26 + "fl-gradient5", // OxydMagnum floor 0x27 + "fl-gradient6", // OxydMagnum floor 0x28 + "fl-gradient7", // OxydMagnum floor 0x29 + "fl-gradient8", // OxydMagnum floor 0x2a + "fl-gradient9", // OxydMagnum floor 0x2b + "fl-gradient10", // OxydMagnum floor 0x2c + "fl-gradient11", // OxydMagnum floor 0x2d + "fl-gradient12", // OxydMagnum floor 0x2e + "fl-gradient13", // OxydMagnum floor 0x2f + "fl-gradient14", // OxydMagnum floor 0x30 + "fl-gradient15", // OxydMagnum floor 0x31 + "fl-gradient16", // OxydMagnum floor 0x32 + "fl-brick", // OxydMagnum floor 0x33 (common was 'fl-rough') + "fl-brick", // OxydMagnum floor 0x34 (common was 'fl-rough') + UNUSED, // OxydMagnum floor 0x35 + UNUSED, // OxydMagnum floor 0x36 + "fl-brick", // OxydMagnum floor 0x37 + "fl-brick", // OxydMagnum floor 0x38 + UNUSED, // OxydMagnum floor 0x39 + UNUSED, // OxydMagnum floor 0x3a + UNUSED, // OxydMagnum floor 0x3b + "fl-rock", // OxydMagnum floor 0x3c + UNUSED, // OxydMagnum floor 0x3d + UNUSED, // OxydMagnum floor 0x3e + 0, // OxydMagnum floor 0x3f (Level 121 ?)(in normal Oxyd Magnum Levels unused!) + "fl-stwood1", // OxydMagnum floor 0x40 + "fl-wood", // OxydMagnum floor 0x41 + "fl-wood1", // OxydMagnum floor 0x42 + UNUSED, // OxydMagnum floor 0x43 + UNUSED, // OxydMagnum floor 0x44 + UNUSED, // OxydMagnum floor 0x45 + "fl-gravel", // OxydMagnum floor 0x46 + "fl-gravel", // OxydMagnum floor 0x47 + UNUSED, // OxydMagnum floor 0x48 + UNUSED, // OxydMagnum floor 0x49 + UNUSED, // OxydMagnum floor 0x4a + UNUSED, // OxydMagnum floor 0x4b + "fl-stone", // OxydMagnum floor 0x4c + "fl-stone", // OxydMagnum floor 0x4d + UNUSED, // OxydMagnum floor 0x4e + UNUSED, // OxydMagnum floor 0x4f + UNUSED, // OxydMagnum floor 0x50 + UNUSED, // OxydMagnum floor 0x51 + "fl-bridge", // OxydMagnum floor 0x52 (common was 'fl-bridge') + "fl-springboard", // OxydMagnum floor 0x53 + "fl-light", // OxydMagnum floor 0x54 + "fl-lightgray", // OxydMagnum floor 0x55 + "fl-darkgray", // OxydMagnum floor 0x56 + UNUSED, // OxydMagnum floor 0x57 + "fl-darkgray", // OxydMagnum floor 0x58 + "fl-leaves", // OxydMagnum floor 0x59 + "fl-normal", // OxydMagnum floor 0x5a (Only Oxyd Magnum Gold #119,120) + "fl-plank", // OxydMagnum floor 0x5b + // codes >= 0x5c are unused +}; + +const char *oxyd::oxydmag_stone_map[256] = { + 0, // OxydMagnum stone 0x00 + 0,0,0,0,0,0,0,0, // 0x01 -- 0x08 Oxyd stones + 0,0,0,0,0,0,0,0, // 0x09 -- 0x10 Oxyd stones + "st-likeoxydd", // OxydMagnum stone 0x11 + "st-plain", // OxydMagnum stone 0x12 + "st-bigbrick-es", // OxydMagnum stone 0x13 + "st-bigbrick-sw", // OxydMagnum stone 0x14 + "st-bigbrick-ne", // OxydMagnum stone 0x15 + "st-bigbrick-nw", // OxydMagnum stone 0x16 + UNUSED, // OxydMagnum stone 0x17 + UNUSED, // OxydMagnum stone 0x18 + "st-glass1_hole", // OxydMagnum stone 0x19 + "st-grate1", // OxydMagnum stone 0x1a + "st-grate2", // OxydMagnum stone 0x1b + "st-bug", // OxydMagnum stone 0x1c + UNUSED, // OxydMagnum stone 0x1d + UNUSED, // OxydMagnum stone 0x1e + "st-brick", // OxydMagnum stone 0x1f + "st-rock1", // OxydMagnum stone 0x20 + "st-rock1", // OxydMagnum stone 0x21 (Level 121 ?)(Oxyd Magnum Gold) + "st-rock1", // OxydMagnum stone 0x22 (common was 'st-glass')(Level 73 only)(looks like st-rock1)(looks like 0x20, but not like 0x87) + "st-woven", // OxydMagnum stone 0x23 + "st-marble", // OxydMagnum stone 0x24 + "st-glass1", // OxydMagnum stone 0x25 + "st-glass3", // OxydMagnum stone 0x26 + "st-glass1", // OxydMagnum stone 0x27 + "st-plain_break", // OxydMagnum stone 0x28 + "st-plain_hole", // OxydMagnum stone 0x29 + "st-plain_move", // OxydMagnum stone 0x2a + "st-wood", // OxydMagnum stone 0x2b + "st-switch", // OxydMagnum stone 0x2c + "st-floppy", // OxydMagnum stone 0x2d (in normal Oxyd Magnum Levels unused!) + UNUSED, // OxydMagnum stone 0x2e + "st-fourswitch", // OxydMagnum stone 0x2f (Level 89 and 92) + "st-key", // OxydMagnum stone 0x30 + "st-floppy", // OxydMagnum stone 0x31 + "st-knight", // OxydMagnum stone 0x32 + "st-fourswitch", // OxydMagnum stone 0x33 (Level 51 and 100) + "st-death", // OxydMagnum stone 0x34 + "st-death_invisible", // OxydMagnum stone 0x35 + "st-oneway_black-w", // OxydMagnum stone 0x36 + "st-oneway_black-e", // OxydMagnum stone 0x37 + "st-oneway_black-n", // OxydMagnum stone 0x38 + "st-oneway_black-s", // OxydMagnum stone 0x39 + "st-oneway_white-w", // OxydMagnum stone 0x3a + "st-oneway_white-e", // OxydMagnum stone 0x3b + "st-oneway_white-n", // OxydMagnum stone 0x3c + "st-oneway_white-s", // OxydMagnum stone 0x3d + UNUSED, // OxydMagnum stone 0x3e + UNUSED, // OxydMagnum stone 0x3f + UNUSED, // OxydMagnum stone 0x40 + UNUSED, // OxydMagnum stone 0x41 + "st-actorimpulse", // OxydMagnum stone 0x42 + "st-actorimpulse_invisible", // OxydMagnum stone 0x43 + "st-laser-1", // OxydMagnum stone 0x44 The laser-names are fake names! + "st-laser-2", // OxydMagnum stone 0x45 Direction and state are generated by Enigma. + "st-laser-3", // OxydMagnum stone 0x46 + "st-mirror-p|", // OxydMagnum stone 0x47 + "st-mirror-p/", // OxydMagnum stone 0x48 + "st-mirror-p-", // OxydMagnum stone 0x49 + "st-mirror-p\\", // OxydMagnum stone 0x4a + "st-mirror-p|m", // OxydMagnum stone 0x4b + "st-mirror-p/m", // OxydMagnum stone 0x4c + "st-mirror-p-m", // OxydMagnum stone 0x4d + "st-mirror-p\\m", // OxydMagnum stone 0x4e + "st-mirror-p|t", // OxydMagnum stone 0x4f + "st-mirror-p/t", // OxydMagnum stone 0x50 + "st-mirror-p-t", // OxydMagnum stone 0x51 + "st-mirror-p\\t", // OxydMagnum stone 0x52 + "st-mirror-3>", // OxydMagnum stone 0x53 + "st-mirror-3^", // OxydMagnum stone 0x54 + "st-mirror-3<", // OxydMagnum stone 0x55 + "st-mirror-3v", // OxydMagnum stone 0x56 + "st-puzzle2-es", // Oxyd1 stone 0x57 + "st-puzzle2-sw", // Oxyd1 stone 0x58 + "st-puzzle2-nw", // Oxyd1 stone 0x59 + "st-puzzle2-ne", // Oxyd1 stone 0x5a + "st-puzzle2-ew", // Oxyd1 stone 0x5b + "st-puzzle2-ns", // Oxyd1 stone 0x5c + "st-puzzle2-n", // Oxyd1 stone 0x5d + "st-puzzle2-s", // Oxyd1 stone 0x5e + "st-puzzle2-w", // Oxyd1 stone 0x5f + "st-puzzle2-e", // Oxyd1 stone 0x60 + "st-puzzle2-nesw", // Oxyd1 stone 0x61 + "st-metal_hole", // OxydMagnum stone 0x62 (st-explosive: like st-metal, but explodes when touched ...) + "st-plain_break", // OxydMagnum stone 0x63 + "st-fakeoxyda", // OxydMagnum stone 0x64 + UNUSED, // OxydMagnum stone 0x65 + UNUSED, // OxydMagnum stone 0x66 + UNUSED, // OxydMagnum stone 0x67 + "st-bombs", // OxydMagnum stone 0x68 (common was 'st-shogun-l') + "st-flash", // OxydMagnum stone 0x69 + "st-coinslot", // OxydMagnum stone 0x6a + "st-thief", // OxydMagnum stone 0x6b + "st-shogun-s", // OxydMagnum stone 0x6c + "st-stoneimpulse", // OxydMagnum stone 0x6d + "st-lasertimeswitch", // OxydMagnum stone 0x6e + "st-mail-n", // OxydMagnum stone 0x6f + "st-mail-w", // OxydMagnum stone 0x70 + "st-mail-e", // OxydMagnum stone 0x71 + "st-mail-s", // OxydMagnum stone 0x72 + "st-door-h", // OxydMagnum stone 0x73 + "st-door-v", // OxydMagnum stone 0x74 + "st-metal", // OxydMagnum stone 0x75 + "st-invisible", // OxydMagnum stone 0x76 + UNUSED, // OxydMagnum stone 0x77 + "st-door-v-open", // OxydMagnum stone 0x78 (st-door-h-open was wrong, look at level #32) + UNUSED, // OxydMagnum stone 0x79 + "st-timeswitch", // OxydMagnum stone 0x7a (Level 66, this is a solid stone) + UNUSED, // OxydMagnum stone 0x7b + UNUSED, // OxydMagnum stone 0x7c + UNUSED, // OxydMagnum stone 0x7d + UNUSED, // OxydMagnum stone 0x7e + UNUSED, // OxydMagnum stone 0x7f + UNUSED, // OxydMagnum stone 0x80 + UNUSED, // OxydMagnum stone 0x81 + UNUSED, // OxydMagnum stone 0x82 + UNUSED, // OxydMagnum stone 0x83 + UNUSED, // OxydMagnum stone 0x84 + UNUSED, // OxydMagnum stone 0x85 + "st-rock3_hole", // OxydMagnum stone 0x86 + "st-rock4", // OxydMagnum stone 0x87 (was st-rock1, but the pic has straight edges and surfaces) + 0, // OxydMagnum stone 0x88 (Level 121 ?)(in normal Oxyd Magnum Levels unused!) + 0, // OxydMagnum stone 0x89 (Level 121 ?)(in normal Oxyd Magnum Levels unused!) + "st-stone1", // OxydMagnum stone 0x8a + "st-rock6", // OxydMagnum stone 0x8b + "st-white1", // OxydMagnum stone 0x8c + "st-black1", // OxydMagnum stone 0x8d + "st-yinyang2", // OxydMagnum stone 0x8e + UNUSED, // OxydMagnum stone 0x8f + "st-redrock", // OxydMagnum stone 0x90 (Level 89 only) + UNUSED, // OxydMagnum stone 0x91 + "st-fourswitch", // OxydMagnum stone 0x92 (Level 51 only) + "st-yinyang1", // OxydMagnum stone 0x93 + "st-break_acwhite", // OxydMagnum stone 0x94 + "st-break_acblack", // OxydMagnum stone 0x95 + "st-chargeplus", // OxydMagnum stone 0x96 + "st-chargeminus", // OxydMagnum stone 0x97 + "st-chargezero", // OxydMagnum stone 0x98 + 0, // OxydMagnum stone 0x99 (common was 'st-blocker')(Level 121 ?)(in normal Oxyd Magnum Levels unused!) + "st-turnstile", // OxydMagnum stone 0x9a + "st-turnstile-n", // OxydMagnum stone 0x9b + "st-turnstile-s", // OxydMagnum stone 0x9c + "st-turnstile-w", // OxydMagnum stone 0x9d + "st-turnstile-e", // OxydMagnum stone 0x9e + UNUSED, // OxydMagnum stone 0x9f + UNUSED, // OxydMagnum stone 0xa0 + UNUSED, // OxydMagnum stone 0xa1 + UNUSED, // OxydMagnum stone 0xa2 + UNUSED, // OxydMagnum stone 0xa3 + "st-stoneimpulse-hollow", // OxydMagnum stone 0xa4 + UNUSED, // OxydMagnum stone 0xa5 + UNUSED, // OxydMagnum stone 0xa6 + "st-lightpassenger", // OxydMagnum stone 0xa7 + UNUSED, // OxydMagnum stone 0xa8 + UNUSED, // OxydMagnum stone 0xa9 + // codes >= 0xaa are unused +}; + +ITEMMAPTYPE oxyd::oxydmag_item_map[256] = { + ITEMSPEC(it_none), // OxydMagnum item 0x00 + ITEMSPEC(it_extralife), // OxydMagnum item 0x01 + ITEMSPEC(it_EXTERNAL), // OxydMagnum item 0x02 document 1 + ITEMSPEC(it_EXTERNAL), // OxydMagnum item 0x03 document 2 + ITEMSPEC(it_hammer), // OxydMagnum item 0x04 + ITEMSPEC(it_coffee), // OxydMagnum item 0x05 + ITEMSPEC(it_cherry), // OxydMagnum item 0x06 + ITEMSPEC(it_umbrella), // OxydMagnum item 0x07 + ITEMSPEC(it_glasses), // OxydMagnum item 0x08 + ITEMSPEC(it_glasses_broken), // OxydMagnum item 0x09 + ITEMSPEC(it_dynamite), // OxydMagnum item 0x0a + ITEMSPEC(it_blackbomb), // OxydMagnum item 0x0b + ITEMSPEC(it_whitebomb), // OxydMagnum item 0x0c + ITEMSPEC(it_crack0), // OxydMagnum item 0x0d + ITEMSPEC(it_crack1), // OxydMagnum item 0x0e + ITEMSPEC(it_crack2), // OxydMagnum item 0x0f + ITEMSPEC(it_crack3), // OxydMagnum item 0x10 + ITEMSPEC(it_coin1), // OxydMagnum item 0x11 + ITEMSPEC(it_coin2), // OxydMagnum item 0x12 + ITEMSPEC(it_coin4), // OxydMagnum item 0x13 + ITEMSPEC(it_key_a), // OxydMagnum item 0x14 + ITEMSPEC(it_key_b), // OxydMagnum item 0x15 + ITEMSPEC(it_key_c), // OxydMagnum item 0x16 + ITEMSPEC(it_floppy), // OxydMagnum item 0x17 + ITEMSPEC(it_sword), // OxydMagnum item 0x18 + ITEMSPEC(it_flagwhite), // OxydMagnum item 0x19 + ITEMSPEC(it_flagblack), // OxydMagnum item 0x1a + ITEMSPEC(it_ring), // OxydMagnum item 0x1b + ITEMSPEC(it_pipe_wn), // OxydMagnum item 0x1c + ITEMSPEC(it_pipe_sw), // OxydMagnum item 0x1d + ITEMSPEC(it_pipe_ne), // OxydMagnum item 0x1e + ITEMSPEC(it_pipe_es), // OxydMagnum item 0x1f + ITEMSPEC(it_pipe_v), // OxydMagnum item 0x20 + ITEMSPEC(it_pipe_h), // OxydMagnum item 0x21 + ITEMSPEC(it_spade), // OxydMagnum item 0x22 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x23 + ITEMSPEC(it_pin), // OxydMagnum item 0x24 + ITEMSPEC(it_seed), // OxydMagnum item 0x25 + ITEMSPEC(it_spring2), // OxydMagnum item 0x26 + ITEMSPEC(it_spring1), // OxydMagnum item 0x27 + ITEMSPEC(it_bag), // OxydMagnum item 0x28 + ITEMSPEC(it_magnet_off), // OxydMagnum item 0x29 + ITEMSPEC(it_inversesensor), // OxydMagnum item 0x2a + ITEMSPEC(it_sensor), // OxydMagnum item 0x2b + ITEMSPEC(it_shogun_s), // OxydMagnum item 0x2c + ITEMSPEC(it_vortex_open), // OxydMagnum item 0x2d + ITEMSPEC(it_vortex_closed), // OxydMagnum item 0x2e + ITEMSPEC(it_wormhole_on), // OxydMagnum item 0x2f + ITEMSPEC(it_hill), // OxydMagnum item 0x30 + ITEMSPEC(it_tinyhill), // OxydMagnum item 0x31 + ITEMSPEC(it_hollow), // OxydMagnum item 0x32 + ITEMSPEC(it_tinyhollow), // OxydMagnum item 0x33 + ITEMSPEC(it_vstrip), // OxydMagnum item 0x34 + ITEMSPEC(it_hstrip), // OxydMagnum item 0x35 + ITEMSPEC(it_springboard), // OxydMagnum item 0x36 + ITEMSPEC(it_MISSING), // OxydMagnum item 0x37 + ITEMSPEC(it_bridge_oxyd), // OxydMagnum item 0x38 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x39 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3a + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3b + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3c + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3d + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3e + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x3f + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x40 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x41 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x42 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x43 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x44 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x45 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x46 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x47 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x48 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x49 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x4a + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x4b + ITEMSPEC(it_springboard), // OxydMagnum item 0x4c + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x4d + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x4e + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x4f + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x50 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x51 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x52 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x53 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x54 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x55 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x56 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x57 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x58 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x59 + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x5a + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x5b + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x5c + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x5d + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x5e + ITEMSPEC(it_oxyd5f), // OxydMagnum item 0x5f + ITEMSPEC(it_drop), // OxydMagnum item 0x60 (drunk) + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x61 (rev. breaking area) + ITEMSPEC(it_UNUSED), // OxydMagnum item 0x62 (player exchange) + ITEMSPEC(it_trigger), // OxydMagnum item 0x63 + ITEMSPEC(it_puller_n), // OxydMagnum item 0x64 + ITEMSPEC(it_puller_s), // OxydMagnum item 0x65 + ITEMSPEC(it_puller_w), // OxydMagnum item 0x66 + ITEMSPEC(it_puller_e), // OxydMagnum item 0x67 + // codes >= 0x68 are unused +}; diff --git a/project/jni/application/enigma/src/ox_oxyd1.cpp b/project/jni/application/enigma/src/ox_oxyd1.cpp new file mode 100644 index 000000000..0d3c31828 --- /dev/null +++ b/project/jni/application/enigma/src/ox_oxyd1.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) May 2003 by Ralf Westram + * Copyright (C) 2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if defined(PLAIN_SPEC_ONLY) +// This is used from lib-src/oxydlib/EnigmaNames.cpp. +// +// defining PLAIN_SPEC_ONLY only shows xxx_floor_map, xxx_stone_map and xxx_item_map +// Note: the xxx_item_map changes it's type (ItemID -> const char *) + +#define ITEMSPEC(i) #i +#define ITEMMAPTYPE const char * + +#else +// Standard enigma section : +#define ITEMSPEC(i) i +#define ITEMMAPTYPE ItemID + +#include "objects.hh" +#include "server.hh" +#include "oxyd_internal.hh" + +using namespace enigma; +using namespace world; +using namespace oxyd; + +/* -------------------- Oxyd 1 level pack -------------------- */ + +LP_Oxyd1::LP_Oxyd1 (DatFile *dat, bool twoplayers) +: LevelPack_Oxyd (OxydVersion_Oxyd1, dat, + twoplayers ? 100 : 0, + twoplayers ? 199 : 99, + twoplayers) +{ +} + +void LP_Oxyd1::load (const OxydLib::Level &level) +{ + LoaderConfig c (needs_twoplayers(), + get_gamemode(), + oxyd1_floor_map, + oxyd1_item_map, + oxyd1_stone_map); + c.id_timer = 0x33; + c.id_laser1 = 0x44; + + OxydLoader (level, c).load(); + + if (server::GetDifficulty() == DIFFICULTY_EASY) + server::WaterSinkSpeed = 100.0; + else + server::WaterSinkSpeed = 200.0; +} + +#endif // PLAIN_SPEC_ONLY +// only tables following! + +const char *oxyd::oxyd1_floor_map[256] = { + "fl-abyss", // Oxyd1 floor 0x00 + "fl-gray", // Oxyd1 floor 0x01 + "fl-metal", // Oxyd1 floor 0x02 + "fl-samba", // Oxyd1 floor 0x03 was: checkered floor + UNUSED, // Oxyd1 floor 0x04 + UNUSED, // Oxyd1 floor 0x05 + UNUSED, // Oxyd1 floor 0x06 + "fl-normal", // Oxyd1 floor 0x07 + "fl-himalaya", // Oxyd1 floor 0x08 + "fl-himalaya", // Oxyd1 floor 0x09 + "fl-himalaya", // Oxyd1 floor 0x0a + "fl-himalaya", // Oxyd1 floor 0x0b + "fl-inverse2", // Oxyd1 floor 0x0c + "fl-acblack", // Oxyd1 floor 0x0d + "fl-acwhite", // Oxyd1 floor 0x0e + "fl-swamp", // Oxyd1 floor 0x0f + UNUSED, // Oxyd1 floor 0x10 + UNUSED, // Oxyd1 floor 0x11 + UNUSED, // Oxyd1 floor 0x12 + "fl-water", // Oxyd1 floor 0x13 + UNUSED, // Oxyd1 floor 0x14 + UNUSED, // Oxyd1 floor 0x15 + UNUSED, // Oxyd1 floor 0x16 + "fl-ice", // Oxyd1 floor 0x17 + UNUSED, // Oxyd1 floor 0x18 + UNUSED, // Oxyd1 floor 0x19 + UNUSED, // Oxyd1 floor 0x1a + UNUSED, // Oxyd1 floor 0x1b + "fl-space", // Oxyd1 floor 0x1c + "fl-space", // Oxyd1 floor 0x1d + UNUSED, // Oxyd1 floor 0x1e + UNUSED, // Oxyd1 floor 0x1f + "fl-space", // Oxyd1 floor 0x20 + "fl-space-force", // Oxyd1 floor 0x21 + "fl-space", // Oxyd1 floor 0x22 + "fl-gradient1", // Oxyd1 floor 0x23 + "fl-gradient2", // Oxyd1 floor 0x24 + "fl-gradient3", // Oxyd1 floor 0x25 + "fl-gradient4", // Oxyd1 floor 0x26 + "fl-gradient5", // Oxyd1 floor 0x27 + "fl-gradient6", // Oxyd1 floor 0x28 + "fl-gradient7", // Oxyd1 floor 0x29 + "fl-gradient8", // Oxyd1 floor 0x2a + "fl-gradient9", // Oxyd1 floor 0x2b + "fl-gradient10", // Oxyd1 floor 0x2c + "fl-gradient11", // Oxyd1 floor 0x2d + "fl-gradient12", // Oxyd1 floor 0x2e + "fl-gradient13", // Oxyd1 floor 0x2f + "fl-gradient14", // Oxyd1 floor 0x30 + "fl-gradient15", // Oxyd1 floor 0x31 + "fl-gradient16", // Oxyd1 floor 0x32 + "fl-brick", // Oxyd1 floor 0x33 + "fl-brick", // Oxyd1 floor 0x34 (common was 'fl-rough') + UNUSED, // Oxyd1 floor 0x35 + UNUSED, // Oxyd1 floor 0x36 + "fl-brick", // Oxyd1 floor 0x37 + "fl-brick", // Oxyd1 floor 0x38 + UNUSED, // Oxyd1 floor 0x39 + UNUSED, // Oxyd1 floor 0x3a + UNUSED, // Oxyd1 floor 0x3b + "fl-rock", // Oxyd1 floor 0x3c + UNUSED, // Oxyd1 floor 0x3d + UNUSED, // Oxyd1 floor 0x3e + UNUSED, // Oxyd1 floor 0x3f + "fl-stwood1", // Oxyd1 floor 0x40 + "fl-wood", // Oxyd1 floor 0x41 + "fl-samba1", // Oxyd1 floor 0x42 + UNUSED, // Oxyd1 floor 0x43 + UNUSED, // Oxyd1 floor 0x44 + UNUSED, // Oxyd1 floor 0x45 + "fl-gravel", // Oxyd1 floor 0x46 + "fl-gravel", // Oxyd1 floor 0x47 + UNUSED, // Oxyd1 floor 0x48 + UNUSED, // Oxyd1 floor 0x49 + UNUSED, // Oxyd1 floor 0x4a + UNUSED, // Oxyd1 floor 0x4b + UNUSED, // Oxyd1 floor 0x4c + "fl-tigris", // Oxyd1 floor 0x4d + UNUSED, // Oxyd1 floor 0x4e + UNUSED, // Oxyd1 floor 0x4f + UNUSED, // Oxyd1 floor 0x50 + UNUSED, // Oxyd1 floor 0x51 + "fl-bridge", // Oxyd1 floor 0x52 + "fl-springboard", // Oxyd1 floor 0x53 + UNUSED, // Oxyd1 floor 0x54 + "fl-gray", // Oxyd1 floor 0x55 + "fl-light", // Oxyd1 floor 0x56 + "fl-lightgray", // Oxyd1 floor 0x57 + "fl-darkgray", // Oxyd1 floor 0x58 + "fl-dunes", // Oxyd1 floor 0x59 + "fl-normal", // Oxyd1 floor 0x5a + "fl-plank", // Oxyd1 floor 0x5b + // codes >= 0x5c are unused +}; + +const char *oxyd::oxyd1_stone_map[256] = { + 0, // Oxyd1 stone 0x00 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "st-fakeoxyd", // Oxyd1 stone 0x11 + "st-plain_break", // Oxyd1 stone 0x12 + "st-bigbrick-es", // Oxyd1 stone 0x13 + "st-bigbrick-sw", // Oxyd1 stone 0x14 + "st-bigbrick-ne", // Oxyd1 stone 0x15 + "st-bigbrick-nw", // Oxyd1 stone 0x16 + "st-plain_hole", // Oxyd1 stone 0x17 + "st-oxyd-0x18", // Oxyd1 stone 0x18 + "st-glass1_hole", // Oxyd1 stone 0x19 + "st-grate1", // Oxyd1 stone 0x1a + "st-grate2", // Oxyd1 stone 0x1b + "st-bug", // Oxyd1 stone 0x1c + "st-surprise", // Oxyd1 stone 0x1d + "st-chameleon", // Oxyd1 stone 0x1e + "st-brick", // Oxyd1 stone 0x1f + "st-rock1", // Oxyd1 stone 0x20 + "st-rock1", // Oxyd1 stone 0x21 + UNUSED, // Oxyd1 stone 0x22 + "st-woven", // Oxyd1 stone 0x23 + "st-marble", // Oxyd1 stone 0x24 + "st-glass1", // Oxyd1 stone 0x25 + "st-glass3", // Oxyd1 stone 0x26 + "st-glass2", // Oxyd1 stone 0x27 + "st-plain_break", // Oxyd1 stone 0x28 [see Level#12] + "st-plain_hole", // Oxyd1 stone 0x29 + "st-plain_move", // Oxyd1 stone 0x2a + "st-wood", // Oxyd1 stone 0x2b + "st-switch", // Oxyd1 stone 0x2c + "st-switch_black", // Oxyd1 stone 0x2d + "st-switch_white", // Oxyd1 stone 0x2e + "st-fourswitch", // Oxyd1 stone 0x2f + "st-key", // Oxyd1 stone 0x30 + "st-floppy", // Oxyd1 stone 0x31 + "st-knight", // Oxyd1 stone 0x32 + 0, // Oxyd1 stone 0x33 oscillator (properties set in oxyd.cc) + "st-death", // Oxyd1 stone 0x34 + "st-death_invisible", // Oxyd1 stone 0x35 + "st-oneway_black-w", // Oxyd1 stone 0x36 + "st-oneway_black-e", // Oxyd1 stone 0x37 + "st-oneway_black-n", // Oxyd1 stone 0x38 + "st-oneway_black-s", // Oxyd1 stone 0x39 + "st-oneway_white-w", // Oxyd1 stone 0x3a + "st-oneway_white-e", // Oxyd1 stone 0x3b + "st-oneway_white-n", // Oxyd1 stone 0x3c + "st-oneway_white-s", // Oxyd1 stone 0x3d + "st-window", // Oxyd1 stone 0x3e + "", // Oxyd1 stone 0x3f magic stone + "", // Oxyd1 stone 0x40 magic stone + "", // Oxyd1 stone 0x41 magic stone + "st-actorimpulse", // Oxyd1 stone 0x42 + "st-actorimpulse_invisible", // Oxyd1 stone 0x43 + "st-laser-1", // Oxyd1 stone 0x44 The laser-names are fake names! + "st-laser-2", // Oxyd1 stone 0x45 Direction and state are generated by Enigma. + "st-laser-3", // Oxyd1 stone 0x46 + "st-mirror-p|", // Oxyd1 stone 0x47 + "st-mirror-p/", // Oxyd1 stone 0x48 + "st-mirror-p-", // Oxyd1 stone 0x49 + "st-mirror-p\\", // Oxyd1 stone 0x4a + "st-mirror-p|m", // Oxyd1 stone 0x4b + "st-mirror-p/m", // Oxyd1 stone 0x4c + "st-mirror-p-m", // Oxyd1 stone 0x4d + "st-mirror-p\\m", // Oxyd1 stone 0x4e + "st-mirror-p|t", // Oxyd1 stone 0x4f + "st-mirror-p/t", // Oxyd1 stone 0x50 + "st-mirror-p-t", // Oxyd1 stone 0x51 + "st-mirror-p\\t", // Oxyd1 stone 0x52 + "st-mirror-3>", // Oxyd1 stone 0x53 + "st-mirror-3^", // Oxyd1 stone 0x54 + "st-mirror-3<", // Oxyd1 stone 0x55 + "st-mirror-3v", // Oxyd1 stone 0x56 + "st-puzzle2-es", // Oxyd1 stone 0x57 + "st-puzzle2-sw", // Oxyd1 stone 0x58 + "st-puzzle2-nw", // Oxyd1 stone 0x59 + "st-puzzle2-ne", // Oxyd1 stone 0x5a + "st-puzzle2-ew", // Oxyd1 stone 0x5b + "st-puzzle2-ns", // Oxyd1 stone 0x5c + "st-puzzle2-n", // Oxyd1 stone 0x5d + "st-puzzle2-s", // Oxyd1 stone 0x5e + "st-puzzle2-w", // Oxyd1 stone 0x5f + "st-puzzle2-e", // Oxyd1 stone 0x60 + "st-puzzle2-nesw", // Oxyd1 stone 0x61 + "st-metal_hole", // Oxyd1 stone 0x62 + "st-laserbreak", // Oxyd1 stone 0x63 + "st-fakeoxyda", // Oxyd1 stone 0x64 + UNUSED, // Oxyd1 stone 0x65 + "st-disco-medium", // Oxyd1 stone 0x66 + "st-disco-light", // Oxyd1 stone 0x67 + "st-bombs", // Oxyd1 stone 0x68 + "st-flash", // Oxyd1 stone 0x69 + "st-coinslot", // Oxyd1 stone 0x6a + "st-thief", // Oxyd1 stone 0x6b + "st-shogun-s", // Oxyd1 stone 0x6c + "st-stoneimpulse", // Oxyd1 stone 0x6d + "st-lasertimeswitch", // Oxyd1 stone 0x6e + "st-mail-n", // Oxyd1 stone 0x6f + "st-mail-w", // Oxyd1 stone 0x70 + "st-mail-e", // Oxyd1 stone 0x71 + "st-mail-s", // Oxyd1 stone 0x72 + "st-door-h", // Oxyd1 stone 0x73 + "st-door-v", // Oxyd1 stone 0x74 + "st-metal", // Oxyd1 stone 0x75 + "st-invisible", // Oxyd1 stone 0x76 + "st-door-h-open", // Oxyd1 stone 0x77 + "st-door-v-open", // Oxyd1 stone 0x78 + UNUSED, // Oxyd1 stone 0x79 + "st-timeswitch", // Oxyd1 stone 0x7a (only used in level #23) + UNUSED, // Oxyd1 stone 0x7b + UNUSED, // Oxyd1 stone 0x7c + UNUSED, // Oxyd1 stone 0x7d + UNUSED, // Oxyd1 stone 0x7e + UNUSED, // Oxyd1 stone 0x7f + UNUSED, // Oxyd1 stone 0x80 + "", // Oxyd1 stone 0x81 + UNUSED, // Oxyd1 stone 0x82 + UNUSED, // Oxyd1 stone 0x83 + UNUSED, // Oxyd1 stone 0x84 + UNUSED, // Oxyd1 stone 0x85 + "st-metal_hole", // Oxyd1 stone 0x86 + "st-rock2", // Oxyd1 stone 0x87 + UNUSED, // Oxyd1 stone 0x88 + UNUSED, // Oxyd1 stone 0x89 + "st-stone1", // Oxyd1 stone 0x8a + "st-blue-sand", // Oxyd1 stone 0x8b + "st-white1", // Oxyd1 stone 0x8c + "st-black1", // Oxyd1 stone 0x8d + "st-yinyang2", // Oxyd1 stone 0x8e + UNUSED, // Oxyd1 stone 0x8f + "st-whiteballs", // Oxyd1 stone 0x90 + "st-blackballs", // Oxyd1 stone 0x91 + "st-fourswitch", // Oxyd1 stone 0x92 + "st-yinyang1", // Oxyd1 stone 0x93 + "st-break_acwhite", // Oxyd1 stone 0x94 + "st-break_acblack", // Oxyd1 stone 0x95 + "st-chargeplus", // Oxyd1 stone 0x96 + "st-chargeminus", // Oxyd1 stone 0x97 + "st-chargezero", // Oxyd1 stone 0x98 + "st-glass1", // Oxyd1 stone 0x99 + // codes >= 0x9a are unused +}; + +ITEMMAPTYPE oxyd::oxyd1_item_map[256] = { + ITEMSPEC(it_none), // 0x00 + ITEMSPEC(it_extralife), // 0x01 + ITEMSPEC(it_EXTERNAL), // 0x02 document 1 + ITEMSPEC(it_EXTERNAL), // 0x03 document 2 + ITEMSPEC(it_hammer), // 0x04 + ITEMSPEC(it_coffee), // 0x05 + ITEMSPEC(it_cherry), // 0x06 + ITEMSPEC(it_umbrella), // 0x07 + ITEMSPEC(it_glasses), // 0x08 + ITEMSPEC(it_glasses_broken), // 0x09 + ITEMSPEC(it_dynamite), // 0x0a + ITEMSPEC(it_blackbomb), // 0x0b + ITEMSPEC(it_whitebomb), // 0x0c + ITEMSPEC(it_crack0), // 0x0d + ITEMSPEC(it_crack1), // 0x0e + ITEMSPEC(it_crack2), // 0x0f + ITEMSPEC(it_crack3), // 0x10 + ITEMSPEC(it_coin1), // 0x11 + ITEMSPEC(it_coin2), // 0x12 + ITEMSPEC(it_coin4), // 0x13 + ITEMSPEC(it_key_a), // 0x14 + ITEMSPEC(it_key_b), // 0x15 + ITEMSPEC(it_key_c), // 0x16 + ITEMSPEC(it_floppy), // 0x17 + ITEMSPEC(it_sword), // 0x18 + ITEMSPEC(it_flagwhite), // 0x19 + ITEMSPEC(it_flagblack), // 0x1a + ITEMSPEC(it_ring), // 0x1b + ITEMSPEC(it_pipe_wn), // 0x1c + ITEMSPEC(it_pipe_sw), // 0x1d + ITEMSPEC(it_pipe_ne), // 0x1e + ITEMSPEC(it_pipe_es), // 0x1f + ITEMSPEC(it_pipe_v), // 0x20 + ITEMSPEC(it_pipe_h), // 0x21 + ITEMSPEC(it_spade), // 0x22 + ITEMSPEC(it_surprise), // 0x23 + ITEMSPEC(it_pin), // 0x24 + ITEMSPEC(it_seed), // 0x25 + ITEMSPEC(it_spring2), // 0x26 + ITEMSPEC(it_spring1), // 0x27 + ITEMSPEC(it_bag), // 0x28 + ITEMSPEC(it_magnet_off), // 0x29 + ITEMSPEC(it_inversesensor), // 0x2a + ITEMSPEC(it_sensor), // 0x2b + ITEMSPEC(it_shogun_s), // 0x2c + ITEMSPEC(it_vortex_open), // 0x2d + ITEMSPEC(it_vortex_closed), // 0x2e + ITEMSPEC(it_wormhole_on), // 0x2f + ITEMSPEC(it_hill), // 0x30 + ITEMSPEC(it_tinyhill), // 0x31 + ITEMSPEC(it_hollow), // 0x32 + ITEMSPEC(it_tinyhollow), // 0x33 + ITEMSPEC(it_vstrip), // 0x34 + ITEMSPEC(it_hstrip), // 0x35 + ITEMSPEC(it_springboard), // 0x36 + ITEMSPEC(it_bridge_oxyd), // 0x37 bridge active + ITEMSPEC(it_bridge_oxyd), // 0x38 bridge inactive + ITEMSPEC(it_bridge_oxyd_active), // 0x39 walkable bridge (?) + ITEMSPEC(it_UNUSED), // 0x3a + ITEMSPEC(it_UNUSED), // 0x3b + ITEMSPEC(it_UNUSED), // 0x3c + ITEMSPEC(it_UNUSED), // 0x3d + ITEMSPEC(it_UNUSED), // 0x3e + ITEMSPEC(it_UNUSED), // 0x3f + ITEMSPEC(it_UNUSED), // 0x40 + ITEMSPEC(it_UNUSED), // 0x41 + ITEMSPEC(it_UNUSED), // 0x42 + ITEMSPEC(it_UNUSED), // 0x43 + ITEMSPEC(it_UNUSED), // 0x44 + ITEMSPEC(it_UNUSED), // 0x45 + ITEMSPEC(it_UNUSED), // 0x46 + ITEMSPEC(it_UNUSED), // 0x47 + ITEMSPEC(it_UNUSED), // 0x48 + ITEMSPEC(it_UNUSED), // 0x49 + ITEMSPEC(it_UNUSED), // 0x4a + ITEMSPEC(it_UNUSED), // 0x4b + ITEMSPEC(it_UNUSED), // 0x4c + ITEMSPEC(it_UNUSED), // 0x4d + ITEMSPEC(it_UNUSED), // 0x4e + ITEMSPEC(it_UNUSED), // 0x4f + ITEMSPEC(it_UNUSED), // 0x50 + ITEMSPEC(it_UNUSED), // 0x51 + ITEMSPEC(it_UNUSED), // 0x52 + ITEMSPEC(it_UNUSED), // 0x53 + ITEMSPEC(it_UNUSED), // 0x54 + ITEMSPEC(it_UNUSED), // 0x55 + ITEMSPEC(it_UNUSED), // 0x56 + ITEMSPEC(it_UNUSED), // 0x57 + ITEMSPEC(it_UNUSED), // 0x58 + ITEMSPEC(it_UNUSED), // 0x59 + ITEMSPEC(it_UNUSED), // 0x5a + ITEMSPEC(it_UNUSED), // 0x5b + ITEMSPEC(it_UNUSED), // 0x5c + ITEMSPEC(it_UNUSED), // 0x5d + ITEMSPEC(it_UNUSED), // 0x5e + ITEMSPEC(it_oxyd5f), // 0x5f + ITEMSPEC(it_drop), // 0x60 drop (turns actor into rotor) + // codes >= 0x61 are unused +}; diff --git a/project/jni/application/enigma/src/ox_peroxyd.cpp b/project/jni/application/enigma/src/ox_peroxyd.cpp new file mode 100644 index 000000000..a501954f3 --- /dev/null +++ b/project/jni/application/enigma/src/ox_peroxyd.cpp @@ -0,0 +1,513 @@ +/* + * Copyright (C) May 2003 by Ralf Westram + * Copyright (C) 2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#if defined(PLAIN_SPEC_ONLY) +// This is used from lib-src/oxydlib/EnigmaNames.cpp. +// +// defining PLAIN_SPEC_ONLY only shows xxx_floor_map, xxx_stone_map and xxx_item_map +// Note: the xxx_item_map changes it's type (ItemID -> const char *) + +#define ITEMSPEC(i) #i +#define ITEMMAPTYPE const char * + +#else +// Standard enigma section : + +#define ITEMSPEC(i) i +#define ITEMMAPTYPE world::ItemID + +#include "objects.hh" +#include "server.hh" +#include "oxyd_internal.hh" + +using namespace world; +using namespace oxyd; + +Stone *PerOxydLoader::make_stone (int type, int x, int y) +{ + Stone *st = 0; + switch (type) { + case 0x39: case 0x3a: case 0x3b: + // Create magic stones only if they are absolutely necessary + if (level.getRequireMagicPiece()) + st = MakeStone ("st-magic"); + break; + default: + st = OxydLoader::make_stone (type, x, y); + break; + } + return st; +} + + + +/* -------------------- Per.Oxyd level pack -------------------- */ + +LP_PerOxyd::LP_PerOxyd (DatFile *dat, bool twoplayers) +: LevelPack_Oxyd (OxydVersion_PerOxyd, dat, + twoplayers ? 100 : 0, + twoplayers ? 199 : 99, + twoplayers) +{ +} + +void LP_PerOxyd::load (const OxydLib::Level &level) +{ + LoaderConfig c (needs_twoplayers(), + get_gamemode(), + peroxyd_floor_map, + peroxyd_item_map, + peroxyd_stone_map); + c.id_timer = 0x2e; + c.id_laser1 = 0x3e; + PerOxydLoader (level, c).load(); +} + +bool LP_PerOxyd::hasEasymode(size_t index) { + static const bool easymode_map[200] = { // 1-100 single, 101 - 200 netplay + false, false, true, false, false, false, false, true, true, true, // 1 - 10 + true, false, false, true, false, false, true, false, false, false, // 11 - 20 + false, false, false, true, false, true, true, false, true, true, // 21 - 30 + true, false, true, true, false, false, true, true, false, true, // 31 - 40 + true, true, true, false, true, false, true, true, false, true, // 41 - 50 + true, true, false, true, true, true, false, true, false, true, // 51 - 60 + true, false, false, false, true, false, true, true, false, true, // 61 - 70 + false, true, true, true, true, true, true, true, true, true, // 71 - 80 + true, true, true, true, false, true, false, true, true, true, // 81 - 90 + false, true, false, true, true, true, true, true, true, true, // 91 - 100 + false, false, true, true, true, false, false, true, false, true, // 101 - 110 + false, true, false, true, true, true, false, false, false, false, // 111 - 120 + true, false, false, true, false, false, false, false, true, false, // 121 - 130 + false, false, true, true, false, true, false, true, true, true, // 131 - 140 + false, false, true, false, false, true, true, true, true, false, // 141 - 150 + true, true, true, true, false, true, true, true, false, false, // 151 - 160 + true, true, true, true, true, true, false, true, true, true, // 161 - 170 + false, true, true, true, true, true, true, true, true, true, // 171 - 180 + false, true, false, true, true, true, false, true, false, true, // 181 - 190 + false, true, false, false, true, true, true, true, true, true // 191 - 200 + }; + return easymode_map[index]; +} + +#endif // PLAIN_SPEC_ONLY +// only tables following! + +const char *oxyd::peroxyd_floor_map[256] = { + "fl-abyss", // PerOxyd floor 0x00 + "fl-gray", // PerOxyd floor 0x01 + "fl-metal", // PerOxyd floor 0x02 + "fl-metal", // PerOxyd floor 0x03 + "fl-metal", // PerOxyd floor 0x04 + "fl-metal", // PerOxyd floor 0x05 + "fl-metal", // PerOxyd floor 0x06 + "fl-metal", // PerOxyd floor 0x07 + "fl-rough-red", // PerOxyd floor 0x08 + "fl-rough-blue", // PerOxyd floor 0x09 + "fl-rough-blue", // PerOxyd floor 0x0a + "fl-rough-red", // PerOxyd floor 0x0b + "fl-inverse", // PerOxyd floor 0x0c + "fl-acblack", // PerOxyd floor 0x0d + "fl-acwhite", // PerOxyd floor 0x0e + "fl-swamp", // PerOxyd floor 0x0f + UNUSED, // PerOxyd floor 0x10 + UNUSED, // PerOxyd floor 0x11 + UNUSED, // PerOxyd floor 0x12 + "fl-water", // PerOxyd floor 0x13 + "fl-water", // PerOxyd floor 0x14 + UNUSED, // PerOxyd floor 0x15 + UNUSED, // PerOxyd floor 0x16 + "fl-ice", // PerOxyd floor 0x17 + UNUSED, // PerOxyd floor 0x18 + UNUSED, // PerOxyd floor 0x19 + UNUSED, // PerOxyd floor 0x1a + UNUSED, // PerOxyd floor 0x1b + "fl-space", // PerOxyd floor 0x1c + UNUSED, // PerOxyd floor 0x1d + UNUSED, // PerOxyd floor 0x1e + UNUSED, // PerOxyd floor 0x1f + UNUSED, // PerOxyd floor 0x20 + "fl-space-force", // PerOxyd floor 0x21 (flat force) + UNUSED, // PerOxyd floor 0x22 + "fl-gradient1", // PerOxyd floor 0x23 + "fl-gradient2", // PerOxyd floor 0x24 + "fl-gradient3", // PerOxyd floor 0x25 + "fl-gradient4", // PerOxyd floor 0x26 + "fl-gradient5", // PerOxyd floor 0x27 + "fl-gradient6", // PerOxyd floor 0x28 + "fl-gradient7", // PerOxyd floor 0x29 + "fl-gradient8", // PerOxyd floor 0x2a + "fl-gradient9", // PerOxyd floor 0x2b + "fl-gradient10", // PerOxyd floor 0x2c + "fl-gradient11", // PerOxyd floor 0x2d + "fl-gradient12", // PerOxyd floor 0x2e + "fl-gradient13", // PerOxyd floor 0x2f (flat force) + "fl-gradient14", // PerOxyd floor 0x30 (flat force) + "fl-gradient15", // PerOxyd floor 0x31 (flat force) + "fl-gradient16", // PerOxyd floor 0x32 (flat force) + "fl-rough", // PerOxyd floor 0x33 (common was 'fl-rough', should be a 'fl-paper')(only level 75) + "fl-rough", // PerOxyd floor 0x34 + UNUSED, // PerOxyd floor 0x35 + UNUSED, // PerOxyd floor 0x36 + UNUSED, // PerOxyd floor 0x37 + "fl-concrete", // PerOxyd floor 0x38 (Tan/green horizontal Grain) + "fl-concrete", // PerOxyd floor 0x39 (Tan/green horizontal Grain, same as 0x38)(only level 118) + UNUSED, // PerOxyd floor 0x3a + UNUSED, // PerOxyd floor 0x3b + "fl-stwood1", // PerOxyd floor 0x3c + "fl-samba", // PerOxyd floor 0x3d (common was 'fl-wood', horizontal or vertical slats)(many) + "fl-samba1", // PerOxyd floor 0x3e (common was 'fl-wood', horizontal slats)(only level 32) + "fl-samba2", // PerOxyd floor 0x3f (common was 'fl-wood', vertical slats)(levels: 35, 152) + UNUSED, // PerOxyd floor 0x40 + UNUSED, // PerOxyd floor 0x41 + "fl-bumps", // PerOxyd floor 0x42 + "fl-bumps", // PerOxyd floor 0x43 + UNUSED, // PerOxyd floor 0x44 + UNUSED, // PerOxyd floor 0x45 + UNUSED, // PerOxyd floor 0x46 + UNUSED, // PerOxyd floor 0x47 + "fl-concrete", // PerOxyd floor 0x48 + UNUSED, // PerOxyd floor 0x49 + UNUSED, // PerOxyd floor 0x4a + UNUSED, // PerOxyd floor 0x4b + "fl-bridge-open", // PerOxyd floor 0x4c + UNUSED, // PerOxyd floor 0x4d + "fl-gray", // PerOxyd floor 0x4e + "fl-gradient13", // PerOxyd floor 0x4f (flat force up, beige) (maybe gradient22?) + "fl-black", // PerOxyd floor 0x50 (flat force down, dark olive) (maybe gradient21?) + "fl-white", // PerOxyd floor 0x51 (flat force left, beige) (maybe gradient24?) + "fl-black", // PerOxyd floor 0x52 (flat force right, dark olive)(maybe gradient23?) + "fl-floor_001", // PerOxyd floor 0x53 + UNUSED, // PerOxyd floor 0x54 + "fl-plank", // PerOxyd floor 0x55 + UNUSED, // PerOxyd floor 0x56 + "fl-woven_orange", // PerOxyd floor 0x57 (only used in Link-Level 91, friction (2.5) questionable!) + UNUSED, // PerOxyd floor 0x58 + UNUSED, // PerOxyd floor 0x59 + UNUSED, // PerOxyd floor 0x5a + "fl-gravel", // PerOxyd floor 0x5b + UNUSED, // PerOxyd floor 0x5c + UNUSED, // PerOxyd floor 0x5d + "fl-gravel", // PerOxyd floor 0x5e (same as 0x5b)(only level 192) + // codes >= 0x5f are unused +}; + +const char *oxyd::peroxyd_stone_map[256] = { + 0, // PerOxyd stone 0x00 no stone + 0,0,0,0,0,0,0,0, // PerOxyd stone 0x01 - 0x08 oxyd stones + 0,0,0,0,0,0,0,0, // PerOxyd stone 0x08 - 0x10 oxyd stones + "st-likeoxydc", // PerOxyd stone 0x11 + "st-plain_break", // PerOxyd stone 0x12 no, should be destructible with hammer + "st-rock4", // PerOxyd stone 0x13 + "st-beads", // PerOxyd stone 0x14 + "st-rock5", // PerOxyd stone 0x15 + "st-rock7", // PerOxyd stone 0x16 + "st-white1", // PerOxyd stone 0x17 (Looks a little bit different, but function should be the same.) + "st-grate1", // PerOxyd stone 0x18 + UNUSED, // PerOxyd stone 0x19 + "st-surprise", // PerOxyd stone 0x1a + "st-chameleon", // PerOxyd stone 0x1b (levels: 23 142 178 183) + "st-brick_magic", // PerOxyd stone 0x1c + "st-camouflage", // PerOxyd stone 0x1d (green camouflage piece)(if st-camouflage is implemented, put it in here) + "st-camouflage", // PerOxyd stone 0x1e (green camouflage piece) + UNUSED, // PerOxyd stone 0x1f + "st-stone2", // PerOxyd stone 0x20 + "st-greenbrown", // PerOxyd stone 0x21 + "st-glass", // PerOxyd stone 0x22 (semi opaque piece, not really glass) + "st-glass", // PerOxyd stone 0x23 (must be st-glass, see link level 92) + "st-plain_break", // PerOxyd stone 0x24 + "st-bluegray_hole", // PerOxyd stone 0x25 + "st-plain_move", // PerOxyd stone 0x26 + "st-wood", // PerOxyd stone 0x27 + "st-switch", // PerOxyd stone 0x28 + "st-switch_black", // PerOxyd stone 0x29 + "st-switch_white", // PerOxyd stone 0x2a + "st-fourswitch", // PerOxyd stone 0x2b + "st-key", // PerOxyd stone 0x2c + "st-floppy", // PerOxyd stone 0x2d + "", // PerOxyd stone 0x2e (?) + "st-death", // PerOxyd stone 0x2f + "st-death_invisible", // PerOxyd stone 0x30 + "st-oneway_black-w", // PerOxyd stone 0x31 + "st-oneway_black-e", // PerOxyd stone 0x32 + "st-oneway_black-n", // PerOxyd stone 0x33 + "st-oneway_black-s", // PerOxyd stone 0x34 + "st-oneway_white-w", // PerOxyd stone 0x35 + "st-oneway_white-e", // PerOxyd stone 0x36 + "st-oneway_white-n", // PerOxyd stone 0x37 + "st-oneway_white-s", // PerOxyd stone 0x38 + "st-magic", // PerOxyd stone 0x39 ('st-magic') + "st-magic", // PerOxyd stone 0x3a ('st-magic') + "st-magic", // PerOxyd stone 0x3b ('st-magic') + "st-actorimpulse", // PerOxyd stone 0x3c + "st-actorimpulse_invisible", // PerOxyd stone 0x3d + "st-laser-1", // PerOxyd stone 0x3e The laser-names are fake names! + "st-laser-2", // PerOxyd stone 0x3f Direction and state are generated by Enigma. + "st-laser-3", // PerOxyd stone 0x40 + "st-mirror-p|", // PerOxyd stone 0x41 + "st-mirror-p/", // PerOxyd stone 0x42 + "st-mirror-p-", // PerOxyd stone 0x43 + "st-mirror-p\\", // PerOxyd stone 0x44 + UNUSED, // PerOxyd stone 0x45 + "st-mirror-p/m", // PerOxyd stone 0x46 + "st-mirror-p-m", // PerOxyd stone 0x47 + "st-mirror-p\\m", // PerOxyd stone 0x48 + "st-mirror-p|t", // PerOxyd stone 0x49 + "st-mirror-p/t", // PerOxyd stone 0x4a + "st-mirror-p-t", // PerOxyd stone 0x4b + "st-mirror-p\\t", // PerOxyd stone 0x4c + "st-mirror-3>", // PerOxyd stone 0x4d + "st-mirror-3^", // PerOxyd stone 0x4e + "st-mirror-3<", // PerOxyd stone 0x4f + "st-mirror-3v", // PerOxyd stone 0x50 + "st-puzzle-es", // PerOxyd stone 0x51 + "st-puzzle-sw", // PerOxyd stone 0x52 + "st-puzzle-nw", // PerOxyd stone 0x53 + "st-puzzle-ne", // PerOxyd stone 0x54 + "st-puzzle-ew", // PerOxyd stone 0x55 + "st-puzzle-ns", // PerOxyd stone 0x56 + "st-puzzle-n", // PerOxyd stone 0x57 + "st-puzzle-s", // PerOxyd stone 0x58 + "st-puzzle-w", // PerOxyd stone 0x59 + "st-puzzle-e", // PerOxyd stone 0x5a + "st-puzzle-nesw", // PerOxyd stone 0x5b + "st-puzzle-hollow", // PerOxyd stone 0x5c + "st-laserbreak", // PerOxyd stone 0x5d + "st-coffee", // PerOxyd stone 0x5e + "st-shogun", // PerOxyd stone 0x5f (oxyd with a hole, movable ... strange stone! st-shogun as workaround, only link level 74) + "st-disco-dark", // PerOxyd stone 0x60 + "st-disco-medium", // PerOxyd stone 0x61 + "st-bombs", // PerOxyd stone 0x62 + "st-flash", // PerOxyd stone 0x63 + "st-coinslot", // PerOxyd stone 0x64 + "st-thief", // PerOxyd stone 0x65 + "st-shogun-s", // PerOxyd stone 0x66 + "st-shogun-m", // PerOxyd stone 0x67 + "st-shogun-l", // PerOxyd stone 0x68 + "st-shogun-sml", // PerOxyd stone 0x69 + "st-shogun-ml", // PerOxyd stone 0x6a + "st-shogun-sl", // PerOxyd stone 0x6b + "st-shogun-sm", // PerOxyd stone 0x6c + "st-stoneimpulse", // PerOxyd stone 0x6d + "st-lasertimeswitch", // PerOxyd stone 0x6e + "st-mail-n", // PerOxyd stone 0x6f + "st-mail-w", // PerOxyd stone 0x70 + "st-mail-e", // PerOxyd stone 0x71 + "st-mail-s", // PerOxyd stone 0x72 + "st-door-h", // PerOxyd stone 0x73 + "st-door-v", // PerOxyd stone 0x74 + "st-metal", // PerOxyd stone 0x75 + "st-stonebrush", // PerOxyd stone 0x76 + "st-door-h-open", // PerOxyd stone 0x77 + "st-door-v-open", // PerOxyd stone 0x78 + "st-white1", // PerOxyd stone 0x79 (Can be either st-white1 or st-white4) + "st-black1", // PerOxyd stone 0x7a (Can be either st-black1 or st-black4) + "st-metal_hole", // PerOxyd stone 0x7b (because it looks similar ...; only link level 79) + 0, // PerOxyd stone 0x7c (probably complex stone, behavior unknown)(only levels: 23 142, these levels are identical!) + "st-yinyang3", // PerOxyd stone 0x7d + "st-break_acwhite", // PerOxyd stone 0x7e + "st-break_acblack", // PerOxyd stone 0x7f + "st-chargeplus", // PerOxyd stone 0x80 + "st-chargeminus", // PerOxyd stone 0x81 + "st-chargezero", // PerOxyd stone 0x82 + "st-bolder-n", // PerOxyd stone 0x83 + "st-bolder-s", // PerOxyd stone 0x84 + "st-bolder-w", // PerOxyd stone 0x85 + "st-bolder-e", // PerOxyd stone 0x86 + "st-rock5", // PerOxyd stone 0x87 + UNUSED, // PerOxyd stone 0x88 + UNUSED, // PerOxyd stone 0x89 + "st-rock6", // PerOxyd stone 0x8a + "st-blue-sand", // PerOxyd stone 0x8b + "st-rock1", // PerOxyd stone 0x8c + "st-rock2", // PerOxyd stone 0x8d (simple border stone, not exactly st-rock2 ...; only link level 92) + "st-rock2", // PerOxyd stone 0x8e + UNUSED, // PerOxyd stone 0x8f + "st-rock8", // PerOxyd stone 0x90 + "st-rotator_move-left", // PerOxyd stone 0x91 + "st-rotator_move-right", // PerOxyd stone 0x92 + "st-swap", // PerOxyd stone 0x93 + "st-spitter", // PerOxyd stone 0x94 + 0, // PerOxyd stone 0x95 (dynamite holder, will implememnt it)(levels: 41 184 200) + "st-rubberband", // PerOxyd stone 0x96 + "st-scissors", // PerOxyd stone 0x97 + "st-grate3", // PerOxyd stone 0x98 + "st-blocker", // PerOxyd stone 0x99 + "st-grate1", // PerOxyd stone 0x9a + "st-metal_hole", // PerOxyd stone 0x9b + "st-stone1", // PerOxyd stone 0x9c + "st-fart", // PerOxyd stone 0x9d + "st-turnstile", // PerOxyd stone 0x9e + "st-turnstile-n", // PerOxyd stone 0x9f + "st-turnstile-s", // PerOxyd stone 0xa0 + "st-turnstile-w", // PerOxyd stone 0xa1 + "st-turnstile-e", // PerOxyd stone 0xa2 + UNUSED, // PerOxyd stone 0xa3 + "st-likeoxydc", // PerOxyd stone 0xa4 laserswitch on + UNUSED, // PerOxyd stone 0xa5 + UNUSED, // PerOxyd stone 0xa6 + UNUSED, // PerOxyd stone 0xa7 + UNUSED, // PerOxyd stone 0xa8 + UNUSED, // PerOxyd stone 0xa9 + UNUSED, // PerOxyd stone 0xaa + UNUSED, // PerOxyd stone 0xab + UNUSED, // PerOxyd stone 0xac + UNUSED, // PerOxyd stone 0xad + UNUSED, // PerOxyd stone 0xae + UNUSED, // PerOxyd stone 0xaf + UNUSED, // PerOxyd stone 0xb0 + UNUSED, // PerOxyd stone 0xb1 + UNUSED, // PerOxyd stone 0xb2 + UNUSED, // PerOxyd stone 0xb3 + UNUSED, // PerOxyd stone 0xb4 + UNUSED, // PerOxyd stone 0xb5 + UNUSED, // PerOxyd stone 0xb6 + "st-easymode", // PerOxyd stone 0xb7 + "st-peroxyd-0xb8", // PerOxyd stone 0xb8 + "st-peroxyd-0xb9", // PerOxyd stone 0xb9 + // codes >= 0xba are unused +}; + + +ITEMMAPTYPE oxyd::peroxyd_item_map[256] = { + ITEMSPEC(it_none), // 0x00 + ITEMSPEC(it_extralife), // 0x01 + ITEMSPEC(it_EXTERNAL), // 0x02 document 1 + ITEMSPEC(it_EXTERNAL), // 0x03 document 2 + ITEMSPEC(it_hammer), // 0x04 + ITEMSPEC(it_coffee), // 0x05 + ITEMSPEC(it_cherry), // 0x06 + ITEMSPEC(it_umbrella), // 0x07 + ITEMSPEC(it_glasses), // 0x08 + ITEMSPEC(it_glasses_broken), // 0x09 + ITEMSPEC(it_dynamite), // 0x0a + ITEMSPEC(it_blackbomb), // 0x0b + ITEMSPEC(it_whitebomb), // 0x0c + ITEMSPEC(it_crack0), // 0x0d + ITEMSPEC(it_crack1), // 0x0e + ITEMSPEC(it_crack2), // 0x0f + ITEMSPEC(it_crack3), // 0x10 + ITEMSPEC(it_coin1), // 0x11 + ITEMSPEC(it_coin2), // 0x12 + ITEMSPEC(it_coin4), // 0x13 + ITEMSPEC(it_key_a), // 0x14 + ITEMSPEC(it_key_b), // 0x15 + ITEMSPEC(it_key_c), // 0x16 + ITEMSPEC(it_floppy), // 0x17 + ITEMSPEC(it_flagwhite), // 0x18 + ITEMSPEC(it_flagblack), // 0x19 + ITEMSPEC(it_ring), // 0x1a + ITEMSPEC(it_pipe_wn), // 0x1b + ITEMSPEC(it_pipe_sw), // 0x1c + ITEMSPEC(it_pipe_ne), // 0x1d + ITEMSPEC(it_pipe_es), // 0x1e + ITEMSPEC(it_pipe_v), // 0x1f + ITEMSPEC(it_pipe_h), // 0x20 + ITEMSPEC(it_spade), // 0x21 + ITEMSPEC(it_surprise), // 0x22 + ITEMSPEC(it_pin), // 0x23 + ITEMSPEC(it_seed), // 0x24 + ITEMSPEC(it_spring2), // 0x25 + ITEMSPEC(it_spring1), // 0x26 + ITEMSPEC(it_bag), // 0x27 + ITEMSPEC(it_magnet_off), // 0x28 + ITEMSPEC(it_signalfilter0), // 0x29 + ITEMSPEC(it_signalfilter1), // 0x2a + ITEMSPEC(it_shogun_s), // 0x2b + ITEMSPEC(it_shogun_l), // 0x2c + ITEMSPEC(it_vortex_open), // 0x2d + ITEMSPEC(it_vortex_closed), // 0x2e + ITEMSPEC(it_wormhole_on), // 0x2f XXX + ITEMSPEC(it_hill), // 0x30 + ITEMSPEC(it_tinyhill), // 0x31 + ITEMSPEC(it_hollow), // 0x32 + ITEMSPEC(it_tinyhollow), // 0x33 + ITEMSPEC(it_vstrip), // 0x34 + ITEMSPEC(it_hstrip), // 0x35 + ITEMSPEC(it_springboard), // 0x36 + ITEMSPEC(it_MISSING), // 0x37 + ITEMSPEC(it_bridge_oxyd), // 0x38 + ITEMSPEC(it_UNUSED), // 0x39 + ITEMSPEC(it_UNUSED), // 0x3a + ITEMSPEC(it_UNUSED), // 0x3b + ITEMSPEC(it_cross), // 0x3c + ITEMSPEC(it_spoon), // 0x3d + ITEMSPEC(it_MISSING), // 0x3e rubber band + ITEMSPEC(it_changefloor), // 0x3f + ITEMSPEC(it_trigger), // 0x40 + ITEMSPEC(it_brush), // 0x41 + ITEMSPEC(it_banana), // 0x42 + ITEMSPEC(it_pencil), // 0x43 + ITEMSPEC(it_brake), // 0x44 + ITEMSPEC(it_squashed), // 0x45 + ITEMSPEC(it_blocker), // 0x46 + ITEMSPEC(it_magicwand), // 0x47 + ITEMSPEC(it_wrench), // 0x48 + ITEMSPEC(it_UNUSED), // 0x49 + ITEMSPEC(it_odometer), // 0x4a + ITEMSPEC(it_puller_n), // 0x4b + ITEMSPEC(it_puller_s), // 0x4c + ITEMSPEC(it_puller_w), // 0x4d + ITEMSPEC(it_puller_e), // 0x4e + ITEMSPEC(it_UNUSED), // 0x4f + ITEMSPEC(it_UNUSED), // 0x50 + ITEMSPEC(it_MISSING), // 0x51 puller left, active + ITEMSPEC(it_UNUSED), // 0x52 + ITEMSPEC(it_MISSING), // 0x53 oxyd on floor (?) + ITEMSPEC(it_UNUSED), // 0x54 + ITEMSPEC(it_UNUSED), // 0x55 + ITEMSPEC(it_UNUSED), // 0x56 + ITEMSPEC(it_UNUSED), // 0x57 + ITEMSPEC(it_MISSING), // 0x58 oxyd on floor + ITEMSPEC(it_MISSING), // 0x59 oxyd on floor + ITEMSPEC(it_UNUSED), // 0x5a + ITEMSPEC(it_UNUSED), // 0x5b + ITEMSPEC(it_UNUSED), // 0x5c + ITEMSPEC(it_UNUSED), // 0x5d + ITEMSPEC(it_UNUSED), // 0x5e + ITEMSPEC(it_UNUSED), // 0x5f + ITEMSPEC(it_UNUSED), // 0x60 + ITEMSPEC(it_UNUSED), // 0x61 + ITEMSPEC(it_UNUSED), // 0x62 + ITEMSPEC(it_UNUSED), // 0x63 + ITEMSPEC(it_UNUSED), // 0x64 + ITEMSPEC(it_UNUSED), // 0x65 + ITEMSPEC(it_UNUSED), // 0x66 + ITEMSPEC(it_UNUSED), // 0x67 + ITEMSPEC(it_UNUSED), // 0x68 + ITEMSPEC(it_UNUSED), // 0x69 + ITEMSPEC(it_blackbomb_burning), // 0x6a + ITEMSPEC(it_UNUSED), // 0x6b + ITEMSPEC(it_UNUSED), // 0x6c + ITEMSPEC(it_UNUSED), // 0x6d + ITEMSPEC(it_UNUSED), // 0x6e + ITEMSPEC(it_UNUSED), // 0x6f + ITEMSPEC(it_UNUSED), // 0x70 + ITEMSPEC(it_UNUSED), // 0x71 + ITEMSPEC(it_UNUSED), // 0x72 + ITEMSPEC(it_UNUSED), // 0x73 + ITEMSPEC(it_UNUSED), // 0x74 + ITEMSPEC(it_UNUSED), // 0x75 + ITEMSPEC(it_easykillstone), // 0x76 + ITEMSPEC(it_easykeepstone), // 0x77 + ITEMSPEC(it_2pkillstone), // 0x78 + ITEMSPEC(it_1pkillstone), // 0x79 + // codes >= 0x7a are unused +}; + diff --git a/project/jni/application/enigma/src/oxyd.cpp b/project/jni/application/enigma/src/oxyd.cpp new file mode 100644 index 000000000..34efa3c3b --- /dev/null +++ b/project/jni/application/enigma/src/oxyd.cpp @@ -0,0 +1,1092 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This files contains the interface between Enigma and the original + * Oxyd games. It is responsible for converting level descriptions or + * sound effects. + */ + +#include "errors.hh" +#include "oxyd.hh" +#include "sound.hh" +#include "lua.hh" +#include "server.hh" +#include "player.hh" +#include "main.hh" + +#include +#include +#include + +#include "oxyd_internal.hh" + +#define SOUND sound::GetEngine() + +using namespace std; +using namespace enigma; +using world::Stone; +using world::MakeStone; +using world::Item; +using world::MakeItem; + +using namespace oxyd; +using OxydLib::Level; + + +/* -------------------- Helper functions -------------------- */ + +namespace +{ + enigma::Direction + direction_oxyd2enigma (OxydLib::Direction odir) + { + switch (odir) { + case Direction_Up: return NORTH; break; + case Direction_Down: return SOUTH; break; + case Direction_Left: return WEST; break; + case Direction_Right: return EAST; break; + default : + fprintf(stderr, "Unknown OxydLib-direction %i!\n", int(odir)); + return NODIR; + } + } + + GameType to_gametype (OxydLib::OxydVersion ver) + { + GameType typ = GAMET_UNKNOWN; + switch (ver) { + case OxydVersion_Oxyd1: typ = GAMET_OXYD1; break; + case OxydVersion_OxydMagnum: + case OxydVersion_OxydMagnumGold: typ = GAMET_OXYDMAGNUM; break; + case OxydVersion_OxydExtra: typ = GAMET_OXYDEXTRA; break; + case OxydVersion_PerOxyd: typ = GAMET_PEROXYD; break; + default : + assert(0); + break; + } + return typ; + } + + + void dumpUnknownObjects (const OxydLib::Level& level) + { + set stones, items, floors; + + const Grid &sgrid = level.getGrid (GridType_Pieces); + for (unsigned y=0; yint_attrib("code")) + stones.insert(code); + + const Grid &igrid = level.getGrid (GridType_Objects); + for (unsigned y=0; yint_attrib("code")) + items.insert(code); + + const Grid &fgrid = level.getGrid (GridType_Objects); + for (unsigned y=0; yint_attrib("code")) + floors.insert(code); + + if (!stones.empty()) { + enigma::Log << "Unknown stones:"; + for (set::iterator i = stones.begin(); i != stones.end(); ++i) + enigma::Log << ' ' << *i; + enigma::Log << endl; + } + if (!items.empty()) { + enigma::Log << "Unknown items:"; + for (set::iterator i = items.begin(); i != items.end(); ++i) + enigma::Log << ' ' << *i; + enigma::Log << endl; + } + if (!floors.empty()) { + enigma::Log << "Unknown floors:"; + for (set::iterator i = floors.begin(); i != floors.end(); ++i) + enigma::Log << ' ' << *i; + enigma::Log << endl; + } + } + + GridLoc to_gridloc (const SignalLocation &a) + { + assert (a.getGridType() >= GridType_First && + a.getGridType() <= GridType_Last); + static GridLayer tab[3] = { GRID_FLOOR, GRID_STONES, GRID_ITEMS }; + return GridLoc (tab[a.getGridType()], GridPos(a.getX(), a.getY())); + } + + string patchfile_name (enigma::GameType t, size_t index, bool twoplayers) + { + string patchfile = "levels/patches/"; + switch (t) { + case GAMET_OXYD1: patchfile += "ox1_"; break; + case GAMET_PEROXYD: patchfile += "pox_"; break; + case GAMET_OXYDMAGNUM: patchfile += "oxm_"; break; + case GAMET_OXYDEXTRA: patchfile += "oxe_"; break; + default: + assert (0); + } + if (twoplayers) + patchfile += ecl::strf ("%03d.lua", index+101); + else + patchfile += ecl::strf ("%03d.lua", index+1); + + return patchfile; + } + + bool parse_gridpos (CommandString &cs, int levelw, int levelh, GridPos &p) + { + int pos = cs.get_int (0, levelw * levelh - 1, -1); + if (pos != -1) { + p.x = pos % levelw; + p.y = pos / levelw; + return true; + } + return false; + } + +} + + +/* -------------------- CommandString implementation -------------------- */ + +CommandString::CommandString(const string &cmd) +: m_cmd(cmd), + m_iter (m_cmd.begin()) +{} + +int CommandString::get_char () { + if (m_iter == m_cmd.end()) + return 0; + return *m_iter++; +} + +bool CommandString::get_bool (bool dflt) { + int c = get_char(); + if (c == 0xf8 || c == 0xf9) + return (c == 0xf9); + return dflt; +} + + +int CommandString::get_int (int min, int max, int dflt) +{ + int val = 0; + bool error = false; + int c = get_char(); + if (c != '(') + error = true; + else { + bool positive = true; + c = get_char(); + if (c == '+') { + c = get_char(); + } + else if (c == '-') { + positive = false; + c = get_char(); + } + if (isdigit(c)) { + val = 0; + do { + val = 10*val + (c - '0'); + c = get_char(); + } while (isdigit(c)); + if (!positive) + val = -val; + } + else + error = true; + } + return (error || val > max || val < min) ? dflt : val; +} + + +/* -------------------- OxydLoader -------------------- */ + +OxydLoader::OxydLoader (const OxydLib::Level &level_, + const LoaderConfig config_) +: level (level_), + config (config_) +{ + harmless_medi = false; +} + +void OxydLoader::load () +{ + // Prepare Enigma game engine + world::Resize (level.getWidth(), level.getHeight()); + if (config.twoplayers) + server::TwoPlayerGame = true; + display::ResizeGameArea (20, 11); + if (level.getScrolling()) + SetFollowMode (display::FOLLOW_SCROLLING); + else + SetFollowMode (display::FOLLOW_SCREEN); + + // Populate Enigma game + load_floor (); + load_items (); + load_stones (); + scramble_puzzles (); + + load_actors (); + connect_signals (); + connect_rubberbands (); + + parse_specials (); + + // Debugging output + dumpUnknownObjects(level); +} + +Stone * OxydLoader::make_laser (int type) +{ + ecl::Assert + (type >= 0 && type <= 2, + "Oxyd supports only three different lasers per level"); + + const Laser& laser = level.getLaser(type); + + enigma::Direction dir = direction_oxyd2enigma(laser.getDir()); + bool on = laser.getOn(); + + Stone *st = 0; + if (dir != NODIR) { + string lasername("st-laser"); + lasername += to_suffix(dir); + st = MakeStone(lasername.c_str()); + st->set_attrib("on", Value(on)); // OnOffStone attribute + } + return st; +} + +Stone *OxydLoader::make_timer (int x, int y) +{ + const OscillatorMap &oscillators = level.getOscillators(config.gamemode); + Stone *st = world::MakeStone ("st-timer"); + st->set_attrib("interval", Value(0.2)); + + Block block(x, y); + OscillatorMap::const_iterator i = oscillators.find(block); + if (i != oscillators.end()) { + const Oscillator &o = i->second; + st->set_attrib("on", Value(o.getOn())); + double interval = (1 + o.getPeriod()) * config.timer_factor; + st->set_attrib("interval", Value(interval)); + } + return st; +} + +Stone *OxydLoader::make_stone (int type, int x, int y) +{ + world::Stone *st = 0; + + if (type == 0) { + // ignore + } + else if (type >= 0x01 && type <= 0x10) { + // Oxyd stones of different colors + char color[2] = "0"; + color[0] += (type-1) / 2; + + st = world::MakeStone("st-oxyd"); + st->set_attrib("color", color); + st->set_attrib("flavor", config.oxyd_flavor); + } + else if (type == config.id_timer) { + st = make_timer (x, y); + } + else if (type >= config.id_laser1 && type < config.id_laser1 + 3) { + st = make_laser (type - config.id_laser1); + } + else { + // No special case -> get Stone from map + const char *name = config.stonetable[type]; + if (name == 0) { + Log << ecl::strf("Unknown stone %X\n", type); + st = world::MakeStone ("st-dummy"); + st->set_attrib("code", type); + } + else if (name[0] != '\0') { // ignore if name=="" + st = world::MakeStone (name); + } + } + return st; +} + +static std::string convert_encoding (const std::string &t) +{ + std::string tt = t; + for (size_t i = 0; i 128) + Log << "Unknown character in Oxyd text: " << int(c) << ' '; + break; + } + } + Log << ">> '" << t << "'\n"; + Log << ">> '" << tt << "'\n"; + return tt; +} + +Item *OxydLoader::make_item (int type) +{ + using namespace world; + + Item *it = 0; + + OxydLib::Language lang = Language_English; + std::string localelang = ecl::GetLanguageCode (ecl::SysMessageLocaleName()); + if (localelang == "de") + lang = Language_German; + else if (localelang == "fr") + lang = Language_French; + + switch (type) { + case 0x00: break; // ignore + case 0x02: // note 1 + { + it = MakeItem (world::it_document); + string text = convert_encoding(level.getNoteText(0, lang)); + it->set_attrib ("text", text.c_str()); + } + break; + case 0x03: // note 2 + { + it = MakeItem (world::it_document); + string text = convert_encoding(level.getNoteText(1, lang)); + it->set_attrib ("text", text.c_str()); + } + break; + default: + { + ItemID id = config.itemtable[type]; + if (id == it_INVALID) { + Log << ecl::strf ("Unknown item %X\n",type); + it = MakeItem (world::it_dummy); + it->set_attrib("code", type); + } + else + it = MakeItem (id); + } + } + return it; +} + + +void OxydLoader::load_floor () +{ + using namespace world; + + const Grid &grid = level.getGrid (GridType_Surfaces); + for (unsigned y=0; yset_attrib("code", code); + } + else { + fl = MakeFloor(name); + } + + SetFloor (GridPos(x, y), fl); + } + } +} + +void OxydLoader::load_items () +{ + const Grid &grid = level.getGrid (GridType_Objects); + for (unsigned y=0; yset_attrib ("player", Value(0.0)); + break; + case MarbleType_White: + ac = MakeActor (world::ac_whiteball); + ac->set_attrib ("player", Value(1.0)); + break; + case MarbleType_Meditation: + if (have_black_marble && !level.getHarmlessMeditationMarbles()) { + // # example: Oxyd Extra #28 + ac = MakeActor (world::ac_killerball); +// ac->set_attrib ("player", Value(0.0)); + ac->set_attrib ("mouseforce", Value (1.0)); + ac->set_attrib ("controllers", Value (3.0)); + } + else { + ac = MakeActor (world::ac_meditation); + nmeditationmarbles += 1; + + if (config.twoplayers && (nmeditationmarbles % 2) == 0) + ac->set_attrib("player", Value(1.0)); + else + ac->set_attrib ("player", Value(0.0)); + } + + if (minfo.is_default(MI_FORCE)) { + ac->set_attrib("mouseforce", Value(1.0)); + } + else { + ac->set_attrib("mouseforce", Value(minfo.get_value(MI_FORCE) / 32.0)); // just a guess + } + break; + case MarbleType_Jack: + ac = MakeActor (world::ac_top); + if (!minfo.is_default(MI_FORCE)) { + double force = minfo.get_value(MI_FORCE) / 4; // just a guess + ac->set_attrib("force", Value(force) ); + enigma::Log << "Set jack force to " << force << endl; + } + if (!minfo.is_default(MI_RANGE)) { + double range = minfo.get_value(MI_RANGE) / 32.0; // value seems to contain distance in pixels + ac->set_attrib("range", Value(range) ); + enigma::Log << "Set jack range to " << range << endl; + } + break; + + case MarbleType_Rotor: { + ac = MakeActor (world::ac_rotor); + + double force = minfo.get_value (MI_FORCE, 30) * 0.3; + double range = minfo.get_value (MI_RANGE, 100) / 32.0; + int gohome = minfo.get_value (MI_GOHOME, 1); + + ac->set_attrib ("force", force); + ac->set_attrib ("range", range); + ac->set_attrib ("gohome", Value (gohome)); + + enigma::Log << "rotor force " << force << endl; + enigma::Log << "rotor range " << range << endl; + + break; + } + + case MarbleType_Horse: { + ac = MakeActor (world::ac_horse); + int levelw = level.getWidth(); + if (!minfo.is_default(MI_HORSETARGET1)) { + int targetpos = minfo.get_value(MI_HORSETARGET1); + ac->set_attrib("target1", ecl::strf("%d %d", + targetpos % levelw, + int(targetpos / levelw))); + } + if (!minfo.is_default(MI_HORSETARGET2)) { + int targetpos = minfo.get_value(MI_HORSETARGET2); + ac->set_attrib("target2", ecl::strf("%d %d", + targetpos % levelw, + int(targetpos / levelw))); + } + if (!minfo.is_default(MI_HORSETARGET3)) { + int targetpos = minfo.get_value(MI_HORSETARGET3); + ac->set_attrib("target3", ecl::strf("%d %d", + targetpos % levelw, + int(targetpos / levelw))); + } + if (!minfo.is_default(MI_HORSETARGET4)) { + int targetpos = minfo.get_value(MI_HORSETARGET4); + ac->set_attrib("target4", ecl::strf("%d %d", + targetpos % levelw, + int(targetpos / levelw))); + } + break; + } + case MarbleType_Bug: + ac = MakeActor (world::ac_bug); + break; + default: + enigma::Log << "Unhandled actor type " << int(marble.getMarbleType()) << endl; + break; +// case MarbleType_LifeSpitter: +// case MarbleType_DynamiteHolder: +// break; + } + + if (ac) + world::AddActor (x, y, ac); + + m_actors.push_back (ac); + } +} + +void OxydLoader::connect_rubberbands () +{ + GameMode game_mode = config.gamemode; + int num_rubberbands = level.getNumRubberBands(game_mode); + for (int i=0; i senders; + level.getSenders(&senders); + set::const_iterator senderIter = senders.begin(); + set::const_iterator senderEnd = senders.end(); + for (; senderIter != senderEnd; ++senderIter) { + const SignalLocation &sender = *senderIter; + + int nrec = level.getNumRecipients(sender); + for (int irec=0; irec &special = level.getSpecialItems(); + vector numberlist; + int levelw = level.getWidth(); + int levelh = level.getHeight(); + for (size_t i=0; igetLevel(i).empty()) { + level_index[nlevels] = i; + + char txt[5]; + snprintf(txt, sizeof(txt), "%d", nlevels); + lev::Proxy * aProxy = lev::Proxy::registerLevel( + "#" + get_name() + "#" + txt, "#" + get_name(), + ecl::strf ("Import %s %d", get_name().c_str(), nlevels), + ecl::strf ("%s #%d", get_name().c_str(), nlevels+1), + "Dongleware", 1, 1, has_easymode(i), get_gametype()); + proxies.push_back(aProxy); + nlevels++; + } + } + // post initialization of superclass Index based on dat file values + indexName = get_name(); + indexGroup = defaultGroup = "Dongleware"; + indexDefaultLocation = get_default_location(); + indexLocation = indexDefaultLocation; + Log << "Levelpack '" << get_name() << "' has " << nlevels << " levels." << endl; +} + +const char* LevelPack_Oxyd::get_default_SoundSet() const +{ + return sound::GetOxydSoundSet(m_datfile->getVersion()).c_str(); +} + +bool LevelPack_Oxyd::needs_twoplayers() const +{ + return m_twoplayers; +} + +bool LevelPack_Oxyd::has_easymode(size_t index) const { + if (get_version() != OxydVersion_PerOxyd) + return false; + else + return LP_PerOxyd::hasEasymode(index); +} + +GameType LevelPack_Oxyd::get_gametype() const +{ + return to_gametype(m_datfile->getVersion()); +} + +GameMode LevelPack_Oxyd::get_gamemode() const +{ + return (server::GetDifficulty() == DIFFICULTY_EASY + ? GameMode_Easy : GameMode_Hard); +} + +string LevelPack_Oxyd::get_name() const +{ + static const char *names1p[] = { + "Oxyd 1", "Oxyd magnum", "Magnum Gold", "Per.Oxyd", "Oxyd extra" + }; + static const char *names2p[] = { + "Oxyd 1 (2p)", "", "", "Per.Oxyd (2p)", "Oxyd extra (2p)" + }; + OxydVersion v = get_version(); + return level_index[0]>99 ? names2p[v] : names1p[v]; +} + +int LevelPack_Oxyd::get_default_location() const +{ + static const int location1p[] = { + 90100, 90700, 90800, 90300, 90500 + }; + static const int location2p[] = { + 90200, 0, 0, 90400, 90600 + }; + OxydVersion v = get_version(); + return level_index[0]>99 ? location2p[v] : location1p[v]; +} + +void LevelPack_Oxyd::load_oxyd_level (size_t index) +{ + ecl::Assert (index < size(), "Invalid level index"); + + // Prepare level data + string msg; + Level level; + if (!parseLevel (m_datfile->getLevel(level_index[index]), &level, &msg)) { + throw XLevelLoading(msg); + } + + // Load level + load (level); + + // Apply patch file + string patchname = patchfile_name (get_gametype(), index, m_twoplayers); + string patchfile; + if (app.resourceFS->findFile (patchname, patchfile)) { + if (lua::Dofile (lua::LevelState(), patchname) != 0) { + string err = string("While executing '")+patchname+"':\n"+lua::LastError(lua::LevelState()); + throw XLevelLoading(err); + } + } +} + + +/* -------------------- Oxyd extra level pack -------------------- */ + +LP_OxydExtra::LP_OxydExtra (DatFile *dat) +: LevelPack_Oxyd (OxydVersion_OxydExtra, dat, 0, 99, false) +{ +} + + +void LP_OxydExtra::load (const OxydLib::Level &level) +{ + LoaderConfig c (needs_twoplayers(), + get_gamemode(), + oxydextra_floor_map, + oxydextra_item_map, + oxydextra_stone_map); +// c.id_timer = 0x2e; + c.id_laser1 = 0x3e; + PerOxydLoader (level, c).load(); +} + + + +/* -------------------- Oxyd magnum level pack -------------------- */ + +LP_OxydMagnum::LP_OxydMagnum(OxydVersion version, DatFile *dat) +: LevelPack_Oxyd (version, dat, 0, + (version==OxydVersion_OxydMagnumGold) ? 120 : 99, + false) +{ +} + +void LP_OxydMagnum::load (const OxydLib::Level &level) +{ + LoaderConfig c (needs_twoplayers(), + get_gamemode(), + oxydmag_floor_map, + oxydmag_item_map, + oxydmag_stone_map); + c.id_timer = 0x33; + c.id_laser1 = 0x44; + PerOxydLoader (level, c).load(); + + // Add a yinyang item if a white marble is present + if (world::CountActorsOfKind (world::ac_whiteball) > 0) + player::AddYinYang(); +} + + +/* -------------------- MarbleInfo -------------------- */ + +MarbleInfo::MarbleInfo (const Marble& marble) +{ + const string& data = marble.getData(server::GetDifficulty() == DIFFICULTY_EASY + ? GameMode_Easy : GameMode_Hard); + size_t from = 0; + int idx = 0; + + while (from != string::npos) { + size_t par_open = data.find('(', from); + from = string::npos; + + if (par_open != string::npos) { + size_t par_close = data.find(')', par_open); + if (par_close != string::npos) { + from = par_close; + if (par_close == par_open+1) { + value[idx++] = DEFAULT_VALUE; + } + else { + value[idx++] = atoi(data.substr(par_open+1, par_close-par_open-1).c_str()); + } + } + else { + Log << "Error in MarbleInfo: missing closing parenthesis" << endl; + } + } + } + for (; idxfindFile (datfile_name_, datfile_path) || + app.resourceFS->findFile (alt_datfile_name, datfile_path)) { + enigma::Log << "Found " << game << " data file\n"; + m_present = true; + openDatFile(); + + if (m_present) { + lev::Index::registerIndex(makeLevelIndex(false)); + lev::Index::registerIndex(makeLevelIndex(true)); + } + } +} + +void GameInfo::openDatFile() +{ + assert(m_present); + + OxydLib::ByteVec data; + readFile (datfile_path, &data); + + datfile = new DatFile; + + string errmsg; + if (!parseDatFile (data, ver, datfile, &errmsg)) { + enigma::Log << "Error loading " << datfile_path << ": " << errmsg << endl; + delete datfile; + datfile = 0; + m_present = false; + } else { + enigma::Log << "Loaded "<< datfile_path << endl; + } +} + +lev::Index *GameInfo::makeLevelIndex(bool twoplayers) +{ + if (datfile == 0) + return 0; + + if (twoplayers && (ver == OxydVersion_OxydExtra || + ver == OxydVersion_OxydMagnum || + ver == OxydVersion_OxydMagnumGold)) + { + return 0; // no twoplayer levels available + } + + switch (ver) { + case OxydVersion_Oxyd1: + return new LP_Oxyd1 (datfile, twoplayers); + case OxydVersion_OxydExtra: + return new LP_OxydExtra(datfile); + case OxydVersion_PerOxyd: + return new LP_PerOxyd (datfile, twoplayers); + case OxydVersion_OxydMagnum: + case OxydVersion_OxydMagnumGold: + return new LP_OxydMagnum (ver, datfile); + + default: + return 0; + } +} + + +/* -------------------- Local variables -------------------- */ + +namespace +{ + vector games; +} + + + +/* -------------------- Functions -------------------- */ + +void oxyd::Init(bool searchDAT) +{ + games.clear(); + games.resize(OxydVersion_Count); + + games[OxydVersion_Oxyd1] + = new GameInfo (OxydVersion_Oxyd1, "Oxyd 1", "oxyd1ibm.dat", searchDAT); + games[OxydVersion_OxydMagnum] + = new GameInfo (OxydVersion_OxydMagnum, "Oxyd magnum", "oxydmibm.dat", searchDAT); + games[OxydVersion_OxydMagnumGold] + = new GameInfo(OxydVersion_OxydMagnumGold, "Oxyd magnum gold", "oxydmgg.dat", searchDAT); + games[OxydVersion_OxydExtra] + = new GameInfo(OxydVersion_OxydExtra, "Oxyd extra", "oxydex.dat", searchDAT); + games[OxydVersion_PerOxyd] + = new GameInfo(OxydVersion_PerOxyd, "Per.Oxyd", "peroxyd.dat", searchDAT); +} + +void oxyd::Shutdown() +{ + ecl::delete_sequence(games.begin(), games.end()); +} + +bool oxyd::FoundOxyd (OxydVersion ver) { + return games[ver]->is_present(); +} + +bool oxyd::InitOxydSoundSet(OxydVersion ver) +{ + GameInfo& gi = *(games[ver]); + + if (!gi.is_present()) + return false; // not installed -> use enigma soundset + + static const char *oxydsounds[] = { + "OXBLOOP.SDD", "OXBOING.SDD", "OXBOLD.SDD", "OXCRACK.SDD", + "OXCRASH1.SDD", "OXCRASH2.SDD", "OXCUT.SDD", "OXDROP.SDD", + "OXEXIT.SDD", "OXFINITO.SDD", "OXINTRO.SDD", "OXINVENT.SDD", + "OXINVROT.SDD", "OXJUMP.SDD", "OXKLICK1.SDD", "OXKLICK2.SDD", + "OXKLICK3.SDD", "OXKLICK4.SDD", "OXKLICK5.SDD", "OXKLICK6.SDD", + "OXKLIRR.SDD", "OXLASER.SDD", "OXMAGIC.SDD", "OXMAGIC2.SDD", + "OXMAGIC3.SDD", "OXMAGIC4.SDD", "OXMATSCH.SDD", "OXMEMCL.SDD", + "OXMEMOK.SDD", "OXMEMOP.SDD", "OXMONEY.SDD", "OXMOTOR.SDD", + "OXMOVE.SDD", "OXPULLER.SDD", "OXSWOFF.SDD", "OXSWON.SDD", + "OXTHIEF.SDD", "OXTRANS.SDD", "OXTURN.SDD", "OXUNTITL.SDD", + "OXWOUOU.SDD", 0 + }; + + OxydLib::DatFile *datfile = gi.getDatfile(); + for (int i=0; oxydsounds[i]; ++i) { + string chunkname = oxydsounds[i]; + const OxydLib::ByteVec *snddata = datfile->getChunk(chunkname); + + if (snddata) { + //enigma::Log << "Loaded sound file " << chunkname<< "\n"; + + sound::SoundData snd; + snd.buf.assign (snddata->begin(), snddata->end() - 4); +// snd.buf = *snddata; + snd.freq = 6274; + snd.signedp = true; + snd.samplesize = 1; + snd.nchannels = 1; + sound::DefineSound (oxydsounds[i], snd); + } + } + + return true; +} diff --git a/project/jni/application/enigma/src/oxyd.hh b/project/jni/application/enigma/src/oxyd.hh new file mode 100644 index 000000000..8e5a36149 --- /dev/null +++ b/project/jni/application/enigma/src/oxyd.hh @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef ENIGMA_OXYD_HH +#define ENIGMA_OXYD_HH + +#include "oxydlib/OxydVersion.h" + +namespace oxyd +{ + void Init(bool searchDAT); + void Shutdown(); + + bool FoundOxyd (OxydLib::OxydVersion ver); + + // Initialise an oxyd sound set + bool InitOxydSoundSet(OxydLib::OxydVersion ver); +} + +#endif diff --git a/project/jni/application/enigma/src/oxyd_internal.hh b/project/jni/application/enigma/src/oxyd_internal.hh new file mode 100644 index 000000000..9eba02d19 --- /dev/null +++ b/project/jni/application/enigma/src/oxyd_internal.hh @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef OXYD_INTERNAL_HH +#define OXYD_INTERNAL_HH + +#include "world.hh" + +#include "oxydlib/DatFile.h" +#include "oxydlib/FileUtils.h" +#include "oxydlib/Level.h" +#include "lev/Proxy.hh" +#include "lev/Index.hh" + +#include + +#define it_UNUSED it_INVALID +#define it_EXTERNAL it_INVALID +#define it_MISSING it_INVALID + +#define UNUSED "" +#define SPECIAL "" + +namespace oxyd +{ + using namespace OxydLib; + using namespace enigma; + + using OxydLib::Level; + +/* -------------------- OxydLoader -------------------- */ + + /** This data structure is used to encode differences between different + Oxyd versions and other .*/ + struct LoaderConfig { + // Variables + const char *oxyd_flavor; + double timer_factor; + bool twoplayers; + GameMode gamemode; + const char **floortable; + world::ItemID *itemtable; + const char **stonetable; + + int id_timer; + int id_laser1; + + // Constructor + LoaderConfig (bool twoplayers_, + GameMode gamemode_, + const char **floortable_, + world::ItemID *itemtable_, + const char **stonetable_, + const char *oxyd_flavor_ = "a" + ) + : oxyd_flavor(oxyd_flavor_), + timer_factor (0.678), + twoplayers (twoplayers_), + gamemode (gamemode_), + floortable (floortable_), + itemtable (itemtable_), + stonetable (stonetable_), + id_timer (-1), + id_laser1 (-1) + {} + }; + + class OxydLoader { + public: + OxydLoader (const Level &level_, + const LoaderConfig config_); + + virtual ~OxydLoader() + {} + + void load(); + + Stone *make_laser (int type); + Stone *make_timer (int x, int y); + + /* ---------- OxydLoader interface ---------- */ + + virtual Stone *make_stone (int type, int x, int y); + virtual Item *make_item (int type); + + + /* ---------- Variables ---------- */ + const Level &level; + LoaderConfig config; + + private: + /* ---------- Private methods ---------- */ + void load_floor (); + void load_items (); + void load_stones (); + void load_actors (); + void parse_specials (); + void scramble_puzzles (); + + void connect_rubberbands (); + void connect_signals (); + world::Actor *get_actor (int idx); + + + /* ---------- Private variables ---------- */ + std::vector m_actors; + bool harmless_medi; + }; + + class Oxyd1Loader : public OxydLoader { + public: + Oxyd1Loader (const OxydLib::Level &level_, + const LoaderConfig &config_) + : OxydLoader (level_, config_) + {} + + virtual Stone *make_stone (int type, int x, int y) { return 0; } + }; + + class PerOxydLoader : public OxydLoader { + public: + PerOxydLoader (const OxydLib::Level &level_, + const LoaderConfig &config_) + : OxydLoader (level_, config_) + {} + + virtual Stone *make_stone (int type, int x, int y); + }; + + +/* -------------------- LevelPack_Oxyd -------------------- */ + + class LevelPack_Oxyd : public lev::Index { + public: + LevelPack_Oxyd (OxydVersion ver, DatFile *dat, + int idx_start, int idx_end, bool twoplayers); + + void load_oxyd_level (size_t index); + + /* ---------- LevelPack interface ---------- */ + string get_name() const; + int size() const { return nlevels; } + const char* get_default_SoundSet() const; + bool needs_twoplayers() const; + protected: + virtual bool has_easymode(size_t /*index*/) const; + GameMode get_gamemode() const; + + private: + /* ---------- Private interface ---------- */ + + virtual void load (const Level &level) = 0; + + /* ---------- Private methods ---------- */ + + GameType get_gametype() const; + OxydVersion get_version () const { + return m_datfile->getVersion(); + } + int get_default_location() const; + + /* ---------- Variables ---------- */ + DatFile *m_datfile; // just a reference (owned by GameInfo) + bool m_twoplayers; // true -> twoplayer game + + int m_index_start; // first index of this level pack + int level_index[120]; + int nlevels; + }; + + class LP_Oxyd1 : public LevelPack_Oxyd { + // LevelPack_Oxyd interface + void load (const Level &); + public: + LP_Oxyd1 (DatFile *dat, bool twoplayers); + }; + + class LP_OxydExtra : public LevelPack_Oxyd { + // LevelPack_Oxyd interface + void load (const Level &); + public: + LP_OxydExtra (DatFile *dat); + }; + + class LP_PerOxyd : public LevelPack_Oxyd { + // LevelPack_Oxyd interface + void load (const Level &); + public: + LP_PerOxyd (DatFile *dat, bool twoplayers); + static bool hasEasymode(size_t index); + }; + + class LP_OxydMagnum : public LevelPack_Oxyd { + // LevelPack_Oxyd interface + void load (const Level &); + public: + LP_OxydMagnum(OxydVersion, DatFile *dat); + }; + + /* -------------------- CommandString -------------------- */ + + /** A class used to decode "command strings" used for + settings in Oxyd levels. */ + class CommandString { + public: + CommandString(const string &cmd); + + int get_int (int min, int max, int dflt); + int get_char (); + bool get_bool (bool dflt); + private: + // Variables + string m_cmd; + string::iterator m_iter; + }; + + + /* -------------------- GameInfo -------------------- */ + + class GameInfo { + public: + GameInfo(); + GameInfo (OxydVersion ver_, const string &game_, const string &datfile_name_, const bool searchDAT); + ~GameInfo(); + + bool is_present() const { return m_present; } + DatFile *getDatfile() { return datfile; } + + private: + // Variables. + OxydVersion ver; + string game; + DatFile *datfile; + string datfile_path; + bool m_present; + + // Private methods. + void openDatFile(); + lev::Index *makeLevelIndex(bool twoplayer); + }; + + + enum MarbleInfoIndices { + // valid for all actor types: + MI_FORCE = 0, + + // marbles only: + + // Jack/Rotor only: + MI_RANGE = 1, + MI_GOHOME = 2, + + // Horse: + MI_HORSETARGET1 = 1, + MI_HORSETARGET2 = 2, + MI_HORSETARGET3 = 3, + MI_HORSETARGET4 = 4, + }; + + class MarbleInfo { + enum { MAX_MARBLE_INFO_FIELDS = 11 }; + enum { DEFAULT_VALUE = -1 }; + + int value[MAX_MARBLE_INFO_FIELDS]; + bool interpreted[MAX_MARBLE_INFO_FIELDS]; + + public: + MarbleInfo(const Marble& marble); + ~MarbleInfo(); + + bool is_default(int idx) { + assert(idx >= 0 && idx +#include +#include + +using namespace std; +using namespace enigma; +using namespace world; +using world::Actor; +using enigma::Inventory; + +namespace +{ + class PlayerInfo { + public: + PlayerInfo(); + + string name; + Inventory inventory; + vector actors; + bool out_of_lives; + double dead_dtime; // number of seconds the player is already dead + bool inhibit_pickup; + }; + + typedef vector PlayerList; + + struct RespawnInfo { + RespawnInfo (Actor *a, double t) + : actor(a), time_left(t) + {} + + Actor *actor; // The actor to respawn + double time_left; // Time left before respawning + }; + + + struct LevelLocalData { + // Functions + LevelLocalData() {} + + void respawn_dead_actors(double dtime); + void resurrect_actor (Actor *a); + bool remove_extralife (Actor *a); + + // Variables + vector respawn_list; + }; +} + +/* -------------------- PlayerInfo -------------------- */ + +PlayerInfo::PlayerInfo () +: name(), + inventory(), + actors(), + out_of_lives(false), + dead_dtime(0), + inhibit_pickup(false) +{} + +/* -------------------- LevelLocalData -------------------- */ + + +void LevelLocalData::respawn_dead_actors(double dtime) +{ + for (unsigned i=0; irespawn(); + respawn_list.erase(respawn_list.begin()+i); + continue; // don't increment i + } + ++i; + } +} + +void LevelLocalData::resurrect_actor (Actor *a) +{ + const double RESPAWN_TIME = 1.5; + +// a->find_respawnpos(); + world::SendMessage(a, "resurrect"); + remove_extralife(a); + respawn_list.push_back(RespawnInfo(a, RESPAWN_TIME)); +} + +bool LevelLocalData::remove_extralife (Actor *a) +{ + Inventory *inv = player::GetInventory(a); + int idx = inv->find("it-extralife"); + + if (idx == -1) // no extralife found + return false; + else { + delete inv->yield_item(idx); + player::RedrawInventory (inv); + return true; + } +} + + +/* -------------------- Local variables -------------------- */ + +namespace +{ + LevelLocalData leveldat; + PlayerList players(2); // this currently has always size 2 + unsigned icurrent_player = 0; + std::vector unassignedActors; +} + + +/* -------------------- Functions -------------------- */ + +void player::NewGame (bool isRestart) { + int nplayers = 2; // Always prepare for two players + std::vector extralives(2); + + // calculate number of extralives + for (int i=0; iadd_item (MakeItem (world::it_extralife)); + } + + unassignedActors.clear(); +} + +void player::AddYinYang () +{ + for (unsigned i=0; ifind ("it-yinyang") == -1) + inv->add_item (world::MakeItem (world::it_yinyang)); + } +} + +void player::LevelLoaded(bool isRestart) +{ + if (server::TwoPlayerGame && server::SingleComputerGame) + AddYinYang(); + RedrawInventory (); +} + +void player::PrepareLevel() +{ + // Clear up the inventories of all players: keep only extra lifes. + for (unsigned iplayer=0; iplayersize(); ++i) + if (get_id (inv->get_item(i)) == world::it_extralife) + nextralifes += 1; + inv->clear(); + for (int i=0; iadd_item (world::MakeItem (world::it_extralife)); + + players[iplayer].actors.clear(); + } + + SetCurrentPlayer(0); + leveldat = LevelLocalData(); +} + +void player::LevelFinished() +{ + for (unsigned i=0; iget_attrib("player")) + return GetInventory(to_int(*v)); + return 0; +} + + +bool player::WieldedItemIs (Actor *a, const string &kind) +{ + if (Inventory *inv = GetInventory(a)) + if (Item *it = inv->get_item(0)) + return it->is_kind(kind); + return false; +} + + +int player::CurrentPlayer() { + return icurrent_player; +} + +void player::SetCurrentPlayer(unsigned iplayer) +{ + if (iplayer >= players.size()) + Log << ecl::strf("SetCurrentPlayer: no such player %d\n", iplayer); + else { + icurrent_player = iplayer; + RedrawInventory (GetInventory(iplayer)); + } +} + +unsigned player::NumberOfRealPlayers() { + unsigned real_players = 0; + + for (unsigned i=0; i &al = players[i].actors; + + for (unsigned j=0; jget_attrib(black ? "blackball" : "whiteball"); + if (val) { + al[j]->set_respawnpos(center); + } + } + } +} + +/*! Remove respawn positions for black or white actors */ +void player::RemoveRespawnPositions(bool black) { + for (unsigned i=0; i &al = players[i].actors; + + for (unsigned j=0; jget_attrib(black ? "blackball" : "whiteball"); + if (val) { + al[j]->remove_respawnpos(); + } + } + } +} + +void player::Suicide() +{ + for (unsigned i=0; i &al = players[i].actors; + for (unsigned j=0; j= players.size()) + server::RaiseError ("Invalid actor number"); + + vector &al = players[iplayer].actors; + for (unsigned i=0; i= players.size()) + server::RaiseError ("Invalid actor number"); + + world::ReleaseActor(a); + players[iplayer].actors.push_back(a); + + if (players[iplayer].actors.size() == 1) { + // the ``main actor'' was set + client::Msg_PlayerPosition (iplayer, a->get_pos()); + } +} + +bool player::HasActor(unsigned iplayer, Actor *a) { + if (iplayer >= 0 && iplayer < players.size()) { + for (int i = 0; i < players[iplayer].actors.size(); i++) { + if (players[iplayer].actors[i] == a) + return true; + } + } + return false; +} + +void player::SwapPlayers() +{ + if (NumberOfRealPlayers() >= 2) { + SetCurrentPlayer(1-icurrent_player); + } +} + +static bool has_extralive(unsigned pl, unsigned num) { + size_t idx = 0; + for (int i = 0; i < num; i++) { + int idxLife = players[pl].inventory.find("it-extralife", idx); + if (idxLife == -1) + return false; + else + idx = idxLife + 1; + } + return true; +} + +static bool resurrect_actor (unsigned pl, Actor *a) +{ + assert(server::ConserveLevel); // no resurrection otherwise! + + bool has_life = has_extralive(pl, 1); + if (has_life) + leveldat.resurrect_actor(a); // = resurrect with delay + else + players[pl].out_of_lives = true; + return has_life; +} + +void player::AddUnassignedActor (Actor *a) { + unassignedActors.push_back(a); +} + +static void CheckDeadActors() +{ + bool toggle_player = false; + const unsigned NO_PLAYER = UINT_MAX; + unsigned toggle_to_player = NO_PLAYER; + bool new_game = false; // complete restart (new lives) + + // to live means to be not dead and to be able to move + for (int pl = -1; pl<(int)players.size(); ++pl) { // -1 are unassigned actors + vector& actors = (pl == -1) ? unassignedActors : players[pl].actors; + bool has_living_actor = false; // controllable and living + std::map essMap; + std::map::iterator itEss; + + for (size_t i=0; istring_attrib ("essential_id", &essId)) + essId = a->get_traits().name; + int essential = a->int_attrib("essential"); + // count number of necessary actors per kind + if (essential == 1) + --essMap[essId]; + + if (!a->is_dead() || + (pl >= 0 && server::ConserveLevel && resurrect_actor(pl, a))) { + // actor is still alive + if (pl >= 0 && a->controlled_by(pl) && a->get_mouseforce() != 0.0) { + has_living_actor = true; + } + // count number of alive actors per kind + if (essential >= 0) + ++essMap[essId]; + } + else { + // player is dead and could not resurrect + if (essential == -1) { + // actor is personnally essential but dead + new_game = true; + } + } + } + // check if for any kind we have less living actors as required + for (itEss = essMap.begin(); itEss != essMap.end(); itEss++) { + if ((*itEss).second < 0) + new_game = true; + } + + if (has_living_actor) { + if (toggle_to_player == NO_PLAYER) + toggle_to_player = pl; + } + else { + if (pl == icurrent_player) + // check if player has yinyang for single gamer mode + if (player::GetInventory(pl)->find("it-yinyang",0) >= 0) + toggle_player = true; + else + new_game = true; + } + } + + // if no_living_player -> toggle_player is true and toggle_to_player is NO_PLAYER + // => new_game is set to true below + + if ((server::ConserveLevel == false) && !server::IsRestartingLevel() && + (new_game || (toggle_player && toggle_to_player == NO_PLAYER))) { + // check if we have enough extralives for a restart instead of new game + std::vector numDead; + bool reset_level = true; + for (unsigned pl = 0; pl& actors = players[pl].actors; + for (size_t i = 0; iis_dead()) { + numDead[pl]++; + } + } + if (!has_extralive(pl, numDead[pl])) { + reset_level = false; + break; + } + } + if (reset_level) { + for (unsigned pl = 0; plfind("it-extralife"); + ASSERT (idx != -1, XLevelRuntime, "Missing extralife for restart of level"); + delete inv->yield_item(idx); + } + } + server::RestartLevel(); // should restart w/o scrolling + return; + } + } + + if (new_game) { + server::Msg_RestartGame(); + } + else if (toggle_player) { + if (toggle_to_player == NO_PLAYER) + server::Msg_RestartGame(); + else + player::SetCurrentPlayer(toggle_to_player); + } +} + +Actor *player::GetMainActor (unsigned iplayer) +{ + vector &actors = players[iplayer].actors; + return actors.empty() ? 0 : actors[0]; +} + +void player::Tick(double dtime) +{ + STATUSBAR->set_counter (server::GetMoveCounter()); + + + // Tell clients about position of main actor for stereo sound and + // screen position + for (unsigned iplayer = 0; iplayer < players.size(); ++iplayer) { + if (Actor *ac = GetMainActor(iplayer)) + client::Msg_PlayerPosition (iplayer, ac->get_pos()); + } + + // Respawn actors that have been dead for a certain amount of time + leveldat.respawn_dead_actors(dtime); + + // Update the respawn list or restart the game when all actors are + // dead and no extra lifes are left. + CheckDeadActors(); +} + +void player::InhibitPickup(bool flag) { + players[icurrent_player].inhibit_pickup = flag; +} + +/*! Return pointer to inventory if actor may pick up items, 0 + otherwise. */ +Inventory *player::MayPickup(Actor *a, Item *it) +{ + int iplayer=-1; + a->int_attrib("player", &iplayer); + if (iplayer < 0 || (unsigned)iplayer >= players.size()) { + // cerr << "PickupItem: illegal 'player' entry\n"; + return 0; + } + + Inventory *inv = GetInventory(iplayer); + bool dont_pickup = players[iplayer].inhibit_pickup + || a->is_flying() + || !inv->willAddItem(it) + || a->is_dead(); + + return dont_pickup ? 0 : inv; +} + +void player::PickupItem (Actor *a, GridPos p) +{ + if (Inventory *inv = MayPickup(a, GetField(p)->item)) { + if (Item *item = world::YieldItem(p)) { + item->on_pickup(a); + inv->add_item(item); + RedrawInventory (inv); + sound::EmitSoundEvent ("pickup", p.center()); + } + } +} + +void player::PickupStoneAsItem (Actor *a, enigma::GridPos p) +{ + if (Inventory *inv = MayPickup(a, GetField(p)->item)) + { + if (world::Stone *stone = world::YieldStone(p)) + { + string kind = stone->get_kind(); + if (kind[0] == 's') + kind[0] = 'i'; + + if (Item *item = world::MakeItem(kind.c_str())) { + KillRubberBands(stone); + world::DisposeObject (stone); + inv->add_item(item); + player::RedrawInventory(inv); + sound::EmitSoundEvent ("pickup", p.center()); + } + } + } +} + +void player::ActivateFirstItem() +{ + Inventory &inv = players[icurrent_player].inventory; + + + if (inv.size() > 0) { + Item *it = inv.get_item (0); + world::Actor *ac = 0; + GridPos p; + bool can_drop_item = false; + if (!players[icurrent_player].actors.empty()) { + ac = players[icurrent_player].actors[0]; + p = GridPos(ac->get_pos()); + can_drop_item = ac->can_drop_items(); + } + + switch (it->activate(ac, p)) { + case world::ITEM_DROP: + // only drop if no item underneath and actor allows it + if (it->can_drop_at(p) && can_drop_item) { + it = inv.yield_first (); + RedrawInventory (&inv); + it->drop(ac, p); + } + break; + case world::ITEM_KILL: + DisposeObject (inv.yield_first ()); + RedrawInventory (&inv); + break; + case world::ITEM_KEEP: + break; + } + } +} + +void player::RotateInventory(int dir) +{ + sound::EmitSoundEvent ("invrotate", ecl::V2()); + Inventory &inv = players[icurrent_player].inventory; + if (dir == 1) + inv.rotate_left (); + else + inv.rotate_right (); + RedrawInventory (&inv); +} + + +/** Update the specified inventory on the screen, provided it is the + inventory of the current player. For all other inventories, this + function does nothing. */ +void player::RedrawInventory (Inventory *inv) +{ + if (inv == GetInventory (CurrentPlayer())) + RedrawInventory(); +} + + +void player::RedrawInventory() +{ + Inventory *inv = GetInventory (CurrentPlayer()); + std::vector modelnames; + for (size_t i=0; isize(); ++i) { + world::Item *it = inv->get_item(i); + modelnames.push_back(it->get_inventory_model()); + } + STATUSBAR->set_inventory (modelnames); +} + diff --git a/project/jni/application/enigma/src/player.hh b/project/jni/application/enigma/src/player.hh new file mode 100644 index 000000000..768195da1 --- /dev/null +++ b/project/jni/application/enigma/src/player.hh @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PLAYER_HH_INCLUDED +#define PLAYER_HH_INCLUDED + +/** + * @file + * + * Player management. Deals mostly with inventory management, + * switching between players, respawning, etc. Players are numbered + * from 0..N-1, where N is the number of players in the current game + * (currently always 1 or 2). + */ + +#include "objects.hh" + +namespace enigma +{ + class Inventory; + + namespace player { + + /*! Start a new game for two virtual players. */ + void NewGame(bool isRestart); + + /*! This is called whenever a new level is reached in a running + game. The inventories of all players are cleaned up, i.e., all + items except for extra lifes are removed. */ + void PrepareLevel(); + + /*! Add a yinyang item to all players' inventories. */ + void AddYinYang (); + + /*! Called after the loading a level but before starting the + game. It initializes the inventory. */ + void LevelLoaded(bool isRestart); + + /*! Called as soon as the current level is finished; it removes + the actors of all players from the level. */ + void LevelFinished(); + + /* This function is only used by the YinYang items to exchange the + two players. */ + void SwapPlayers(); + + void RedrawInventory(); + void RedrawInventory(Inventory *inv); + + // set/remove respawn positions for all black or all white actors + // (used when it-flagwhite/black is dropped) + void SetRespawnPositions(enigma::GridPos pos, bool black); + void RemoveRespawnPositions(bool black); + + int CurrentPlayer(); + void SetCurrentPlayer(unsigned iplayer); + unsigned NumberOfRealPlayers(); + + + Inventory *MayPickup(Actor *a, Item *it); + Inventory *GetInventory(int iplayer); + Inventory *GetInventory(Actor *a); + bool WieldedItemIs(Actor *a, const std::string &kind); + + void Suicide(); + + void AddActor (unsigned iplayer, Actor *a); + void AddUnassignedActor (Actor *a); // actors not assigned to a player + bool HasActor(unsigned iplayer, Actor *a); + Actor *ReplaceActor (unsigned iplayer, Actor *old, Actor *a); + Actor *GetMainActor (unsigned iplayer); + + bool AllActorsDead(); + + void InhibitPickup (bool yesno); + void PickupItem (Actor *a, enigma::GridPos p); + void PickupStoneAsItem (Actor *a, enigma::GridPos p); + void RotateInventory (int dir=1); + + void ActivateFirstItem(); + world::ItemAction ActivateItem (Item *it); + + void Tick (double dtime); + + } // namespace player +} // namespace enigma +#endif diff --git a/project/jni/application/enigma/src/server.cpp b/project/jni/application/enigma/src/server.cpp new file mode 100644 index 000000000..fc5518cb0 --- /dev/null +++ b/project/jni/application/enigma/src/server.cpp @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "server.hh" + +#include "errors.hh" +#include "game.hh" +#include "actors.hh" +#include "client.hh" +#include "lua.hh" +#include "lev/Index.hh" +#include "lev/Proxy.hh" +#include "main.hh" +#include "nls.hh" +#include "options.hh" +#include "player.hh" +#include "player.hh" +#include "StateManager.hh" +#include "world.hh" + +#include "enet/enet.h" + +#ifdef WIN32 +// SendMessage is a Syscall on Windows, so we simply undefine it to use this +// name for one of the methods +#undef SendMessage +#endif + +#include + +using namespace enigma::server; +using namespace world; +using namespace std; + +namespace enigma_server +{ + enum ServerState { + sv_idle, + sv_waiting_for_clients, + sv_running, + sv_paused, + sv_restart_level, + sv_restart_game, + sv_finished, + }; + + class Server { + public: + + + private: + + static Server *instance; + }; + + void PrepareLevel(); + +} + + +/* -------------------- Global variables -------------------- */ + + +bool server::CreatingPreview = false; + +bool server::NoCollisions = false; + +double server::LevelTime; +bool server::ConserveLevel; +bool server::TwoPlayerGame; +bool server::SingleComputerGame; +bool server::AllowTogglePlayer; +bool server::ShowMoves; +GameType server::GameCompatibility; +double server::Brittleness; +double server::SlopeForce; +double server::FlatForce; +double server::FrictionFactor; +double server::IceFriction; +double server::ElectricForce; +double server::BumperForce; +double server::WaterSinkSpeed; +double server::SwampSinkSpeed; +double server::MagnetForce; +double server::MagnetRange; +double server::WormholeForce; +double server::WormholeRange; +double server::HoleForce; + + +/* -------------------- Local variables -------------------- */ + +namespace +{ + ServerState state = sv_idle; + double time_accu = 0; + double current_state_dtime = 0; + int move_counter; // counts movements of stones + lev::Index *currentIndex = NULL; // volatile F6 jump back history + int currentLevel; + lev::Index *previousIndex = NULL; + int previousLevel; + ENetAddress network_address; + ENetHost *network_host = 0; +} + +void load_level(lev::Proxy *levelProxy, bool isRestart) +{ + server::PrepareLevel(); + + try { + // clear inventory before level load and give us 2 extralives + player::NewGame(isRestart); + + levelProxy->loadLevel(); // sets the compatibility mode + + game::ResetGameTimer(); + + world::InitWorld(); + if (!CreatingPreview) { + player::LevelLoaded(isRestart); + client::Msg_LevelLoaded(isRestart); + } + } + catch (XLevelLoading &err) { + std::string levelPathString = + (levelProxy->getNormPathType() == lev::Proxy::pt_resource) ? + levelProxy->getAbsLevelPath() : levelProxy->getNormLevelPath(); + std::string msg = _("Server Error: could not load level '") + + levelPathString + "'\n" + + err.what(); + if (!CreatingPreview) { + client::Msg_Error(msg); + state = sv_idle; + } else { + throw XLevelLoading(msg); + } + } + catch (XLevelRuntime &err) { + std::string levelPathString = + (levelProxy->getNormPathType() == lev::Proxy::pt_resource) ? + levelProxy->getAbsLevelPath() : levelProxy->getNormLevelPath(); + std::string msg = _("Server Error: could not load level '") + + levelPathString + "'\n" + + err.what(); + if (!CreatingPreview) { + client::Msg_Error(msg); + state = sv_idle; + } else { + throw XLevelLoading(msg); + } + } +} + + +void server::RaiseError (const std::string &msg) +{ + throw XLevelLoading (msg); +} + +void gametick(double dtime) +{ + const double timestep = 0.01; // 10ms + + server::LevelTime += dtime; + + time_accu += dtime; + if (time_accu > 1.0) { + fprintf (stderr, "Whoa, system overload!\n"); + time_accu = 1.0; + } + player::Tick (time_accu); + for (;time_accu >= timestep; time_accu -= timestep) { + world::Tick (timestep); + if (lua::CallFunc (lua::LevelState(), "Tick", timestep, NULL) != 0) { + throw XLevelRuntime (string("Calling 'Tick' failed:\n") + + lua::LastError(lua::LevelState())); + } + } + world::TickFinished (); +} + + +/* -------------------- Functions -------------------- */ + +void server::Init() +{ +} + +void server::Shutdown() +{ + lua::ShutdownLevel(); + if (network_host != 0) + enet_host_destroy (network_host); +} + + +void server::InitNewGame() +{ + PrepareLevel(); +} + +bool server::NetworkStart() +{ + return true; +} + + + +void server::PrepareLevel() +{ + state = sv_waiting_for_clients; + + server::NoCollisions = false; + + server::LevelTime = 0.0; + server::ConserveLevel = true; + server::TwoPlayerGame = false; + server::SingleComputerGame= true; + server::AllowTogglePlayer = true; + server::ShowMoves = false; + server::Brittleness = 0.5; + server::SlopeForce = 25.0; + server::FlatForce = 0.0; + server::FrictionFactor = 1.0; + server::IceFriction = 1.0; + server::ElectricForce = 15.0; + server::BumperForce = 200.0; + server::WaterSinkSpeed = 1000; + server::SwampSinkSpeed = 4; + server::MagnetForce = 30; + server::MagnetRange = 10; + server::WormholeForce = 30; + server::WormholeRange = 10; + server::HoleForce = 1.0; + + move_counter = 0; + + world::PrepareLevel (); + player::PrepareLevel(); + + /* Restart the Lua environment so symbol definitions from + different levels do not get in each other's way.*/ + lua::ShutdownLevel(); + lua_State *L = lua::InitLevel(); + if (lua::DoSysFile(L, "compat.lua") != lua::NO_LUAERROR) { + throw XLevelLoading("While processing 'compat.lua':\n"+lua::LastError(L)); + } + if (lua::DoSysFile(L, "init.lua") != lua::NO_LUAERROR) { + throw XLevelLoading("While processing 'init.lua':\n"+lua::LastError(L)); + } + if (lua::DoSysFile(L, "security.lua") != lua::NO_LUAERROR) { + throw XLevelLoading("While processing 'security.lua':\n"+lua::LastError(L)); + } +} + +void server::RestartLevel() +{ + if (state == sv_running || state == sv_finished) { + state = sv_restart_level; + current_state_dtime = 0; + } +} + +bool server::IsRestartingLevel() { + return state == sv_restart_level; +} + +void server::Msg_RestartGame() +{ + if (state == sv_running || state == sv_finished) { + state = sv_restart_game; + current_state_dtime = 0; + } +} + +void server::FinishLevel() +{ + if (state == sv_running) { + state = sv_finished; + current_state_dtime = 0; + player::LevelFinished(); // remove player-controlled actors + client::Msg_Command("level_finished"); + } + else { + // XXX server internal error: should only be called while game is running + } +} + + +void server::Tick (double dtime) +{ + switch (state) { + case sv_idle: + break; + case sv_paused: + break; + case sv_waiting_for_clients: + break; + case sv_running: + gametick (dtime); + break; + case sv_restart_level: + case sv_restart_game: + current_state_dtime += dtime; + if (current_state_dtime >= 1.0) { + lev::Index *ind = lev::Index::getCurrentIndex(); + load_level(ind->getCurrent(), (state == sv_restart_level)); + } else { + gametick(dtime); + } + break; + case sv_finished: + current_state_dtime += dtime; + if (current_state_dtime <= 2.5) + gametick(dtime); + else { + client::Msg_AdvanceLevel (lev::ADVANCE_NEXT_MODE); + state = sv_waiting_for_clients; + } + break; + } +} + +void server::Msg_SetLevelPack (const std::string &name) { + lev::Index::setCurrentIndex(name); +} + +void server::Msg_LoadLevel (lev::Proxy *levelProxy, bool isPreview) { + server::CreatingPreview = isPreview; + if (!isPreview) { + // update F6 jump back history + if (currentIndex != lev::Index::getCurrentIndex() || + currentLevel != currentIndex->getCurrentPosition()) { + previousIndex = currentIndex; + previousLevel = currentLevel; + currentIndex = lev::Index::getCurrentIndex(); + currentLevel = currentIndex->getCurrentPosition(); + } + } + load_level(levelProxy, false); +} + +void server::Msg_JumpBack() { + if (previousIndex != NULL) { + lev::Index::setCurrentIndex(previousIndex->getName()); + previousIndex->setCurrentPosition(previousLevel); + currentIndex = previousIndex; + currentLevel = previousLevel; + Msg_LoadLevel(currentIndex->getProxy(currentLevel), false); + Msg_Command("restart"); + } +} + + +void server::Msg_StartGame() +{ + if (state == sv_waiting_for_clients) { + time_accu = 0; + state = sv_running; + } + else { + // Warning << "server: Received unexpected StartGame message.\n"; + // XXX discard message if not waiting for it? + } +} + +namespace enigma_server { + + void Msg_Command_jumpto(const string& dest) { + // global level jump + // e.g.: dest = "Enigma IV,33" -> jump to level #33 of index "Enigma IV" + // note: level counter start at 1 (not at 0) + + size_t comma = dest.find_first_of(','); + string error; + + if (comma != string::npos) { + std::string name = dest.substr(0, comma); + Log << "Jumpto '" << name <<"'\n"; + int ilevel = atoi(dest.c_str()+comma+1)-1; + + if (lev::Index::setCurrentIndex(name)) { + + if (ilevel >= 0 && ilevel < lev::Index::getCurrentIndex()->size()) { + lev::Index * curInd = lev::Index::getCurrentIndex(); + curInd->setCurrentPosition(ilevel); + Msg_LoadLevel (curInd->getProxy(ilevel), false); + Msg_Command("restart"); + } else { + error = ecl::strf("Illegal level %i (1-%i)", ilevel+1, lev::Index::getCurrentIndex()->size()); + // May be we want to reset the current index ? + } + } else error = ecl::strf("Illegal level pack %s", name.c_str()); + } else if (lev::Index::setCurrentIndex(dest)) { + lev::Index * curInd = lev::Index::getCurrentIndex(); + Msg_LoadLevel (curInd->getCurrent(), false); + Msg_Command("restart"); + } else { + error = "Syntax: jumpto pack[,level]"; + } + + if (!error.empty()) client::Msg_ShowText(error, false, 2); + } + + void Msg_Command_find(const string& text) { + std::string indName = lev::Proxy::search(text); + if (!indName.empty()) { + lev::Index::setCurrentIndex(indName); + lev::Index * searchResult = lev::Index::getCurrentIndex(); + searchResult->setCurrentPosition(0); + if (searchResult->size() == 1) { + // play unique level directly + Msg_LoadLevel (searchResult->getProxy(0), false); + Msg_Command("restart"); + } else { + // display multiple levels as pack + Msg_Command("abort"); + } + } else { + client::Msg_ShowText(string("Couldn't find '")+text+'\'', false, 2); + } + } +}; + +void server::Msg_Command (const string &cmd) +{ + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::Proxy *curProxy = ind->getCurrent(); + + // ------------------------------ normal commands + if (cmd == "invrotate") { + player::RotateInventory(); + } + else if (cmd == "suicide") { + player::Suicide(); + } + else if (cmd == "restart") { + player::Suicide(); + server::Msg_RestartGame(); + } + else if (cmd == "abort") { + client::Msg_Command(cmd); + } + + // ------------------------------ cheats + else if (cmd == "god") { + SendMessage (player::GetMainActor (player::CurrentPlayer()), + "shield"); + client::Msg_Command("cheater"); + } + else if (cmd == "collision") { + server::NoCollisions = !server::NoCollisions; + if (server::NoCollisions) + client::Msg_ShowText ("collision handling disabled", false, 2); + else + client::Msg_ShowText ("collision handling enabled", false, 2); + client::Msg_Command("cheater"); + } + + // ------------------------------ quick options + else if (cmd == "easy") { + if (app.state->getInt("Difficulty") == DIFFICULTY_HARD) { + if (curProxy->hasEasymode()) { + client::Msg_ShowText("Restarting in easy mode", false, 2); + app.state->setProperty("Difficulty", DIFFICULTY_EASY); + server::Msg_Command("restart"); + } + else + client::Msg_ShowText("No easymode available.", false, 2); + } + else + client::Msg_ShowText("Already in easy mode.", false, 2); + } + else if (cmd == "noeasy") { + if (app.state->getInt("Difficulty") == DIFFICULTY_EASY) { + app.state->setProperty("Difficulty", DIFFICULTY_HARD); + if (curProxy->hasEasymode()) { + client::Msg_ShowText("Restarting in normal mode", false, 2); + server::Msg_Command("restart"); + } + else { + client::Msg_ShowText("No difference between easy and normal.", false, 2); + } + } + else + client::Msg_ShowText("Already in normal mode.", false, 2); + } + else if (cmd == "time") { + if (options::GetBool("TimeHunting") == false) { + client::Msg_ShowText("Restarting in time-hunt mode", false, 2); + options::SetOption("TimeHunting", true); + server::Msg_Command("restart"); + } + else + client::Msg_ShowText("Already in time-hunt mode.", false, 2); + } + else if (cmd == "notime") { + if (options::GetBool("TimeHunting")) { + client::Msg_ShowText("Switched to easy-going mode", false, 2); + options::SetOption("TimeHunting", false); + client::Msg_Command("easy_going"); + } + else + client::Msg_ShowText("Already in easy-going mode.", false, 2); + } + else if (cmd == "info") { + string infotext = + ecl::strf("Level #%i of '", ind->getCurrentLevel()) + ind->getName() + + "' (" + curProxy->getAbsLevelPath() + ") - \"" + curProxy->getTitle() + + "\" by " + curProxy->getAuthor() + + ecl::strf(" (rev=%i,", curProxy->getReleaseVersion()) + + "id=\"" + curProxy->getId() + "\")"; + + client::Msg_ShowText(infotext, true); + } + else if (cmd.substr(0, 5) == "find ") { // global level-jump + string args = cmd.substr(5); + server::Msg_Command_find(args); + } + else if (cmd.substr(0, 7) == "jumpto ") { // global level-jump + string args = cmd.substr(7); + server::Msg_Command_jumpto(args); + } + else if (cmd == "help") { + client::Msg_ShowText("suicide, restart, abort, easy, noeasy, time, notime, jumpto, find, info", true); + } + else if (cmd == "cheats") { + client::Msg_ShowText("god, collision -- Be aware: you'll get no medals!", true); + } + + else { + enigma::Log << "Warning: Server received unknown command '" << cmd << "'\n"; + } +} + + +void server::Msg_Pause (bool onoff) { + if (onoff && state == sv_running) + state = sv_paused; + else if (!onoff && state == sv_paused) + state = sv_running; +} + +void server::Msg_Panic (bool onoff) { + if (onoff && state == sv_running) + state = sv_idle; + else if (!onoff && state == sv_idle) + state = sv_running; +} + +void server::Msg_MouseForce (const ecl::V2 &f) { + world::SetMouseForce (f); +} + + +void server::SetCompatibility(const char *version) { + GameType type = GetGameType(version); + + if (type == GAMET_UNKNOWN) { + fprintf(stderr, "Invalid compatibility mode '%s' (ignored. using enigma behavior)\n", version); + fprintf(stderr, "Valid modes:"); + for (int v = 0; vgetEngineCompatibility(); + if (server::GameCompatibility == GAMET_UNKNOWN) + throw XLevelLoading("unknown engine compatibility"); +} + +enigma::Difficulty server::GetDifficulty() +{ + lev::Index *ind = lev::Index::getCurrentIndex(); + lev::Proxy *curProxy = ind->getCurrent(); + int i= app.state->getInt ("Difficulty"); + if (i == DIFFICULTY_EASY && !server::CreatingPreview && curProxy->hasEasymode()) + return DIFFICULTY_EASY; + else + return DIFFICULTY_HARD; +} + +void server::InitMoveCounter() +{ + move_counter = 0; +} + +int server::IncMoveCounter(int increment) +{ + move_counter += increment; + return move_counter; +} + +int server::GetMoveCounter() +{ + return move_counter; +} + +void server::Msg_ActivateItem() +{ + player::ActivateFirstItem(); +} diff --git a/project/jni/application/enigma/src/server.hh b/project/jni/application/enigma/src/server.hh new file mode 100644 index 000000000..825276b48 --- /dev/null +++ b/project/jni/application/enigma/src/server.hh @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/*! @file + The client->server interface +*/ +#ifndef SERVER_HH_INCLUDED +#define SERVER_HH_INCLUDED + +//#include "levels.hh" +#include "lev/Proxy.hh" + +namespace enigma_server +{ + using namespace enigma; +// using levels::LevelPack; + +/* -------------------- Global variables -------------------- */ + + /*! Pointer to the current level pack. */ +// extern LevelPack *CurrentLevelPack; + + /*! Index of the current level. */ +// extern unsigned CurrentLevel; + + /*! True if the level is only being loaded for the purpose of + making a preview image. */ + extern bool CreatingPreview; + +/* -------------------- Cheats -------------------- */ + + extern bool NoCollisions; + +/* -------------------- Per-level settings -------------------- */ + + // The number of seconds passed since the level game started. + extern double LevelTime; + + // True: do not reset level when player dies + extern bool ConserveLevel; + + extern bool TwoPlayerGame; + + extern bool SingleComputerGame; + + // True: allow to control 2nd player (e.g. after first died) + extern bool AllowTogglePlayer; + + // True -> show move counter (Sokoban style) + extern bool ShowMoves; + + // Behave like Oxyd/Enigma version + extern GameType GameCompatibility; + + // Default brittleness of the floor: 0 = stable..1=unstable. + // Really: probability that a floor tile will crack when an actor + // enters or leaves it. + extern double Brittleness; + + extern double WaterSinkSpeed; + + extern double SwampSinkSpeed; + +/* -------------------- Force multipliers... -------------------- */ + + // ...for floor tiles that look sloped + extern double SlopeForce; + + // ...for floor tiles that DON'T look sloped + extern double FlatForce; + + // ...for friction on certain floor types + extern double FrictionFactor; + + // ...for friction on ice + extern double IceFriction; + + // ...for electrostatic forces between actors (default: 15) + extern double ElectricForce; + + // ...for the bumber stones (st-actorimpulse*) + extern double BumperForce; + + // ...for magnets + extern double MagnetForce; + extern double MagnetRange; + + // ...for wormholes + extern double WormholeForce; + extern double WormholeRange; + + // ...for holes + extern double HoleForce; + +/* -------------------- Functions -------------------- */ + + /** Initialize the server at program start. */ + void Init(); + + /** Shutdown the server at program end. */ + void Shutdown(); + +// TEST + bool NetworkStart(); + + /** Prepare the server for a new game. */ + void InitNewGame(); + + void Tick (double dtime); + void RestartLevel(); + bool IsRestartingLevel(); + void FinishLevel(); + + void SetCompatibility(const char *version); // set compatibility (from lua) + void SetCompatibility(lev::Proxy *levelProxy); // set compatibility from xml + + enigma::Difficulty GetDifficulty(); + + // move counter + void InitMoveCounter(); + int IncMoveCounter(int increment = 1); + int GetMoveCounter(); + + void RaiseError (const std::string &msg); + +/* -------------------- Client -> Server messages -------------------- */ + + void Msg_SetLevelPack (const std::string &name); + + void Msg_LoadLevel (lev::Proxy *levelProxy, bool isPreview); + + void Msg_JumpBack(); + + /*! After loading the level, the server sends a "LevelLoaded" + message to all clients. The game only starts after they have + answered with a "StartGame" message. */ + void Msg_StartGame(); + + void Msg_RestartGame(); + + void Msg_Command (const std::string &command); + + void Msg_Pause (bool onoff); + + void Msg_Panic (bool onoff); + + void Msg_MouseForce (const ecl::V2 &f); + + + void Msg_ActivateItem(); +} + +namespace enigma +{ + namespace server = enigma_server; +} + +#endif diff --git a/project/jni/application/enigma/src/server_internal.hh b/project/jni/application/enigma/src/server_internal.hh new file mode 100644 index 000000000..4cdc266f7 --- /dev/null +++ b/project/jni/application/enigma/src/server_internal.hh @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef SERVER_INTERNAL_HH_INCLUDED +#define SERVER_INTERNAL_HH_INCLUDED + +namespace enigma_server +{ + enum ServerMessageTypes { + SVMSG_NOOP, + SVMSG_LOADLEVEL, + SVMSG_MOUSEFORCE, + SVMSG_ACTIVATEITEM, + }; + +} + + + +#endif diff --git a/project/jni/application/enigma/src/sound.cpp b/project/jni/application/enigma/src/sound.cpp new file mode 100644 index 000000000..31420ee62 --- /dev/null +++ b/project/jni/application/enigma/src/sound.cpp @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * Copyright (C) 2007 Andreas Lochmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "errors.hh" +#include "enigma.hh" +#include "options.hh" +#include "sound.hh" +#include "main.hh" +#include "oxyd.hh" +#include "oxydlib/OxydVersion.h" +#include "nls.hh" +#include "client.hh" + +#include "SDL.h" +#include "SDL_mixer.h" + +#include +#include +#include +#include + +using namespace std; +using namespace enigma; +using namespace sound; +using namespace OxydLib; + +#include "sound_internal.hh" + +/*! This function converts raw audio data with a specified format to + the mixer's audio format. This is used for converting the original + Oxyd sounds to a format usable by Enigma. */ +Mix_Chunk *ChunkFromRaw (const Uint8 *buf, Uint32 len, + int freq, int format, int channels); + + +/* -------------------- SoundEvent implementation -------------------- */ +SoundEvent::SoundEvent () +: name(""), has_position(false), position(), + priority (0), volume (0.0), + left (0), right(0), active (false), + playing_time (0) +{ +} + +bool not_active (const SoundEvent &s) +{ + return !s.active; +} + +bool lower_priority (const SoundEvent &s, const SoundEvent &t) +{ + return s.priority < t.priority; +} + + + +/* -------------------- SoundEngine_SDL implementation -------------------- */ + +class MutexLock { +public: + MutexLock (SDL_mutex *m) { + mutex = m; + SDL_mutexP (mutex); + } + ~MutexLock () { + SDL_mutexV (mutex); + }; +private: + SDL_mutex *mutex; +}; + +SoundEngine_SDL *SoundEngine_SDL::m_instance = 0; + +SoundEngine_SDL::SoundEngine_SDL() +: m_initialized(false), + m_soundvolume (MIX_MAX_VOLUME), + m_musicvolume (MIX_MAX_VOLUME), + m_current_music (0), + m_freq (MIX_DEFAULT_FREQUENCY), + m_format (MIX_DEFAULT_FORMAT), + m_channels (MIX_DEFAULT_CHANNELS) +{ + assert (m_instance == 0); + m_instance = this; +} + +SoundEngine_SDL::~SoundEngine_SDL() +{ + shutdown(); + m_instance = 0; +} + +bool SoundEngine_SDL::init() +{ + if (!m_initialized) { + // Initialize SDL audio subsystem + if (SDL_InitSubSystem (SDL_INIT_AUDIO) == -1) { + fprintf(stderr, "Couldn't open SDL audio subsystem: %s\n", SDL_GetError()); + return false; + } + + const SDL_version* vi = Mix_Linked_Version(); + Log << ecl::strf("SDL_mixer Version: %u.%u.%u\n", vi->major, vi->minor, vi->patch); +#ifdef SDL_MIX_INIT + int mix_flags = MIX_INIT_MOD; + if (Mix_Init(mix_flags) & mix_flags != mix_flags) { + Log << ecl::strf( "Couldn't initialize SDL_mixer: %s\n", Mix_GetError()); + return false; + } +#endif + + // Initialize SDL_mixer lib + if (Mix_OpenAudio(m_freq, m_format, m_channels, 1024) < 0) { + fprintf(stderr, "Couldn't open mixer: %s\n", Mix_GetError()); + return false; + } + + // Update number of available channels + m_channels = Mix_GroupCount (-1); + m_channelinfo.resize (m_channels); + + Mix_ChannelFinished (&channel_finished); + m_mutex = SDL_CreateMutex(); + if (!m_mutex) + return false; + + m_initialized = true; + } + return true; +} + +void SoundEngine_SDL::shutdown() +{ + if (m_initialized) { + Mix_FreeMusic(m_current_music); + Mix_CloseAudio(); + clear_cache(); + SDL_DestroyMutex (m_mutex); +#ifdef SDL_MIX_INIT + Mix_Quit(); +#endif + m_initialized = false; + } +} + + +void SoundEngine_SDL::clear_cache() +{ + for (ecl::Dict::iterator it = wav_cache.begin(); it != wav_cache.end(); ++it) + Mix_FreeChunk(it->second); + wav_cache.clear(); +} + +void SoundEngine_SDL::set_sound_volume (double soundvol) +{ + if (!m_initialized) + return; // SDL_mixer crashes without this check + + m_soundvolume = ecl::round_down(ecl::Clamp(soundvol, 0.0, 1.0) * MIX_MAX_VOLUME); + Mix_Volume (-1, m_soundvolume); +} + +void SoundEngine_SDL::set_music_volume (double musicvol) +{ + if (!m_initialized) + return; // SDL_mixer crashes without this check + + m_musicvolume = ecl::round_down(ecl::Clamp(musicvol, 0.0, 1.0) * MIX_MAX_VOLUME); + Mix_VolumeMusic (m_musicvolume); +} + + +void SoundEngine_SDL::stop_music() +{ + Mix_HaltMusic(); + Mix_FreeMusic(m_current_music); + m_current_music = 0; +} + +bool SoundEngine_SDL::play_music (const std::string &filename) +{ + if (Mix_Music *music = Mix_LoadMUS(filename.c_str())) { + if (m_current_music) + Mix_FreeMusic (m_current_music); + m_current_music = music; + Mix_PlayMusic (m_current_music, -1); + Mix_VolumeMusic (m_musicvolume); + return true; + } + return false; +} + +void SoundEngine_SDL::fadeout_music() +{ + while (Mix_FadingMusic() != MIX_NO_FADING) + SDL_Delay(10); + + if (Mix_PlayingMusic()) { + Mix_FadeOutMusic(500); + SDL_Delay(400); + } + while (Mix_PlayingMusic()) + SDL_Delay(10); +} + +void SoundEngine_SDL::update_channel (int channel) +{ + // If distance sound origin <= this value, play at full volume + const double fullvol_range = 0.2; + + // How far can sound travel? + const double range = 30; + + SoundEvent &se = m_channelinfo[channel]; + + double volume; + int left; + int right; + if (se.has_position) { + ecl::V2 distv = se.position - m_listenerpos; + double dist = max(0.0, length(distv) - fullvol_range); + + int xdist = int(distv[0] * options::GetDouble("StereoSeparation")); + + left = ecl::Clamp (255 - xdist, 0, 255); + right = ecl::Clamp (255 + xdist, 0, 255); + volume = (1 - dist/range) * se.volume; + } + else + { + volume = se.volume; + left = se.left; + right = se.right; + } + + Mix_SetPanning (channel, left, right); + + int mixvol = ecl::round_down(volume * MIX_MAX_VOLUME); + Mix_Volume(channel, ecl::Clamp(mixvol, 0, MIX_MAX_VOLUME)); +} + +int SoundEngine_SDL::already_playing (const SoundEvent &s) +{ + for (size_t i=0; i (i); + } + return -1; +} + + +Mix_Chunk *SoundEngine_SDL::cache_sound(const std::string &name) +{ + ecl::Dict::iterator i=wav_cache.find(name); + if (i == wav_cache.end()) { + Mix_Chunk *ch = 0; + string filename; + if (app.resourceFS->findFile("soundsets/" + name + ".wav", filename)) + ch = Mix_LoadWAV(filename.c_str()); + if (ch != 0) + wav_cache.insert(name, ch); + else + enigma::Log << "Couldn't load sample '" << name << "': " + << Mix_GetError() << endl; + return ch; + } else + return i->second; +} + +bool SoundEngine_SDL::play_sound (const SoundEvent &s) +{ + int channel = already_playing (s); + if (channel != -1) { + MutexLock (m_instance->m_mutex); + SoundEvent &se = m_channelinfo [channel]; + if (se.has_position) { + se.position = (se.position + s.position) / 2; + update_channel (channel); + return true; + } + } + + if (Mix_Chunk *chunk = cache_sound(s.name)) { + channel = -1; //Mix_GroupOldest(-1); + + channel = Mix_PlayChannel(channel, chunk, 0); + + if (channel != -1) { + { + MutexLock (m_instance->m_mutex); + SoundEvent &se = m_channelinfo[channel]; + se = s; + se.active = true; + se.playing_time = 0.0; + } + update_channel (channel); + } + return true; // even if no free channel was found + } else + return false; +} + +void SoundEngine_SDL::tick (double dtime) +{ + MutexLock (m_instance->m_mutex); + for (size_t i=0; i (data.buf.size()); + Mix_Chunk *ch= ChunkFromRaw (&data.buf[0], bufsize, + data.freq, AUDIO_S8, data.nchannels); + if (ch != 0) + wav_cache.insert(name, ch); +} + +void SoundEngine_SDL::channel_finished (int channel) +{ + MutexLock (m_instance->m_mutex); + SoundEvent &se = m_instance->m_channelinfo[channel]; + se.active = false; +} + + + + +/* -------------------- Local variables -------------------- */ +namespace +{ + auto_ptr sound_engine; + + bool sound_enabled = true; + bool music_enabled = true; + bool sound_enabled_temp = false; + + string current_music_name; +} + + + +/* -------------------- Functions -------------------- */ + +void sound::Init(bool withMusic, bool withSound) +{ + sound_enabled = withSound; + music_enabled = withMusic; + if (!sound_engine.get()) { + sound_engine.reset(new SoundEngine_SDL); + } + + if (sound_engine->init()) { + options::UpdateVolume(); + } + else { + sound_enabled = false; + music_enabled = false; + sound_engine.reset(new SoundEngine_Null); + } +} + +void sound::Shutdown() +{ + if (sound_engine.get()) + sound_engine->shutdown(); +} + +void sound::Tick (double dtime) +{ + sound_engine->tick (dtime); +} + +void sound::TempDisableSound() { + sound_enabled_temp = sound_enabled; + sound_enabled = false; +} + +void sound::TempReEnableSound() { + sound_enabled = sound_enabled_temp; +} + +void sound::SetListenerPosition (const ecl::V2 &pos) +{ + sound_engine->set_listenerpos (pos); +} + +bool sound::PlaySound (const SoundName &name, const ecl::V2 &pos, double volume, int priority) +{ + if (!sound_enabled) + return false; + + SoundEvent se; + se.name = name; + se.has_position = true; + se.position = pos; + se.priority = priority; + se.volume = volume * options::GetDouble("SoundVolume"); + se.left = se.right = 0; + + return sound_engine->play_sound (se); +} + +bool sound::PlaySoundGlobal (const SoundName &name, double volume, int priority) +{ + SoundEvent se; + se.name = name; + se.has_position = false; + se.position = ecl::V2(); + se.priority = priority; + se.volume = volume * options::GetDouble("SoundVolume"); + se.left = 255; + se.right = 255; + + return sound_engine->play_sound (se); +} + +void sound::FadeoutMusic() +{ + sound_engine->fadeout_music(); +} + +void sound::PlayMusic (const std::string &name) +{ + if (!sound_enabled || !music_enabled || name==current_music_name) + return; + + FadeoutMusic(); + + string fname; + if (app.resourceFS->findFile (name, fname) && sound_engine->play_music (fname)) + current_music_name = name; + else + current_music_name = ""; + +} + +void sound::StopMusic() { + sound_engine->stop_music(); + current_music_name = ""; +} + +void sound::StopMusic (const std::string &name) { + if (name==current_music_name) + StopMusic(); +} + +void sound::ClearCache() +{ + sound_engine->clear_cache(); +} + +void sound::DefineSound (const SoundName &name, const SoundData &data) +{ + sound_engine->define_sound (name, data); +} + +void sound::SetSoundVolume (double vol) +{ + sound_engine->set_sound_volume (vol); +} + +void sound::SetMusicVolume (double vol) +{ + sound_engine->set_music_volume (vol); +} + +/* SDL_ConvertAudio is only capable of changing the sound frequency by + integer powers of 2 (i.e., by a factor of ... 1/4 1/2 1 2 ...). + The sound files used by Oxyd are sampled at 6kHz which we must + convert to roughly 22kHz. This function resamples between any two + frequencies using simple linear interpolation. It is not capable + of changing the sample format or dealing with more than one + channel. + + FIXME: We should apply a lowpass filter after reampling to get rid + of the artifacts introduced by linear interpolation or use a better + interpolator. +*/ +namespace +{ + Sint8 *resample (const Sint8 *data, Uint32 len, int oldfreq, int newfreq, + Uint32 *newlen_) + { + assert (data); + assert (len>0); + assert (oldfreq > 0); + assert (newfreq > 0); + const int sample_size = 1; // 8bit sample data + + float ratio = float(oldfreq) / float(newfreq); + Uint32 newlen = ecl::round_down (len / ratio); + *newlen_ = newlen; + Sint8 *newdata = (Sint8*) malloc (sample_size * newlen); + if (!newdata) + return 0; + + const Sint8 *src = data; + Sint8 *dst = newdata; + + float srcinc = float (len-1) / float (newlen); + for (unsigned i=0; i (i * srcinc); // srcpos); + float a2 = i*srcinc - srcidx; + float a1 = 1.0f - a2; + dst[i] = static_cast ((a1*src[srcidx] + a2*src[srcidx+1])/2); + } + return newdata; + } + +// typedef float (*FilterFunc)(float); + +// inline double sinc(double x) +// { +// if (x == 0) +// return 1; +// x *= M_PI; +// return sin(x)/x; +// } + +// class Sinc { +// public: +// static const int support = 4; + +// static double func (double x) { +// if (x < -4 || x > 4) +// return 0; +// else +// return sinc(x); +// } +// }; + + +// class Lanczos2 { +// public: +// static const int support = 2; + +// static double func (double x) { +// if (x < -2 || x > 2) +// return 0; +// else +// return sinc(x) * sinc(x/2); +// } +// }; + +// class Lanczos3 { +// public: +// static const int support = 3; + +// static double func (double x) { +// if (x < -3 || x > 3) +// return 0; +// else +// return sinc(x) * sinc(x/3); +// } +// }; + + +// template +// class Resampler { +// double ratio; +// vector G; +// public: +// Resampler (double ratio_) +// : ratio (ratio_) +// { +// } + +// size_t calc_destlen (size_t sourcelen) +// { +// return static_cast ((sourcelen / ratio + 1) +// * sizeof (SampleT)); +// } + +// void resample (const SampleT *source, size_t sourcelen, +// SampleT *dest, size_t destlen) +// { +// double factor = (double)sourcelen / destlen; +// int support = Filter::support; +// double dx = ecl::Max(factor, 1.0); +// vector F (2*support + 1); + +// SampleT *dp = dest; +// for (size_t t=0; t(0, center-support + 0.5); +// int stop = ecl::Min(sourcelen, center+support+0.5); +// int n = stop-start; + +// // Calculate filter coefficients +// double c = 0; +// double pos = start-center+0.5; +// for (int j=0; j=0; --j) +// accu += F[j] * sp[j]; +// *dp++ = static_cast (accu); +// // *dp++ = static_cast (ecl::Clamp (accu, -128, 127)); +// } +// } + +// }; +} + +Mix_Chunk * ChunkFromRaw (const Uint8 *buf, Uint32 len, + int sfreq, int sformat, int schannels) +{ + if (!sound_enabled || !buf) + return 0; + + // Get destination format + int dfreq, dchannels; + Uint16 dformat; + Mix_QuerySpec (&dfreq, &dformat, &dchannels); + + // Resample +// Resampler r (double(sfreq)/dfreq); +// size_t newlen = r.calc_destlen(len); +// Uint8 *newbuf = (Uint8*) malloc (newlen); +// r.resample ((Sint8*)buf, len, (Sint8*)newbuf, newlen); + + // Resample + Uint32 newlen=0; + Uint8 *newbuf = (Uint8*)resample((const Sint8*)buf, len, sfreq, dfreq, &newlen); + + // Convert audio data + SDL_AudioCVT cvt; + if (!SDL_BuildAudioCVT (&cvt, sformat, schannels, dfreq, + dformat, dchannels, dfreq)) + return 0; + + cvt.buf = (Uint8*) malloc(newlen * cvt.len_mult); + cvt.len = newlen; + memcpy(cvt.buf, newbuf, newlen); + free(newbuf); + + SDL_ConvertAudio(&cvt); + + Mix_Chunk *chunk = Mix_QuickLoad_RAW(cvt.buf, cvt.len_cvt); + chunk->allocated = 1; + return chunk; +} + + +/* ------------- Conversion and helper functions ------------- */ + +/*! This function defines how to put soundset_key and eventname together to + create an eventkey. */ + +string SoundEngine::effectKey(string effect_name, string soundset_name) +{ + if (soundset_name == "") + return getActiveSoundSetKey() + "#" + effect_name; + else + return soundset_name + "#" + effect_name; +} + +/*! This function searches the known sound sets for a sound set with given + oxyd version, and returns the sound set name (or empty string if none). */ + +string SoundEngine::getOxydSoundSet(OxydVersion oxyd_ver) +{ + for (SoundSetRepository::iterator i = sound_sets.begin(); + i != sound_sets.end(); ++i) + if((*i).second.getOxydVersion() == oxyd_ver) + return (*i).first; + return ""; +} + +string sound::GetOxydSoundSet(OxydVersion oxyd_ver) +{ + return sound_engine->getOxydSoundSet(oxyd_ver); +} + +/*! Enigma 1.00 only knew the option "SoundSet", which was an integer. This + was quite unhandy if one wanted to add additional sound sets. Since 1.01 + Enigma features the option "SoundSetName". Still, old "SoundSet" is needed + if user wants to switch to <= 1.00 again; so here are the conversion + functions. Any user sound set is mapped to 0 ("Default"). */ + +int SoundEngine::convertToOldSoundSetNumber(string soundset_name) +{ + if(soundset_name == "Default") return 0; + if(soundset_name == "Enigma") return 1; + SoundSet sd = sound_sets[soundset_name]; + if(sd.isOxyd()) + return ((int) sd.getOxydVersion()) + 2; + return 0; +} + +string SoundEngine::convertFromOldSoundSetNumber(int soundset_number) +{ + if(soundset_number == 0) return "Default"; + if(soundset_number == 1) return "Enigma"; + return getOxydSoundSet((OxydVersion) (soundset_number - 2)); +} + +/* -------------------- Sound set handling -------------------- */ + +/*! These functions fill in data for the sound sets, initialises and + activates them. Return false, if something went wrong, e.g. when an + oxyd sound set is mentioned to not accessible oxyd version. */ + +bool SoundEngine::defineSoundSet(string soundset_name, string soundset_key, + int button_position) +{ + sound_sets[soundset_name] = SoundSet(soundset_key, button_position); + Log << "Added sound set '" << soundset_name << "' (key '" << soundset_key + << "') on position " << button_position << ".\n"; + return true; +} + +bool SoundEngine::defineSoundSetOxyd(string soundset_name, string soundset_key, + OxydVersion oxyd_ver, int button_position) +{ + if(oxyd::FoundOxyd(oxyd_ver)) { + sound_sets[soundset_name] = + SoundSet(soundset_key, button_position, (OxydVersion) oxyd_ver); + Log << "Added sound set '" << soundset_name << "' (key '" << soundset_key + << "') on position " << button_position << ".\n"; + return true; + } else { + Log << "Skipped sound set '" << soundset_name << "'.\n"; + return false; + } +} + +bool SoundSet::activate() +{ + if(getSoundSetKey() == "") + return false; + if(isOxyd() && !oxyd::InitOxydSoundSet(getOxydVersion())) + return false; + sound_engine->setActiveSoundSetKey(getSoundSetKey()); + return true; +} + +void sound::InitSoundSets() { + sound_engine->initSoundSets(); +} + +void SoundEngine::initSoundSets() +{ + // Define sound sets + sound_sets.clear(); + assert(sound_sets.empty()); + assert(defineSoundSet ("Enigma", "Enigma", 1)); + int pos = 2; // position in options menu button + if (defineSoundSetOxyd ("Oxyd", "Oxyd*", OxydVersion_Oxyd1, pos)) pos++; + if (defineSoundSetOxyd ("Magnum", "Magnum*", OxydVersion_OxydMagnum, pos)) pos++; + if (defineSoundSetOxyd ("Mag.Gold", "Magnum*", OxydVersion_OxydMagnumGold, pos)) pos++; + if (defineSoundSetOxyd ("Per.Oxyd", "Oxyd*", OxydVersion_PerOxyd, pos)) pos++; + if (defineSoundSetOxyd ("Extra", "Oxyd*", OxydVersion_OxydExtra, pos)) pos++; + // Define user sound sets, as given by sound_effects + for (SoundEffectRepository::iterator i = sound_effects.begin(); + i != sound_effects.end(); ++i) { + string soundset_key = (*i).second.getSoundSetKey(); + bool found = false; + // ignore Oxyd* and Magnum* sound effects, if no + if ((soundset_key != "Oxyd*") && (soundset_key != "Magnum*")) { + for (SoundSetRepository::iterator j = sound_sets.begin(); + j != sound_sets.end(); ++j) + if((*j).second.getSoundSetKey() == soundset_key) + found = true; + if(!found) + if (defineSoundSet (soundset_key, soundset_key, pos)) pos++; + } + } + Log << "Found " << pos - 1 << " different sound sets.\n"; + setSoundSetCount(pos - 1); + setDefaultSoundSet("Enigma"); + // Extract sound set names and keys from options; activate! + string soundset_name = app.state->getString("SoundSetName"); + if (soundset_name == "") { // just switched from 1.00 to higher + soundset_name = convertFromOldSoundSetNumber(options::GetInt("SoundSet")); + app.state->setProperty("SoundSetName", soundset_name); + } + if (soundset_name == "Default") + soundset_name = getDefaultSoundSet(); + clear_cache(); + if (sound_sets[soundset_name].activate()) + Log << "Activated sound set '" << soundset_name << "'.\n"; + else { + // Fallback, happens e.g. when oxyd sound set can't be established or + // a user soundset is given which doesn't exist anymore. + Log << "Warning: Soundset '" << soundset_name << "' not available.\n"; + if (sound_sets["Enigma"].activate()) { + app.state->setProperty("SoundSetName", "Enigma"); + options::SetOption("SoundSet", convertToOldSoundSetNumber("Enigma")); + } else + ASSERT(false, XFrontend, + "Soundsets defect and fallback 'Enigma' not available."); + } +} + +void SoundEngine::setActiveSoundSet(string soundset_name) +{ + string soundset_key = sound_sets[soundset_name].getSoundSetKey(); + if (soundset_key == getActiveSoundSetKey()) + return; + if (soundset_key == "Default") { + Log << "Warning: Tried to choose 'Default' as effective sound set.\n"; + return; + } + if (soundset_key == "") { + Log << "Warning: Tried to choose empty sound set key as effective sound set.\n"; + return; + } + clear_cache(); + if (sound_sets[soundset_name].activate()) { + Log << "Switched to sound set '" << soundset_name << "' (key '" + << soundset_key << "').\n"; + } else + Log << "Warning: Problems loading sound set '" << soundset_name << "' (key'" + << soundset_key << "').\n"; +} + +void sound::SetActiveSoundSet(string soundset_name) +{ + sound_engine->setActiveSoundSet(soundset_name); +} + +void sound::SetDefaultSoundSet(string soundset_name) +{ + sound_engine->setDefaultSoundSet(soundset_name); + if(app.state->getString("SoundSetName") == "Default") + SetActiveSoundSet(soundset_name); +} + +string SoundEngine::getSoundSetByPosition(int button_position) +{ + for (SoundSetRepository::iterator i = sound_sets.begin(); + i != sound_sets.end(); ++i) + if((*i).second.getButtonPosition() == button_position) + return (*i).first; + return ""; +} + +/* -------------------- Playing sound events -------------------- */ + +/*! The first function creates an interface to add sound events to Enigma. + It is accessed via sound-defaults.lua and user sound definitions. + The second method is the interface between the formal SoundEffect + and the sound engine (via PlaySound[Global]). The last two functions + define the interface between level objects and SoundEffect. */ + +void sound::DefineSoundEffect(string soundset_key, string name, string filename, + double volume, bool loop, bool global, int priority, + double damp_max, double damp_inc, double damp_mult, + double damp_min, double damp_tick, string silence_string) { + assert(sound_engine.get()); + if(soundset_key == "") { + Log << "Warning: Tried to define sound event '" << name + << "' without sound set key. Skipped.\n"; + return; + } + sound_engine->defineSoundEffect(soundset_key, name, + SoundEffect(name, soundset_key, filename, volume, loop, global, priority, + damp_max, damp_inc, damp_mult, damp_min, damp_tick, silence_string)); +} + +bool SoundEffect::play(const ecl::V2 &pos, double vol, bool glob) +{ + if (filename == "") { + Log << "No soundfile given for sound event " << name << ".\n"; + return false; + } + if (glob || global) + return PlaySoundGlobal (filename, volume * vol, priority); + else + return PlaySound (filename, pos, volume * vol, priority); +} + +bool SoundEngine::emitSoundEvent (const std::string &eventname, const ecl::V2 &pos, + double volume, bool force_global) +{ + string effectkey = effectKey(eventname); + SoundEffectRepository::iterator i = sound_effects.find(effectkey); + if (i == sound_effects.end()) { + Log << "Undefined sound event " << effectkey << " @ " + << pos[0] << "," << pos[1] << "\n"; + return false; + } else { + return (*i).second.play(pos, volume, force_global); + } +} + +bool sound::EmitSoundEvent (const std::string &eventname, const ecl::V2 &pos, + double volume, bool force_global) +{ + return sound_engine->emitSoundEvent(eventname, pos, volume, force_global); +} + +bool sound::EmitSoundEventGlobal (const std::string &eventname, double volume) +{ + return sound_engine->emitSoundEvent(eventname, ecl::V2(), volume, true); +} + +void SoundEngine::writeSilenceString (const std::string &eventname) +{ + string effectkey = effectKey(eventname); + SoundEffectRepository::iterator i = sound_effects.find(effectkey); + if (i != sound_effects.end()) { + string silence_string = (*i).second.getSilenceString(); + if (silence_string != "") + client::Msg_ShowText (silence_string, true); + } +} + +void sound::WriteSilenceString (const std::string &eventname) +{ + sound_engine->writeSilenceString(eventname); +} + +/* -------------------- Sound damping implementation -------------------- */ + +/*! These methods are connected to the sound damping mechanism, designed + to reduce the noise created by some objects like st-lightpassenger. */ + +SoundDamping::SoundDamping(std::string effect_name_, const void *origin_) +: effect_name(effect_name_), origin(origin_) +{ + damp = sound_engine->getDampingData(effect_name_); + factor = damp.incr; + //Log << "New damping entry " << effect_name << " with " << damp.incr << ".\n"; +} + +float SoundDamping::get_volume(float def_volume) { + if (factor < damp.maxi) + factor += damp.incr; + //Log << " Found entry " << effect_name << ". Factor is now " << i->factor << ".\n"; + float q = factor * damp.mult; + if (q > 1.0) + return def_volume / q; + return def_volume; +} + +bool SoundDamping::tick() { + // return true, if this entity is to be destroyed. + factor *= damp.tick; + return (factor <= damp.mini); +} + +/* -------------------- Sound option helpers -------------------- */ + +/*! These functions are used in OptionsMenu.cc for the Soundset-Button. */ + +int sound::GetOptionSoundSetCount() +{ + return sound_engine->getSoundSetCount() + 1; +} + +int sound::GetOptionSoundSet() +{ + string soundSet = app.state->getString("SoundSetName"); + if (soundSet == "Default") + return 0; + int pos = sound_engine->getButtonPosition(soundSet); + assert(pos > 0); + return pos; +} + +void sound::SetOptionSoundSet(int value) +{ + if(value == 0) { + // settting to default sound set + if (app.state->getString("SoundSetName") == "Default") + return; + app.state->setProperty("SoundSetName", "Default"); + options::SetOption("SoundSet", sound_engine->convertToOldSoundSetNumber("Default")); + SetActiveSoundSet(sound_engine->getDefaultSoundSet()); + } else { + string newSet = sound_engine->getSoundSetByPosition(value); + assert(newSet != ""); + if (app.state->getString("SoundSetName") == newSet) + return; + app.state->setProperty("SoundSetName", newSet); + options::SetOption("SoundSet", sound_engine->convertToOldSoundSetNumber(newSet)); + SetActiveSoundSet(newSet); + } +} + +string sound::GetOptionSoundSetText(int value) +{ + if(value == 0) + return N_("Default"); + string soundset_name = sound_engine->getSoundSetByPosition(value); + if(soundset_name == "") + return "INVALID"; + return soundset_name; +} + diff --git a/project/jni/application/enigma/src/sound.hh b/project/jni/application/enigma/src/sound.hh new file mode 100644 index 000000000..fa58b25b5 --- /dev/null +++ b/project/jni/application/enigma/src/sound.hh @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * Copyright (C) 2007 Andreas Lochmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef SOUND_HH +#define SOUND_HH + +#include "ecl_math.hh" +#include "oxydlib/OxydVersion.h" + +#include +#include + +/** ----------- Survey of the formal data structure ----------- + * + * The wav-files are not to be accessed directly. There are several layers + * between the wav-data and the final EmitSoundEvent-function, to allow for the + * following uses: + * (a) Sound Sets, possibly user defined, + * (b) Access to oxyd's sound data, + * (c) Sound damping for loud objects with user defined data, + * (d) A "Default" mode which switches the sound set between level packs. + * (e) A "silence string" that can be written instead of playing the sound. + * For this, six layers of sound information exist. + * Note: A sound effect is given by a wav-file and several data how to play it. + * An object may choose a sound effect to play. Then it becomes a sound + * event (= sound effect + position etc.). + * + * (1) SoundEngine + * -> Providing global information and methods for using SDL. + * (2) SoundData + * -> Holding technical details about how to play the wav-files. + * (3) SoundEffect + * -> Holding game-related information about how to play a sound + * effect. This includes the effect's filename, default volume, + * if it's looped, if it's played on a global scale by default, + * the default damping information for this event, etc. + * (4) SoundSet + * -> Holding the sound set key of a sound set (see below), and if + * it is connected to Oxyd. It also provides methods to + * activate a sound set. + * (5) SoundEvent + * -> A struct to hold information about a sound event, like the + * effect's name, position, priority, volume etc. + * (6) SoundDamping + * -> Each time an object makes noise, a SoundDamping-record + * is created with an entry of the objects address (as void *). + * This class modulates the volume with which the next effect + * connected to this object is played. + * + * Sound events are normally invoked by "EmitSoundEvent(EFFECTNAME, ...)". + * EFFECTNAME can then be e.g. "pickup" or "laseron". This effect name does not + * yet define the sound effect completely. Instead, the actually activated sound + * set is looked up. It holds a string called sound set key (e.g. "Enigma" or + * "Oxyd*"), and together with the effect name they form the effect key, here + * "Enigma#pickup" or "Oxyd*#laseron". This effect key is looked up in the sound + * effect repository and results in a SoundEffect-entry, which is then played. + * + */ + +/** -------------- About the sound damping system -------------- + * + * The sound damping is not automatically used. Instead, it is called through + * the "World::getVolume"-function: + * sound::EmitSoundEvent (NAME, POS, getVolume(NAME, OBJECT_CALLING, DEF_VOLUME)) + * OBJECT_CALLING need not be a real address, it is never dereferenced. It just + * holds as a representative of the calling object. The SoundDamping-records + * are evaluated and finally erased by World::tick_sound_dampings. + * + * The values used in the sound damping systes can be user defined. In the + * following, we use the defaults: + * + * tick_sound_dampings is only to be evaluated every 10th tick (0.1s). + * Each damping factor (the ivar connected to the noisy object and its sound + * effect name) is reduced by 0.9. This is less than the 10th root of 0.5 (0.933), + * so after 1s the damping factor is reduced by more than one half. If the factor + * shrinks under 0.5, it is considered 0. + * + * Examples: + * 1) Frequency less than one sound event per 0.6 seconds. + * Then there is no damping at all. + * 2) N events per second. For each event, factor (F) is raised by + * one. And each 0.1 seconds it is multiplied with 0.9. We now + * have N/10 events per 0.1 seconds, hence in equilibrium f + * oscillates between + * f = (f + N/10) * 0.9 => f = N + * and f + N/10 = N * 1.1 (geometric series!). + * In particular, for large enough N, f is approximately proportional + * to N with half-life of less than a second. This is then evaluated + * in getVolume. + * + * World::getVolume returns the volume, if object OBJECT_CALLING wants + * to play sound effect NAME with default volume DEF_VOLUME. Often played + * sounds from always the same object are damped to reduce noise-level. + * Note that OBJECT_CALLING == NULL is explicitly allowed and used e.g. + * for all laser-sounds. The damping factor is increased by 1.0 for each + * event, and multiplied with 0.9 each 0.1 seconds, thereby approximately + * equals the average number of events per second. + * + */ + +/** -------------- Compatibility with 1.00 -------------- + * + * Enigma 1.01 uses a new option "SoundSetName", which replaces "SoundSet". + * To enable Enigma 1.00 to run on the same system, the "SoundSet" variable + * still exists. It is set to 0 (= "Default") for all user sound sets: + * When exiting 1.01 with a user sound set and starting 1.00, the default + * sound set will be activated for 1.00. Yet, as "SoundSetName" still shows + * the old value, 1.01 will use it to find its user sound set. Even if + * you change the sound set in 1.00, this will have no effect on the + * chosen sound set for 1.01. In contrast to this, if you change the 1.01 + * sound set, the 1.00 "SoundSet"-variable will be adapted. Except for this + * "restriction", you can use two different sound sets for 1.00 and 1.01. + * + */ + +namespace sound +{ +/* -------------------- Data types -------------------- */ + + typedef std::string SoundName; + + typedef std::vector ByteVec; + + struct SoundData { + ByteVec buf; + unsigned freq; + size_t samplesize; + bool signedp; + int nchannels; + }; + +/* -------------------- SoundDampingList ---------------- */ + +/*! This class stores object addresses and assigns volumes to + sound events, based on the frequency of the event. */ + + struct DampingData { + double incr; + double maxi; + double mult; + double mini; + double tick; + }; + + class SoundDamping { + private: + std::string effect_name; + const void *origin; + float factor; + DampingData damp; + + public: + SoundDamping(std::string effect_name_, const void *origin_); + bool is_equal(std::string name2, const void *origin2) { + return (origin2 == origin) && (effect_name == name2); + } + float get_volume(float def_volume); + bool tick(); // returns true if this entry should be erased + }; + +/* -------------------- Functions -------------------- */ + + void Init(bool withMusic =true, bool withSound =true); + void Shutdown(); + + void Tick (double dtime); + + void TempDisableSound(); + void TempReEnableSound(); + + void SetListenerPosition (const ecl::V2 &pos); + bool PlaySound (const SoundName &, const ecl::V2 &pos, + double relative_volume = 1.0, int priority=0); + bool PlaySoundGlobal (const SoundName &, double relative_volume = 1.0, int priority=0); + + void PlayMusic (const std::string &name); + void FadeoutMusic(); + + /*! Stop any music currently playing. */ + void StopMusic(); + + /*! Stop music only if it has the specified name, otherwise + continue playing. */ + void StopMusic (const std::string &name); + + void ClearCache(); + void DefineSound (const SoundName &, const SoundData &); + void SetSoundVolume (double vol); + void SetMusicVolume (double vol); + + /*! Helper function for oxyd.cc */ + std::string GetOxydSoundSet(OxydLib::OxydVersion oxyd_ver); + + /*! Sound set handling */ + void InitSoundSets(); + void SetActiveSoundSet(std::string soundset_name); + void SetDefaultSoundSet(std::string soundset_name); + + /*! Define a new sound event. */ + void DefineSoundEffect(std::string soundset_key, std::string name, std::string filename, + double volume, bool loop, bool global, int priority, + double damp_max, double damp_inc, double damp_mult, + double damp_min, double damp_tick, std::string silence_string); + + /*! Trigger a sound event. Return whether the event was handled. */ + bool EmitSoundEvent (const std::string &eventname, + const ecl::V2 &pos = ecl::V2 (), + double volume = 1.0, bool force_global = false); + bool EmitSoundEventGlobal (const std::string &eventname, double volume = 1.0); + + /*! Send the silence string of a sound effect to command line. */ + void WriteSilenceString (const std::string &eventname); + + /*! Helper functions for options menu */ + int GetOptionSoundSetCount(); + int GetOptionSoundSet(); + void SetOptionSoundSet(int value); + std::string GetOptionSoundSetText(int value); +} + +#endif diff --git a/project/jni/application/enigma/src/sound_internal.hh b/project/jni/application/enigma/src/sound_internal.hh new file mode 100644 index 000000000..751ec7a92 --- /dev/null +++ b/project/jni/application/enigma/src/sound_internal.hh @@ -0,0 +1,240 @@ +namespace +{ + +/* -------------------- SoundEvent -------------------- */ + + struct SoundEvent { + // Variables + SoundName name; + bool has_position; + ecl::V2 position; + int priority; + double volume; // Volume between 0.0 and 1.0 + int left; + int right; + + // Variables used internally by sound engine + bool active; + double playing_time; + + // Constructor + SoundEvent (); + }; + +/* -------------------- SoundEffect and SoundEffectRepository ---------------- */ + +/*! This class stores information about how to play sound events + and provides methods to play them. */ + + class SoundEffect { + public: + SoundEffect(string name_, string soundset_key_, string filename_, + double volume_, bool loop_, bool global_, int priority_, + double damp_max_, double damp_inc_, double damp_mult_, + double damp_min_, double damp_tick_, string silence_string_) + : name(name_), soundset_key(soundset_key_), filename(filename_), volume(volume_), + loop(loop_), global(global_), priority(priority_), + silence_string(silence_string_) { + damp.maxi = damp_max_; + damp.incr = damp_inc_; + damp.mult = damp_mult_; + damp.mini = damp_min_; + damp.tick = damp_tick_; + } + + SoundEffect() // standard empty data set, compare sound-defaults.lua + : name("EMPTY_EVENT"), filename(""), soundset_key(""), volume(1.0), + loop(false), global(false), priority(1), silence_string("") { + damp.maxi = 20.0; + damp.incr = 1.0; + damp.mult = 1.0; + damp.mini = 0.5; + damp.tick = 0.9; + } + + void setFilename(string filename_) { filename = filename_; } + bool play(const ecl::V2 &pos = ecl::V2(), double vol = 1.0, bool glob = false); + DampingData getDampingData() { return damp; } + string getSoundSetKey() { return soundset_key; } + string getSilenceString() { return silence_string; } + + private: + string name; + string filename; + string soundset_key; + string silence_string; + double volume; + bool loop; + bool global; + int priority; + DampingData damp; + }; + + class SoundSet { + public: + SoundSet(string soundset_key_, int button_position_, OxydLib::OxydVersion oxyd_ver_) + : soundset_key(soundset_key_), is_oxyd(true), oxyd_ver(oxyd_ver_), + button_position(button_position_) {} + + SoundSet(string soundset_key_, int button_position_) + : soundset_key(soundset_key_), is_oxyd(false), + oxyd_ver(OxydLib::OxydVersion_Invalid), button_position(button_position_) {} + + SoundSet() + : soundset_key(""), is_oxyd(false), + oxyd_ver(OxydLib::OxydVersion_Invalid), button_position(-1) {} + + bool activate(); + OxydLib::OxydVersion getOxydVersion() { return oxyd_ver; } + bool isOxyd() { return is_oxyd; } + string getSoundSetKey() { return soundset_key; } + int getButtonPosition() { return button_position; } + void setButtonPosition(int pos) { button_position = pos; } + + private: + string soundset_key; + bool is_oxyd; + OxydLib::OxydVersion oxyd_ver; + int button_position; + }; + + typedef map SoundEffectRepository; + typedef map SoundSetRepository; + +/* -------------------- SoundEngine -------------------- */ + + class SoundEngine { + public: + virtual ~SoundEngine() {} + + //! Returns true if successful. + virtual bool init() = 0; + virtual void shutdown() = 0; + virtual bool is_initialized() const = 0; + + virtual void set_sound_volume (double soundvol) = 0; + virtual void set_music_volume (double musicvol) = 0; + + // ---------- Music ---------- + + virtual bool play_music (const std::string &filename) = 0; + virtual void stop_music() = 0; + virtual void fadeout_music() = 0; + + // ---------- Sound effects ---------- + + virtual void clear_cache() = 0; + virtual void define_sound (const SoundName &, const std::string &filename)=0; + virtual void define_sound (const SoundName &, const SoundData &)=0; + virtual bool play_sound (const SoundEvent &s) = 0; + virtual void set_listenerpos (ecl::V2 pos) = 0; + virtual void tick (double dtime) {} + + // ---------- Sound effect repository and sound sets ---------- + + void setActiveSoundSetKey(string soundset_key) {active_sound_set_key=soundset_key;} + string getActiveSoundSetKey() { return active_sound_set_key; } + void setDefaultSoundSet(string soundset_name) {default_sound_set=soundset_name;} + string getDefaultSoundSet() { return default_sound_set; } + string effectKey(string effect_name, string soundset_name = ""); + DampingData getDampingData(string effect_name) { + return sound_effects[effectKey(effect_name)].getDampingData(); } + void defineSoundEffect(string soundset_key, string name, SoundEffect se) { + sound_effects[effectKey(name, soundset_key)] = se; + } + bool emitSoundEvent (const std::string &eventname, const ecl::V2 &pos = ecl::V2 (), + double volume = 1.0, bool force_global = false); + void writeSilenceString (const std::string &eventname); + void initSoundSets(); + bool defineSoundSet(string soundset_name, string soundset_key, int button_position); + bool defineSoundSetOxyd(string soundset_name, string soundset_key, + OxydLib::OxydVersion oxyd_ver, int button_position); + string getOxydSoundSet(OxydLib::OxydVersion oxyd_ver); + int convertToOldSoundSetNumber(string soundset_name); + string convertFromOldSoundSetNumber(int soundset_number); + void setActiveSoundSet(string soundset_name); + void setSoundSetCount(int count) { sound_set_count = count; } + int getSoundSetCount() { return sound_set_count; } + int getButtonPosition(string soundset_name) { + return sound_sets[soundset_name].getButtonPosition(); + } + string getSoundSetByPosition(int button_position); + + private: + SoundSetRepository sound_sets; + SoundEffectRepository sound_effects; + string active_sound_set_key; + string default_sound_set; + int sound_set_count; + }; + + class SoundEngine_Null : public SoundEngine { + public: + + // SoundEngine interface + bool init() { return true; } + void shutdown() {} + void clear_cache() {} + bool is_initialized() const { return true; } + void set_sound_volume(double /*soundvol*/) {} + void set_music_volume(double /*musicvol*/) {} + bool play_music (const std::string &/*filename*/) { return false; } + void stop_music() {} + void fadeout_music() {} + bool play_sound (const SoundEvent &) { return false;} + void define_sound (const SoundName &, const std::string &/*filename*/) {} + void define_sound (const SoundName &, const SoundData &) {} + void set_listenerpos (ecl::V2 pos) {} + }; + + class SoundEngine_SDL : public SoundEngine { + public: + SoundEngine_SDL(); + ~SoundEngine_SDL(); + + // ---------- SoundEngine interface ---------- + bool init(); + void shutdown(); + + bool is_initialized() const { return m_initialized; } + void set_sound_volume(double soundvol); + void set_music_volume(double musicvol); + + bool play_music (const std::string &filename); + void stop_music(); + void fadeout_music(); + + bool play_sound(const SoundEvent &s); + void clear_cache(); + void define_sound (const SoundName &, const std::string &filename); + void define_sound (const SoundName &, const SoundData &); + + + void set_listenerpos (ecl::V2 pos) { m_listenerpos = pos; } + void tick (double dtime); + private: + // ---------- Private methods ---------- + Mix_Chunk *cache_sound(const std::string &name); + + void update_channel (int channel); + int already_playing (const SoundEvent &s); + + + static void channel_finished (int channel); + + + // ---------- Variables ---------- + bool m_initialized; + int m_soundvolume; + int m_musicvolume; + Mix_Music *m_current_music; + int m_freq; + Uint16 m_format; + int m_channels; + ecl::Dict wav_cache; + vector m_channelinfo; + ecl::V2 m_listenerpos; + SDL_mutex *m_mutex; + static SoundEngine_SDL *m_instance; + }; +} diff --git a/project/jni/application/enigma/src/st_switches.cpp b/project/jni/application/enigma/src/st_switches.cpp new file mode 100644 index 000000000..1d2bd027a --- /dev/null +++ b/project/jni/application/enigma/src/st_switches.cpp @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "laser.hh" +#include "player.hh" +#include "server.hh" +#include "Inventory.hh" + +#include "stones_internal.hh" + +using namespace std; +using namespace world; +using namespace stones; + + +/* -------------------- Switch -------------------- */ + +namespace +{ + class SwitchStone : public OnOffStone { + CLONEOBJ(SwitchStone); + public: + SwitchStone() : OnOffStone("st-switch"), state(IDLE) {} + private: + enum State { IDLE, TOGGLING } state; + + void init_model() { + set_model(is_on() ? "st-switch-on" : "st-switch-off"); + } + + void actor_hit(const StoneContact &/*sc*/) { + set_on (!is_on()); + } + + virtual void set_on(bool newon) { + if (state == IDLE && newon != is_on()) { +// set_attrib("on", enigma::Value(newon)); + sound_event ("switchon"); + state = TOGGLING; + if (newon) + set_anim("st-switch-turnon"); + else + set_anim("st-switch-turnoff"); + PerformAction(this, newon); + } + } + + void animcb() { + state = IDLE; + set_attrib("on", enigma::Value(!is_on())); + init_model(); +// set_on(!is_on()); + } + const char *collision_sound() { return "metal"; } + }; +} + +/* -------------------- Switch_black -------------------- */ + +namespace +{ + class Switch_black : public OnOffStone { + CLONEOBJ(Switch_black); + public: + Switch_black() : OnOffStone("st-switch_black"), + state(IDLE) + {} + private: + enum State { IDLE, TOGGLING } state; + void init_model() { + set_model(is_on() ? "st-switch_black-on" : "st-switch_black-off"); + } + + void actor_hit(const StoneContact &sc) { + if (get_id (sc.actor) == world::ac_blackball) + set_on (!is_on()); + } + + virtual void set_on(bool newon) { + if (state == IDLE && newon != is_on()) { +// set_attrib("on", enigma::Value(newon)); + sound_event ("switchon"); + state = TOGGLING; + if (newon) + set_anim("st-switch_black-turnon"); + else + set_anim("st-switch_black-turnoff"); + PerformAction(this, newon); + } + } + void animcb() { + state = IDLE; + set_attrib("on", enigma::Value(!is_on())); + init_model(); +// set_on(!is_on()); + } + + const char *collision_sound() { return "metal"; } + }; +} + +/* -------------------- Switch_white -------------------- */ + +namespace +{ + class Switch_white : public OnOffStone { + CLONEOBJ(Switch_white); + public: + Switch_white() : OnOffStone("st-switch_white"), + state(IDLE) + {} + private: + enum State { IDLE, TOGGLING } state; + void init_model() { + set_model(is_on() ? "st-switch_white-on" : "st-switch_white-off"); + } + + void actor_hit(const StoneContact &sc) { + if (get_id (sc.actor) == world::ac_whiteball) + set_on (!is_on()); + } + + virtual void set_on(bool newon) { + if (state == IDLE && newon != is_on()) { +// set_attrib("on", enigma::Value(newon)); + sound_event ("switchon"); + state = TOGGLING; + if (newon) + set_anim("st-switch_white-turnon"); + else + set_anim("st-switch_white-turnoff"); + PerformAction(this, newon); + } + } + void animcb() { + state = IDLE; + set_attrib("on", enigma::Value(!is_on())); + init_model(); + } + + const char *collision_sound() { return "metal"; } + }; +} + + + +/* -------------------- Coin slot -------------------- */ + +namespace +{ + class CoinSlot : public OnOffStone, public TimeHandler { + CLONEOBJ(CoinSlot); + public: + CoinSlot(); + ~CoinSlot(); + + private: + // Variables. + enum State { ACTIVE, INACTIVE } state; + double remaining_time; + + // GridObject interface + void init_model(); + void animcb(); + + // TimeHandler interface + void tick(double dtime); + + // Stone interface + void actor_hit(const StoneContact &sc); + const char *collision_sound() { return "metal"; } + + // Private methods. + void change_state(State newstate); + }; +} + +CoinSlot::CoinSlot() +: OnOffStone("st-coinslot"), state(INACTIVE), remaining_time(0) +{ +} + +CoinSlot::~CoinSlot() { + GameTimer.remove_alarm (this); +} + + +void CoinSlot::init_model() { + set_model(state==ACTIVE ? "st-coinslot-active" : "st-coinslot"); +} + + +void CoinSlot::change_state(State newstate) { + if (state == newstate) return; + + switch (newstate) { + case ACTIVE: + PerformAction(this, true); + GameTimer.activate(this); + break; + case INACTIVE: + PerformAction(this, false); + GameTimer.deactivate(this); + sound_event ("coinslotoff"); + break; + } + state = newstate; + init_model(); +} + +void CoinSlot::animcb() { + change_state(ACTIVE); + init_model(); +} + +void CoinSlot::tick(double dtime) +{ + ASSERT(remaining_time > 0, XLevelRuntime, "CoinSlot: tick called, but no remaining time"); + + remaining_time -= dtime; + if (remaining_time <= 0) + change_state(INACTIVE); +} + +void CoinSlot::actor_hit(const StoneContact &sc) +{ + if (enigma::Inventory *inv = player::GetInventory(sc.actor)) { + if (Item *it = inv->get_item (0)) { + ItemID id = get_id(it); + if (id == it_coin1 || id == it_coin2 || id == it_coin4) { + sound_event ("coinsloton"); + set_anim("st-coin2slot"); + + double coin_value = 0; + it->double_attrib("value", &coin_value); + remaining_time += coin_value; + + inv->yield_first(); + player::RedrawInventory (inv); + delete it; + } + } + } +} + + +/* -------------------- Key switches -------------------- */ + +// Attributes: +// +// :keycode a numerical code; only keys with the same code +// can activate this switch +// :on 1 or 0 +// :target,action as usual + +/** \page st-key Key Switch Stone + +This stone acts as a lock and can only be activated by +using a key item. You can use keycodes to let keys only +open specific key stones. + +\subsection keye Example +\verbatim +set_stone( "st-key_b", 14,77, {action="openclose", target="door2"}) +set_item("it-key_b", 12,7) +\endverbatim + +\image html st-key0.png +*/ +namespace +{ + class KeyStone : public OnOffStone { + CLONEOBJ(KeyStone); + + void init_model() {set_model(is_on() ? "st-key1" : "st-key0");} + void actor_hit(const StoneContact &sc); + const char *collision_sound() { return "metal"; } + bool check_matching_key (enigma::Inventory *inv); + public: + KeyStone(const char *kind="st-key", int keycode=0) + : OnOffStone(kind) + { + set_attrib("keycode", keycode); + } + }; + + class KeyStone_a : public KeyStone { + CLONEOBJ(KeyStone_a); + public: + KeyStone_a() : KeyStone("st-key_a", 1) {} + }; + class KeyStone_b : public KeyStone { + CLONEOBJ(KeyStone_b); + public: + KeyStone_b() : KeyStone("st-key_b", 2) {} + }; + class KeyStone_c : public KeyStone { + CLONEOBJ(KeyStone_c); + public: + KeyStone_c() : KeyStone("st-key_c", 3) {} + }; +} + +bool KeyStone::check_matching_key (enigma::Inventory *inv) +{ + Item *it = inv->get_item(0); + int keycode, my_keycode = int_attrib ("keycode"); + return (it + && it->is_kind("it-key*") + && it->int_attrib("keycode", &keycode) + && my_keycode == keycode); +} + +void KeyStone::actor_hit(const StoneContact &sc) +{ + enigma::Inventory *inv = player::GetInventory(sc.actor); + if (!inv) + return; + + bool toggle = false; + + if (server::GameCompatibility == enigma::GAMET_ENIGMA) { + if (is_on()) { + if (!inv->is_full()) { + Item *key = MakeItem("it-key"); + key->set_attrib ("keycode", int_attrib ("keycode")); + inv->add_item(key); + toggle = true; + } + } + else if (check_matching_key (inv)) { + DisposeObject (inv->yield_first()); + toggle = true; + } + player::RedrawInventory (inv); + } + else { + if (check_matching_key (inv)) + toggle = true; + } + + if (toggle) { + set_on (!is_on()); + PerformAction (this, is_on()); + } +} + + +/* -------------------- FourSwitch -------------------- */ + +// Attributes: +// +// :on 1 or 0 +// :target,action as usual + +namespace +{ + class FourSwitch : public OnOffStone { + CLONEOBJ(FourSwitch); + public: + FourSwitch() : OnOffStone("st-fourswitch"), + m_direction(NORTH), + m_inactive_so_far (true) + {} + private: + // Variables + Direction m_direction; + bool m_inactive_so_far; + + // Private methods + void turn() + { + static int direction2idx[] = { + 3, // WEST + 2, // SOUTH + 1, // EAST + 0 // NORTH + }; + + if (!m_inactive_so_far) { + world::EmitSignalByIndex(this, direction2idx[m_direction], 0); + } else + m_inactive_so_far = false; + + m_direction = rotate_cw (m_direction); + init_model(); + set_on(!is_on()); + sound_event ("fourswitch"); + + if (world::HaveSignals (this)) { + world::EmitSignalByIndex(this, direction2idx[m_direction], 1); + } else { + // no signal handler defined + PerformAction(this, is_on()); + } + } + + void init_model() { + switch (m_direction) { + case NORTH: set_model("st-fourswitch-n"); break; + case EAST: set_model("st-fourswitch-e"); break; + case SOUTH: set_model("st-fourswitch-s"); break; + case WEST: set_model("st-fourswitch-w"); break; + case NODIR: ASSERT(0, XLevelRuntime, + "FourSwitch: no direction defined (found in init_model)"); + } + } + + void actor_hit(const StoneContact &/*sc*/) { + turn(); + } + + virtual Value on_message (const Message &m) + { + if (m.message == "signal" || m.message == "trigger") + turn(); + return Value(); + } + + const char *collision_sound() { return "metal"; } + }; +} + + +/* -------------------- Laser / Time switches -------------------- */ + +namespace +{ + class LaserTimeSwitchBase : public PhotoStone, public TimeHandler { + public: + LaserTimeSwitchBase(const char *kind); + virtual ~LaserTimeSwitchBase(); + + private: + // LaserTimeSwitchBase interface + virtual const char *get_active_model() const = 0; + virtual const char *get_inactive_model() const = 0; + virtual double timer_delay() const; + + bool inverse() { return int_attrib("inverse") == 1; } + + // Stone interface + void on_creation (GridPos p); + void on_removal (GridPos p); + const char *collision_sound() { return "metal"; } + + // PhotoStone interface. + void notify_laseron(); + void notify_laseroff(); + + // TimeHandler interface + void alarm(); + + protected: + // variables : + enum State { IDLE, LIGHTED, TOUCHED }; + State state; + + void change_state(State newstate); + }; + + + class LaserSwitch : public LaserTimeSwitchBase { + CLONEOBJ(LaserSwitch); + public: + LaserSwitch(); + private: + // LaserTimeSwitchBase interface + const char *get_active_model() const; + const char *get_inactive_model() const; + }; + + + class LaserTimeSwitch : public LaserTimeSwitchBase { + CLONEOBJ(LaserTimeSwitch); + public: + LaserTimeSwitch(const char *kind = "st-lasertimeswitch"); + private: + // LaserTimeSwitchBase interface + const char *get_active_model() const; + const char *get_inactive_model() const; + double timer_delay() const; + + // Stone interface + void actor_hit(const StoneContact &sc); + }; + + class TimeSwitch : public LaserTimeSwitch { + CLONEOBJ(TimeSwitch); + public: + TimeSwitch(); + private: + // ignore laser: + void notify_laseron(); + void notify_laseroff(); + }; +} + + +LaserTimeSwitchBase::LaserTimeSwitchBase(const char *kind) +: PhotoStone(kind) , state(IDLE) +{} + +LaserTimeSwitchBase::~LaserTimeSwitchBase() { + GameTimer.remove_alarm (this); +} + +void LaserTimeSwitchBase::change_state(State newstate) { + if (state == newstate) + return; + + if (state == IDLE) { +// sound_event ("st-switch"); + set_model(get_active_model()); + PerformAction(this, !inverse()); + if (newstate == TOUCHED) { + double delay = timer_delay(); + ASSERT(delay>0.0, XLevelRuntime, "LaserTimeSwitchBase: delay non-positive"); + GameTimer.set_alarm(this, delay, false); + } + } + else if (newstate == IDLE) { +// sound_event ("st-switch"); + set_model(get_inactive_model()); + PerformAction(this, inverse()); + } + else { + // it's not allowed to switch from LIGHTED to TOUCHED + ASSERT(!(state == LIGHTED && newstate == TOUCHED), XLevelRuntime, + "LaserTimeSwitchBase: trying to switch from lighted to touched"); + } + state = newstate; +} + +void LaserTimeSwitchBase::on_creation(GridPos) +{ + set_model(get_inactive_model()); + photo_activate(); +} + +void LaserTimeSwitchBase::on_removal(GridPos) +{ + photo_deactivate(); +} + +void LaserTimeSwitchBase::notify_laseron() { + if (state != LIGHTED) + change_state(LIGHTED); +} + +void LaserTimeSwitchBase::notify_laseroff() { + if (state == LIGHTED) + change_state(IDLE); +} + +void LaserTimeSwitchBase::alarm() { + if (state == TOUCHED) + change_state(IDLE); +} + +double LaserTimeSwitchBase::timer_delay() const { + return -1; // we have no timer delay +} + +/* ---------- LaserSwitch ---------- */ + +LaserSwitch::LaserSwitch() +: LaserTimeSwitchBase("st-laserswitch") +{} + +const char *LaserSwitch::get_active_model() const { + return "st-laserswitch1"; +} + +const char *LaserSwitch::get_inactive_model() const { + return "st-laserswitch0"; +} + +/* ---------- LaserTimeSwitch ---------- */ + +LaserTimeSwitch::LaserTimeSwitch(const char *kind) + : LaserTimeSwitchBase(kind) +{ + set_attrib("delay", 1.8); +} + +const char *LaserTimeSwitch::get_active_model() const { + return "st-time1switch"; +} + +const char *LaserTimeSwitch::get_inactive_model() const { + return "st-timeswitch"; +} + +double LaserTimeSwitch::timer_delay() const { + double delay; + if (!double_attrib("delay", &delay)) + ASSERT(0, XLevelRuntime, "LaserTimeSwitch: delay not properly defined"); + return delay; +} + +void LaserTimeSwitch::actor_hit(const StoneContact &sc) { + if (sc.actor && state == IDLE) + change_state(TOUCHED); +} + +/* ---------- TimeSwitch ---------- */ + +// Attributes: +// +// :on 1 or 0 +// :target,action as usual + +TimeSwitch::TimeSwitch() +: LaserTimeSwitch("st-timeswitch") +{ +} + +void TimeSwitch::notify_laseron() {} // ignore laser +void TimeSwitch::notify_laseroff() {} + + +/* -------------------- Floppy switch -------------------- */ + +// Attributes: +// +// :on 1 or 0 +// :target,action as usual +namespace +{ + class FloppyStone : public OnOffStone { + CLONEOBJ(FloppyStone); + public: + FloppyStone() : OnOffStone("st-floppy") {} + private: + // Stone interface + void init_model(); + void actor_hit(const StoneContact &sc); + const char *collision_sound() { return "metal"; } + }; +} + +void FloppyStone::init_model() +{ + set_model(is_on() ? "st-floppy1" : "st-floppy0"); +} + +void FloppyStone::actor_hit (const StoneContact &sc) +{ + if (enigma::Inventory *inv = player::GetInventory(sc.actor)) { + if (is_on()) { + if (!inv->is_full()) { + inv->add_item (MakeItem("it-floppy")); + set_on(false); + PerformAction(this, is_on()); + } + } + else if (player::WieldedItemIs (sc.actor, "it-floppy")) { + DisposeObject (inv->yield_first()); + set_on(true); + PerformAction(this, is_on()); + } + player::RedrawInventory (inv); + } +} + + +/* -------------------- Functions -------------------- */ + +void InitSwitches() +{ + Register (new CoinSlot); + Register (new FloppyStone); + Register (new FourSwitch); + Register (new KeyStone); + Register (new KeyStone_a); + Register (new KeyStone_b); + Register (new KeyStone_c); + Register (new LaserSwitch); + Register (new LaserTimeSwitch); + Register (new SwitchStone); + Register (new Switch_black); + Register (new Switch_white); + Register (new TimeSwitch); +} diff --git a/project/jni/application/enigma/src/stones.cpp b/project/jni/application/enigma/src/stones.cpp new file mode 100644 index 000000000..ea5f2b348 --- /dev/null +++ b/project/jni/application/enigma/src/stones.cpp @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "stones_internal.hh" +#include "server.hh" +#include "client.hh" +#include "player.hh" +#include "Inventory.hh" + +using namespace std; +using namespace world; +using namespace stones; + + +/* -------------------- Helper routines -------------------- */ + +/*! Determine whether the actor hitting the stone can move stone + and return either the direction the stone should move or NODIR. */ +Direction stones::get_push_direction (const StoneContact &sc) +{ + ActorInfo *ai = sc.actor->get_actorinfo(); + Direction dir = contact_face(sc); + + // Make sure the speed component towards the face of the stone is + // large enough and pointing towards the stone. + if (dir!=enigma::NODIR && ai->vel * sc.normal < -4) + return reverse(dir); + return NODIR; +} + +/* Move a stone (by sending an impulse) Called when an actor hits a + stone. */ +bool stones::maybe_push_stone (const StoneContact &sc) +{ + Direction dir = get_push_direction(sc); + if (dir != enigma::NODIR) { + sc.actor->send_impulse(sc.stonepos, dir); + return GetStone(sc.stonepos) == 0; // return true only if stone vanished + } + return false; +} + + +//====================================================================== +// STONES +//====================================================================== + +/** \page lstones Available Stones + +Oxyd Stones: +- \ref st-oxyd +- \ref st-fakeoxyd +- \ref st-fart + +Movable stones: +- \ref st-brownie +- \ref st-wood +- \ref st-block + +Stones that can trigger actions: +- \ref st-switch +- \ref st-fourswitch +- \ref st-laserswitch +- \ref st-key +- \ref st-coinslot +- \ref st-timer + +Stones that can change their behaviour: +- \ref st-brick_magic +- \ref st-stonebrush +- \ref st-invisible_magic +- \ref st-break_invisible + +Lasers and Mirrors: +- \ref st-laser +- \ref st-pmirror +- \ref st-3mirror + +Other stones: +- \ref st-death +- \ref st-swap +- \ref st-bolder +- \ref st-puzzle +- \ref st-stone_break +- \ref st-window +- \ref st-break_acwhite +- \ref st-break_acblack +- \ref st-oneway +- \ref st-oneway_black +- \ref st-oneway_white +- \ref st-chameleon +*/ + +Stone::Stone() +{} + +Stone::Stone(const char * kind) +: GridObject (kind) +{} + +Stone::~Stone() { revokeDelayedImpulses(this); } + +const StoneTraits &Stone::get_traits() const +{ + static StoneTraits default_traits = { + "INVALID", st_INVALID, stf_none, material_stone, 1.0 + }; + return default_traits; +} + +const char *Stone::get_kind() const +{ + const StoneTraits &tr = get_traits(); + if (tr.id != st_INVALID) + return tr.name; + else + return Object::get_kind(); +} + +StoneResponse Stone::collision_response(const StoneContact &) { + return STONE_REBOUND; +} + + +void Stone::actor_hit(const StoneContact &sc) +{ + if (is_movable()) + stones::maybe_push_stone (sc); +} + +void Stone::actor_touch(const StoneContact &sc) { +} + +void Stone::on_impulse(const Impulse& impulse) { + if (is_movable()) + move_stone(impulse.dir); +} + +const char * Stone::collision_sound() { + return "stone"; +} + +/* Move a stone (regardless whether it is_movable() or not) if + the destination field is free. + Returns: true if stone has been moved. + + Note: This should be used by on_impulse() to perform a move. +*/ +bool Stone::move_stone(GridPos newPos, const char *soundevent) { + GridPos p = get_pos(); + + if (!GetStone(newPos)) { + sound_event (soundevent); + + MoveStone(p, newPos); + server::IncMoveCounter(); + + on_move(); + if (Item *it = GetItem(newPos)) it->on_stonehit(this); + + return true; + } + return false; +} +bool Stone::move_stone(Direction dir) { + return move_stone(move(get_pos(), dir), "movesmall"); +} + +void Stone::on_move() { + if (!is_floating()) + ShatterActorsInsideField (get_pos()); +} + +/* Multiplies velocity with the attribute-matrix + hit_distortion_[xx,xy,yx,yy] and factor hit_factor + If components are not set, use ((1,0),(0,1)) as + default matrix, resp. defaultfactor as hit_factor. */ +ecl::V2 Stone::distortedVelocity (ecl::V2 vel, double defaultfactor = 1.0) { + ecl::V2 newvel(0,0); + if(const Value *xx = this->get_attrib("hit_distortion_xx")) + newvel[0] += to_double(*xx) * vel[0]; + else + newvel[0] += vel[0]; + if(const Value *xy = this->get_attrib("hit_distortion_xy")) + newvel[0] += to_double(*xy) * vel[1]; + if(const Value *yx = this->get_attrib("hit_distortion_yx")) + newvel[1] += to_double(*yx) * vel[0]; + if(const Value *yy = this->get_attrib("hit_distortion_yy")) + newvel[1] += to_double(*yy) * vel[1]; + else + newvel[1] += vel[1]; + if (const Value *factor = this->get_attrib("hit_factor")) + newvel *= to_double(*factor); + else + newvel *= defaultfactor; + return newvel; +} + + + +// ******************************************************************************* +// Stones under development : + + + +/* -------------------- Explosion stone -------------------- */ +namespace +{ + class ExplosionStone : public Stone { + CLONEOBJ(ExplosionStone); + + StoneResponse collision_response(const StoneContact &) { + return STONE_PASS; + } + + void actor_contact (Actor *a) { + SendMessage(a, "shatter"); + } + void init_model() { + set_anim("st-explosion"); + } + + void animcb() { + KillStone(get_pos()); + } + public: + ExplosionStone(): Stone("st-explosion") + {} + }; +} + + +/* -------------------- Charge stone -------------------- */ + +// Attributes: +// +// :charge + - 0 +namespace +{ + class ChargeStone : public Stone { + CLONEOBJ(ChargeStone); + public: + ChargeStone(const char *kind, double charge) + : Stone(kind) + { + set_attrib("charge", charge); + } + private: + double get_charge() { + double q = 0; + double_attrib("charge", &q); + return max(-1.0, min(1.0, q)); + } + void animcb() { init_model(); } + void actor_hit (const StoneContact &sc) { + ActorInfo *ai = sc.actor->get_actorinfo(); + ai->charge = get_charge(); + set_anim(string(get_kind())+"-anim"); + } + }; +} + + +/* -------------------- SpitterStone -------------------- */ + +namespace +{ + class SpitterStone : public Stone { + CLONEOBJ(SpitterStone); + + enum State { IDLE, LOADING, SPITTING }; + + // Variables + State state; + V2 ball_velocity; + + // Functions. + void animcb(); + void actor_hit (const StoneContact &sc); + + public: + SpitterStone () : Stone("st-spitter"), state (IDLE) { + } + }; +} + +void SpitterStone::animcb() { + switch (state) { + case IDLE: + ASSERT(0, XLevelRuntime, "SpitterStone: animcb called with inconsistent state"); + case LOADING: { + Actor *ball = MakeActor (ac_cannonball); + ActorInfo *ai = ball->get_actorinfo(); + V2 center = get_pos().center(); + + state = SPITTING; + ai->vel = ball_velocity; + world::AddActor (center[0], center[1], ball); + set_anim ("st-spitter-spitting"); + break; + } + case SPITTING: + init_model(); + state = IDLE; + break; + } +} + +void SpitterStone::actor_hit (const StoneContact &sc) +{ + if (state != IDLE) + return; + + if (enigma::Inventory *inv = player::GetInventory(sc.actor)) { + int lifepos = inv->find("it-extralife"); + if (lifepos != -1) { + delete inv->yield_item(lifepos); + player::RedrawInventory (inv); + ball_velocity = distortedVelocity(sc.actor->get_vel(), 1.0); + state = LOADING; + set_anim ("st-spitter-loading"); + } + } +} + + +/* -------------------- YieldedGridStone -------------------- */ + +YieldedGridStone::YieldedGridStone(Stone *st) +: stone(st) +{ + GridPos pos = stone->get_pos(); + model = display::YieldModel(GridLoc(GRID_STONES, pos)); + YieldStone(pos); +} + +YieldedGridStone::~YieldedGridStone() +{ + ASSERT(!stone, XLevelRuntime, + "YieldedGridStone: destructor called though stone still exists"); + ASSERT(!model, XLevelRuntime, + "YieldedGridStone: destructor called though model still exists"); +} + +void YieldedGridStone::set_stone(GridPos pos) +{ + SetStone(pos, stone); + display::SetModel(GridLoc(GRID_STONES, stone->get_pos()), model); + stone->on_move(); + stone = 0; + model = 0; +} + +void YieldedGridStone::dispose() +{ + stone->dispose(); + delete model; + stone = 0; + model = 0; +} + + +/* -------------------- Oxyd compatibility stones -------------------- */ + +namespace +{ + /* I have no idea what these stones are _really_ supposed to do; + they seemingly do not appear in the landscape and they create + normal floor tiles on creation. Other than that... who + knows... */ + class Peroxyd_0xb8 : public Stone { + CLONEOBJ(Peroxyd_0xb8); + public: + Peroxyd_0xb8() : Stone("st-peroxyd-0xb8") + {} + + void on_creation (GridPos p) { + SetFloor (p, MakeFloor ("fl-normal")); + KillStone(p); + } + }; + + class Peroxyd_0xb9 : public Stone { + CLONEOBJ(Peroxyd_0xb9); + public: + Peroxyd_0xb9() : Stone("st-peroxyd-0xb9") + {} + + void on_creation (GridPos p) { + SetFloor (p, MakeFloor ("fl-normal")); + KillStone(p); + } + }; + + class Oxyd_0x18 : public Stone { + CLONEOBJ(Oxyd_0x18); + public: + Oxyd_0x18() : Stone("st-oxyd-0x18") { + } + void on_creation (GridPos p) { + KillStone (p); + } + }; +} + + +/* -------------------- Flash stone -------------------- */ +namespace +{ + class FlashStone : public Stone { + CLONEOBJ(FlashStone); + + void actor_hit (const StoneContact &sc) { + if (Actor *other = FindOtherMarble(sc.actor)) { + other->add_force (distortedVelocity(sc.actor->get_vel(), 20)); + } + } + + public: + FlashStone() : Stone ("st-flash") + {} + }; +} + + +/* -------------------- Surprise stone -------------------- */ +namespace +{ + class SurpriseStone : public Stone { + CLONEOBJ (SurpriseStone); + public: + SurpriseStone() : Stone("st-surprise") + {} + + void actor_hit (const StoneContact &) { + static const char *stonename[] = { + "st-grate1", + "st-death", + "st-surprise", + "st-glass1_hole", + "st-magic", + "st-knight", + "st-thief", + "st-plain_break", + "st-plain_breaking" + }; + int idx = enigma::IntegerRand (1, 9) - 1; + sound_event ("stonetransform"); + ReplaceStone (get_pos(), MakeStone (stonename[idx])); + } + }; +} + + +/* -------------------- Coffee stone -------------------- */ +namespace +{ + class CoffeeStone : public Stone { + CLONEOBJ(CoffeeStone); + public: + CoffeeStone() : Stone ("st-coffee") { + } + + void actor_hit (const StoneContact &) { + sound_event ("stonetransform"); + ReplaceStone(get_pos(), MakeStone("st-glass_move")); + } + }; +} + + +/* -------------------- Breaking stone -------------------- */ +namespace +{ + class BreakingStone : public Stone { + CLONEOBJ(BreakingStone); + + void init_model() { + sound_event("stonedestroy"); + set_anim("st-breaking"); + } + + void animcb() { + KillStone(get_pos()); + } + public: + BreakingStone() : Stone("st-breaking") { + } + }; +} + + +/* -------------------- Bug stone -------------------- */ +namespace +{ + class BugStone : public Stone { + CLONEOBJ(BugStone); + public: + BugStone() : Stone("st-bug") { + } + + void actor_hit (const StoneContact &sc) { + if (get_id(sc.actor) == ac_bug) { + ReplaceStone(get_pos(), MakeStone("st-breaking")); + } + } + }; +} + + +/* -------------------- Plain stones -------------------- */ + +/* These stones mimic the behaviour of the plain-looking stones in + Oxyd. */ +namespace +{ + class PlainStone : public Stone { + CLONEOBJ(PlainStone); + + + void on_laserhit (Direction) { + ReplaceStone (get_pos(), MakeStone("st-plain_cracked")); + } + + const char *collision_sound() {return "stone";} + + virtual Value message (const string &msg, const Value &) { + if (msg == "trigger" || msg == "signal") { + ReplaceStone(get_pos(), MakeStone("st-plain_hole")); + } + return Value(); + } + void actor_hit (const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-pencil")) { + enigma::Inventory *inv = player::GetInventory(sc.actor); + if (inv && inv->size() > 0) { + delete inv->yield_item(0); + player::RedrawInventory(inv); + sound_event("stonepaint"); + ReplaceStone(get_pos(), MakeStone("st-firebreak")); + } + } else + Stone::actor_hit(sc); + } + public: + PlainStone() : Stone("st-plain") {} + }; + + class PlainStone_Hollow : public Stone { + CLONEOBJ(PlainStone_Hollow); + + virtual Value message (const string &msg, const Value &) { + if (msg == "trigger" || msg == "signal") { + ReplaceStone(get_pos(), MakeStone("st-plain")); + } + return Value(); + } + + StoneResponse collision_response(const StoneContact &) + { return STONE_PASS; } + + bool is_floating() const { return true; } + public: + PlainStone_Hollow() : Stone("st-plain_hole") { + } + }; + + class PlainStone_Breaking : public Stone { + CLONEOBJ(PlainStone_Breaking); + + void init_model() { + set_anim("st-plain_breaking"); + } + void animcb() { + KillStone(get_pos()); + } + const char *collision_sound() {return "metal";} + public: + PlainStone_Breaking() : Stone ("st-plain_breaking") { + } + }; + + class PlainStone_Breakable : public Stone { + CLONEOBJ(PlainStone_Breakable); + + const char *collision_sound() {return "metal";} + + void break_me() { + sound_event ("stonedestroy"); + ReplaceStone(get_pos(), MakeStone ("st-plain_breaking")); + } + void on_laserhit (Direction) { + break_me(); + } + virtual Value message (const string &msg, const Value &) { + if (msg =="ignite" || msg == "expl" || msg == "bombstone") + break_me(); + return Value(); + } + void actor_hit (const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-hammer")) { + break_me(); + } + } + + void on_floor_change() { + if (Floor *fl = GetFloor (get_pos())) + if (fl->is_kind("fl-abyss")) + ReplaceStone (get_pos(), MakeStone("st-plain_falling")); + } + + public: + PlainStone_Breakable() : Stone("st-plain_break") { + } + }; + + class PlainStone_Cracked : public Stone { + CLONEOBJ(PlainStone_Cracked); + + void break_me() { + sound_event ("stonedestroy"); + ReplaceStone(get_pos(), MakeStone("st-plain_breaking")); + } + + void actor_hit (const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-hammer")) { + break_me(); + } + } + + virtual Value message (const string &msg, const Value &) { + if (msg =="ignite" || msg == "expl" || msg == "bombstone") + break_me(); + return Value(); + } + const char *collision_sound() {return "metal";} + public: + PlainStone_Cracked() : Stone("st-plain_cracked") { + } + }; + + class PlainStone_Falling : public Stone { + CLONEOBJ(PlainStone_Falling); + + void init_model() { + set_anim("st-plain_falling"); + } + + void animcb() { + sound_event ("stonedestroy"); + KillStone(get_pos()); + } + public: + PlainStone_Falling() : Stone("st-plain_falling") { + } + }; + + class PlainStone_Movable : public Stone { + CLONEOBJ(PlainStone_Movable); + + void break_me() { + sound_event ("stonedestroy"); + ReplaceStone(get_pos(), MakeStone ("st-plain_breaking")); + } + virtual Value message (const string &msg, const Value &) { + if (msg =="ignite" || msg == "expl" || msg == "bombstone") + break_me(); + return Value(); + } + void on_move() { + Stone::on_move(); + GridPos p = get_pos(); + if (Floor *fl = GetFloor (p)) { + if (fl->is_kind("fl-abyss")) { + ReplaceStone (p, MakeStone("st-plain_falling")); + } + else if (fl->is_kind("fl-swamp") || fl->is_kind("fl-water")) { + sound_event ("drown"); + client::Msg_Sparkle (p.center()); + KillStone (p); + } + } + } + + bool is_movable () const { return true; } + + void actor_hit (const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-pencil")) { + enigma::Inventory *inv = player::GetInventory(sc.actor); + if (inv && inv->size() > 0) { + delete inv->yield_item(0); + player::RedrawInventory(inv); + sound_event("stonepaint"); + ReplaceStone(get_pos(), MakeStone("st-firebreak_move")); + } + } else + Stone::actor_hit(sc); + } + + public: + PlainStone_Movable() : Stone("st-plain_move") { + } + }; +} + + +/* -------------------- Black- and Whiteballs Stones -------------------- */ + +namespace +{ + class BlackBallsStone : public Stone { + CLONEOBJ(BlackBallsStone); + + virtual Value on_message (const Message &m) + { + GridPos p = get_pos(); + Actor *a = world::CurrentCollisionActor; + if (a && get_id(a) == ac_blackball) { + if (p.y == m.gridpos.y) { + SendMessage (GetStone (move (p, EAST)), "signal", 1.0); + SendMessage (GetStone (move (p, WEST)), "signal", 1.0); + SendMessage (GetStone (move (p, NORTH)), "signal", 0.0); + SendMessage (GetStone (move (p, SOUTH)), "signal", 0.0); + } + else { + SendMessage (GetStone (move (p, EAST)), "signal", 0.0); + SendMessage (GetStone (move (p, WEST)), "signal", 0.0); + SendMessage (GetStone (move (p, NORTH)), "signal", 1.0); + SendMessage (GetStone (move (p, SOUTH)), "signal", 1.0); + } + } + return Value(); + } + public: + BlackBallsStone() : Stone ("st-blackballs") { + } + }; + + class WhiteBallsStone : public Stone { + CLONEOBJ(WhiteBallsStone); + + virtual Value on_message (const Message &m) + { + GridPos p = get_pos(); + Actor *a = world::CurrentCollisionActor; + if (a && get_id(a) == ac_whiteball) { + if (p.y == m.gridpos.y) { + SendMessage (GetStone (move (p, EAST)), "signal", 1.0); + SendMessage (GetStone (move (p, WEST)), "signal", 1.0); + SendMessage (GetStone (move (p, NORTH)), "signal", 0.0); + SendMessage (GetStone (move (p, SOUTH)), "signal", 0.0); + } + else { + SendMessage (GetStone (move (p, EAST)), "signal", 0.0); + SendMessage (GetStone (move (p, WEST)), "signal", 0.0); + SendMessage (GetStone (move (p, NORTH)), "signal", 1.0); + SendMessage (GetStone (move (p, SOUTH)), "signal", 1.0); + } + } + return Value(); + } + + public: + WhiteBallsStone() : Stone ("st-whiteballs") { + } + + }; +} + +/* -------------------- Unimplemented stones -------------------- */ + +namespace +{ + class FakeOxydA : public Stone { + CLONEOBJ(FakeOxydA); + public: + + FakeOxydA() : Stone("st-fakeoxyda") { + } + + void actor_hit (const StoneContact &) { + sound_event ("stonetransform"); + ReplaceStone(get_pos(), MakeStone("st-glass1_move")); + } + }; +} + + +// -------------------------------------------------------------------------------- + +extern void InitSwitches(); + +void stones::Init() +{ + + // Register(new ...); + + Register (new ExplosionStone); + Register (new ChargeStone ("st-chargeplus", +1.0)); + Register (new ChargeStone ("st-chargeminus", -1.0)); + Register (new ChargeStone ("st-chargezero", 0.0)); + Register (new SpitterStone); + Register (new Peroxyd_0xb8); + Register (new Peroxyd_0xb9); + Register (new Oxyd_0x18); + Register (new FlashStone); + Register (new SurpriseStone); + Register (new CoffeeStone); + Register (new BlackBallsStone); + Register (new WhiteBallsStone); + Register (new FakeOxydA); + Register (new BreakingStone); + Register (new BugStone); + Register (new PlainStone); + Register (new PlainStone_Hollow); + Register (new PlainStone_Breakable); + Register (new PlainStone_Breaking); + Register (new PlainStone_Cracked); + Register (new PlainStone_Movable); + Register (new PlainStone_Falling); + + // Init stones from stones_simple.cc and stones_complex.cc: + Init_simple(); + Init_complex(); + InitSwitches(); +} diff --git a/project/jni/application/enigma/src/stones.hh b/project/jni/application/enigma/src/stones.hh new file mode 100644 index 000000000..84ae65404 --- /dev/null +++ b/project/jni/application/enigma/src/stones.hh @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef STONES_HH_INCLUDED +#define STONES_HH_INCLUDED + +#include "objects_decl.hh" + +namespace world +{ + using namespace enigma; + + enum StoneID { + st_INVALID = -1, + st_FIRST = 0, + st_none = 0, + + st_borderstone, + st_black1, + st_black2, + st_black3, + st_black4, + st_chameleon, + st_death, + st_death_invisible, + st_easymode, + st_fart, + st_knight, + st_magic, + st_rubberband, + st_scissors, + st_thief, + st_white1, + st_white2, + st_white3, + st_white4, + st_window, + st_lightpassenger, + st_camouflage, + st_polarswitch, + + st_LAST, + st_COUNT = st_LAST + }; + + enum StoneFlags { + stf_none = 0x0, + stf_transparent = 0x1, //< Laser beams can pass + }; + + enum Material { + material_FIRST = 0, + material_stone = 0, + material_glass, + material_metal, + material_wood, + material_cloth, + material_LAST + }; + + struct StoneTraits { + const char *name; + StoneID id; + int flags; + Material material; + double restitution; + // Note that many properties of stones are implemented as functions. + }; + + /*! Things that may happen when an actor hits a stone. */ + enum StoneResponse { + STONE_PASS, // Actor may pass stone + STONE_REBOUND // Actor bounces off the stone + }; + + class Stone : public GridObject { + public: + Stone(); + Stone(const char *kind); + ~Stone(); + + /* ---------- Virtual functions ---------- */ + virtual Stone *clone() = 0; + const char *get_kind() const; + + /* ---------- Stone interface (properties) ---------- */ + + virtual const StoneTraits &get_traits() const; + + virtual const char *collision_sound(); + + virtual StoneResponse collision_response(const StoneContact &sc); + + /*! Is this stone movable? Affects impulse-stones, fire, ordinary pushes... */ + virtual bool is_movable() const { return false;} + + /*! Can a swap-stone or pull-stone swap this stone? */ + virtual bool is_removable() const { return true; } + + /*! Is this stone floating above the floor (e.g. actors may pass)? */ + virtual bool is_floating() const { return false; } + + /*! Can laser beams pass through stone? Return is_floating() by default. */ + virtual bool is_transparent (Direction) const { + return is_floating(); + } + + /*! Do actors get stuck in this stone? */ + virtual bool is_sticky(const Actor *) const { + return !is_floating(); + } + + /* ---------- Stone interface (events) ---------- */ + + virtual void actor_hit (const StoneContact &sc); + virtual void actor_touch (const StoneContact &sc); + virtual void actor_inside (Actor * /*a*/) {} + virtual void actor_contact (Actor * /*a*/) {} + + virtual void on_move(); + virtual void on_floor_change() {} + virtual void on_impulse(const Impulse& impulse); + + protected: + bool move_stone(GridPos newPos, const char *soundevent); + bool move_stone(Direction dir); + ecl::V2 distortedVelocity (ecl::V2 vel, double defaultfactor); + + protected: + // GridObject interface + virtual void set_model (const std::string &mname) { + display::SetModel(GridLoc(GRID_STONES, get_pos()), mname); + } + + virtual display::Model *get_model () { + return display::GetModel(GridLoc(GRID_STONES, get_pos())); + } + + virtual void kill_model (GridPos p) { + display::KillModel (GridLoc (GRID_STONES, p)); + } + }; + + inline StoneID get_id(Stone *st) { + return st->get_traits().id; + } +} + +namespace stones +{ + void Init(); +} + +#endif diff --git a/project/jni/application/enigma/src/stones_complex.cpp b/project/jni/application/enigma/src/stones_complex.cpp new file mode 100644 index 000000000..cea37053a --- /dev/null +++ b/project/jni/application/enigma/src/stones_complex.cpp @@ -0,0 +1,3559 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/ or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "laser.hh" +#include "sound.hh" +#include "server.hh" +#include "player.hh" +#include "Inventory.hh" +#include "stones_internal.hh" +#include "actors.hh" + +#include "ecl_util.hh" + +#include +#include + +using namespace std; +using namespace world; +using namespace stones; + + +/* -------------------- RotatorStone -------------------- */ +namespace +{ + class RotatorStone : public PhotoStone { + public: + RotatorStone(bool clockwise_, bool movable_) + : PhotoStone("st-rotator"), clockwise(clockwise_), movable(movable_) + {} + + private: + static const double RATE; + static const double IMPULSE_DELAY; + + bool clockwise; + bool movable; + + Stone *clone() { return new RotatorStone(clockwise, movable); } + void dispose() { delete this; } + + // Stone interface + void on_creation (GridPos p) { + Stone::on_creation(p); + photo_activate(); + } + void on_removal (GridPos p){ + photo_deactivate(); + Stone::on_removal(p); + } + + void send_impulses() { + GridPos p = get_pos(); + + if (clockwise) { + send_impulse (move(p, NORTH), EAST, IMPULSE_DELAY); + send_impulse (move(p, EAST), SOUTH, IMPULSE_DELAY); + send_impulse (move(p, SOUTH), WEST, IMPULSE_DELAY); + send_impulse (move(p, WEST), NORTH, IMPULSE_DELAY); + } else { + send_impulse (move(p, NORTH), WEST, IMPULSE_DELAY); + send_impulse (move(p, EAST), NORTH, IMPULSE_DELAY); + send_impulse (move(p, SOUTH), EAST, IMPULSE_DELAY); + send_impulse (move(p, WEST), SOUTH, IMPULSE_DELAY); + } + } + + + void init_model() { + set_anim(clockwise ? "st-rotator-right" : "st-rotator-left"); + } + void animcb() { + init_model(); + send_impulses(); + } + + bool is_movable () const { return movable; } + + void actor_hit (const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-wrench")) { + clockwise = !clockwise; + init_model(); + } + + if (movable) + maybe_push_stone(sc); + } + + void on_impulse(const Impulse& impulse) { + if (movable) + move_stone(impulse.dir); + } + + // PhotoStone interface + void notify_laseron() { + clockwise = !clockwise; + init_model(); + } + void notify_laseroff() {} + }; + + const double RotatorStone::RATE = 1.0; + const double RotatorStone::IMPULSE_DELAY = 0.1; +} + + +/* -------------------- PullStone -------------------- */ + +// When pushed this stone acts like pulled. +// When pushed by an actor it exchanges its position with the actor. + +namespace +{ + struct PulledActor { + // Variables + Actor *actor; + V2 speed; + + // Constructor + PulledActor(Actor *actor_, const V2& speed_) + : actor(actor_), speed(speed_) + { + } + + }; + + class PullInfo { + list actors; + YieldedGridStone *ystone; + + public: + PullInfo(Stone *st) + : ystone(st ? new YieldedGridStone(st) : 0) + {} + ~PullInfo() { delete ystone; } + + void add_actor(Actor *actor, const V2& speed) { + actors.push_back(PulledActor(actor, speed)); + } + + list& get_actors() { return actors; } + + void set_stone(GridPos pos) { if (ystone) ystone->set_stone(pos); } + void dispose() { if (ystone) ystone->dispose(); } + }; + + + class PullStone : public Stone, public TimeHandler { + // Variables. + enum State { IDLE, MOVING, VANISHED } state; + Direction m_movedir; + PullInfo *pull_info; // information about moved objects (only valid during pull) + + public: + PullStone(); + ~PullStone(); + + private: + // Object interface. + PullStone *clone(); + void dispose(); + + // Stone interface. + bool is_movable () const { + return state == IDLE; + } + void actor_hit(const StoneContact &sc) { + if (state == IDLE) + maybe_push_stone(sc); + } + void on_impulse(const Impulse& impulse); + bool is_removable() const { + return state == IDLE; + } + + // TimeHandler interface. + void alarm(); + + // Functions. + void change_state(State new_state); + void set_move_state(bool appearing, Direction move_dir); + + }; +} + +PullStone::PullStone() +: Stone("st-pull"), state(IDLE), m_movedir(NODIR) , pull_info(0) +{} + +PullStone::~PullStone() { + GameTimer.remove_alarm(this); + delete pull_info; +} + +PullStone *PullStone::clone() { + PullStone *other = new PullStone(*this); + other->pull_info = 0; + return other; +} + +void PullStone::dispose() { + if (state == MOVING && pull_info != 0) + pull_info->dispose(); + delete this; +} + +void PullStone::set_move_state (bool appearing, Direction move_dir) { + if (appearing) { + m_movedir = move_dir; + // only the half-stone on the new field gets an alarm + // wherein it completes the move + GameTimer.set_alarm(this, 0.09, false); + } + else + m_movedir = reverse(move_dir); + change_state(MOVING); +} + +void PullStone::change_state (State new_state) { + switch (new_state) { + case IDLE: set_model("st-pull"); break; + case MOVING: { + string mname = string("st-pull") + to_suffix(m_movedir); + set_model(mname); + break; + } + case VANISHED: break; + } + state = new_state; +} + +void PullStone::alarm() { + ASSERT(state == MOVING, XLevelRuntime, "PullStone: alarm called with inconsistent state"); + GridPos oldpos = move (get_pos(), reverse(m_movedir)); + + // remove the disappearing half of the PullStone : + PullStone *oldStone = dynamic_cast(GetStone(oldpos)); + ASSERT(oldStone, XLevelRuntime, "PullStone: oldStone non-existent in alarm"); + oldStone->change_state(VANISHED); + KillStone(oldpos); + + if (pull_info) { // have other objects been moved ? + pull_info->set_stone(oldpos); // re-sets any pulled stone + + // set pulled actor(s): + list::iterator e = pull_info->get_actors().end(); + for (list::iterator i = pull_info->get_actors().begin(); i != e; ++i) { + PulledActor& pulled = *i; + pulled.actor->get_actorinfo()->vel = pulled.speed; + } + delete pull_info; + pull_info = 0; + } + + change_state(IDLE); +} + +void PullStone::on_impulse(const Impulse& impulse) +{ + if (state != IDLE) + return; + + Direction move_dir = reverse(impulse.dir); + const GridPos& oldPos = get_pos(); + GridPos newPos = move(oldPos, move_dir); + Stone *other_stone = GetStone(newPos); + + if (other_stone && + (!other_stone->is_removable() || IsLevelBorder(newPos))) { + return; // avoid unremoveable and border stones + } + + PullStone *newStone = this->clone(); + + if (other_stone) { + // yield other_stone: + newStone->pull_info = new PullInfo(other_stone); + } + else { + newStone->pull_info = new PullInfo(0); + } + + // search for affected actors + vector found_actors; + const double range_one_field = 1.415; // approx. 1 field [ > sqrt(1+1) ] + GetActorsInRange(newPos.center(), range_one_field, found_actors); + vector::iterator e = found_actors.end(); + for (vector::iterator i = found_actors.begin(); i != e; ++i) { + Actor *actor = *i; + ActorInfo *ai = actor->get_actorinfo(); + GridPos actor_pos(ai->pos); + + if (actor_pos == newPos) { // if the actor is in the dest field + V2 vel = ai->vel; + V2 mid_dest = ai->pos; + + mid_dest[0] = ecl::Clamp (mid_dest[0], oldPos.x+0.01, oldPos.x+0.99); + mid_dest[1] = ecl::Clamp (mid_dest[1], oldPos.y+0.01, oldPos.y+0.99); + WarpActor(actor, mid_dest[0], mid_dest[1], false); + + newStone->pull_info->add_actor(actor, vel); + } + } + + SetStone(newPos, newStone); + newStone->set_move_state(true, move_dir); + set_move_state(false, move_dir); + + sound_event("moveslow"); +} + + +/* -------------------- Oneway stones -------------------- */ + +// These stone can only be passed in one direction. + +namespace +{ + class OneWayBase : public Stone { + protected: + OneWayBase(const char *kind, Direction dir); + + void init_model(); + virtual Value message(const string& msg, const Value &val); + + void actor_hit (const StoneContact&); + StoneResponse collision_response(const StoneContact &sc); + bool is_floating() const { return true; } + + Direction get_orientation() const { + return Direction(int_attrib("orientation")); + } + void set_orientation(Direction dir) { + set_attrib("orientation", Value(dir)); + } + + virtual bool actor_may_pass (Actor *a) = 0; + }; + + class OneWayStone : public OneWayBase { + public: + OneWayStone(Direction dir=SOUTH) : OneWayBase("st-oneway", dir) {} + private: + CLONEOBJ(OneWayStone); + virtual bool actor_may_pass (Actor * /*a*/) { return true; } + }; + + + class OneWayStone_black : public OneWayBase { + public: + OneWayStone_black(Direction dir=SOUTH) + : OneWayBase("st-oneway_black",dir) {} + private: + CLONEOBJ(OneWayStone_black); + virtual bool actor_may_pass (Actor *a) { + return a->get_attrib("blackball") != 0; + } + void actor_hit (const StoneContact&) { + // do nothing if hit by actor + } + }; + + class OneWayStone_white : public OneWayBase { + public: + OneWayStone_white(Direction dir=SOUTH) + : OneWayBase("st-oneway_white", dir) {} + private: + CLONEOBJ(OneWayStone_white); + virtual bool actor_may_pass (Actor *a) { + return a->get_attrib("whiteball") != 0; + } + void actor_hit (const StoneContact&) { + // do nothing if hit by actor + } + }; +} + +OneWayBase::OneWayBase(const char *kind, Direction dir) +: Stone(kind) +{ + set_orientation(dir); +} + +void OneWayBase::init_model() +{ + string mname = get_kind(); + mname += to_suffix(get_orientation()); + set_model (mname); +} + +Value OneWayBase::message(const string& msg, const Value &val) { + if (msg == "direction" && val.get_type() == Value::DOUBLE) { + set_orientation(to_direction(val)); + init_model(); + } + else if (msg == "signal" || msg == "flip") { + Direction dir = get_orientation(); + set_orientation(reverse(dir)); + init_model(); + } + return Value(); +} + +void OneWayBase::actor_hit(const StoneContact &sc) { + Direction o=get_orientation(); + + if (has_dir(contact_faces(sc), o)) { + if (player::WieldedItemIs (sc.actor, "it-magicwand")) { + set_orientation(reverse(o)); + init_model(); + } + } +} + +StoneResponse OneWayBase::collision_response(const StoneContact &sc) { + DirectionBits dirs=contact_faces(sc); + Direction o=get_orientation(); + + if (!sc.actor->is_flying() && actor_may_pass(sc.actor)) + return has_dir(dirs,o) ? STONE_REBOUND : STONE_PASS; + else + return STONE_REBOUND; +} + + +/* -------------------- BolderStone -------------------- */ + +/** \page st-bolder Bolder Stone + +The bolder stone will move in one direction until another stone will +block. When hit with a magic wand, the bolder stone reverse its +direction. When hitting a blocking stone it can activate switches or +oxyd stones. + +\subsection boldera Attributes + +- \c direction \n NORTH, EAST, SOUTH, WEST + +*/ +namespace +{ + class BolderStone : public Stone { + CLONEOBJ(BolderStone); + public: + BolderStone(Direction dir=NORTH) + : Stone("st-bolder"), state(IDLE) + { + set_attrib("direction", dir); + // do not use set_dir, because this will set the state to ACTIVE + } + + private: + enum State { + ACTIVE, // may send trigger into direction + IDLE, // already sent trigger w/o success + FALLING // falling into abyss + } state; + + + Direction get_dir() const { + return static_cast(int_attrib("direction")); + } + void set_dir(Direction d) { + if (d != get_dir()) + state = ACTIVE; // if turned by it-magicwand -> allow triggering + set_attrib("direction", d); + } + + void on_floor_change() { + Floor *fl = GetFloor(get_pos()); + if (fl->is_kind("fl-abyss")) { + state = FALLING; + init_model(); + } + } + + bool have_obstacle (Direction dir) { + return GetStone(move(get_pos(), dir)) != 0; + } + + void trigger_obstacle (Direction dir) { + if (Stone *st = GetStone(move(get_pos(), dir))) { + SendMessage(st, "trigger", Value(dir)); + } + } + + void on_move() { + state = ACTIVE; + trigger_obstacle(get_dir()); + Stone::on_move(); + } + + // Stone interface. + void init_model() { + string mname = "st-bolder" + to_suffix(get_dir()); + if (state == FALLING) + mname += "-fall-anim"; + set_anim(mname); + } + + void animcb() { + display::Model *m = get_model(); + Direction dir = get_dir(); + switch (state) { + case FALLING: + KillStone(get_pos()); +// init_model(); + break; + + case IDLE: + if (!have_obstacle(dir)) { + state = ACTIVE; + trigger_obstacle(dir); + } +// if (Model *m = get_model()) + m->restart(); +// init_model(); + break; + + case ACTIVE: { + trigger_obstacle(dir); + if (!move_stone(dir)) { +// if (state == MOVING) // state may be FALLING + state = IDLE; + } + init_model(); + break; + } + } + } + + bool is_movable() const { return state != FALLING; } + + void actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-magicwand")) { + set_dir(reverse(get_dir())); + init_model(); + } + } + + void on_laserhit(Direction) { + set_dir(reverse(get_dir())); + init_model(); + + // @@@ FIXME: the direction should only be inverted on NEW laserbeam + // not on every light-recalc. Need to use PhotoCell! + } + + void on_impulse (const Impulse& impulse) { + if (state == FALLING) + return; + + if (impulse.sender && impulse.sender->is_kind("st-rotator")) { + set_dir(impulse.dir); + } + init_model(); + move_stone(impulse.dir); + } + + virtual Value message(const string& msg, const Value &val) { + if (msg == "direction" && state != FALLING) { + set_dir (to_direction(val)); + init_model(); + } + return Value(); + } + }; +} + + +/* -------------------- BlockerStone -------------------- */ + +/** \page st-blocker Blocker Stone + +The BlockerStone acts like a normal stone until it is hit by a +BolderStone. Then it shrinks and morphs into a 'Blocker' item. + +*/ + +namespace +{ + class BlockerStone : public Stone { + CLONEOBJ(BlockerStone); + public: + BlockerStone(bool solid) + : Stone(solid ? "st-blocker" : "st-blocker-growing"), + state(solid ? SOLID : GROWING) + {} + + private: + enum State { SOLID, SHRINKING, GROWING } state; + + void init_model() { + switch (state) { + case SOLID: + set_model("st-blocker"); + break; + + case SHRINKING: + set_anim("st-blocker-shrinking"); + break; + + case GROWING: + set_anim("st-blocker-growing"); + break; + } + } + + void change_state(State newState) { + if (state != newState) { + if (state == GROWING && newState == SHRINKING) { + state = SHRINKING; + get_model()->reverse(); + } + else if (state == SHRINKING && newState == GROWING) { + state = GROWING; + get_model()->reverse(); + } + else { + state = newState; + init_model(); + if (newState == SOLID) { + set_attrib("kind", "st-blocker"); + } + } + } + } + + void animcb() { + switch (state) { + case SHRINKING: { + Item *it = world::MakeItem("it-blocker-new"); + world::SetItem(get_pos(), it); + TransferObjectName(this, it); + world::KillStone(get_pos()); + break; + } + case GROWING: + change_state(SOLID); + break; + default : + ASSERT(0, XLevelRuntime, + "BlockerStone: animcb called with inconsistent state"); + break; + } + } + + virtual Value message(const string &msg, const Value &val) { + if (msg == "trigger" || msg == "openclose") { + if (state == SHRINKING) { + change_state(GROWING); + } + else { + change_state(SHRINKING); + } + } + else if (msg == "signal") { + int value = to_int(val); +// warning("received signal (value=%i)", value); + if (value) { // value == 1 -> shrink + if (state != SHRINKING) + change_state(SHRINKING); + } + else { // value == 0 -> grow + if (state == SHRINKING) + change_state(GROWING); + } + } + else if (msg == "open") { // aka "shrink" + if (state != SHRINKING) + change_state(SHRINKING); + } + else if (msg == "close") { // aka "grow" + if (state == SHRINKING) + change_state(GROWING); + } + return Value(); + } + + void actor_contact(Actor *a) { + if (state == GROWING) { + SendMessage(a, "shatter"); + } + } + void actor_inside(Actor *a) { + if (state == GROWING) { + SendMessage(a, "shatter"); + } + } + }; +} + + +/* -------------------- Volcano -------------------- */ +namespace +{ + class VolcanoStone : public Stone { + CLONEOBJ(VolcanoStone); + public: + enum State {INACTIVE, ACTIVE, FINISHED, BREAKING}; + VolcanoStone( State initstate=INACTIVE) : Stone("st-volcano"), state( initstate) {} + private: + enum State state; + + void init_model() { + switch( state) { + case FINISHED: + case INACTIVE: set_model( "st-plain"); break; + case ACTIVE: set_anim( "st-farting"); break; + case BREAKING: set_anim("st-stone_break-anim"); break; + } + } + + void animcb() { + if (state == ACTIVE) { + // Spread + GridPos p = get_pos(); + if (DoubleRand(0, 1) > 0.7) spread (move(p, NORTH)); + if (DoubleRand(0, 1) > 0.7) spread (move(p, EAST)); + if (DoubleRand(0, 1) > 0.7) spread (move(p, SOUTH)); + if (DoubleRand(0, 1) > 0.7) spread (move(p, WEST)); + + // Be finished at random time + if (DoubleRand(0, 1) > 0.95) + state = FINISHED; + init_model(); + } else if( state == BREAKING) { + KillStone( get_pos()); + } + } + + virtual Value message(const string &msg, const Value &) { + if (msg == "trigger") { + if (state == INACTIVE) { + state = ACTIVE; + init_model(); + } + } + return Value(); + } + + void spread( GridPos p) { + Stone *st = GetStone(p); + if( !st) { + Item *it = MakeItem("it-seed_volcano"); + SetItem( p, it); + SendMessage( it, "grow"); + } + } + + void actor_hit(const StoneContact &sc) { + Actor *a = sc.actor; + + if( state == ACTIVE && player::WieldedItemIs (a, "it-hammer")) { + state = BREAKING; + init_model(); + } + } + }; +} + + +/* -------------------- ConnectiveStone -------------------- */ + +// base class for PuzzleStone and BigBrick + +namespace { + class ConnectiveStone : public Stone { + public: + ConnectiveStone(const char *kind, int connections) + : Stone(kind) + { + set_attrib("connections", connections); + } + + DirectionBits get_connections() const; + protected: + void init_model(); + private: + virtual int get_modelno() const; + }; +} + +DirectionBits +ConnectiveStone::get_connections() const +{ + int conn=int_attrib("connections") - 1; + if (conn >=0 && conn <16) + return DirectionBits(conn); + else + return NODIRBIT; +} + +void ConnectiveStone::init_model() { + set_model(get_kind()+ecl::strf("%d", get_modelno())); +} + +int ConnectiveStone::get_modelno() const { + return int_attrib("connections"); +} + + +/* -------------------- BigBrick -------------------- */ + +// BigBricks allow to build stones of any size + +namespace +{ + class BigBrick : public ConnectiveStone { + CLONEOBJ(BigBrick); + public: + BigBrick(int connections) + : ConnectiveStone("st-bigbrick", connections) + {} + bool is_removable() const { return false; } + }; +} + +/* -------------------- BigBlueSand -------------------- */ + +// Same as BigBrick but with st-blue-sand + +namespace +{ + class BigBlueSand : public ConnectiveStone { + CLONEOBJ(BigBlueSand); + public: + BigBlueSand(int connections) + : ConnectiveStone("st-bigbluesand", connections) + {} + bool is_removable() const { return false; } + }; +} + + +/* -------------------- Puzzle stones -------------------- */ + +/** \page st-puzzle Puzzle Stone + +Puzzle stones can be connected to other stones of the same type. Any +of the four faces of the stone can have ``socket''. If the adjoining +faces of two neighboring stones both have a socket, the two stones +link up and henceforth move as group. + +A cluster of puzzle stones may for example look like this: + +\verbatim ++---+---+---+---+ +| | | | | +| --+-+-+---+-+ | +| | | | | | | ++---+-+-+---+-+-+ + | | | | | | + | | | | | | + | | | | + +---+ +---+ +\endverbatim + +This example actually presents the special case of a ``complete'' +cluster. A cluster is complete if none of its stones has an +unconnected socket. + +When touched with a magic wand the puzzle stones rotate +row- or columnwise. + +\subsection puzzlea Attributes + +- \b connections + number between 1 an 16. Each bit in (connections-1) corresponds to + a socket on one of the four faces. You will normally simply use + one of the Lua constants \c PUZ_0000 to \c PUZ_1111. + +- \b oxyd + If 1 then the puzzle stones act oxyd-compatible: Complete clusters + explode, when they get touched. All other puzzle stones rotate row- + or columnwise. Groups of oxyd-compatible puzzle stones are shuffled + randomly at level startup. + +\subsection puzzlee Example + + + + + + +
\image html st-puzzletempl_0001.png "PUZ_0000" + \image html st-puzzletempl_0002.png "PUZ_0001" + \image html st-puzzletempl_0003.png "PUZ_0010" + \image html st-puzzletempl_0004.png "PUZ_0011" +
\image html st-puzzletempl_0005.png "PUZ_0100" + \image html st-puzzletempl_0006.png "PUZ_0101" + \image html st-puzzletempl_0007.png "PUZ_0110" + \image html st-puzzletempl_0008.png "PUZ_0111" +
\image html st-puzzletempl_0009.png "PUZ_1000" + \image html st-puzzletempl_0010.png "PUZ_1001" + \image html st-puzzletempl_0011.png "PUZ_1010" + \image html st-puzzletempl_0012.png "PUZ_1011" +
\image html st-puzzletempl_0013.png "PUZ_1100" + \image html st-puzzletempl_0014.png "PUZ_1101" + \image html st-puzzletempl_0015.png "PUZ_1110" + \image html st-puzzletempl_0016.png "PUZ_1111" +
+*/ +namespace +{ + class PuzzleStone : public ConnectiveStone, public TimeHandler, public world::PhotoCell { + INSTANCELISTOBJ(PuzzleStone); + public: + PuzzleStone(int connections, bool oxyd1_compatible_); + protected: + virtual ~PuzzleStone() { + GameTimer.remove_alarm (this); + } + private: + typedef vector Cluster; + + /* ---------- Private methods ---------- */ + + bool oxyd1_compatible() const { return int_attrib("oxyd") != 0; } + + static bool visit_dir(vector &stack, GridPos curpos, + Direction dir, int wanted_oxyd_attrib); + static void visit_adjacent(vector& stack, GridPos curpos, + Direction dir, int wanted_oxyd_attrib); + + bool find_cluster(Cluster &); + void find_adjacents(Cluster &); + void find_row_or_column_cluster(Cluster &c, GridPos startpos, + Direction dir, int wanted_oxyd_attrib); + + bool cluster_complete(); + bool can_move_cluster (Cluster &c, Direction dir); + void maybe_move_cluster(Cluster &c, bool is_complete, bool actor_with_wand, + Direction dir); + void rotate_cluster(const Cluster &c); + void maybe_rotate_cluster(Direction dir); + + int get_modelno() const; + + void trigger_explosion(double delay); + static void trigger_explosion_at(GridPos p, double delay, int wanted_oxyd_attrib); + void explode(); + bool explode_complete_cluster(); + + /* ---------- TimeHandler interface ---------- */ + + void alarm(); + + /* ---------- PhotoCell interface ---------- */ + + void on_recalc_start(); + void on_recalc_finish(); + + /* ---------- Stone interface ---------- */ + + virtual Value message(const string& msg, const Value &val); + + void on_creation (GridPos p); + void on_removal (GridPos p); + void on_impulse (const Impulse& impulse); + void on_laserhit (Direction dir); + + bool is_floating() const; + + StoneResponse collision_response(const StoneContact &sc); + void actor_hit (const StoneContact &sc); + void actor_contact (Actor *a); + + /* ---------- Variables ---------- */ + bool visited; // flag for DFS + enum { IDLE, EXPLODING } state; + DirectionBits illumination; // last state of surrounding laser beams + }; +} + +PuzzleStone::InstanceList PuzzleStone::instances; + +PuzzleStone::PuzzleStone(int connections, bool oxyd1_compatible_) +: ConnectiveStone("st-puzzle", connections), + state (IDLE), + illumination (NODIRBIT) +{ + set_attrib("oxyd", int(oxyd1_compatible_)); +} + + +bool PuzzleStone::visit_dir(vector &stack, GridPos curpos, + Direction dir, int wanted_oxyd_attrib) +{ + GridPos newpos = move(curpos, dir); + PuzzleStone *pz = dynamic_cast(GetStone(newpos)); + + if ((!pz) || (wanted_oxyd_attrib != pz->int_attrib("oxyd"))) + return false; + + DirectionBits cfaces = pz->get_connections(); + + if (cfaces==NODIRBIT || has_dir(cfaces, reverse(dir))) { + // Puzzle stone at newpos is connected to stone at curpos + if (!pz->visited) { + pz->visited = true; + stack.push_back(newpos); + } + return true; + } else { + // The two stones are adjacent but not connected + return false; + } +} + +/* Use a depth first search to determine the group of all stones that + are connected to the current stone. Returns true if the cluster is + ``complete'' in the sense defined above. */ +bool PuzzleStone::find_cluster(Cluster &cluster) { + for (unsigned i=0; ivisited=false; + + vector pos_stack; + bool is_complete = true; + pos_stack.push_back(get_pos()); + this->visited = true; + int wanted_oxyd_attrib = int_attrib("oxyd"); + + while (!pos_stack.empty()) + { + GridPos curpos = pos_stack.back(); + pos_stack.pop_back(); + + PuzzleStone *pz = dynamic_cast(GetStone(curpos)); + ASSERT(pz, XLevelRuntime, "PuzzleStone: missing stone in find_cluster"); + + cluster.push_back(curpos); + DirectionBits cfaces = pz->get_connections(); + + if (cfaces==NODIRBIT) + cfaces = DirectionBits(NORTHBIT | SOUTHBIT | EASTBIT | WESTBIT); + + if (has_dir(cfaces, NORTH)) + is_complete &= visit_dir(pos_stack, curpos, NORTH, wanted_oxyd_attrib); + if (has_dir(cfaces, EAST)) + is_complete &= visit_dir(pos_stack, curpos, EAST, wanted_oxyd_attrib); + if (has_dir(cfaces, SOUTH)) + is_complete &= visit_dir(pos_stack, curpos, SOUTH, wanted_oxyd_attrib); + if (has_dir(cfaces, WEST)) + is_complete &= visit_dir(pos_stack, curpos, WEST, wanted_oxyd_attrib); + } + return is_complete; +} + +void PuzzleStone::visit_adjacent (vector& stack, GridPos curpos, + Direction dir, int wanted_oxyd_attrib) +{ + GridPos newpos = move(curpos, dir); + if (PuzzleStone *pz = dynamic_cast(GetStone(newpos))) { + if (!pz->visited) { + if (wanted_oxyd_attrib == pz->int_attrib("oxyd")) { + pz->visited = true; + stack.push_back(newpos); + } + } + } +} + +/* Use a depth first search to determine the group of all puzzle stones + with the same "oxyd" attrib that are adjacent to the current stone + (or to any other member of the group). +*/ +void PuzzleStone::find_adjacents(Cluster &cluster) { + for (unsigned i=0; ivisited=false; + + vector pos_stack; + pos_stack.push_back(get_pos()); + this->visited = true; + + int wanted_oxyd_attrib = int_attrib("oxyd"); + + while (!pos_stack.empty()) { + GridPos curpos = pos_stack.back(); + pos_stack.pop_back(); + + cluster.push_back(curpos); + visit_adjacent(pos_stack, curpos, NORTH, wanted_oxyd_attrib); + visit_adjacent(pos_stack, curpos, SOUTH, wanted_oxyd_attrib); + visit_adjacent(pos_stack, curpos, EAST, wanted_oxyd_attrib); + visit_adjacent(pos_stack, curpos, WEST, wanted_oxyd_attrib); + } +} + +/* searches from 'startpos' into 'dir' for puzzle-stones. + wanted_oxyd_attrib == -1 -> take any puzzle stone + else -> take only puzzle stones of same type +*/ + +void PuzzleStone::find_row_or_column_cluster(Cluster &c, GridPos startpos, + Direction dir, int wanted_oxyd_attrib) +{ + ASSERT(dir != NODIR, XLevelRuntime, + "PuzzleStone: no direction in find_row_or_column_cluster"); + + GridPos p = startpos; + while (Stone *puzz = dynamic_cast(GetStone(p))) { + if (wanted_oxyd_attrib != -1 && wanted_oxyd_attrib != puzz->int_attrib("oxyd")) + break; // stop when an unrequested puzzle stone type is readed + c.push_back(p); + p.move(dir); + } +} + +bool PuzzleStone::can_move_cluster (Cluster &c, Direction dir) +{ + sort(c.begin(), c.end()); + Cluster mc(c); // Moved cluster + Cluster diff; // Difference between mc and c + + for (unsigned i=0; iis_kind("fl-abyss")) { + if (!is_complete) + create_bridge = false; + } + else if (fl->is_kind("fl-water")) { + if (!is_complete && actor_with_wand) + create_bridge = false; + } + else + create_bridge = false; + } + } + + // Finally, either move the whole cluster or create a bridge + sound_event("movebig"); + if (create_bridge) { + for (unsigned i=0; i clusterstones; + for (unsigned i=0; ion_move(); + } + } + + server::IncMoveCounter (static_cast (c.size())); +} + +bool PuzzleStone::cluster_complete() { + Cluster c; + return find_cluster(c); +} + +int PuzzleStone::get_modelno() const { + int modelno = int_attrib("connections"); + if (oxyd1_compatible()) modelno += 16; + return modelno; +} + +void PuzzleStone::rotate_cluster(const Cluster &c) { + size_t size = c.size(); + if (size > 1) { + int cn = GetStone(c[size-1])->int_attrib("connections"); + for (size_t i=size-1; i>0; --i) { + PuzzleStone *st = dynamic_cast (GetStone (c[i])); + st->set_attrib ("connections", GetStone(c[i-1])->int_attrib ("connections")); + st->init_model(); + } + GetStone(c[0])->set_attrib ("connections", cn); + dynamic_cast (GetStone(c[0]))->init_model(); + } +} + +StoneResponse PuzzleStone::collision_response(const StoneContact &/*sc*/) { + if (get_connections() == NODIRBIT) + return STONE_PASS; + return STONE_REBOUND; +} + +void PuzzleStone::trigger_explosion(double delay) { + if (state == IDLE) { + state = EXPLODING; + GameTimer.set_alarm(this, delay, false); + } +} + +void PuzzleStone::trigger_explosion_at (GridPos p, double delay, + int wanted_oxyd_attrib) +{ + PuzzleStone *puzz = dynamic_cast(GetStone(p)); + if (puzz && wanted_oxyd_attrib == puzz->int_attrib("oxyd")) { + // explode adjacent puzzle stones of same type + puzz->trigger_explosion(delay); + } +} + +void PuzzleStone::explode() { + GridPos p = get_pos(); + int ox_attr = int_attrib("oxyd"); + + // exchange puzzle stone with explosion + sound_event("stonedestroy"); + SetStone(p, MakeStone("st-explosion")); + + // trigger all adjacent puzzle stones : + const double DEFAULT_DELAY = 0.2; + trigger_explosion_at(move(p, NORTH), DEFAULT_DELAY, ox_attr); + trigger_explosion_at(move(p, SOUTH), DEFAULT_DELAY, ox_attr); + trigger_explosion_at(move(p, EAST), DEFAULT_DELAY, ox_attr); + trigger_explosion_at(move(p, WEST), DEFAULT_DELAY, ox_attr); + + // @@@ FIXME: At the moment it's possible to push partial puzzle stones + // next to an already exploding cluster. Then the part will explode as well. + // Possible fix : mark whole cluster as "EXPLODING_SOON" when explosion is initiated + + // ignite adjacent fields +// SendExplosionEffect(p, DYNAMITE); +} + +void PuzzleStone::alarm() { + explode(); +} + +Value PuzzleStone::message(const string& msg, const Value &val) { + if (msg == "scramble") { + // oxyd levels contain explicit information on how to + // scramble puzzle stones. According to that information + // a "scramble" message is send to specific puzzle stones + // together with information about the direction. + // + // enigma levels may create scramble messages using + // AddScramble() and SetScrambleIntensity() + + Direction dir = to_direction(val); + Cluster c; + find_row_or_column_cluster(c, get_pos(), dir, oxyd1_compatible()); + + size_t size = c.size(); + + // warning("received 'scramble'. dir=%s size=%i", to_suffix(dir).c_str(), size); + + if (size >= 2) { + int count = IntegerRand(0, static_cast (size-1)); + while (count--) + rotate_cluster(c); + } + else { + warning("useless scramble (cluster size=%i)", size); + } + } + return Value(); +} + +void PuzzleStone::on_impulse(const Impulse& impulse) +{ +// if (!oxyd1_compatible() && state == IDLE) { + if (state == IDLE) { + Cluster c; + bool is_complete = find_cluster(c); + bool actor_with_wand = false; + + if (Actor *ac = dynamic_cast(impulse.sender)) + actor_with_wand = player::WieldedItemIs (ac, "it-magicwand"); + + maybe_move_cluster(c, is_complete, actor_with_wand, impulse.dir); + } +} + +bool PuzzleStone::explode_complete_cluster() +{ + // @@@ FIXME: explode_complete_cluster should mark the whole cluster + // as "EXPLODING_SOON" (otherwise it may be changed before it explodes completely) + + ASSERT(state == IDLE, XLevelRuntime, + "PuzzleStone: explode_complete_cluster called with inconsistent state"); + bool exploded = false; + + Cluster complete; + if (find_cluster(complete)) { + Cluster all; + find_adjacents(all); + + // If all adjacent stones build one complete cluster explode it + if (all.size() == complete.size()) { + explode(); // explode complete cluster + exploded = true; + } + else { + ASSERT(all.size() > complete.size(), XLevelRuntime, + "PuzzleStone: sizes don't match in explode_complete_cluster"); + if (!oxyd1_compatible()) { + // check if 'all' is made up of complete clusters : + + sort(all.begin(), all.end()); + + while (1) { + sort(complete.begin(), complete.end()); + + // remove one complete cluster from 'all' + { + Cluster rest; + set_symmetric_difference(all.begin(), all.end(), + complete.begin(), complete.end(), + back_inserter(rest)); + // now rest contains 'all' minus 'complete' + swap(all, rest); + } + + if (all.empty()) { // none left -> all were complete + exploded = true; + break; + } + + // look for next complete cluster : + complete.clear(); + { + PuzzleStone *pz = dynamic_cast(GetStone(all[0])); + ASSERT(pz, XLevelRuntime, + "PuzzleStone: missing stone in explode_complete_cluster"); + if (!pz->find_cluster(complete)) { + break; // incomplete cluster found -> don't explode + } + } + } + + if (exploded) { +// warning("exploding complete cluster"); + explode(); + } + } + } + } + + return exploded; +} + +bool PuzzleStone::is_floating() const { + return get_connections() == 0; +} + +void PuzzleStone::maybe_rotate_cluster(Direction dir) +{ + if (dir != NODIR) { + Cluster c; + find_row_or_column_cluster(c, get_pos(), dir, int_attrib ("oxyd")); + if (c.size() >= 2) { +// warning("ok -> rotate"); + rotate_cluster(c); + } + } +} + +void PuzzleStone::on_creation (GridPos p) { + photo_activate(); + ConnectiveStone::on_creation (p); + illumination = NODIRBIT; +} + +void PuzzleStone::on_removal(GridPos p) { + photo_deactivate(); + ConnectiveStone::on_removal(p); +} + +void PuzzleStone::on_laserhit (Direction dir) { + ecl::set_flags (illumination, to_bits(reverse(dir))); +} + +void PuzzleStone::on_recalc_start() { + illumination = NODIRBIT; +} + +void PuzzleStone::on_recalc_finish() { + if (illumination != (ALL_DIRECTIONS+1) && + illumination != NODIRBIT && + state == IDLE) + { + if (!explode_complete_cluster() && oxyd1_compatible()) { + if (illumination & NORTHBIT) maybe_rotate_cluster(SOUTH); + if (illumination & SOUTHBIT) maybe_rotate_cluster(NORTH); + if (illumination & EASTBIT) maybe_rotate_cluster(WEST); + if (illumination & WESTBIT) maybe_rotate_cluster(EAST); + } + } +} + +void PuzzleStone::actor_hit(const StoneContact &sc) +{ + if (get_connections() == NODIRBIT) + return; // Puzzle stone is hollow + + if (state == EXPLODING) + return; + + Cluster c; + find_cluster (c); + + Direction rotate_dir = reverse (contact_face (sc)); + Direction move_dir = get_push_direction(sc); + + if (oxyd1_compatible()) { + // Oxyd 1 + + if (explode_complete_cluster()) + return; + + // 1) If unconnected puzzle stones -> try to move it + if (c.size() == 1 && move_dir != NODIR) { + // if cluster contains single stone + // -> move it if dest pos is free + GridPos dest = move(c[0], move_dir); + if (GetStone(dest) == 0) { + Stone *puzz = YieldStone(c[0]); + SetStone(dest, puzz); + puzz->on_move(); + sound_event ("movesmall"); + } else + maybe_rotate_cluster (rotate_dir); + } + // 2) If more than one stone, + else + maybe_rotate_cluster (rotate_dir); + } + else { + // Not Oxyd 1 + + bool has_magic_wand = player::WieldedItemIs (sc.actor, "it-magicwand"); + + // 1) Try to start explosion of complete cluster + if (has_magic_wand && explode_complete_cluster()) + return; + + // 2) Failed? Try to move the cluster + if (move_dir != NODIR && can_move_cluster (c, move_dir)) { + sc.actor->send_impulse(get_pos(), move_dir); + return; + } + + // 3) Last chance: try to rotate the row or column + if (has_magic_wand) + maybe_rotate_cluster (rotate_dir); + } +} + +void PuzzleStone::actor_contact (Actor *a) +{ + if (state == EXPLODING) + SendMessage (a, "shatter"); +} + + + +/* -------------------- DoorBase -------------------- */ + +// Base class for everything that behaves like a door, i.e., it has +// four states OPEN, CLOSED, OPENING, CLOSING. +namespace +{ + class DoorBase : public Stone { + protected: + enum State { OPEN, CLOSED, OPENING, CLOSING } state; + + DoorBase(const char *name, State initstate=CLOSED) + : Stone(name), state(initstate) + {} + + State get_state() const { return state; } + void set_state(State st) { state=st; } + + private: + // DoorBase interface + virtual string model_basename() { return get_kind(); } + virtual void init_model(); + virtual string opening_sound() const { return ""; } + virtual string closing_sound() const { return ""; } + + // Private methods + void change_state(State newstate) ; + virtual Value message(const string &m, const Value &); + + StoneResponse collision_response(const StoneContact &sc); + + void animcb(); + + // Stone interface + virtual bool is_transparent (Direction) const + { return state==OPEN; } + + virtual bool is_sticky (const Actor *) const + { return false; } + }; +} + +Value DoorBase::message(const string &m, const Value &val) { + State newstate = state; + int ival = to_int (val); + + if (m == "open") + newstate = OPENING; + else if (m == "close") + newstate = CLOSING; + else if (m == "openclose") + newstate = (state==OPEN || state==OPENING) ? CLOSING : OPENING; + else if (m == "signal") + newstate = ival > 0 ? OPENING : CLOSING; + + if (newstate==OPENING && (state==CLOSED || state==CLOSING)) + change_state(OPENING); + else if (newstate==CLOSING && (state==OPEN || state==OPENING)) + change_state(CLOSING); + return Value(); +} + +void DoorBase::init_model() { + string mname = model_basename(); + if (state == CLOSED) + mname += "-closed"; + else if (state==OPEN) + mname += "-open"; + set_model(mname); +} + +void DoorBase::animcb() { + if (state == OPENING) + change_state(OPEN); + else if (state == CLOSING) + change_state(CLOSED); +} + +StoneResponse +DoorBase::collision_response(const StoneContact &/*sc*/) +{ + return (state == OPEN) ? STONE_PASS:STONE_REBOUND; +} + +void DoorBase::change_state(State newstate) +{ + string basename = model_basename(); + + switch (newstate) { + case OPEN: + set_model(basename+"-open"); + lasers::MaybeRecalcLight(get_pos()); + break; + case CLOSED: + set_model(basename+"-closed"); + world::ShatterActorsInsideField (get_pos()); + lasers::MaybeRecalcLight(get_pos()); // maybe superfluous + break; + case OPENING: + sound_event (opening_sound().c_str()); + if (state == CLOSING) + get_model()->reverse(); + else + set_anim(basename+"-opening"); + break; + case CLOSING: + sound_event (closing_sound().c_str()); + if (state == OPENING) + get_model()->reverse(); + else + set_anim(basename+"-closing"); + world::ShatterActorsInsideField (get_pos()); + lasers::MaybeRecalcLight(get_pos()); + break; + } + set_state(newstate); +} + + +/* -------------------- Door -------------------- */ + +// Attributes: +// +// :type h or v for a door that opens horizontally or vertically +namespace +{ + class Door : public DoorBase { + CLONEOBJ(Door); + public: + Door(const char *type="h", bool open=false) + : DoorBase("st-door", open ? OPEN : CLOSED) + { + set_attrib("type", type); + } + private: + virtual string opening_sound() const { return "dooropen"; } + virtual string closing_sound() const { return "doorclose"; } + virtual const char *collision_sound() { return "electric"; } + string get_type() const { + string type="h"; + string_attrib("type", &type); + return type; + } + + bool is_transparent (Direction) const; + bool is_floating () const { + return true; // don't let door press buttons + } + + void actor_hit(const StoneContact &) + { + if (Item *it = GetItem (get_pos())) + PerformAction (it, true); + } + + string model_basename() { return string("st-door")+get_type(); } + StoneResponse collision_response(const StoneContact &sc); + }; + + class Door_a : public DoorBase { + CLONEOBJ(Door_a); + public: + Door_a() : DoorBase("st-door_a") {} + }; + + class Door_b : public DoorBase { + CLONEOBJ(Door_b); + public: + Door_b() : DoorBase("st-door_b") {} + }; + + class Door_c : public DoorBase { + CLONEOBJ(Door_c); + public: + Door_c() : DoorBase("st-door_c") {} + }; +} + +bool Door::is_transparent (Direction dir) const { + if (get_type() == "h") + return state==OPEN || dir==EAST || dir==WEST; + else + return state==OPEN || dir==NORTH || dir==SOUTH; +} + +StoneResponse +Door::collision_response(const StoneContact &sc) +{ + Direction cf = contact_face(sc); + if (state == OPEN) + return STONE_PASS; + else if (state == CLOSING) + return STONE_REBOUND; + else { + string t = get_type(); + return ((t == "v" && (cf==WEST || cf==EAST)) || + (t == "h" && (cf==SOUTH || cf==NORTH))) + ? STONE_REBOUND + : STONE_PASS; + } +} + + +/* -------------------- ShogunStone -------------------- */ + +// Attributes: +// +// :holes 1..7 +namespace +{ + class ShogunStone : public Stone { + CLONEOBJ(ShogunStone); + + enum Holes { SMALL = 1, MEDIUM = 2, LARGE = 4}; + static Holes smallest_hole(Holes s); + void set_holes(Holes h) { set_attrib("holes", h); } + + public: + ShogunStone(int holes=SMALL) : Stone("st-shogun") { + set_holes(static_cast(holes)); + } + private: + Holes get_holes() const; + void notify_item(); + + virtual Value message(const string &m, const Value &) { + if (m == "init") { // request from ShogunDot (if set _after_ ShogunStone) + notify_item(); + } + return Value(); + } + + void add_hole(Holes h) { + set_attrib("holes", get_holes() | h); + notify_item(); + init_model(); + } + + void on_creation (GridPos p) { + init_model(); + notify_item(); + } + + void on_impulse(const Impulse& impulse); + + void init_model() { + set_model(ecl::strf("st-shogun%d", int(get_holes()))); + } + + bool is_movable() const { return false; } + + void actor_hit (const StoneContact &sc) { + maybe_push_stone (sc); + } + }; +} + +ShogunStone::Holes ShogunStone::get_holes() const { + int h=int_attrib("holes"); + if (h>=1 && h<=7) + return Holes(h); + else { + warning("Wrong 'holes' attribute (%i)", h); + return SMALL; + } +} + +ShogunStone::Holes ShogunStone::smallest_hole(Holes s) { + if (s & SMALL) return SMALL; + if (s & MEDIUM) return MEDIUM; + if (s & LARGE) return LARGE; + throw XLevelRuntime ("ShogunStone: internal error"); +} + +void ShogunStone::notify_item () +{ + if (Item *it = GetItem(get_pos())) { + switch (get_holes()) { + case SMALL: SendMessage(it, "shogun1"); break; + case (MEDIUM | SMALL): SendMessage(it, "shogun2"); break; + case (LARGE | MEDIUM | SMALL): SendMessage(it, "shogun3"); break; + default: SendMessage(it, "noshogun"); break; + } + } +} + +void ShogunStone::on_impulse(const Impulse& impulse) { + GridPos destpos = move(get_pos(), impulse.dir); + Holes holes = get_holes(); + Holes smallest = smallest_hole(holes); + ShogunStone *target = 0; + + if (Stone *st = GetStone(destpos)) { + target = dynamic_cast(st); + + /* If the stone at `p' is not a shogun stone or if smallest hole + does not fit into target, do not transfer the smallest hole. */ + if (!target || smallest >= smallest_hole(target->get_holes())) + return; + } + + /* It's important to remove the old stone before setting the new + one: otherwise it is possible to activate two triggers with one + shogun stone when shifting from one shogun target to a second + adjacent shogun target. */ + + GridPos my_pos = get_pos(); + string old_name; + + // Remove/modify source stone: + if (Holes newholes = Holes(holes & ~smallest)) { + set_holes(newholes); + notify_item(); + init_model(); + } + else { + string_attrib("name", &old_name); // store name of disappearing stone + SendMessage(GetItem(my_pos), "noshogun"); + KillStone(my_pos); + } + + // Modify/create target stone: + if (target) { + target->add_hole(smallest); +// target->sound_event("st-magic"); +// sound::PlaySound("st-magic", my_pos.center()); // object already disappeared + } + else { // create new + target = new ShogunStone(smallest); + SetStone(destpos, target); + target->on_move(); + } + + if (!old_name.empty()) + NameObject(target, old_name); + + server::IncMoveCounter(); + sound::EmitSoundEvent ("movesmall", my_pos.center()); +} + + + +/* -------------------- Stone impulse stones -------------------- */ + +// Messages: +// +// :trigger +namespace +{ + class StoneImpulse_Base : public Stone { + protected: + StoneImpulse_Base(const char *kind) : Stone(kind), state(IDLE), incoming(NODIR) + {} + + enum State { IDLE, PULSING, CLOSING }; + State state; + Direction incoming; // direction of incoming impulse (may be NODIR) + + void change_state(State st); + + virtual void on_impulse(const Impulse& impulse) { + incoming = impulse.dir; + change_state(PULSING); + } + + private: + + virtual void notify_state(State st) = 0; + + virtual Value message(const string &m, const Value &value) { + if (m=="trigger") { + incoming = (value.get_type() == Value::DOUBLE) + ? Direction( static_cast (value.get_double()+0.1)) + : NODIR; + + change_state(PULSING); + } + else if (m == "signal" && to_double (value) != 0) { + incoming = NODIR; + change_state (PULSING); + } + return Value(); + } + + void animcb() { + if (state == PULSING) + change_state (CLOSING); + else if (state == CLOSING) + change_state (IDLE); + } + + void on_laserhit(Direction dir) { + incoming = dir; + change_state(PULSING); + } + }; + +} + +void StoneImpulse_Base::change_state(State new_state) { + if (new_state == state) return; + + GridPos p = get_pos(); + switch (new_state) { + case IDLE: { + state = new_state; + notify_state(state); + break; + } + case PULSING: + if (state != IDLE) { + return; // do not set new state + } + state = new_state; + notify_state(state); + sound_event("impulse"); + break; + case CLOSING: { + GridPos targetpos[4]; + bool haveStone[4]; + + // set CLOSING model _before_ sending impulses !!! + // (any impulse might have side effects that move this stone) + + state = new_state; + notify_state(state); + + for (int d = 0; d < 4; ++d) { + targetpos[d] = move(p, Direction(d)); + haveStone[d] = GetStone(targetpos[d]) != 0; + } + + for (int d = int(incoming)+1; d <= int(incoming)+4; ++d) { + int D = d%4; + if (haveStone[D]) { + send_impulse(targetpos[D], Direction(D)); + } + } + + incoming = NODIR; // forget impulse direction + break; + } + } +} + + +namespace +{ + class StoneImpulseStone : public StoneImpulse_Base { + CLONEOBJ(StoneImpulseStone); + public: + StoneImpulseStone() : StoneImpulse_Base("st-stoneimpulse") + {} + + private: + void notify_state(State st) { + switch (st) { + case IDLE: + init_model(); + break; + case PULSING: + set_anim("st-stoneimpulse-anim1"); + break; + case CLOSING: + set_anim("st-stoneimpulse-anim2"); + break; + } + } + + void actor_hit(const StoneContact &/*sc*/) { + change_state(PULSING); + } + + }; + + + class HollowStoneImpulseStone : public StoneImpulse_Base { + CLONEOBJ(HollowStoneImpulseStone); + public: + HollowStoneImpulseStone() + : StoneImpulse_Base("st-stoneimpulse-hollow") {} + private: + void notify_state(State st) { + switch (st) { + case IDLE: + init_model(); + lasers::MaybeRecalcLight(get_pos()); + break; + case PULSING: + lasers::MaybeRecalcLight(get_pos()); + set_anim("st-stoneimpulse-hollow-anim1"); + break; + case CLOSING: + set_anim("st-stoneimpulse-hollow-anim2"); + break; + } + } + + StoneResponse collision_response(const StoneContact &/*sc*/) { + return (state == IDLE) ? STONE_PASS : STONE_REBOUND; + } + void actor_inside(Actor *a) { + if (state == PULSING || state == CLOSING) + SendMessage(a, "shatter"); + } + + bool is_floating () const { + return true; + } + + void on_laserhit (Direction) { + // hollow StoneImpulseStones cannot be activated using lasers + } + }; + + + class MovableImpulseStone : public StoneImpulse_Base { + CLONEOBJ(MovableImpulseStone); + public: + MovableImpulseStone() + : StoneImpulse_Base("st-stoneimpulse_movable"), + repulse(false) + { + } + + private: + + void notify_state(State st) { + switch (st) { + case IDLE: + if (repulse) { + repulse = false; + change_state(PULSING); + } + else + init_model(); + break; + case PULSING: + set_anim("st-stoneimpulse-anim1"); + break; + case CLOSING: + set_anim("st-stoneimpulse-anim2"); + break; + } + } + + void init_model() { + set_model("st-stoneimpulse"); + } + + // Stone interface: + + void actor_hit(const StoneContact &sc) { + if (!maybe_push_stone (sc)) { + incoming = NODIR; // bad, but no real problem! + if (state == IDLE) + change_state(PULSING); + } + } + + void on_impulse(const Impulse& impulse) { + State oldstate = state; + + if (move_stone(impulse.dir)) { + notify_state(oldstate); // restart anim if it was animated before move + + Actor *hitman = dynamic_cast(impulse.sender); + if (hitman && player::WieldedItemIs (hitman, "it-magicwand")) { + return; // do not change state to PULSING + } + } + + if (state == IDLE) + change_state(PULSING); + } + + void on_move() { + if (state != PULSING) + repulse = true; // pulse again + Stone::on_move(); + } + + bool is_movable() const { + // moving the stone is handled explicitly in actor_hit() + return false; //true; + } + + // Variables. + bool repulse; + }; +} + + +/* -------------------- Oxyd stone -------------------- */ + +/** \page st-oxyd Oxyd Stone + +Oxyd stones are characterized by two attributes: Their flavor and +their color. The flavor only affects the visual representation of +the stone; it can be either 'a' (opening like a flower) or 'b' +(displaying a fade-in animation). The color attribute determines +the shape on the oxyd stone. + +\b Note: You should usually not to create Oxyd stones manually +with \c set_stone(). Use the predefined \c oxyd() function instead. + +\subsection oxyda Attributes + +- \b flavor "a", "b", "c", or "d" +- \b color number between 0 and 7 + +\subsection oxydm Messages + +- \b closeall close all oxyd stones +- \b shuffle interchange the colors of the oxyd stones in the current landscape +- \b trigger open the stone + + +
\image html st-oxyda.png "flavor A" +\image html st-oxydb.png "flavor B" +\image html st-oxydc.png "flavor C" +\image html st-oxydd.png "flavor D" +
+*/ + +namespace +{ + class OxydStone : public PhotoStone { + INSTANCELISTOBJ(OxydStone); + public: + OxydStone(); + + static void shuffle_colors(); + virtual bool is_removable() const; + private: + enum State { CLOSED, OPEN, OPENING, CLOSING, BLINKING }; + State state; + + // Stone interface + void actor_hit(const StoneContact &sc); + void on_creation (GridPos p); + void on_removal (GridPos p); + const char *collision_sound() { return "stone"; } + virtual Value message(const string &m, const Value &); + + + // PhotoStone interface + void notify_laseron() { maybe_open_stone(); } + void notify_laseroff() {} + + // Animation callback + void animcb(); + + // Private methods + void maybe_open_stone(); + void change_state(State newstate); + + + static bool blinking(OxydStone *a) { + return (a->state==BLINKING); + } + static bool blinking_or_opening(OxydStone *a) { + return (a->state==BLINKING || a->state == OPENING); + } + static bool not_open(OxydStone *a) { + return !(a->state==OPEN || a->state==OPENING); + } + + }; +} + +OxydStone::InstanceList OxydStone::instances; + +OxydStone::OxydStone() +: PhotoStone("st-oxyd"), + state(CLOSED) +{ + set_attrib("flavor", "b"); + set_attrib("color", "0"); +} + +Value OxydStone::message(const string &m, const Value &val) +{ + if (m=="closeall") { + for (unsigned i=0; ichange_state(CLOSING); + } + else if (m=="shuffle") { + shuffle_colors(); + } + else if (m=="trigger" || m=="spitter") { + maybe_open_stone(); + } + else if (m=="signal" && to_int(val) != 0) { + maybe_open_stone(); + } + else if (m=="init") { + // odd number of oxyd stones in the level? no problem, turn a + // random one into a fake oxyd + + if (instances.size() % 2) { + // "odd number of oxyd stones"; + // TODO + } + } + return Value(); +} + +void OxydStone::shuffle_colors() +{ + vector closed_oxyds; + size_t isize = instances.size(); + for (size_t i=0; istate == CLOSED) { + closed_oxyds.push_back(i); + } + } + + unsigned size = closed_oxyds.size(); + if (size>1) { + for (unsigned i = 0; i (size-2)); + if (a >= i) ++a; // make a always different from j + + OxydStone *o1 = instances[closed_oxyds[i]]; + OxydStone *o2 = instances[closed_oxyds[a]]; + + string icolor, acolor; + o1->string_attrib("color", &icolor); + o2->string_attrib("color", &acolor); + + o1->set_attrib("color", acolor.c_str()); + o2->set_attrib("color", icolor.c_str()); + } + } +} + +void OxydStone::change_state(State newstate) +{ + string flavor = "a"; + string color = "1"; + string_attrib("flavor", &flavor); + string_attrib("color", &color); + + string modelname = string("st-oxyd") + flavor + color; + + State oldstate = state; + state = newstate; + + switch (newstate) { + case CLOSED: + set_model(string("st-oxyd")+flavor); + break; + + case BLINKING: + set_model(modelname + "-blink"); + break; + + case OPEN: + if (oldstate == CLOSED) { + sound_event("oxydopen"); + sound_event("oxydopened"); + set_anim(modelname+"-opening"); + } else { + set_model(modelname + "-open"); + } + /* If this was the last closed oxyd stone, finish the + level */ + if (find_if(instances.begin(),instances.end(),not_open) + ==instances.end()) + { + server::FinishLevel(); + } + break; + + case OPENING: + sound_event("oxydopen"); + if (oldstate == CLOSED) + set_anim(modelname + "-opening"); + else if (oldstate == CLOSING) + get_model()->reverse(); + + break; + + case CLOSING: + if (oldstate == CLOSED || oldstate==CLOSING) { + state = oldstate; + return; + } + + sound_event("oxydclose"); + if (oldstate == OPENING) + get_model()->reverse(); + else if (oldstate == BLINKING || oldstate == OPEN) { + set_anim(modelname + "-closing"); + } + break; + } +} + +void OxydStone::animcb() { + if (state == CLOSING) + change_state(CLOSED); + else if (state == OPENING) + change_state(BLINKING); + else if (state == OPEN) + change_state(OPEN); // set the right model +} + +void OxydStone::maybe_open_stone() { + if (state == CLOSED || state == CLOSING) { + int mycolor = int_attrib("color"); + + // Is another oxyd stone currently blinking? + InstanceList::iterator i; + i=find_if(instances.begin(), instances.end(), blinking_or_opening); + + if (i != instances.end()) { + + bool can_open; + + if (server::GameCompatibility != GAMET_ENIGMA) { + // If colors match and stone (*i) is already blinking, + // open both stones. Close one of them otherwise. + // (This is the Oxyd behaviour; it doesn't work with + // some Enigma levels.) + can_open = (mycolor == (*i)->int_attrib("color") && (*i)->state==BLINKING); + } + else + can_open = (mycolor == (*i)->int_attrib("color")); + + if (can_open) { + change_state(OPEN); + (*i)->change_state(OPEN); + } else { + (*i)->change_state(CLOSING); + change_state(OPENING); + } + } + else { + // no blinking stone? -> make this one blink + change_state(OPENING); + } + } +} + +void OxydStone::actor_hit(const StoneContact &/*sc*/) { + maybe_open_stone(); +} + +void OxydStone::on_creation (GridPos) +{ + string flavor = "a"; + string_attrib("flavor", &flavor); + set_model(string("st-oxyd") + flavor); + photo_activate(); +} + +bool OxydStone::is_removable() const { + const Value* isStatic = get_attrib("static"); + if (isStatic != NULL) + return !to_bool(*isStatic); + else + return true; +} + +void OxydStone::on_removal(GridPos p) +{ + photo_deactivate(); + kill_model (p); +} + + +/* -------------------- Turnstiles -------------------- */ +namespace +{ + class Turnstile_Arm; + + /* + ** The stone at the center of a turnstile + */ + class Turnstile_Pivot_Base : public Stone { + public: + Turnstile_Pivot_Base(const char *kind); + + protected: + bool rotate(bool clockwise, Object *impulse_sender); + + friend class Turnstile_Arm; // uses rotate + + private: + // Object interface + virtual Value on_message (const Message &m); + virtual void animcb(); + + // Private methods + DirectionBits arms_present() const; + bool no_stone (int xoff, int yoff) const; + void set_arm (Direction dir, RBI_vector &rubs); + void remove_arms (DirectionBits arms); + void rotate_arms (DirectionBits arms, bool clockwise); + void handleActorsAndItems(bool clockwise, Object *impulse_sender); + + // Turnstile_Pivot_Base interface + virtual const char *model() const = 0; + virtual const char *anim() const = 0; + virtual bool oxyd_compatible() const = 0; + + // Variables + bool active; + }; + + class Turnstile_Pivot : public Turnstile_Pivot_Base { + CLONEOBJ(Turnstile_Pivot); + public: + Turnstile_Pivot() : Turnstile_Pivot_Base(model()) {} + + const char *model() const { return "st-turnstile"; } + const char *anim() const { return "st-turnstile-anim"; } + bool oxyd_compatible() const { return true; } + }; + + class Turnstile_Pivot_Green : public Turnstile_Pivot_Base { + CLONEOBJ(Turnstile_Pivot_Green); + public: + Turnstile_Pivot_Green() : Turnstile_Pivot_Base(model()) {} + + const char *model() const { return "st-turnstile-green"; } + const char *anim() const { return "st-turnstile-green-anim"; } + bool oxyd_compatible() const { return false; } + }; + + /* + ** The base class for any of the four arms of the turnstile + */ + class Turnstile_Arm : public Stone { + virtual Direction get_dir() const = 0; + + void actor_hit(const StoneContact &sc); + void on_impulse(const Impulse& impulse); + + Turnstile_Pivot_Base *get_pivot() { + Stone *st = GetStone (move (get_pos(), reverse(get_dir()))); + return dynamic_cast(st); + } + + bool is_movable () const { return true; } + protected: + Turnstile_Arm (const char *kind) : Stone(kind) + {} + }; + + class Turnstile_N : public Turnstile_Arm { + CLONEOBJ(Turnstile_N); + public: + Turnstile_N(): Turnstile_Arm("st-turnstile-n") {} + Direction get_dir () const { return NORTH; } + }; + + class Turnstile_S : public Turnstile_Arm { + CLONEOBJ(Turnstile_S); + Direction get_dir () const { return SOUTH; } + public: + Turnstile_S(): Turnstile_Arm("st-turnstile-s") {} + }; + + class Turnstile_E : public Turnstile_Arm { + CLONEOBJ(Turnstile_E); + Direction get_dir () const { return EAST; } + public: + Turnstile_E(): Turnstile_Arm("st-turnstile-e") {} + }; + + class Turnstile_W : public Turnstile_Arm { + CLONEOBJ(Turnstile_W); + Direction get_dir () const { return WEST; } + public: + Turnstile_W(): Turnstile_Arm("st-turnstile-w") {} + }; +} + + +/* -------------------- Turnstile_Arm -------------------- */ + +void Turnstile_Arm::on_impulse(const Impulse& impulse) { + enum Action { ROTL, ROTR, stay }; + static Action actions[4][4] = { + { stay, ROTL, stay, ROTR }, // west arm + { ROTR, stay, ROTL, stay }, // south arm + { stay, ROTR, stay, ROTL }, // east arm + { ROTL, stay, ROTR, stay } // north arm + }; + + Turnstile_Pivot_Base *pivot = get_pivot(); + + if (pivot) { + Action a = actions[get_dir()][impulse.dir]; + if (a != stay) { + pivot->rotate(a == ROTR, impulse.sender); // ROTR is clockwise + } + } + else { + // Move arms not attached to a pivot individually + move_stone(impulse.dir); + } +} + +void Turnstile_Arm::actor_hit(const StoneContact &sc) +{ + maybe_push_stone(sc); +} + +// -------------------------------------------- +// Turnstile_Pivot_Base implementation +// -------------------------------------------- + +Turnstile_Pivot_Base::Turnstile_Pivot_Base(const char *kind) +: Stone (kind), + active (false) +{} + +void Turnstile_Pivot_Base::animcb() +{ + set_model(model()); + active = false; +} + +Value Turnstile_Pivot_Base::on_message (const Message &m) +{ + if (m.message == "signal") { + int val = to_int (m.value); + if (val == 1) + rotate(false, 0); + else + rotate(true, 0); + } + return Value(); +} + + +DirectionBits +Turnstile_Pivot_Base::arms_present() const +{ + DirectionBits arms = NODIRBIT; + GridPos p = get_pos(); + if (dynamic_cast(GetStone(move(p, NORTH)))) + ecl::set_flags (arms, NORTHBIT); + if (dynamic_cast(GetStone(move(p, SOUTH)))) + ecl::set_flags (arms, SOUTHBIT); + if (dynamic_cast(GetStone(move(p, EAST)))) + ecl::set_flags (arms, EASTBIT); + if (dynamic_cast(GetStone(move(p, WEST)))) + ecl::set_flags (arms, WESTBIT); + return arms; +} + +bool Turnstile_Pivot_Base::no_stone (int xoff, int yoff) const { + GridPos p = get_pos(); + p.x += xoff; + p.y += yoff; + return (0 == GetStone(p)); +} + +void Turnstile_Pivot_Base::remove_arms (DirectionBits arms) { + GridPos p = get_pos(); + if (arms & NORTHBIT) KillStone (move (p, NORTH)); + if (arms & EASTBIT) KillStone (move (p, EAST)); + if (arms & SOUTHBIT) KillStone (move (p, SOUTH)); + if (arms & WESTBIT) KillStone (move (p, WEST)); +} + +void Turnstile_Pivot_Base::rotate_arms (DirectionBits arms, bool clockwise) { + GridPos p = get_pos(); + + RBI_vector Nrubs; + RBI_vector Erubs; + RBI_vector Srubs; + RBI_vector Wrubs; + + if (arms & NORTHBIT) GiveRubberBands(GetStone(move (p, NORTH)), Nrubs); + if (arms & EASTBIT) GiveRubberBands(GetStone(move (p, EAST)), Erubs); + if (arms & SOUTHBIT) GiveRubberBands(GetStone(move (p, SOUTH)), Srubs); + if (arms & WESTBIT) GiveRubberBands(GetStone(move (p, WEST)), Wrubs); + + remove_arms(arms); + + if (clockwise) { + if (arms & NORTHBIT) set_arm(EAST, Nrubs); + if (arms & EASTBIT) set_arm(SOUTH, Erubs); + if (arms & SOUTHBIT) set_arm(WEST, Srubs); + if (arms & WESTBIT) set_arm(NORTH, Wrubs); + } + else { + if (arms & NORTHBIT) set_arm(WEST, Nrubs); + if (arms & EASTBIT) set_arm(NORTH, Erubs); + if (arms & SOUTHBIT) set_arm(EAST, Srubs); + if (arms & WESTBIT) set_arm(SOUTH, Wrubs); + } +} + +void Turnstile_Pivot_Base::set_arm (Direction dir, RBI_vector &rubs) { + const char *names[4] = { "st-turnstile-w", "st-turnstile-s", + "st-turnstile-e", "st-turnstile-n" }; + Stone *st = MakeStone(names[dir]); + GridPos newp = move(get_pos(), dir); + SetStone (newp, st); + + if (Item *it = GetItem(newp)) + it->on_stonehit(st); + + if (!rubs.empty()) + for (RBI_vector::iterator i = rubs.begin(); i != rubs.end(); ++i) + AddRubberBand (i->act, st, i->data); +} + +bool Turnstile_Pivot_Base::rotate(bool clockwise, Object *impulse_sender) { + if (active) + return false; + + DirectionBits arms = arms_present(); + bool can_rotate = true; + + if (clockwise) { + if (arms & NORTHBIT) { + can_rotate &= no_stone(+1,-1); + if (! (arms & EASTBIT)) can_rotate &= no_stone(+1,0); + } + if (arms & WESTBIT) { + can_rotate &= no_stone(-1,-1); + if (! (arms & NORTHBIT)) can_rotate &= no_stone(0,-1); + } + if (arms & SOUTHBIT) { + can_rotate &= no_stone(-1,+1); + if (! (arms & WESTBIT)) can_rotate &= no_stone(-1,0); + } + if (arms & EASTBIT) { + can_rotate &= no_stone(+1,+1); + if (! (arms & SOUTHBIT)) can_rotate &= no_stone(0,+1); + } + } + else { + if (arms & NORTHBIT) { + can_rotate &= no_stone(-1,-1); + if (! (arms & WESTBIT)) can_rotate &= no_stone(-1,0); + } + if (arms & WESTBIT) { + can_rotate &= no_stone(-1,+1); + if (! (arms & SOUTHBIT)) can_rotate &= no_stone(0,+1); + } + if (arms & SOUTHBIT) { + can_rotate &= no_stone(+1,+1); + if (! (arms & EASTBIT)) can_rotate &= no_stone(+1,0); + } + if (arms & EASTBIT) { + can_rotate &= no_stone(+1,-1); + if (! (arms & NORTHBIT)) can_rotate &= no_stone(0,-1); + } + } + + if (can_rotate) { + sound_event (clockwise ? "turnstileright" : "turnstileleft"); + sound_event("movesmall"); + + active = true; + set_anim(anim()); + rotate_arms(arms, clockwise); + handleActorsAndItems(clockwise, impulse_sender); + + PerformAction (this, clockwise == 0); + server::IncMoveCounter(); + } + return can_rotate; +} + +namespace { + bool calc_arm_seen (bool cw, DirectionBits arms, int field) { + // for each field calculate whether an arm has passed by, first + // counterclockwise and then clockwise: + const DirectionBits neededArm[2][8] = { + {WESTBIT, NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT}, + {NORTHBIT, NORTHBIT, EASTBIT, EASTBIT, SOUTHBIT, SOUTHBIT, WESTBIT, WESTBIT} + }; + return (arms & neededArm[cw][field]) != 0; + } +} + +void Turnstile_Pivot_Base::handleActorsAndItems(bool clockwise, Object *impulse_sender) { + const int to_index[3][3] = { // (read this transposed) + { 0, 7, 6 }, // x == 0 + { 1,-1, 5 }, // x == 1 + { 2, 3, 4 } // x == 2 + }; + const int to_x[8] = { -1, 0, 1, 1, 1, 0, -1, -1 }; + const int to_y[8] = { -1, -1, -1, 0, 1, 1, 1, 0 }; + + bool arm_seen[8]; + DirectionBits arms = arms_present(); // Note: already the rotated state + for (int i = 0; i<8; ++i) + arm_seen[i] = calc_arm_seen (clockwise, arms, i); + + // ---------- Handle items in range ---------- + GridPos pv_pos = get_pos(); + for (int i = 0; i<8; ++i) + if (arm_seen[i]) { + GridPos item_pos(pv_pos.x+to_x[i], pv_pos.y+to_y[i]); + if (Item *it = GetItem(item_pos)) + it->on_stonehit(this); // hit with pivot (shouldn't matter) + } + + // ---------- Handle actors in range ---------- + vector actorsInRange; + + // tested range is sqrt(sqr(1.5)+sqr(1.5)) + if (!GetActorsInRange(pv_pos.center(), 2.124, actorsInRange)) + return; + + vector::iterator iter = actorsInRange.begin(), end = actorsInRange.end(); + for (; iter != end; ++iter) { + Actor *ac = *iter; + const V2 &ac_center = ac->get_pos(); + GridPos ac_pos(ac_center); + int dx = ac_pos.x-pv_pos.x; + int dy = ac_pos.y-pv_pos.y; + + // ignore if actor is not inside the turnstile square or is not + // in distance of the the rotating arms + if ((dx<-1 || dx>1 || dy<-1 || dy>1) || + (length(ac->get_pos() - pv_pos.center()) > 1.58114 + ac->get_actorinfo()->radius)) + continue; + + int idx_source = to_index[dx+1][dy+1]; + if (idx_source == -1) + continue; // actor inside pivot -- should not happen + + const int rot_index[4][8] = { + // The warp-destinations for actors. Why different destinations + // for oxyd/non-oxyd-type turnstiles? Imagine the actor on position + // 1 (North of pivot), the turnstile rotates anticlockwise. Then + // a green turnstile-arm, if at all, would push the actor one field + // to the left (position 0). Now assume it's a red turnstile. If the + // actor is to be warped, it has to be the one that activated the + // turnstile. Yet it is on position 1, in principle not able to + // hit an arm. But it can, if it hits fast enough on the edge of + // pivot and left arm. In this case, the actor should be handled + // as if on position 1, thus warping to 6. + { 6, 0, 0, 2, 2, 4, 4, 6 }, // anticlockwise + { 2, 2, 4, 4, 6, 6, 0, 0 }, // clockwise + { 6, 6, 0, 0, 2, 2, 4, 4 }, // anticlockwise (oxyd-compatible) + { 2, 4, 4, 6, 6, 0, 0, 2 }, // clockwise (oxyd-compatible) + }; + + bool compatible = oxyd_compatible(); + int idx_target = rot_index[clockwise+2*compatible][idx_source]; // destination index + bool do_warp = false; // move the actor along with the turnstile? + + if (compatible) { + // Move only the actor that hit the turnstile in Oxyd mode + do_warp = (ac == dynamic_cast(impulse_sender)); + if (!do_warp && arm_seen[idx_source]) + SendMessage(ac, "shatter"); // hit by an arm + } else { // green turnstile + // move all actors only if pushed by an arm + do_warp = arm_seen[idx_source]; + } + + if (!do_warp) + continue; + + // Pushing an actor out of the level results in a shatter (no warp) instead + GridPos ac_target_pos(pv_pos.x+to_x[idx_target], pv_pos.y+to_y[idx_target]); + + if(!IsInsideLevel(ac_target_pos)) { + SendMessage(ac, "shatter"); + continue; + } + + world::WarpActor(ac, ac_target_pos.x+.5, ac_target_pos.y+.5, false); + + if (Stone *st = GetStone(ac_target_pos)) { + + // destination is blocked + + Turnstile_Arm *arm = dynamic_cast(st); + if (arm && !compatible) { // if blocking stone is turnstile arm -> hit it! + const int impulse_dir[2][8] = { + // anticlockwise + { SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT, + NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT }, + // clockwise + { NORTHBIT|EASTBIT, EASTBIT, SOUTHBIT|EASTBIT, SOUTHBIT, + SOUTHBIT|WESTBIT, WESTBIT, NORTHBIT|WESTBIT, NORTHBIT } + }; + + DirectionBits possible_impulses = + static_cast(impulse_dir[clockwise][idx_target]); + + for (int d = 0; d<4; ++d) + if (has_dir(possible_impulses, Direction(d))) + ac->send_impulse(ac_target_pos, Direction(d)); + +// if (GetStone(ac_target_pos) == 0) // arm disappeared +// break; + } + } + } + +// @@@ FIXME: it's possible that two actors are moved to the same +// destination field. In that case the second actor is put on top of +// the first actor (happens only in non-oxyd-compat-mode with three +// balls or pullers/impulsestones) +// +// Note: With black and whiteball it's normally no problem, because +// when one of the actors was moving, it's looking natural. Problems +// occur when small balls come into play. + +} + + +/* -------------------- Mail stone -------------------- */ + +namespace +{ + class MailStone : public Stone { + CLONEOBJ(MailStone); + Direction m_dir; + + + MailStone (const char *kind, Direction dir); + void actor_hit (const StoneContact &sc); + + GridPos find_pipe_endpoint(); + public: + static void setup(); + }; +} + +void MailStone::setup() +{ + Register (new MailStone ("st-mail-n", NORTH)); + Register (new MailStone ("st-mail-e", EAST)); + Register (new MailStone ("st-mail-s", SOUTH)); + Register (new MailStone ("st-mail-w", WEST)); +} + +MailStone::MailStone (const char *kind, Direction dir) +: Stone(kind), m_dir(dir) +{} + + +void MailStone::actor_hit (const StoneContact &sc) +{ + if (enigma::Inventory *inv = player::GetInventory(sc.actor)) { + if (Item *it = inv->get_item(0)) { + GridPos p = find_pipe_endpoint(); + if (world::IsInsideLevel(p) && it->can_drop_at (p)) { + it = inv->yield_first(); + player::RedrawInventory (inv); + it->drop(sc.actor, p); + } + } + } +} + +/** About recursion detection while finding the end of a mailpipe + * + * Since there are no possibilities for forking a mailpipe, there is only one + * cause that may lead to a closed, circular mailpipe. This is if a pipeitem is + * placed exactly under the mailstone. But not every pipepiece is dangerous, + * there are some that can be placed under the mailstone without problems. + * + * The mailpipe is only closed to a circular one if the pipepiece under the + * mailstone has the same 'output'-direction as the stone. + */ +GridPos MailStone::find_pipe_endpoint() +{ + GridPos p = get_pos(); + Direction move_dir = m_dir; + GridPos q = p; // Store the stonepos for recursion detection. + + while (move_dir != NODIR) { + p.move (move_dir); + if (Item *it = world::GetItem(p)) { + switch (get_id(it)) { + case it_pipe_h: + if (!(move_dir == EAST || move_dir == WEST)) + move_dir = NODIR; + break; + case it_pipe_v: + if (!(move_dir == SOUTH || move_dir == NORTH)) + move_dir = NODIR; + break; + case it_pipe_ne: + if (move_dir == SOUTH) move_dir = EAST; + else if (move_dir == WEST) move_dir = NORTH; + else move_dir = NODIR; + break; + case it_pipe_es: + if (move_dir == NORTH) move_dir = EAST; + else if (move_dir == WEST) move_dir = SOUTH; + else move_dir = NODIR; + break; + case it_pipe_sw: + if (move_dir == NORTH) move_dir = WEST; + else if (move_dir == EAST) move_dir = SOUTH; + else move_dir = NODIR; + break; + case it_pipe_wn: + if (move_dir == SOUTH) move_dir = WEST; + else if (move_dir == EAST) move_dir = NORTH; + else move_dir = NODIR; + break; + default: + move_dir = NODIR;; // end of pipe reached + } + } else + move_dir = NODIR; + + if (p == q) + ASSERT(move_dir != m_dir, XLevelRuntime, "Mailpipe is circular! Recursion detected!"); + } + return p; +} + + +/* -------------------- Chess stone -------------------- */ + +namespace +{ + class ChessStone : public Stone, public TimeHandler { + CLONEOBJ(ChessStone); + public: + ChessStone (int color) : Stone("st-chess") { + newcolor = color; + Stone::set_attrib("color", color); + destination = GridPos(0,0); + capture_retry = 0; + rememberFalling = false; + rememberSwamp = false; + state = IDLE; + } + virtual ~ChessStone() { + GameTimer.remove_alarm (this); + } + void init_model(); + void animcb(); + void set_attrib(const string& key, const Value &val); + virtual Value message(const string &msg, const Value &v); + void actor_hit(const StoneContact &sc); + void alarm(); + private: + // Variables and Constants + enum State {IDLE, APPEARING, DISAPPEARING, CAPTURING, + CAPTURED, FALLING, SWAMP} state; + GridPos destination; + int newcolor; // Buffers a color-changing message while not IDLE. + int capture_retry; + static const int max_capture_retry = 20; + static double capture_interval; + static double hit_threshold; + bool rememberFalling; + bool rememberSwamp; + + // Methods + string get_model_name(); + Value maybe_move_to(Direction dir1, Direction dir2); + virtual Value message_move(Direction dir1, Direction dir2); + bool try_state(State newstate); + void set_color(int color); + void on_floor_change(); + + // Interface for st-swap and st-pull + bool is_removable() const { return state == IDLE; } + }; + double ChessStone::capture_interval = 0.1; + double ChessStone::hit_threshold = 3.0; + + string ChessStone::get_model_name() { + string mname = get_kind(); + mname += int_attrib("color") == 0.0 ? "_black" : "_white"; + return mname; + } + + void ChessStone::init_model() { set_model(get_model_name()); } + + void ChessStone::animcb() { + Stone *st; + switch (state) { + case APPEARING: + if(try_state(IDLE)) + // Maybe falling in the meantime? Otherwise: + init_model(); + break; + case DISAPPEARING: + // Maybe the floor has changed into swamp or abyss? + if(!rememberFalling && !rememberSwamp) { + if(try_state(APPEARING)) { + st = GetStone(destination); + if(st) { + // Something went wrong while killing the old + // stone, or maybe a third one intervened. + // Don't move, just reappear at old position. + } else { + move_stone(destination, "movesmall"); + SendMessage(GetFloor(destination), "capture"); + } + // maybe a floor-change has happened, but during + // state APPEARING this doesn't mean anything: + set_anim(get_model_name()+"-appearing"); + } + break; + } + // Else: If floor is swamp or abyss, kill the stone. Do this + // just by continuing to the next case: + case CAPTURED: + case FALLING: + case SWAMP: + KillStone(get_pos()); + break; + default: + ASSERT(0, XLevelRuntime, "ChessStone: inconsistent state in animcb()"); + } + } + + void ChessStone::actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-magicwand")) { + sound_event ("stonepaint"); + set_color(1 - int_attrib("color")); + // If not IDLE, color will be set next time IDLE is set. + } else if ((sc.actor->get_attrib("blackball") && int_attrib("color") == 0) + || (sc.actor->get_attrib("whiteball") && int_attrib("color") == 1)) { + V2 v = sc.actor->get_vel(); + Direction dir1 = get_push_direction(sc); + if(dir1 == NODIR) return; + Direction dir2 = NODIR; + if (dir1 == SOUTH || dir1 == NORTH) { + dir2 = v[0] > hit_threshold ? EAST : + v[0] < -hit_threshold ? WEST : NODIR; + } else { + dir2 = v[1] > hit_threshold ? SOUTH : + v[1] < -hit_threshold ? NORTH : NODIR; + } + if(dir2 == NODIR) return; + // maybe_move_to tests for state == IDLE by itself. + maybe_move_to(dir1, dir2); + } + } + + Value ChessStone::maybe_move_to(Direction dir1, Direction dir2) { + if(state == IDLE) { + // check for fire, step by step + destination = move(get_pos(), dir1); + if(Item *it = GetItem(destination)) + if(get_id(it) == it_burnable_burning) + return Value(); + destination = move(destination, dir2); + if(Item *it = GetItem(destination)) + if(get_id(it) == it_burnable_burning) + return Value(); + destination = move(destination, dir1); + if(Item *it = GetItem(destination)) + if(get_id(it) == it_burnable_burning) + return Value(); + // check for boundary + if(!IsInsideLevel(destination)) return Value(); + // check for stone + if(!GetStone(destination)) { + // Simple case: Just move. + if(try_state(DISAPPEARING)) { + set_anim(get_model_name() + "-disappearing"); + return Value(1); + } else + return Value(); + } else { + // Test stone. Is it opposite chess stone or totally another one? + Stone *st = GetStone(destination); + const Value *col = get_attrib("color"); + if(to_int(SendMessage(st, "capture", Value(get_model_name()))) ) { + // Give it some time for animation, then replace it. + ASSERT(try_state(CAPTURING), XLevelRuntime, + "ChessStone: strange things happening in maybe_move_to"); + // must work, because state is IDLE + GameTimer.set_alarm(this, capture_interval, false); + capture_retry = 0; + return Value(1); + } + return Value(); + } + } + return Value(); + } + + void ChessStone::alarm() { + switch(state) { + case CAPTURING: + if(!GetStone(destination)) { + if(try_state(DISAPPEARING)) + set_anim(get_model_name() + "-disappearing"); + break; + } else if(capture_retry < max_capture_retry) { + ++capture_retry; + GameTimer.set_alarm(this, capture_interval, false); + } else { + // Cancel efforts to capture foreign stone. + capture_retry = 0; + try_state(IDLE); + } + break; + default: + ASSERT(0, XLevelRuntime, "ChessStone: inconsistent state in alarm()"); + } + } + + Value ChessStone::message(const string &msg, const Value &v) { + if(msg == "capture") { + if(state == IDLE && to_string(v) != get_model_name()) + if(try_state(CAPTURED)) { + set_anim(get_model_name() + "-captured"); + return Value(1); + } + return Value(); + } else if(msg == "move_nne") { return message_move(NORTH, EAST); } + else if(msg == "move_een") { return message_move(EAST, NORTH); } + else if(msg == "move_ees") { return message_move(EAST, SOUTH); } + else if(msg == "move_sse") { return message_move(SOUTH, EAST); } + else if(msg == "move_ssw") { return message_move(SOUTH, WEST); } + else if(msg == "move_wws") { return message_move(WEST, SOUTH); } + else if(msg == "move_wwn") { return message_move(WEST, NORTH); } + else if(msg == "move_nnw") { return message_move(NORTH, WEST); } + else if(msg == "move") { + Direction dir1 = (Direction) int_attrib("direction1"); + Direction dir2 = (Direction) int_attrib("direction2"); + return message_move(dir1, dir2); + } else if(msg == "signal") { set_color(to_int(v)); } + else if(msg == "flip") { set_color(1 - int_attrib("color")); } + else + return Stone::message(msg, v); + return Value(); + } + + Value ChessStone::message_move(Direction dir1, Direction dir2) { + // Restrict message-moves to chess-knight-moves + // state == IDLE is tested by maybe_move_to. + if((((dir1==NORTH || dir1==SOUTH) && (dir2==EAST || dir2==WEST)) + || ((dir1==EAST || dir1==WEST) && (dir2==SOUTH || dir2==NORTH)))) + return maybe_move_to(dir1, dir2); + return Value(); + } + + bool ChessStone::try_state(State newstate) { + if (state != FALLING && state != SWAMP) { + // Switch to FALLING or SWAMP only when IDLE, + // but remember them! + if (newstate == FALLING) + rememberFalling = true; + else if (newstate == SWAMP) + rememberSwamp = true; + else + state = newstate; + if(state == IDLE && newcolor != int_attrib("color")) + set_color(newcolor); + if(state == IDLE && rememberFalling) { + state = FALLING; + set_anim(get_model_name() + "-disappearing"); + } + if(state == IDLE && rememberSwamp) { + state = SWAMP; + set_anim(get_model_name() + "-swamp"); + } + } + return state == newstate; + } + + void ChessStone::set_attrib(const string& key, const Value &val) { + if(key == "color") { + set_color(to_int(val)); + } else + Stone::set_attrib(key, val); + } + + void ChessStone::set_color(int color) { + if(color != 0 && color != 1) { + ASSERT(0, XLevelRuntime, "ChessStone: argument to color not 0 or 1"); + } + if(state == IDLE) { + Stone::set_attrib("color", color); + newcolor = color; + init_model(); + } else { + // Remember this color and set it the next time IDLE is set. + newcolor = color; + } + } + + void ChessStone::on_floor_change() { + Floor *fl = GetFloor(get_pos()); + if (fl != NULL) { + if (fl->is_kind("fl-abyss")) + try_state(FALLING); + if (fl->is_kind("fl-swamp")) + try_state(SWAMP); + } + } +} + + +/* -------------------- Light Passenger Stone -------------------- */ +namespace +{ + class LightPassengerStone : public PhotoStone, public TimeHandler { + CLONEOBJ(LightPassengerStone); + DECL_TRAITS; + public: + LightPassengerStone(bool active_) : PhotoStone("st-lightpassenger"), + skateDir (NODIR), state(active_ ? ACTIVE : INACTIVE), + isLighted (false) { } + + virtual ~LightPassengerStone() { + GameTimer.remove_alarm (this); + } + + private: + Direction skateDir; + //bool isActive; + bool isLighted; + enum State {INACTIVE, ACTIVE, BLINK, BREAK} state; + //bool isBlinking; + + void on_creation(GridPos p) { + PhotoStone::on_creation(p); + photo_activate(); + } + void on_removal(GridPos p) { + photo_deactivate(); + PhotoStone::on_removal(p); + } + void init_model() { + switch(state) { + case INACTIVE: + set_anim("st-lightpassenger_off"); break; + case ACTIVE: + set_anim("st-lightpassenger"); break; + case BLINK: + set_anim("st-lightpassenger-blink"); break; + case BREAK: + GridPos p = get_pos(); + bool NorthSouth = lasers::LightFrom(p,NORTH) && + lasers::LightFrom(p,SOUTH); + bool EastWest = lasers::LightFrom(p,EAST) && + lasers::LightFrom(p,WEST); + sound_event ("stonedestroy"); + if(NorthSouth && !EastWest) + set_anim("st-lightpassenger-break-v"); + else if(!NorthSouth && EastWest) + set_anim("st-lightpassenger-break-h"); + else // and this even in case the laser recently disappeared + set_anim("st-lightpassenger-break-hv"); + break; + } + } + void animcb() { + ASSERT(state == BREAK, XLevelRuntime, + "LightPassengerStone: animcb called with inconsistent state"); + KillStone(get_pos()); + } + void notify_laseroff() { + if (isLighted) GameTimer.remove_alarm(this); + isLighted = false; + if(state == BLINK) { + state = ACTIVE; + init_model(); + } + } + void notify_laseron() { + if (!isLighted) GameTimer.set_alarm(this, get_interval(), false); + isLighted = true; + } + + double get_interval() { + /* Interval is calculated from + 1 + friction_factor * friction + interval = -------------------------------- * baseinterval + 1 + gradient_factor * gradient + and min-maxed to sensible values. "gradient" is just + the force resulting from floor->add_force. "baseinterval" + is 50 ms or the interval given in "interval". + */ + double base = 0.05; + if (const Value *v = this->get_attrib("interval")) + base = to_double(*v); + if (const Value *f = this->get_attrib("friction_factor")) + base *= 1.0 + to_double(*f) * GetFloor(get_pos())->friction(); + if (const Value *g = this->get_attrib("gradient_factor")) + if (skateDir != NODIR) { + V2 vec = V2(0.0,0.0); + double quot = 0; + GetFloor(get_pos())->add_force(0, vec); + quot = skateDir == NORTH ? -vec[1] : skateDir == SOUTH ? vec[1] : + skateDir == EAST ? vec[0] : skateDir == WEST ? -vec[0] : 0; + base /= max(1.0 + to_double(*g) * quot, 0.01); + } + return max(base, 0.02); + } + + void set_on(bool newon){ + if(state == INACTIVE && newon) { + state = ACTIVE; + init_model(); + if(isLighted) + GameTimer.set_alarm(this, get_interval(), false); + } else if((state == ACTIVE || state == BLINK) && !newon) { + state = INACTIVE; + init_model(); + skateDir = NODIR; + } + } + + virtual Value message (const string &msg, const Value &v) { + if (msg == "onoff") set_on(state == INACTIVE); + else if (msg == "signal") set_on(to_int(v) != 0); + else if (msg == "on") set_on(true); + else if (msg == "off") set_on(false); + else if (msg == "trigger") set_on(state == INACTIVE); + return Value(); + } + + void alarm() { + if(isLighted && (state == ACTIVE || state == BLINK)) { + GridPos p = get_pos(); + int toSouth = (lasers::LightFrom(p,NORTH)?1:0) + + (lasers::LightFrom(p,SOUTH)?-1:0); + int toWest = (lasers::LightFrom(p,EAST)?1:0) + + (lasers::LightFrom(p,WEST)?-1:0); + if(toSouth * toWest != 0) { + // Light is coming from two directions. Choose the one you are + // *not* coming from (thus changing beams), in doubt: random. + if(skateDir == NORTH || skateDir == SOUTH) + toSouth = 0; + if(skateDir == EAST || skateDir == WEST) + toWest = 0; + if(skateDir == NODIR) { + toSouth = IntegerRand(0,1) ? 0 : toSouth; + toWest = toSouth ? 0 : toWest; + } + } + skateDir = (toSouth == 1) ? SOUTH : (toSouth == -1) ? NORTH : + (toWest == 1) ? WEST : (toWest == -1) ? EAST : NODIR; + if(skateDir == NODIR && state != BLINK) { + // No direction but lighted? Seems to be lasers from + // two opposite directions. Be sure and then start blinking. + if(lasers::LightFrom(p,EAST) || lasers::LightFrom(p,WEST) || + lasers::LightFrom(p,NORTH) || lasers::LightFrom(p,SOUTH)) { + state = BLINK; + init_model(); + } + } else if(skateDir != NODIR) { + if(state == BLINK) { + state == ACTIVE; + init_model(); + } + if(GetStone(move(p, skateDir))) { + // Skipping each second turn makes the passenger stone seem + // slower when pushing another stone. This looks more + // natural. That's why impulse is delayed: + send_impulse(move(p, skateDir), skateDir, get_interval()); + } + move_stone(skateDir); + } + GameTimer.set_alarm(this, get_interval(), false); + } + } + + void on_impulse(const Impulse &impulse) { + Actor *a = dynamic_cast(impulse.sender); + if(!a && (!isLighted || state == INACTIVE)) + move_stone(impulse.dir); + } + void actor_hit(const StoneContact &sc) { + Actor *a = sc.actor; + if(a && state == BLINK && player::WieldedItemIs(a, "it-hammer")) { + state = BREAK; + init_model(); + } + } + bool is_movable () const { return false; } + }; + + DEF_TRAITS(LightPassengerStone,"st-lightpassenger", st_lightpassenger); +} + +// -------------------------------------------------------------------------------- + +void stones::Init_complex() +{ + Register(new BolderStone); + Register("st-bolder-n", new BolderStone(NORTH)); + Register("st-bolder-e", new BolderStone(EAST)); + Register("st-bolder-s", new BolderStone(SOUTH)); + Register("st-bolder-w", new BolderStone(WEST)); + + Register(new BlockerStone(true)); + Register(new BlockerStone(false)); + + Register(new Door); + Register("st-door-h", new Door("h")); + Register("st-door-v", new Door("v")); + Register("st-door-h-open", new Door("h", true)); + Register("st-door-v-open", new Door("v", true)); + Register(new Door_a); + Register(new Door_b); + Register(new Door_c); + + Register(new HollowStoneImpulseStone); + + MailStone::setup(); + + Register(new MovableImpulseStone); + + Register(new OneWayStone); + Register("st-oneway-n", new OneWayStone(NORTH)); + Register("st-oneway-e", new OneWayStone(EAST)); + Register("st-oneway-s", new OneWayStone(SOUTH)); + Register("st-oneway-w", new OneWayStone(WEST)); + Register(new OneWayStone_black); + Register("st-oneway_black-n", new OneWayStone_black(NORTH)); + Register("st-oneway_black-e", new OneWayStone_black(EAST)); + Register("st-oneway_black-s", new OneWayStone_black(SOUTH)); + Register("st-oneway_black-w", new OneWayStone_black(WEST)); + Register(new OneWayStone_white); + Register("st-oneway_white-n", new OneWayStone_white(NORTH)); + Register("st-oneway_white-e", new OneWayStone_white(EAST)); + Register("st-oneway_white-s", new OneWayStone_white(SOUTH)); + Register("st-oneway_white-w", new OneWayStone_white(WEST)); + + Register(new OxydStone); + + Register (new PullStone); + + Register( new VolcanoStone); + Register("st-volcano_inactive", new VolcanoStone(VolcanoStone::INACTIVE)); + Register("st-volcano_active", new VolcanoStone(VolcanoStone::ACTIVE)); + + // PerOxyd/Enigma compatible puzzle stones: + Register(new PuzzleStone(0, false)); + Register("st-puzzle-hollow", new PuzzleStone(1, false)); + Register("st-puzzle-n", new PuzzleStone(9, false)); + Register("st-puzzle-e", new PuzzleStone(5, false)); + Register("st-puzzle-s", new PuzzleStone(3, false)); + Register("st-puzzle-w", new PuzzleStone(2, false)); + Register("st-puzzle-ne", new PuzzleStone(13, false)); + Register("st-puzzle-nw", new PuzzleStone(10, false)); + Register("st-puzzle-es", new PuzzleStone(7, false)); + Register("st-puzzle-sw", new PuzzleStone(4, false)); + Register("st-puzzle-ns", new PuzzleStone(11, false)); + Register("st-puzzle-ew", new PuzzleStone(6, false)); + Register("st-puzzle-nes", new PuzzleStone(15, false)); + Register("st-puzzle-new", new PuzzleStone(14, false)); + Register("st-puzzle-nsw", new PuzzleStone(12, false)); + Register("st-puzzle-esw", new PuzzleStone(8, false)); + Register("st-puzzle-nesw", new PuzzleStone(16, false)); + + // Oxyd1 compatible puzzle stones: + Register("st-puzzle2-hollow", new PuzzleStone(1, true)); + Register("st-puzzle2-n", new PuzzleStone(9, true)); + Register("st-puzzle2-e", new PuzzleStone(5, true)); + Register("st-puzzle2-s", new PuzzleStone(3, true)); + Register("st-puzzle2-w", new PuzzleStone(2, true)); + Register("st-puzzle2-ne", new PuzzleStone(13, true)); + Register("st-puzzle2-nw", new PuzzleStone(10, true)); + Register("st-puzzle2-es", new PuzzleStone(7, true)); + Register("st-puzzle2-sw", new PuzzleStone(4, true)); + Register("st-puzzle2-ns", new PuzzleStone(11, true)); + Register("st-puzzle2-ew", new PuzzleStone(6, true)); + Register("st-puzzle2-nes", new PuzzleStone(15, true)); + Register("st-puzzle2-new", new PuzzleStone(14, true)); + Register("st-puzzle2-nsw", new PuzzleStone(12, true)); + Register("st-puzzle2-esw", new PuzzleStone(8, true)); + Register("st-puzzle2-nesw", new PuzzleStone(16, true)); + + //Register("st-bigbrick", new BigBrick(1)); // use st-brick instead + Register("st-bigbrick-n", new BigBrick(9)); + Register("st-bigbrick-e", new BigBrick(5)); + Register("st-bigbrick-s", new BigBrick(3)); + Register("st-bigbrick-w", new BigBrick(2)); + Register("st-bigbrick-ne", new BigBrick(13)); + Register("st-bigbrick-nw", new BigBrick(10)); + Register("st-bigbrick-es", new BigBrick(7)); + Register("st-bigbrick-sw", new BigBrick(4)); + Register("st-bigbrick-ns", new BigBrick(11)); + Register("st-bigbrick-ew", new BigBrick(6)); + Register("st-bigbrick-nes", new BigBrick(15)); + Register("st-bigbrick-new", new BigBrick(14)); + Register("st-bigbrick-nsw", new BigBrick(12)); + Register("st-bigbrick-esw", new BigBrick(8)); + Register("st-bigbrick-nesw", new BigBrick(16)); + + //Register("st-bigbluesand", new BigBlueSand(1)); // use st-blue-sand instead + Register("st-bigbluesand-n", new BigBlueSand(9)); + Register("st-bigbluesand-e", new BigBlueSand(5)); + Register("st-bigbluesand-s", new BigBlueSand(3)); + Register("st-bigbluesand-w", new BigBlueSand(2)); + Register("st-bigbluesand-ne", new BigBlueSand(13)); + Register("st-bigbluesand-nw", new BigBlueSand(10)); + Register("st-bigbluesand-es", new BigBlueSand(7)); + Register("st-bigbluesand-sw", new BigBlueSand(4)); + Register("st-bigbluesand-ns", new BigBlueSand(11)); + Register("st-bigbluesand-ew", new BigBlueSand(6)); + Register("st-bigbluesand-nes", new BigBlueSand(15)); + Register("st-bigbluesand-new", new BigBlueSand(14)); + Register("st-bigbluesand-nsw", new BigBlueSand(12)); + Register("st-bigbluesand-esw", new BigBlueSand(8)); + Register("st-bigbluesand-nesw", new BigBlueSand(16)); + + Register ("st-rotator-right", new RotatorStone(true, false)); + Register ("st-rotator-left", new RotatorStone(false, false)); + Register ("st-rotator_move-right", new RotatorStone(true, true)); + Register ("st-rotator_move-left", new RotatorStone(false, true)); + + Register(new ShogunStone); + Register("st-shogun-s", new ShogunStone(1)); + Register("st-shogun-m", new ShogunStone(2)); + Register("st-shogun-sm", new ShogunStone(3)); + Register("st-shogun-l", new ShogunStone(4)); + Register("st-shogun-sl", new ShogunStone(5)); + Register("st-shogun-ml", new ShogunStone(6)); + Register("st-shogun-sml", new ShogunStone(7)); + + Register(new StoneImpulseStone); + + Register (new Turnstile_Pivot); // oxyd-comaptible + Register (new Turnstile_Pivot_Green); // not oxyd-comaptible + Register (new Turnstile_N); + Register (new Turnstile_S); + Register (new Turnstile_E); + Register (new Turnstile_W); + + Register("st-chess_black", new ChessStone(0)); + Register("st-chess_white", new ChessStone(1)); + + Register("st-lightpassenger", new LightPassengerStone(true)); + Register("st-lightpassenger_off", new LightPassengerStone(false)); +} diff --git a/project/jni/application/enigma/src/stones_internal.hh b/project/jni/application/enigma/src/stones_internal.hh new file mode 100644 index 000000000..10a51d332 --- /dev/null +++ b/project/jni/application/enigma/src/stones_internal.hh @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/ or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef STONES_INTERNAL_HH_INCLUDED +#define STONES_INTERNAL_HH_INCLUDED + +#include "world.hh" + +#define DECL_TRAITS \ + static StoneTraits traits; \ + const StoneTraits &get_traits() const { return traits; } + +#define DECL_TRAITS_ARRAY(n, subtype_expr) \ + static StoneTraits traits[n]; \ + const StoneTraits &get_traits() const { return traits[subtype_expr]; } + +#define DEF_TRAITS(classname, name, id) \ + StoneTraits classname::traits = { name, id, stf_none, material_stone, 1.0 } + + + +namespace stones +{ + using namespace world; + + void Init_simple(); + void Init_complex(); + + +/* -------------------- Auxiliary Functions -------------------- */ + + Direction get_push_direction (const StoneContact &sc); + + /* Move a stone (by sending an impulse) Called when actor hits a + stone. */ + bool maybe_push_stone (const StoneContact &sc); + +/* -------------------- YieldedGridStone -------------------- */ + + // allows to completely remove a Stone and its model + // for a short time + // + // - "delays" animation + // + // @@@ FIXME: alarms have to be disabled as well + + class YieldedGridStone { + world::Stone *stone; + display::Model *model; + + YieldedGridStone(const YieldedGridStone&); + YieldedGridStone& operator = (const YieldedGridStone&); + public: + + YieldedGridStone(Stone *st); + ~YieldedGridStone(); + + void set_stone(enigma::GridPos pos); + void dispose(); + }; + +/* -------------------- OnOffStone -------------------- */ + + /*! Base class for all stones that can be on and off. Understands + the messages "on", "off", and "onoff". Whenever the "on" + attribute changes, the object's init_model() method is invoked */ + class OnOffStone : public Stone { + protected: + OnOffStone(const char *kind) + : Stone (kind) + { + set_attrib("on", 0.0); + } + + bool is_on() const { + return int_attrib("on") == 1; + } + + virtual void set_on(bool newon) { + if (newon != is_on()) { + set_attrib("on", enigma::Value(newon)); + init_model(); + notify_onoff(newon); + } + } + + virtual void notify_onoff(bool /*on*/) {} + + virtual Value on_message(const world::Message &msg) + { + const std::string &m = msg.message; + if (m=="onoff") + set_on(!is_on()); + else if (m=="signal") + set_on (to_int(msg.value) != 0); + else if (m == "on") + set_on(true); + else if (m=="off") + set_on(false); + return Value(); + } + }; +} +#endif diff --git a/project/jni/application/enigma/src/stones_simple.cpp b/project/jni/application/enigma/src/stones_simple.cpp new file mode 100644 index 000000000..36531aafe --- /dev/null +++ b/project/jni/application/enigma/src/stones_simple.cpp @@ -0,0 +1,2325 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "errors.hh" +#include "laser.hh" +#include "server.hh" +#include "player.hh" +#include "client.hh" +#include "Inventory.hh" + +#include "stones_internal.hh" + +using namespace std; +using namespace world; +using namespace stones; + + +/* -------------------- SimpleStoneTraits -------------------- */ + +namespace +{ + /*! This class stores some atrributes for SimpleStones. Only one + instance is created for each type of SimpleStone. */ + + struct SimpleStoneTraits { + string sound; // collision sound + bool hollow; // whether stone is hollow + bool glass; // whether it's a glass stone + + // static list of all allocated SimpleStoneTraits (never freed yet) + static vector simple_stone_traits; + + SimpleStoneTraits(const string& sound_, bool hollow_, bool glass_) + : sound(sound_) , hollow(hollow_) , glass(glass_) + {} + + public: + SimpleStoneTraits() {} + +// static void clear() { +// vector::iterator i = simple_stone_traits.begin(); +// vector::iterator e = simple_stone_traits.end(); +// for (; i != e; ++e) +// delete simple_stone_traits[i]; +// simple_stone_traits.clear(); +// } + + static const SimpleStoneTraits* Register(const string& sound_, bool hollow_, bool glass_) { + simple_stone_traits.push_back(new SimpleStoneTraits(sound_, hollow_, glass_)); + return simple_stone_traits.back(); + } + }; + + vector SimpleStoneTraits::simple_stone_traits; +} + + +/* -------------------- SimpleStone -------------------- */ + +namespace +{ + class SimpleStone : public Stone { + public: + SimpleStone(const string &knd, const string & snd, + bool hollow, bool is_glass) + : Stone(knd.c_str()), + traits(SimpleStoneTraits::Register(snd, hollow, is_glass)), + sunglasses(false) + {} + private: + SimpleStone(const SimpleStone& other) + : Stone(other.get_kind()), traits(other.traits), + sunglasses(false) + {} + + Stone *clone() { return new SimpleStone(*this); } + void dispose() { delete this; } + + const char *collision_sound() { + return traits->sound.c_str(); + } + + /** Different kinds of glassstones: + * Stone: visible: invisible: lasertransparent: + * st-glass - pass Y + * st-glass1 - pass Y + * st-glass1_hole pass pass Y + * st-glass2 - pass N + * st-glass2_hole pass pass Y + * st-glass3 - - Y + */ + StoneResponse collision_response(const StoneContact &sc) { + if (traits->hollow) + return STONE_PASS; + if (sc.actor->is_invisible() && ((traits->glass && !is_kind("st-glass3")) || is_kind("st-beads")) ) + return STONE_PASS; + return Stone::collision_response(sc); + } + + bool is_sticky (const Actor *actor) const { + if (traits->glass || is_kind("st-beads")) + return !actor->is_invisible(); + return Stone::is_sticky(actor); + } + + bool is_floating() const { + return traits->hollow; + } + + bool is_transparent (Direction dir) const { + if (traits->hollow || (traits->glass && !is_kind("st-glass2")) ) + return true; + return Stone::is_transparent(dir); + } + + virtual Value on_message (const Message &m) + { + if (traits->hollow && m.message == "glasses") { + if (to_int(m.value)) { + if (!sunglasses) { + sunglasses = true; + set_model( "invisible"); + } + } + else { + if (sunglasses) { + sunglasses = false; + set_model (this->get_kind()); + } + } + } + return Value(); + } + + const SimpleStoneTraits *traits; // owned by simple_stone_traits + bool sunglasses; // seen through glasses + }; +} + + +/* -------------------- SimpleStoneMovable -------------------- */ + +namespace +{ + class SimpleStoneMovable : public Stone { + public: + SimpleStoneMovable(const string &knd, const string & snd, bool is_glass) + : Stone (knd.c_str()), + traits(SimpleStoneTraits::Register(snd, false, is_glass)) + {} + + private: + SimpleStoneMovable(const SimpleStoneMovable& other) + : Stone (other.get_kind()), + traits (other.traits) + {} + + Stone *clone() { return new SimpleStoneMovable(*this); } + void dispose() { delete this; } + + const char *collision_sound() { + return traits->sound.c_str(); + } + + /** Different kinds of movable glassstones: + * Stone: visible: invisible: lasertransparent: + * st-glass_move push pass Y + * st-glass1_move push push Y + * st-glass2_move push push N + */ + StoneResponse collision_response(const StoneContact &sc) { + if (traits->glass && sc.actor->is_invisible() && is_kind("st-glass_move")) + return STONE_PASS; + return Stone::collision_response(sc); + } + + bool is_sticky (const Actor *actor) const { + if (traits->glass) + return !actor->is_invisible(); + return Stone::is_sticky(actor); + } + + bool is_transparent (Direction dir) const { + if (traits->glass && !is_kind("st-glass2_move")) + return true; + return Stone::is_transparent(dir); + } + + bool is_movable() const { return true; } + + const SimpleStoneTraits *traits; // owned by simple_stone_traits + }; +} + + +/* -------------------- DummyStone -------------------- */ + +namespace +{ + /*! Used for debugging; Prints its own oxyd code when hit. */ + class DummyStone : public Stone { + CLONEOBJ(DummyStone); + public: + DummyStone() : Stone("st-dummy") {} + private: + + StoneResponse collision_response(const StoneContact &/*sc*/) { + static int lastCode = -1; + int code = int_attrib("code"); + if (code != lastCode) { + fprintf(stderr, "Collision with stone 0x%02x\n", code); + lastCode = code; + } + return STONE_REBOUND; + } + }; +} + + +/* -------------------- EasyModeStone -------------------- */ + +/** \page st-easymode Easy-Mode Stone + +In easy game mode this stone converts the floor at its +position to fl-normal. +In normal game mode the stone kills any item at its position. +Then in both modes it kills itself. + +E.g. it can be used to hide water-barriers or to insert helper +items that make the level easier in easy game mode. + +\subsection easye Example +\verbatim +set_stone("st-easymode", 10,10) +\endverbatim + +\ref it-easymode +*/ + +namespace +{ + class EasyModeStone : public Stone { + CLONEOBJ(EasyModeStone); + DECL_TRAITS; + + virtual Value message(const std::string &msg, const Value&) { + if (msg == "init") { + if (server::GetDifficulty() == DIFFICULTY_EASY) { + SetFloor (get_pos(), MakeFloor ("fl-normal")); + } else { + KillItem (get_pos()); + } + KillStone (get_pos()); + } + return Value(); + } + public: + EasyModeStone() + {} + }; + DEF_TRAITS(EasyModeStone, "st-easymode", st_easymode); +} + + +/* -------------------- Grates -------------------- */ + +namespace +{ + class GrateBase : public Stone { + public: + GrateBase(const char *kind) : Stone(kind) {} + private: + bool is_floating() const { return true; } + bool is_transparent (Direction) const { return true; } + + virtual StoneResponse collision_response(const StoneContact &sc) { + if (server::GameCompatibility == GAMET_OXYD1) { + return STONE_PASS; + } + // tested with per.oxyd + return sc.actor->is_on_floor() ? STONE_PASS : STONE_REBOUND; + } + }; + + class Grate1 : public GrateBase { + CLONEOBJ(Grate1); + public: + Grate1() : GrateBase("st-grate1") {} + }; + + class Grate2 : public GrateBase { + CLONEOBJ(Grate2); + public: + Grate2() : GrateBase("st-grate2") {} + }; + + /*! Horses and small marbles can move through this stone, but + normal marbles can't. */ + class Grate3 : public GrateBase { + CLONEOBJ(Grate3); + public: + Grate3() : GrateBase("st-grate3") {} + + StoneResponse collision_response(const StoneContact &sc) { + ActorID id = get_id(sc.actor); + if (id == ac_horse || id == ac_meditation || id == ac_killerball) + return STONE_PASS; + else + return STONE_REBOUND; + } + }; +} + + +/* -------------------- Chameleon Stone -------------------- */ + +namespace +{ + class ChameleonStone : public Stone { + CLONEOBJ(ChameleonStone); + DECL_TRAITS; + + void init_model() { + string modelname = "fl-gray"; + if (Floor *fl = GetFloor(get_pos())) + modelname = fl->get_kind(); + set_model(modelname); + } + + bool is_floating() const + { return true; } + + StoneResponse collision_response(const StoneContact &) + { return STONE_PASS; } + + public: + ChameleonStone() + {} + }; + DEF_TRAITS(ChameleonStone, "st-chameleon", st_chameleon); +} + + +/* -------------------- SwapStone -------------------- */ + +namespace +{ + class SwapStone : public Stone, public TimeHandler { + public: + SwapStone(); + ~SwapStone(); + private: + // Object interface + SwapStone *clone(); + void dispose(); + + // GridObject interface + void init_model(); + void on_removal(GridPos p); + + // Stone interface + void on_impulse (const Impulse &impulse); + bool is_removable() const { return state == IDLE; } + void actor_hit (const StoneContact &sc); + + // TimeHandler interface + void alarm(); + + // Variables : + enum State { IDLE, COME, GO } state; + YieldedGridStone *in_exchange_with; + Direction move_dir; + }; +} + +SwapStone::SwapStone() +: Stone("st-swap"), + state(IDLE), + in_exchange_with(0), + move_dir(NODIR) +{} + +SwapStone::~SwapStone() { + GameTimer.remove_alarm(this); +} + +SwapStone *SwapStone::clone() { + SwapStone *other = new SwapStone(*this); + other->in_exchange_with = 0; + return other; +} + +void SwapStone::dispose() { + if (state == COME && in_exchange_with != 0) { + in_exchange_with->dispose(); + delete in_exchange_with; + } + delete this; +} + +void SwapStone::on_removal(GridPos p) { + if (state == COME) { + GameTimer.remove_alarm(this); + } +} + +/* Animation finished; put the "swapped" stone to its new position. */ +void SwapStone::alarm() +{ + GridPos oldPos = move(get_pos(), reverse(move_dir)); + + // Set the swapped stone (this also kills the old (inactive) swap stone) + in_exchange_with->set_stone(oldPos); + delete in_exchange_with; + in_exchange_with = 0; + + state = IDLE; + init_model(); +// sound_event ("moveslow"); +} + +void SwapStone::on_impulse(const Impulse& impulse) +{ + if (state == IDLE) { + GridPos oldp = get_pos(); + GridPos newp = move(oldp, impulse.dir); + + // never swap beyond the world and for non enigma modes do not swap to + // border as well. + if (IsInsideLevel(newp) && + (server::GameCompatibility == GAMET_ENIGMA || !IsLevelBorder(newp))) { + Stone *other = GetStone(newp); + if (other && other->is_removable()) { + SwapStone *newStone = new SwapStone; + newStone->state = COME; + newStone->in_exchange_with = new YieldedGridStone(other); // yields 'other' + newStone->move_dir = impulse.dir; + + GameTimer.set_alarm(newStone, 0.1, false); + + SetStone(newp, newStone); + + state = GO; + move_dir = impulse.dir; + init_model(); + + sound_event ("moveslow"); + server::IncMoveCounter(1); + } + } + } +} + +void SwapStone::actor_hit(const StoneContact &sc) { + Direction dir = get_push_direction (sc); + if (dir != NODIR) { + send_impulse(get_pos(), dir); + } +} + +void SwapStone::init_model() { + static const char *models_come[] = { "st-swap-w", "st-swap-s", "st-swap-e", "st-swap-n" }; + static const char *models_go[] = { "st-swap-e", "st-swap-n", "st-swap-w", "st-swap-s" }; + + const char *model = 0; + switch (state) { + case IDLE: model = "st-swap"; break; + case COME: model = models_come[move_dir]; break; + case GO: model = models_go[move_dir]; break; + } + + set_model(model); +} + + +/* -------------------- BlockStone -------------------- */ + +namespace +{ + class BlockStone : public Stone { + CLONEOBJ(BlockStone); + public: + BlockStone() : Stone("st-block") {} + private: + V2 get_center() const { + return get_pos().center(); + } + + void on_floor_change() { + if (Floor *fl=GetFloor(get_pos())) { + const string &k = fl->get_kind(); + if (k=="fl-water" || k=="fl-abyss" || k == "fl-swamp") { + client::Msg_Sparkle (get_center()); + KillStone(get_pos()); + } + } + } + bool is_movable () const { return true; } + }; + +}; + + +/* -------------------- Window -------------------- */ + +/** \page st-window Breakable Stone + +Hit this window heavily with your marble to blast it into smithereens. + +\image html st-window.png +*/ + +namespace +{ + class Window : public Stone { + CLONEOBJ(Window); + DECL_TRAITS; + const char *collision_sound() {return "glass";} + + bool is_transparent (Direction) const { return true; } + bool is_floating() const { return state != IDLE; } + enum State { IDLE, BREAK } state; + + void actor_hit(const StoneContact &sc) { + Actor *a = sc.actor; + if (state == IDLE) { + double impulse = -(a->get_vel() * sc.normal) * get_mass(a); + if (impulse > 35) { + SendMessage(a, "shatter"); + } + + if (impulse > 25) { + sound_event ("shatter"); + state = BREAK; + set_anim("st-window-anim"); + } + } + } + void animcb() { + KillStone(get_pos()); + } + public: + Window() : state(IDLE) {} + }; + DEF_TRAITS(Window, "st-window", st_window); +} + +// ----------------------- +// BreakableStone +// ----------------------- +// base class for Stone_break, Break_Bolder, Break_acwhite and Break_acblack +// +// breakable stones can be destroyed using +// hammer, laser, dynamite, bombs or bombstones + +namespace +{ + class BreakableStone : public Stone { + public: + BreakableStone(const char *kind) : Stone(kind), state(IDLE) {} + protected: + void break_me() { + if (state == IDLE) { + state = BREAK; + sound_event ("stonedestroy"); + set_anim(get_break_anim()); + } + } + private: + const char *collision_sound() { return "stone"; } + + void actor_hit(const StoneContact &sc) { + if (may_be_broken_by(sc.actor)) + break_me(); + } + void on_laserhit(Direction) { + break_me(); + } + void animcb() { + KillStone(get_pos()); + } + virtual Value message(const string &msg, const Value &) { + if (msg =="ignite" || msg == "expl" || msg == "bombstone") + break_me(); + return Value(); + } + + virtual string get_break_anim() const { + return string(get_kind())+"-anim"; + } + virtual bool may_be_broken_by(Actor *a) const = 0; + + // variables: + + enum State { IDLE, BREAK }; + State state; + }; +} + +//---------------------------------------- +// Stone_break +//---------------------------------------- + +/** \page st-stone_break Breakable Stone + +This stone can be destroyed by an actor having a +hammer and by laser, dynamite, bombs and bombstones. + +\subsection stone_breake Example +\verbatim +set_stone("st-stone_break", 10,10) +\endverbatim + +\image html st-stone_break.png +*/ +namespace +{ + class Stone_break : public BreakableStone { + CLONEOBJ(Stone_break); + public: + Stone_break(const char *kind) : BreakableStone(kind) { } + private: + bool may_be_broken_by(Actor *a) const { + return player::WieldedItemIs (a, "it-hammer"); + } + }; + + class LaserBreakable : public BreakableStone { + CLONEOBJ (LaserBreakable); + + void actor_hit(const StoneContact &) { + } + bool may_be_broken_by(Actor *) const { + return false; + } + public: + + LaserBreakable(): BreakableStone("st-laserbreak") { + } + }; +} + + +//---------------------------------------- +// Break_bolder +//---------------------------------------- + +/** \page st-break_bolder Breakable Stone + +This stone can be destroyed by an actor having a hammer and by laser, +dynamite, bombs and bombstones, bolder + +\subsection break_bolder Example +\verbatim +set_stone("st-break_bolder", 10,10) +\endverbatim + +\image html st-break_bolder.png +*/ +namespace +{ + class Break_bolder : public BreakableStone { + CLONEOBJ(Break_bolder); + public: + Break_bolder() : BreakableStone("st-break_bolder") {} + private: + bool may_be_broken_by(Actor *a) const { + return player::WieldedItemIs (a, "it-hammer"); + } + virtual Value message(const string &msg, const Value &) { + if (msg == "trigger") + break_me(); + return Value(); + } + }; +} + +//---------------------------------------- +// Stone_movebreak +//---------------------------------------- + +/** \page st-rock3_movebreak Breakable Movable Stone + +This stone can be destroyed by an actor having a +hammer and by laser, dynamite, bombs and bombstones. + +\subsection stone_breake Example +\verbatim +set_stone("st-rock3_movebreak", 10,10) +\endverbatim + +\image html st-rock3.png +*/ +namespace +{ + class Stone_movebreak : public BreakableStone { + CLONEOBJ(Stone_movebreak); + public: + Stone_movebreak() : BreakableStone("st-rock3_movebreak") {} + private: + + void on_laserhit(Direction) { + } + + string get_break_anim() const { + return "st-rock3_break-anim"; + } + bool may_be_broken_by(Actor *a) const { + return player::WieldedItemIs (a, "it-hammer"); + } + + bool is_movable() const { return true; } + + void actor_hit(const StoneContact &sc) { + if (may_be_broken_by(sc.actor)) + break_me(); +// else +// maybe_push_stone (sc); + } + void on_impulse(const Impulse& impulse) { + move_stone(impulse.dir); + } + + }; +} + +//---------------------------------------- +// Break_acwhite +//---------------------------------------- + +/** \page st-break_acwhite Breakable Stone + +This stone can be destroyed by actor (whiteball) having a +hammer and by laser, dynamite, bombs and bombstones. + +\subsection break_acwhite Example +\verbatim +set_stone("st-break_acwhite", 10,10) +\endverbatim + +\image html st-break_acwhite.png +*/ +namespace +{ + class Break_acwhite : public BreakableStone { + CLONEOBJ(Break_acwhite); + public: + Break_acwhite() : BreakableStone("st-break_acwhite") {} + private: + bool may_be_broken_by(Actor *a) const { + return a->get_attrib("whiteball") && + player::WieldedItemIs (a, "it-hammer"); + } + }; +} + +//---------------------------------------- +// Break_acblack +//---------------------------------------- + +/** \page st-break_acblack Breakable Stone + +This stone can be destroyed by actor (blackball) having a +hammer. + +\subsection break_acblack Example +\verbatim +set_stone("st-break_acblack", 10,10) +\endverbatim + +\image html st-break_acblack.png +*/ +namespace +{ + class Break_acblack : public BreakableStone { + CLONEOBJ(Break_acblack); + public: + Break_acblack() : BreakableStone("st-break_acblack") {} + private: + bool may_be_broken_by(Actor *a) const { + return a->get_attrib("blackball") && + player::WieldedItemIs (a, "it-hammer"); + } + }; +} + + +/* -------------------- BrickMagic -------------------- */ + +/** \page st-brick_magic Magic Brick Stone + +This stone does initially look like a "st-brick". If touched by the +actor, having a magic wand, it turns into a "st-glass" stone and +allows lasers to go through it. + +\subsection brick_magicke Example +\verbatim +set_stone("st-brick_magic", 10,10) +\endverbatim + +\image html st-brick.png +*/ +namespace +{ + class BrickMagic : public Stone { + CLONEOBJ(BrickMagic); + const char *collision_sound() {return "stone";} + public: + BrickMagic() : Stone("st-brick_magic") {} + private: + void actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-magicwand")) { + sound_event ("stonepaint"); + ReplaceStone (get_pos(), MakeStone("st-glass")); + } + } + }; +} + + +/* -------------------- Stonebrush -------------------- */ + +/** \page st-stonebrush Brush Stone + +This stone is initially invisible. If touched by an actor +having a brush it turns into a "st-rock4". + +\subsection stonebrushe Example +\verbatim +set_stone("st-stonebrush", 10,10) +\endverbatim + +\image html st-rock4.png +*/ +namespace +{ + class Stonebrush : public Stone { + CLONEOBJ(Stonebrush); + const char *collision_sound() {return "stone";} + public: + Stonebrush() : Stone("st-stonebrush"), state(INVISIBLE) {} + private: + enum State { INVISIBLE, BRUSH } state; + + void actor_hit(const StoneContact &sc) { + if( state == INVISIBLE) { + if (player::WieldedItemIs (sc.actor, "it-brush")) { + sound_event ("stonepaint"); + state = BRUSH; + if (server::GameCompatibility == GAMET_PEROXYD) { + set_model("st-likeoxydc-open"); + } + else { + set_model("st-rock4"); + } + } + } + } + }; +} + +//---------------------------------------- +// Break_invisible +//---------------------------------------- + +/** \page st-break_invisible Brush Stone + +This stone is initially invisible. If touched by an actor having a +brush it turns into a "st_stone_break". This stone can be destroyed +by an actor having a hammer. + +\subsection break_invisible Example +\verbatim +set_stone("st-break_invisible", 10,10) +\endverbatim + +\image html st-stone_break.png +*/ +namespace +{ + class Break_invisible : public Stone { + CLONEOBJ(Break_invisible); + const char *collision_sound() {return "stone";} + public: + Break_invisible() : Stone("st-break_invisible"), state(INVISIBLE) {} + private: + enum State { INVISIBLE, BRUSH, DESTROY }; + State state; + void actor_hit(const StoneContact &sc) { + if (state == INVISIBLE) { + if (player::WieldedItemIs (sc.actor, "it-brush")) { + sound_event ("stonepaint"); + state = BRUSH; + set_model("st-stone_break"); + } + } + else if (state == BRUSH) { + if (player::WieldedItemIs (sc.actor, "it-hammer")) { + sound_event ("stonedestroy"); + state = DESTROY; + set_anim("st-stone_break-anim"); + } + } + } + void animcb() { + if (state == DESTROY) + KillStone(get_pos()); + } + }; +} + +//---------------------------------------- +// Invisible Magic +//---------------------------------------- + +/** \page st-invisible_magic Magic Invisible Stone + +This stone is initially invisible, and laserlight can pass through. +If touched by an actor having a magic wand, it will mutate into a +"st-greenbrown" and laserlight is blocked. + +\subsection invisible_magice Example +\verbatim +set_stone("st-invisible_magic", 10,10) +\endverbatim + +\image html st-greenbrown.png +*/ +namespace +{ + class InvisibleMagic : public Stone { + CLONEOBJ(InvisibleMagic); + const char *collision_sound() {return "cloth";} + public: + InvisibleMagic() : Stone("st-invisible_magic"), state(INVISIBLE) {} + private: + enum State { INVISIBLE, STONE } state; + + void actor_hit(const StoneContact &sc) { + if (state == INVISIBLE) { + if (player::WieldedItemIs (sc.actor, "it-magicwand")) { + sound_event ("stonepaint"); + state = STONE; + set_model("st-greenbrown"); + lasers::MaybeRecalcLight(get_pos()); + } + } + } + bool is_transparent (Direction) const { return state==INVISIBLE; } + }; +} + + +/* -------------------- Wooden stone -------------------- */ + +/** \page st-wood Wooden Stone + +This stone is movable. If moved into abyss, water or swamp it builds +a wooden plank. + +\subsection woode Example +\verbatim +set_stone("st-wood", 10,10) +\endverbatim + +Note: There are two flavors of st-wood which may be specified +by using st-wood1 or st-wood2, and a two related kinds: st-flrock, +which creates the unburnable fl-rock and denies fire under it, and +the burnable st-hay, which leaves the burnable but stable fl-hay +behind. + +\image html st-wood.png +*/ +namespace +{ + class WoodenStone : public Stone { + CLONEOBJ(WoodenStone); + public: + WoodenStone(const char *kind, const char *floorkind_, bool blockfire_ = false) : + Stone(kind), floorkind(floorkind_), blockfire(blockfire_) {} + + private: + const char *floorkind; + bool blockfire; + + void maybe_fall_or_stopfire() { + GridPos p = get_pos(); + if (world::IsLevelBorder(p)) + return; + if (Floor *fl = GetFloor(p)) { + const string &k = fl->get_kind(); + if(blockfire) + SendMessage(fl, "stopfire"); + if (k == "fl-abyss" || k == "fl-water" || k == "fl-swamp") { + SetFloor(p, MakeFloor(floorkind)); + KillStone(p); + } + } + } + + virtual Value message (const string &msg, const Value &) { + if (msg == "fire" && !blockfire) { + KillStone(get_pos()); + return Value(1.0); // allow fire to spread + } else if (msg == "heat" && blockfire) { + return Value(1.0); // block fire + } else if (msg == "fall") + maybe_fall_or_stopfire(); + return Value(); + } + + // in oxyd1 only fall when moving + void on_move() { + Stone::on_move(); + if (server::GameCompatibility == GAMET_OXYD1) + maybe_fall_or_stopfire(); + } + + // other oxyds versions: fall everytime the floor changes + void on_floor_change() { + if (server::GameCompatibility != GAMET_OXYD1) + maybe_fall_or_stopfire(); + } + bool is_movable () const { return true; } + }; + + /*! When st-wood is created it randomly becomes st-wood1 or + st-wood2. */ + class RandomWoodenStone : public Stone { + public: + RandomWoodenStone() : Stone("st-wood") {} + private: + Stone *clone() { + if(IntegerRand(0,1) == 0) + return new WoodenStone("st-wood1", "fl-stwood1"); + else + return new WoodenStone("st-wood2", "fl-stwood2"); + } + void dispose() {delete this;} + }; +} + +//---------------------------------------- +// Growing stones used by it-seed +//---------------------------------------- +namespace +{ + class WoodenStone_Growing : public Stone { + CLONEOBJ(WoodenStone_Growing); + public: + WoodenStone_Growing() : Stone("st-wood-growing") {} + private: + void init_model() { set_anim("st-wood-growing"); } + void animcb() { + Stone *st = world::MakeStone ("st-wood"); + world::ReplaceStone (get_pos(), st); + SendMessage (st, "fall"); // instantly builds a bridge on fl-swamp etc + } + void actor_contact(Actor *a) {SendMessage(a, "shatter");} + void actor_inside(Actor *a) {SendMessage(a, "shatter");} + void actor_hit(const StoneContact &sc) {SendMessage(sc.actor, "shatter");} + }; + + class GreenbrownStone_Growing : public Stone { + CLONEOBJ(GreenbrownStone_Growing); + public: + GreenbrownStone_Growing() : Stone("st-greenbrown-growing") {} + private: + void init_model() { set_anim("st-greenbrown-growing"); } + void animcb() { + Stone *st = world::MakeStone("st-greenbrown"); + world::ReplaceStone(get_pos(), st); + } + void actor_contact(Actor *a) {SendMessage(a, "shatter");} + void actor_inside(Actor *a) {SendMessage(a, "shatter");} + void actor_hit(const StoneContact &sc) {SendMessage(sc.actor, "shatter");} + }; + + class VolcanoStone_Growing : public Stone { + CLONEOBJ(VolcanoStone_Growing); + public: + VolcanoStone_Growing() : Stone("st-volcano-growing") {} + private: + void init_model() { set_anim("st-volcano-growing"); } + void animcb() { + Stone *st = world::MakeStone("st-volcano_active"); + world::ReplaceStone(get_pos(), st); + } + void actor_contact(Actor *a) {SendMessage(a, "shatter");} + void actor_inside(Actor *a) {SendMessage(a, "shatter");} + void actor_hit(const StoneContact &sc) {SendMessage(sc.actor, "shatter");} + }; +} + + +/* -------------------- Scissors stone -------------------- */ + +/** \page st-scissors Scissors stone + +This stone cuts \c all rubber bands attached to an actor that touches +it. + +\image html st-scissors +*/ +namespace +{ + class ScissorsStone : public Stone { + CLONEOBJ(ScissorsStone); + DECL_TRAITS; + void actor_hit(const StoneContact &sc) { + if (world::KillRubberBands (sc.actor)) + world::PerformAction(this, false); + sound_event("scissors"); + set_anim("st-scissors-snip"); + } + void animcb() { + set_model("st-scissors"); + } + public: + ScissorsStone() + {} + }; + DEF_TRAITS(ScissorsStone, "st-scissors", st_scissors); +} + + +/* -------------------- Rubberband stone -------------------- */ + +/** \page st-rubberband Rubberband stone + +If hit by a marble, this stone first removes existing connections with +other rubberband stones and then attaches a new elastic between the +marble and itself. Nothing happens if the marble was already attached +to this particular stone. + +This stone can be moved if hit with a magic wand. + +\subsection rubberbanda Attributes + +- \c length The natural length of the rubberband (default: 1) +- \c strength The strength of the rubberband (default: 10) + +\image html st-rubberband.png +*/ +namespace +{ + class RubberBandStone : public Stone { + CLONEOBJ(RubberBandStone); + DECL_TRAITS; + + void actor_hit(const StoneContact &sc) { + double strength = 10.0; + double length = 1.0; + double minlength = 0.0; + double_attrib ("strength", &strength); + double_attrib ("length", &length); + double_attrib ("minlength", &minlength); + + world::RubberBandData rbd; + rbd.strength = strength; + rbd.length = length; + rbd.minlength = minlength; + + // The mode attribute "scissor" defines, if when touching an st-rubberband, + // other rubberbands to the actor will be cut of or not, true means they will. true is default. + enigma::Value const *scissorValue = get_attrib("scissor"); + bool isScissor = (scissorValue == NULL)? true : to_bool(*scissorValue); + + if (!world::HasRubberBand (sc.actor, this)) { + sound_event ("rubberband"); + if (isScissor) { + world::KillRubberBand (sc.actor, (Stone*)0); + } + world::AddRubberBand (sc.actor, this, rbd); + } +// if (player::wielded_item_is (sc.actor, "it-magicwand")) + maybe_push_stone (sc); + } + + void on_impulse (const Impulse& impulse) { + Actor *a = dynamic_cast (impulse.sender); + if (a && player::WieldedItemIs (a, "it-magicwand")) + move_stone(impulse.dir); + } + + + + public: + RubberBandStone () { + set_attrib("length", 1.0); + set_attrib("strength", 10.0); + } + }; + DEF_TRAITS(RubberBandStone, "st-rubberband", st_rubberband); +} + + +/* -------------------- Timer stone -------------------- */ + +// Attributes: +// +// :interval seconds between two "ticks" +// :loop +// :action,target as usual +// :invisible + +/** \page st-timer Timer Stone + +This stone can be used to trigger periodic events or to trigger one +single event after a certain amount of time. + +\subsection timera Attributes + +- \b on: 1 if the timer is running +- \b interval: number of seconds before \b action is performed +- \b loop: if 1, restart the timer after performing \b action +- \b action, \b target: as usual +- \b invisible : if 1, stone is invisible + +\subsection timerm Messages + +- \b on, \b off, \b onoff: as usual + +\subsection timere Example + +\verbatim +-- activate a laser after 5 seconds +set_stone("st-laser", 10,11, {name="laser"}) +set_stone("st-timer", 10,10, + {loop=0, action="onoff", target="laser", interval=5}) +\endverbatim +*/ +namespace +{ + class TimerStone : public OnOffStone, public TimeHandler + { + CLONEOBJ(TimerStone); + public: + TimerStone() : OnOffStone("st-timer"), m_signalvalue(1) { + set_attrib("interval", 1.0); + set_attrib("loop", 1.0); + set_attrib("on", 1.0); + set_attrib("invisible", 0.0); + + // set_on(true); DOESN'T WORK! calls init_model() + } + + virtual ~TimerStone(); + private: + int m_signalvalue; + + double get_interval() const { + double interval = 100; + double_attrib("interval", &interval); + return interval; + } + void init_model() { + if (int_attrib("invisible")) { + set_model("invisible"); + } + else { + set_model(is_on() ? "st-timer" : "st-timeroff"); + } + } + + void on_creation (GridPos p) { + set_alarm(); + Stone::on_creation (p); + } + + void set_alarm() { + if (is_on()) + GameTimer.set_alarm(this, get_interval(), true); + } + + void alarm() { + if (is_on()) { +// sound::PlaySound("st-timer"); + PerformAction(this, m_signalvalue != 0); + m_signalvalue = 1-m_signalvalue; + } + } + + void notify_onoff (bool newon) { + if (newon) + set_alarm(); + else + GameTimer.remove_alarm(this); + } + }; + + TimerStone::~TimerStone() { + GameTimer.remove_alarm(this); + } +} + + +/* -------------------- FartStone -------------------- */ + +/** \page st-fart Fart Stone + +The fart stone has the unpleasant habit of "blowing off" when +triggered (by actor contact or signal) and will close all oxyd stones. + +\subsection fartm Messages + +- \b trigger: as usual + +*/ + +namespace +{ + class FartStone : public Stone { + CLONEOBJ(FartStone); + DECL_TRAITS; + + enum State { IDLE, FARTING, BREAKING }; + State state; + bool rememberBreaking; // set true if an explosion or break-message + // or such occured while farting + + void change_state(State newstate); + void animcb(); + void actor_hit(const StoneContact &sc); + virtual Value message(const string &m, const Value &val); + + void on_laserhit(Direction) { + change_state(BREAKING); + } + public: + FartStone() : state(IDLE), rememberBreaking(false) + {} + }; + DEF_TRAITS(FartStone, "st-fart", st_fart); +} + +void FartStone::change_state(State newstate) +{ + if (state == newstate) + return; + + switch (newstate) { + case IDLE: + state = IDLE; + init_model(); + if (rememberBreaking) + change_state(BREAKING); + break; + case FARTING: + case BREAKING: + if (state == IDLE) { + Object *ox = world::GetObjectTemplate("st-oxyd"); + SendMessage(ox, "closeall"); + sound_event("fart"); + if (newstate == BREAKING) { + sound_event ("stonedestroy"); + set_anim ("st-fartbreak-anim"); + } + else + set_anim("st-farting"); + state = newstate; + } else if (state == FARTING && newstate == BREAKING) + rememberBreaking = true; + break; + } +} + +void FartStone::animcb() +{ + if (state == FARTING) + change_state(IDLE); + else if (state == BREAKING) + KillStone(get_pos()); +} + +void FartStone::actor_hit(const StoneContact &sc) +{ + if (player::WieldedItemIs (sc.actor, "it-hammer")) + change_state(BREAKING); + else + change_state(FARTING); +} +Value FartStone::message (const string &m, const Value &val) +{ + if (m == "signal" && to_int(val) != 0) + change_state(FARTING); + else if (m=="trigger") + change_state(FARTING); + else if (m == "ignite" || m == "expl") + change_state(BREAKING); + return Value(); +} + + + + +/* -------------------- Thief -------------------- */ +namespace +{ + /*! Steals one item from the player's inventory when hit. */ + class ThiefStone : public Stone { + CLONEOBJ(ThiefStone); + DECL_TRAITS; + + enum State { IDLE, EMERGING, RETREATING, CAPTURED } state; + Actor *m_affected_actor; + Item * bag; + public: + ThiefStone(); + virtual ~ThiefStone(); + private: + void steal_from_player(); + + void actor_hit(const StoneContact &sc); + // even a slight touch should steal from the actor: + void actor_touch(const StoneContact &sc) { actor_hit(sc); } + void animcb(); + virtual Value message(const string &msg, const Value &v); + + const char *collision_sound() { return "cloth"; } + int affected_player; + }; + DEF_TRAITS(ThiefStone, "st-thief", st_thief); +} + +ThiefStone::ThiefStone() +: state(IDLE), m_affected_actor (0), affected_player (-1), bag(NULL) {} + +ThiefStone::~ThiefStone() { + if (bag != NULL) + delete bag; +} + +void ThiefStone::actor_hit(const StoneContact &sc) { + ActorID id = get_id(sc.actor); + if (state == IDLE) { + set_anim("st-thief-emerge"); + state = EMERGING; + m_affected_actor = sc.actor; + affected_player = -1; + m_affected_actor->int_attrib("player", &affected_player); + } +} + +void ThiefStone::animcb() { + switch (state) { + case EMERGING: + steal_from_player(); + state = RETREATING; + set_anim("st-thief-retreat"); + break; + case RETREATING: + state = IDLE; + init_model(); + break; + case CAPTURED: + KillStone(get_pos()); + break; + default: + ASSERT(0, XLevelRuntime, "ThiefStone: animcb called with inconsistent state"); + } +} + +void ThiefStone::steal_from_player() +{ + // the actor that hit the thief may no longer exist! + if (m_affected_actor && affected_player >= 0 && + player::HasActor(affected_player, m_affected_actor) && + !m_affected_actor->has_shield()) { + enigma::Inventory *inv = player::GetInventory(m_affected_actor); + if (inv && inv->size() > 0) { + if (bag == NULL) + bag = world::MakeItem(it_bag); + int i = IntegerRand (0, int (inv->size()-1)); + dynamic_cast(bag)->add_item(inv->yield_item(i)); + player::RedrawInventory (inv); + sound_event("thief"); + } + } +} + +Value ThiefStone::message(const string &msg, const Value &v) { + if(msg == "capture" && state == IDLE) { + state = CAPTURED; + Item * it = world::GetItem(get_pos()); + + // add items on grid pos that can be picked up to our bag + if (it != NULL && !(it->get_traits().flags & itf_static) && bag != NULL) { + dynamic_cast(bag)->add_item(world::YieldItem(get_pos())); + } + // drop bag if pos is not occupied by a static item + if (world::GetItem(get_pos()) == NULL) + world::SetItem(get_pos(), bag); + bag = NULL; + set_anim(string(get_kind()) + "-captured"); + return Value(1); + } else + return Stone::message(msg, v); +} + +// ------------------------- +// ActorImpulseBase +// ------------------------- + +namespace +{ + class ActorImpulseBase : public Stone { + public: + ActorImpulseBase(const char *kind) : Stone(kind), state(IDLE) { +// set_attrib("force", Value()); + } + + protected: + virtual void actor_hit (const StoneContact &sc) { + if (state == IDLE) { + // actor_hit is called before reflect, but the force added below + // is applied to actor after the reflection. + + double forcefac = server::BumperForce; + double_attrib("force", &forcefac); + + V2 vec = normalize(sc.actor->get_pos() - get_pos().center()); + sc.actor->add_force (distortedVelocity(vec, forcefac)); + + sound_event("bumper"); + set_anim("st-actorimpulse-anim"); + state = PULSING; + } + } + + private: + virtual const char *collision_sound() { + return ""; + } + + void animcb() { + if (state == PULSING) { + state = IDLE; + init_model(); + } + } + + // Variables + enum State { IDLE, PULSING, BROKEN }; + State state; + }; + + + class ActorImpulseStone : public ActorImpulseBase { + CLONEOBJ(ActorImpulseStone); + + int m_signalidx; + + virtual Value message (const string &msg, const Value &) { + if (msg == "signal") { + world::EmitSignalByIndex (this, m_signalidx, 0); + m_signalidx += 1; + if (!world::EmitSignalByIndex (this, m_signalidx, 1)) { + m_signalidx = 0; + world::EmitSignalByIndex (this, m_signalidx, 1); + } + } else if (msg == "init") { + world::EmitSignalByIndex (this, m_signalidx, 1); + } + return Value(); + } + + public: + ActorImpulseStone() : ActorImpulseBase("st-actorimpulse"), + m_signalidx(0) + {} + }; + + + class ActorImpulseStoneInvisible : public ActorImpulseBase { + CLONEOBJ(ActorImpulseStoneInvisible); + public: + ActorImpulseStoneInvisible() : ActorImpulseBase("st-actorimpulse_invisible") {} + + void actor_hit(const StoneContact& sc) { + if (player::WieldedItemIs (sc.actor, "it-brush")) { + Stone *st = MakeStone("st-actorimpulse"); + SetStone(get_pos(), st); + st->actor_hit(sc); + } + else + ActorImpulseBase::actor_hit(sc); + } + }; +} + +//---------------------------------------- +// FakeOxydStone +//---------------------------------------- + +/** \page st-fakeoxyd Fake Oxyd Stone + +These stones look like real Oxyd stones, but they only blink a little +when touched and do not open or have other special abilities. + +\image html st-fakeoxyd-blink_0001.png +*/ +namespace +{ + class FakeOxydStone : public Stone { + CLONEOBJ(FakeOxydStone); + public: + FakeOxydStone() : Stone("st-fakeoxyd"), state(IDLE) { + set_attrib("blinking", 0.0); + } + private: + enum State { IDLE, BLINKING } state; + void actor_hit(const StoneContact &/*sc*/) { + if (state == IDLE) { + set_anim("st-fakeoxyd-blink"); + sound_event ("fakeoxyd"); + state = BLINKING; + set_attrib("blinking", 1.0); + } + } + const char *collision_sound() { + return "metal"; + } + void animcb() { + set_model("st-fakeoxyd"); + state = IDLE; + set_attrib("blinking", 0.0); + } + }; +} + + +/* -------------------- Black Stones -------------------- */ +namespace +{ + class BlackWhiteStone : public Stone { + CLONEOBJ(BlackWhiteStone); + DECL_TRAITS_ARRAY(8, m_type); + int m_type; + + BlackWhiteStone(int type) : m_type(type) + {} + + StoneResponse collision_response(const StoneContact &sc) { + if (m_type < 4) { + return (sc.actor->get_attrib("blackball")) ? + STONE_PASS : STONE_REBOUND; + } + else { + return (sc.actor->get_attrib("whiteball")) ? + STONE_PASS : STONE_REBOUND; + } + } + + virtual Value on_message (const Message &m) { + if (m.message == "signal" || m.message == "trigger") { + // toggle between black and white stone + m_type = (m_type + 4) % 8; + init_model(); + } + return Value(); + } + + bool is_floating() const { return true; } + bool is_transparent (Direction) const { return true; } + + public: + static void setup() { + for (int i=0; i<8; ++i) + RegisterStone (new BlackWhiteStone(i)); + } + }; + + StoneTraits BlackWhiteStone::traits[8] = { + {"st-black1", st_black1, stf_transparent}, + {"st-black2", st_black2, stf_transparent}, + {"st-black3", st_black3, stf_transparent}, + {"st-black4", st_black4, stf_transparent}, + {"st-white1", st_white1, stf_transparent}, + {"st-white2", st_white2, stf_transparent}, + {"st-white3", st_white3, stf_transparent}, + {"st-white4", st_white4, stf_transparent}, + }; + +} + + +/* -------------------- YinYang stones -------------------- */ +namespace +{ + class YinYangStone : public Stone { + public: + YinYangStone(const char *kind) : Stone(kind) + {} + + protected: + void turn_white(const char *stonename = "st-white1") { + sound_event("yinyang"); + ReplaceStone (get_pos(), MakeStone(stonename)); + } + void turn_black(const char *stonename = "st-black1") { + sound_event("yinyang"); + ReplaceStone (get_pos(), MakeStone(stonename)); + } + }; + + class YinYangStone1 : public YinYangStone { + CLONEOBJ(YinYangStone1); + public: + YinYangStone1() : YinYangStone("st-yinyang1") {} + + private: + void actor_hit(const StoneContact &sc) { + if (sc.actor->get_attrib("blackball")) turn_white(); + else if (sc.actor->get_attrib("whiteball")) turn_black(); + } + }; + + class YinYangStone2 : public YinYangStone { + CLONEOBJ(YinYangStone2); + public: + YinYangStone2() : YinYangStone("st-yinyang2") {} + private: + void actor_hit(const StoneContact &sc) { + if (sc.actor->get_attrib("blackball")) turn_black(); + else if (sc.actor->get_attrib("whiteball")) turn_white(); + } + }; + + + /*! A Per.Oxyd compatible YinYang stone that must be activated + with a magic wand or a brush. */ + class YinYangStone3 : public YinYangStone { + CLONEOBJ(YinYangStone3); + public: + YinYangStone3() : YinYangStone("st-yinyang3") {} + private: + void actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs (sc.actor, "it-magicwand") || + player::WieldedItemIs (sc.actor, "it-brush")) + { + if (sc.actor->get_attrib("blackball")) + turn_white("st-white4"); + else if (sc.actor->get_attrib("whiteball")) + turn_black("st-black4"); + } + } + }; +} + + +/* -------------------- BombStone -------------------- */ + +namespace +{ + /*! This stone add a bomb to the player's inventory when touched. */ + class BombStone : public Stone { + CLONEOBJ(BombStone); + const char *collision_sound() {return "stone";} + public: + BombStone(const char* kind_, const char* itemkind_) : + Stone(kind_), state(IDLE), itemkind(itemkind_) {} + private: + enum State { IDLE, BREAK }; + State state; + const char* itemkind; + void actor_hit (const StoneContact &sc); + void change_state (State newstate); + void animcb(); + virtual Value message (const string &msg, const Value &); + }; +} + +void BombStone::change_state (State newstate) +{ + if (state == IDLE && newstate==BREAK) { + string model = get_kind(); + state = newstate; + sound_event ("stonedestroy"); + set_anim(model + "-anim"); + } +} + +void BombStone::animcb() +{ + ASSERT(state == BREAK, XLevelRuntime, "BombStone: animcb called with inconsistent state"); + GridPos p = get_pos(); + SendExplosionEffect(p, EXPLOSION_BOMBSTONE); + KillStone(p); + if(Item *it = GetItem(get_pos())) { + SendMessage(it, "ignite"); + } else + SetItem(p, it_explosion1); +} + +Value BombStone::message(const string &msg, const Value &) +{ + if (msg =="expl" || msg =="bombstone") + change_state(BREAK); + return Value(); +} + +void BombStone::actor_hit(const StoneContact &sc) +{ + if (enigma::Inventory *inv = player::GetInventory(sc.actor)) { + if (!inv->is_full()) { + Item *it = MakeItem(itemkind); + inv->add_item(it); + player::RedrawInventory (inv); + } + } +} + + +/* -------------------- MagicStone -------------------- */ +namespace +{ + class MagicStone : public Stone { + CLONEOBJ(MagicStone); + DECL_TRAITS; + void actor_hit(const StoneContact &sc) { + if (sc.actor->get_attrib("player") && + sc.actor->get_vel() * sc.normal < -4) + { + KillStone(get_pos()); + client::Msg_ShowText ("We don't sell books..", false, 2.0); + } + } + public: + MagicStone() + {} + }; + DEF_TRAITS(MagicStone, "st-magic", st_magic); +} + + +/* -------------------- DeathStone -------------------- */ + +/** \page st-death Death's Head Stone + +Simply kills all actors that touch it (except for actors that are +immune to these stones). + +\image html st-death.png +*/ +namespace +{ + class DeathStone : public Stone { + CLONEOBJ(DeathStone); + DECL_TRAITS; + bool active; + + void actor_hit(const StoneContact &sc) { + SendMessage(sc.actor, "shatter"); + if (!active) { + active=true; + set_anim("st-death-anim"); + } + } + + // even a slight touch should shatter the actor: + void actor_touch(const StoneContact &sc) { actor_hit(sc); } + + protected: + void animcb() { set_model("st-death"); active=false; } + public: + DeathStone() : active(false) + {} + }; + DEF_TRAITS(DeathStone, "st-death", st_death); +} + + +/* -------------------- Invisible DeathStone -------------------- */ + +/** \page st-death_invisible Death's Head Stone invivible + +Simply kills all actors that touch it (except for actors that are +immune to these stones). This variant is invisible. + +\image html st-death.png +*/ +namespace +{ + class DeathStoneInvisible : public DeathStone { + CLONEOBJ(DeathStoneInvisible); + DECL_TRAITS; + + bool visible; // seen through glasses + + void set_visible_model() { + set_model(visible ? "st-death" : "st-death_invisible"); + } + + void animcb() { + DeathStone::animcb(); + set_visible_model(); + } + + virtual Value message(const string& msg, const Value &val) { + if (msg == "glasses") { + if (to_int(val)) { + if (!visible) { + visible = true; + set_visible_model(); + } + } + else { + if (visible) { + visible = false; + set_visible_model(); + } + } + } + return Value(); + } + public: + DeathStoneInvisible() : visible(false) {} + }; + DEF_TRAITS(DeathStoneInvisible, "st-death_invisible", st_death_invisible); +} + + +/* -------------------- Brake stone -------------------- */ + +/** \page st-brake Brake + +Blocks bolder stones and other movable stones. Can be picked up. + +\image html st-brake.png +*/ + +namespace +{ + class BrakeStone : public Stone { + CLONEOBJ(BrakeStone); + public: + BrakeStone() : Stone("st-brake") {} + + void on_creation (GridPos p) { + Stone::on_creation(p); + + Item *it = GetItem(p); + if (it && it->is_kind("it-blocker")) { + KillItem(p); +// sound_event ("explosion1"); + } + } + + StoneResponse collision_response(const StoneContact &/*sc*/) { + return STONE_PASS; + } + + void actor_inside(Actor *a) { + const double BRAKE_RADIUS = 0.3; + GridPos p = get_pos(); + double dist = length(a->get_pos() - p.center()); + + if (dist < BRAKE_RADIUS) { + player::PickupStoneAsItem(a, p); + } + } + + void explode() { + GridPos p = get_pos(); + KillStone(p); + SetItem(p, it_explosion1); + } + + void on_laserhit(Direction) { + explode(); + } + + virtual Value message(const string &msg, const Value &) { + if (msg == "expl") { + explode(); + } + return Value(); + } + + bool is_sticky(const Actor *) const + { return false; } + }; +} + + +/* -------------------- Disco stones -------------------- */ +namespace +{ + class DiscoStone : public Stone { + CLONEOBJ(DiscoStone); + public: + DiscoStone(const char *kind) : Stone(kind) {} + + StoneResponse collision_response(const StoneContact &) { + return STONE_PASS; + } + + virtual void lighten() {} + virtual void darken() {} + protected: + static void visit_lighten(GridPos p) { + if (DiscoStone *st = dynamic_cast(GetStone(p))) + st->lighten(); + } + static void visit_darken(GridPos p) { + if (DiscoStone *st = dynamic_cast(GetStone(p))) + st->darken(); + } + + private: + bool is_floating() const { return true; } + + virtual Value message(const string &msg, const Value &val) { + if (msg == "signal") { + int ival = to_int (val); + if (ival > 0) + lighten(); + else + darken(); + } + else if (msg == "lighten") + lighten(); + else if (msg == "darken") + darken(); + return Value(); + } + }; + class DiscoLight : public DiscoStone { + CLONEOBJ(DiscoLight); + virtual void darken() { + GridPos p = get_pos(); + SetStone (p, MakeStone("st-disco-medium")); + visit_darken (move(p, NORTH)); + visit_darken (move(p, EAST)); + visit_darken (move(p, SOUTH)); + visit_darken (move(p, WEST)); + } + public: + DiscoLight() : DiscoStone("st-disco-light") {} + }; + class DiscoMedium : public DiscoStone { + CLONEOBJ(DiscoMedium); + public: + DiscoMedium() : DiscoStone("st-disco-medium") {} + virtual void lighten() { + GridPos p = get_pos(); + SetStone (p, MakeStone("st-disco-light")); + visit_lighten (move(p, NORTH)); + visit_lighten (move(p, EAST)); + visit_lighten (move(p, SOUTH)); + visit_lighten (move(p, WEST)); + } + virtual void darken() { + GridPos p = get_pos(); + SetStone (p, MakeStone("st-disco-dark")); + visit_darken (move(p, NORTH)); + visit_darken (move(p, EAST)); + visit_darken (move(p, SOUTH)); + visit_darken (move(p, WEST)); + } + }; + + class DiscoDark : public DiscoStone { + CLONEOBJ(DiscoDark); + virtual void lighten() { + GridPos p = get_pos(); + SetStone (p, MakeStone("st-disco-medium")); + visit_lighten (move(p, NORTH)); + visit_lighten (move(p, EAST)); + visit_lighten (move(p, SOUTH)); + visit_lighten (move(p, WEST)); + } + virtual void darken() {} + public: + DiscoDark() : DiscoStone("st-disco-dark") {} + }; +} + + +/* -------------------- Knight stone -------------------- */ +namespace +{ + class Knight : public Stone { + CLONEOBJ(Knight); + DECL_TRAITS; + + int subtype; + enum {MIN_SUBTYPE=1, MAX_SUBTYPE=5}; + + StoneResponse collision_response(const StoneContact &) { + return (subtype == MAX_SUBTYPE) ? STONE_PASS : STONE_REBOUND; + } + void actor_hit(const StoneContact &sc) + { + if (subtype != MAX_SUBTYPE) { + if (player::WieldedItemIs (sc.actor, "it-sword")) { + subtype += 1; + if (subtype == MAX_SUBTYPE) { + client::Msg_ShowText ("All right, we'll call it a draw", false, 4.0); + } + init_model(); + } else { + SendMessage(sc.actor, "shatter"); + } + } + } + void init_model() { + set_model(ecl::strf("st-knight%d", subtype)); + } + bool is_floating() const { return subtype == MAX_SUBTYPE; } + public: + Knight() : subtype (MIN_SUBTYPE) {} + }; + DEF_TRAITS(Knight, "st-knight", st_knight); +} + + +/* -------------------- Polarization Switch stone -------------------- */ +namespace +{ + class PolarSwitchStone : public OnOffStone { + CLONEOBJ(PolarSwitchStone); + DECL_TRAITS; + public: + PolarSwitchStone() : OnOffStone("st-polarswitch") {} + private: + void actor_hit(const StoneContact &sc) { set_on(!is_on()); } + void init_model() { set_model(is_on() ? "st-glass1" : "st-glass2"); } + bool is_transparent(Direction) const { return this->is_on(); } + void notify_onoff(bool) { lasers::MaybeRecalcLight(this->get_pos()); } + + StoneResponse collision_response(const StoneContact &sc) { + if (sc.actor->is_invisible()) + return STONE_PASS; + return Stone::collision_response(sc); + } + + bool is_sticky (const Actor *actor) const { return !actor->is_invisible(); } + }; + DEF_TRAITS(PolarSwitchStone, "st-polarswitch", st_polarswitch); +} + + +/* -------------------- Fire breakable stones -------------------- */ + +/* These stones mimic the behaviour of the plain-looking stones in + Oxyd. */ +namespace +{ + class Stone_firebreak : public Stone { + CLONEOBJ(Stone_firebreak); + + const char *collision_sound() {return "stone";} + + void break_me() { + sound_event("stonedestroy"); + ReplaceStone(get_pos(), MakeStone("st-plain_breaking")); + } + + virtual Value message (const string &msg, const Value &) { + if (msg =="heat" || msg == "fire") { + break_me(); + return Value(1.0); + } + return Value(); + } + + void actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs(sc.actor, "it-brush")) { + sound_event("stonepaint"); + ReplaceStone(get_pos(), MakeStone("st-plain")); + } else + Stone::actor_hit(sc); + } + + public: + Stone_firebreak() : Stone("st-firebreak") { } + }; + + class Stone_movefirebreak : public Stone { + CLONEOBJ(Stone_movefirebreak); + + void break_me() { + sound_event("stonedestroy"); + ReplaceStone(get_pos(), MakeStone("st-plain_breaking")); + } + + virtual Value message (const string &msg, const Value &) { + if (msg =="fire") + break_me(); + return Value(); + } + + void actor_hit(const StoneContact &sc) { + if (player::WieldedItemIs(sc.actor, "it-brush")) { + sound_event("stonepaint"); + ReplaceStone(get_pos(), MakeStone("st-plain_move")); + } else + Stone::actor_hit(sc); + } + + void on_move() { + GridPos p = get_pos(); + if (Floor *fl = GetFloor (p)) { + if (fl->is_kind("fl-abyss")) { + ReplaceStone (p, MakeStone("st-plain_falling")); + } + else if (fl->is_kind("fl-swamp") || fl->is_kind("fl-water")) { + sound_event ("drown"); + client::Msg_Sparkle (p.center()); + KillStone (p); + } + } + } + + bool is_movable () const { return true; } + + public: + Stone_movefirebreak() : Stone("st-firebreak_move") { } + }; +} + + +/* -------------------- Functions -------------------- */ + +void world::DefineSimpleStone(const std::string &kind, + const std::string &sound, + int hollow, int glass) +{ + Register(new SimpleStone(kind, sound, hollow != 0, glass != 0)); +} + +void world::DefineSimpleStoneMovable(const std::string &kind, + const std::string &sound, + int glass) +{ + Register(new SimpleStoneMovable(kind, sound, glass != 0)); +} + +void stones::Init_simple() +{ + Register(new ActorImpulseStone); + Register(new ActorImpulseStoneInvisible); + + BlackWhiteStone::setup(); + + Register(new BlockStone); + Register(new BombStone("st-bombs", "it-blackbomb")); + //Register(new BombStone("st-dynamite", "it-dynamite")); + //Register(new BombStone("st-whitebombs", "it-whitebomb")); + Register(new BrakeStone); + + Register(new Break_acblack); + Register(new Break_acwhite); + Register(new Break_bolder); + Register(new Break_invisible); + + Register(new BrickMagic); + + RegisterStone (new ChameleonStone); + + RegisterStone (new DeathStone); + RegisterStone (new DeathStoneInvisible); + Register(new DiscoLight); + Register(new DiscoMedium); + Register(new DiscoDark); + Register(new DummyStone); + RegisterStone (new EasyModeStone); + Register(new FakeOxydStone); + Register(new FartStone); + Register(new Grate1); + Register(new Grate2); + Register(new Grate3); + Register(new InvisibleMagic); + Register(new Knight); + Register(new LaserBreakable); + Register(new MagicStone); + Register(new RubberBandStone); + Register(new ScissorsStone); + Register(new Stone_break("st-stone_break")); + Register(new Stone_break("st-rock3_break")); + Register(new Stone_break("st-break_gray")); + Register(new Stone_movebreak); + Register(new Stonebrush); + Register(new SwapStone); + + Register(new ThiefStone); + Register(new TimerStone); + Register(new Window); + + Register(new RandomWoodenStone); // random flavor + Register(new WoodenStone("st-wood1", "fl-stwood1")); // horizontal planks + Register(new WoodenStone("st-wood2", "fl-stwood2")); // vertical planks + Register(new WoodenStone("st-flrock", "fl-rock", true)); + Register(new WoodenStone("st-flhay", "fl-hay")); + Register(new WoodenStone_Growing); + Register(new GreenbrownStone_Growing); + Register(new VolcanoStone_Growing); + + Register(new YinYangStone1); + Register(new YinYangStone2); + Register(new YinYangStone3); + + Register(new PolarSwitchStone); + + Register(new Stone_firebreak); + Register(new Stone_movefirebreak); +} diff --git a/project/jni/application/enigma/src/util.cpp b/project/jni/application/enigma/src/util.cpp new file mode 100644 index 000000000..14d0ffe95 --- /dev/null +++ b/project/jni/application/enigma/src/util.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "ecl_util.hh" + +#include "util.hh" + +using enigma::Timer; +using enigma::TimeHandler; +using namespace std; + + +/* -------------------- Alarm -------------------- */ + +namespace +{ + /** This class helps #Timer keep track of the current list of + registered TimeHandlers. */ + class Alarm { + public: + Alarm (enigma::TimeHandler* h, double interval, bool repeatp); + void tick (double dtime); + bool expired() const; + void mark_removed(); + bool has_handler (enigma::TimeHandler *th) const; + private: + // Variables + enigma::TimeHandler *handler; + double interval; + double timeleft; + bool repeatp; + bool removed; + }; +} + + +Alarm::Alarm(TimeHandler* h, double i, bool r) +: handler(h), interval(i), timeleft(i), repeatp(r), removed(false) +{} + + +bool Alarm::expired() const +{ + return removed || timeleft <= 0; +} + + +void Alarm::mark_removed() +{ + removed = true; +} + + +void Alarm::tick (double dtime) +{ + if (!removed) { + timeleft -= dtime; + if (repeatp) { + while (timeleft <= 0) { + handler->alarm(); + timeleft += interval; + } + } else if (timeleft <= 0) { + handler->alarm(); + } + } +} + + +bool Alarm::has_handler (TimeHandler *th) const +{ + return handler == th; +} + + +/* -------------------- Timer implementation -------------------- */ + +struct Timer::Rep { + std::list handlers; + std::list alarms; +}; + + +Timer::Timer() +: self(*new Rep) +{ +} + + +Timer::~Timer() +{ + clear(); + delete &self; +} + + +void Timer::deactivate(TimeHandler* th) +{ + std::list::iterator i; + + i = find(self.handlers.begin(), self.handlers.end(), th); + if (i != self.handlers.end()) { + *i = 0; + } +} + + +void Timer::activate(TimeHandler *th) +{ + if (find(self.handlers.begin(), self.handlers.end(), th) == self.handlers.end()) + self.handlers.push_back(th); +} + + +void Timer::set_alarm(TimeHandler *th, double interval, bool repeatp) +{ + if (interval > 0) + self.alarms.push_back(Alarm(th, interval, repeatp)); +} + + +void Timer::remove_alarm(TimeHandler *th) +{ + list::iterator e = self.alarms.end(); + for (list::iterator it = self.alarms.begin(); it != e; ++it) + if (it->has_handler (th)) + it->mark_removed(); +} + + +void Timer::tick (double dtime) +{ + self.handlers.remove (0); // remove inactive entries + for_each (self.handlers.begin(), self.handlers.end(), + std::bind2nd (std::mem_fun (&TimeHandler::tick), dtime)); + + // Explicit loop to allow remove_alarm() to be called from inside alarm() + for (list::iterator i=self.alarms.begin(); i != self.alarms.end(); ) { + list::iterator n = ecl::next(i); + i->tick(dtime); + i = n; + } + self.alarms.remove_if (mem_fun_ref(&Alarm::expired)); +} + + +void Timer::clear() +{ + self.handlers.clear(); + self.alarms.clear(); +} diff --git a/project/jni/application/enigma/src/util.hh b/project/jni/application/enigma/src/util.hh new file mode 100644 index 000000000..6d6d64d0e --- /dev/null +++ b/project/jni/application/enigma/src/util.hh @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "enigma.hh" + +namespace enigma +{ + /** + * This class maintains a list of time handlers that are either + * invoked at every tick or after a specified amount of time. If + * the TimeHandler is registered using #activate, it is invoked at + * every tick, the #set_alarm method can be used to register a + * time handler that is invoked (either once or repeatedly) after + * a specified time interval. + */ + class Timer : public ecl::Nocopy { + public: + Timer(); + ~Timer(); + void activate (TimeHandler *th); + void deactivate (TimeHandler* th); + void set_alarm (TimeHandler* th, double interval, bool repeatp = false); + void remove_alarm (TimeHandler *th); + void clear(); + + void tick(double dtime); + private: + struct Rep; + Rep &self; + }; +} diff --git a/project/jni/application/enigma/src/utilXML.cpp b/project/jni/application/enigma/src/utilXML.cpp new file mode 100644 index 000000000..8fead856f --- /dev/null +++ b/project/jni/application/enigma/src/utilXML.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "utilXML.hh" +#include "XMLtoUtf8.hh" + +#include + +XERCES_CPP_NAMESPACE_USE + +namespace enigma +{ + bool boolValue(const XMLCh * const string) { + std::string boolString = XMLtoUtf8(string).c_str(); + if (boolString == "true" || boolString == "1") + return true; + else + // we need no further investigation due to XML validation + return false; + } + + void stripIgnorableWhitespace(DOMElement * elem) { + DOMNode * child = elem->getFirstChild(); + DOMNode * next; + + for (child = elem->getFirstChild(); child != NULL; child = next) { + next = child->getNextSibling(); + if (child->getNodeType() == DOMNode::TEXT_NODE) { + if (dynamic_cast(child)->isIgnorableWhitespace()) { + elem->removeChild(child); + } + } else if (child->getNodeType() == DOMNode::ELEMENT_NODE) { + DOMElement * childElem = dynamic_cast(child); + if (childElem != NULL) + stripIgnorableWhitespace(childElem); + } + } + } + +} //namespace enigma diff --git a/project/jni/application/enigma/src/utilXML.hh b/project/jni/application/enigma/src/utilXML.hh new file mode 100644 index 000000000..91f6ee2d2 --- /dev/null +++ b/project/jni/application/enigma/src/utilXML.hh @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 Ronald Lamprecht + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef ENIGMA_UTILXML_HH +#define ENIGMA_UTILXML_HH + +#include +#include + +namespace enigma +{ + bool boolValue(const XMLCh * const string); + void stripIgnorableWhitespace(XERCES_CPP_NAMESPACE_QUALIFIER DOMElement * elem); + +} //namespace enigma +#endif + diff --git a/project/jni/application/enigma/src/video.cpp b/project/jni/application/enigma/src/video.cpp new file mode 100644 index 000000000..e7e979e26 --- /dev/null +++ b/project/jni/application/enigma/src/video.cpp @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2002,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This file contains the code for initializing the video hardware and + * for various visual effects in the widest sense. This includes + * + * a) managing and rendering the mouse cursor, + * b) changing between windowed and fullscreen mode, + * c) fading between different screen using one of a number of + * predefined effects. + * + * The code in this file is independent of the game. For game-specific + * display code, refer to file display.cc + */ + +#include "enigma.hh" +#include "video.hh" +#include "lua.hh" +#include "options.hh" +#include "main.hh" +#include "ecl.hh" +#include "SDL.h" +#include +#include +#include +#include "config.h" + +#define SCREEN ecl::Screen::get_instance() + +using namespace std; +using namespace ecl; +using namespace video; +using namespace enigma; + +namespace +{ + class Video_SDL { + SDL_Surface* sdlScreen; + string caption; + ecl::Screen* screen; + bool initialized; + public: + Video_SDL(); + ~Video_SDL(); + + bool init(int w, int h, int bpp, bool fullscreen); + void toggle_fullscreen(); + void set_fullscreen(bool on_off); + bool is_fullscreen() const; + void set_caption(const char *str); + const string& get_caption() const { return caption; } + ecl::Screen *get_screen() { return screen; } + }; + + class MouseCursor { + public: + MouseCursor (); + ~MouseCursor(); + + void set_image (ecl::Surface *s, int hotx_, int hoty_); + void move (int newx, int newy); + void redraw (); // Redraw if position/image changed + void draw(); // Draw cursor if visible + void show (); + void hide (); + Rect get_rect() const; + Rect get_oldrect() const; + + bool has_changed() { return changedp; } + int get_x() const { return x; } + int get_y() const { return y; } + + private: + // Private methods + void grab_bg (); + void init_bg(); + void restore_bg(); + + // Variables + Surface *background; // Copy of screen contents behind cursor + Surface *cursor; // Pixmap of the cursor + + int x, y; + int oldx, oldy; + int hotx, hoty; // Coordinates of hotspot inside cursor image + int visible; + bool changedp; + }; +} + + +/* -------------------- Video Engine -------------------- */ + +Video_SDL::Video_SDL() +: sdlScreen(0), + screen(0), + initialized(false) +{} + +Video_SDL::~Video_SDL() +{ + SDL_WM_GrabInput(SDL_GRAB_OFF); +// if (sdlScreen != 0 && fullScreen) +// SDL_WM_ToggleFullScreen(sdlScreen); + delete screen; +} + +void Video_SDL::set_caption(const char *str) { + caption = str; + if (initialized) + SDL_WM_SetCaption(str, 0); +} + + +bool Video_SDL::init(int w, int h, int bpp, bool fullscreen) +{ + SDL_WM_SetCaption(caption.c_str(), 0); + + Uint32 flags = SDL_SWSURFACE; + if (fullscreen) + flags |= SDL_FULLSCREEN; + + // Try to initialize vide mode, return error code on failure + sdlScreen = 0; + bpp = SDL_VideoModeOK (w, h, bpp, flags); + if (bpp == 0) + return false; + sdlScreen = SDL_SetVideoMode(w, h, bpp, flags); + if (sdlScreen == 0) + return false; + + // Video mode could be set + screen = new Screen(sdlScreen); + initialized = true; + +#if 0 + // the Mac SDL port seems to ignore the following ShowCursor, + // so we just set the Cursor to be invisible. + SDL_Cursor *hiddenCursor=SDL_CreateCursor(NULL, NULL, 0, 0, 0, 0); + SDL_SetCursor(hiddenCursor); + SDL_FreeCursor(hiddenCursor); +#endif + + // Hack to hide the cursor after switching between + // window/fullscreen mode. + SDL_ShowCursor (SDL_ENABLE); + SDL_ShowCursor (SDL_DISABLE); + + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY / 2, + SDL_DEFAULT_REPEAT_INTERVAL / 2); + + return true; +} + +bool Video_SDL::is_fullscreen() const +{ + if (sdlScreen) + return (sdlScreen->flags & SDL_FULLSCREEN) != 0; + return false; +} + + +void Video_SDL::set_fullscreen(bool on_off) { + if (on_off != is_fullscreen()) + toggle_fullscreen(); +} + +void Video_SDL::toggle_fullscreen() +{ + SDL_WM_ToggleFullScreen (sdlScreen); +} + + + +/* -------------------- MouseCursor -------------------- */ + +MouseCursor::MouseCursor () +: background(0), cursor(0) +{ + oldx = oldy = 0; + hotx = hoty = 0; + visible = 0; + changedp = true; +} + +MouseCursor::~MouseCursor() { + delete background; + delete cursor; +} + +void MouseCursor::set_image (ecl::Surface *s, int hx, int hy) { + delete cursor; + cursor = s; + hotx = hx; + hoty = hy; + + if (visible > 0) { + init_bg(); + } +} + +void MouseCursor::draw () { + if (visible > 0) { + grab_bg(); + + GC gc(SCREEN->get_surface()); + blit (gc, x-hotx, y-hoty, cursor); + SCREEN->update_rect (get_rect()); + + changedp = false; + } +} + +void MouseCursor::redraw () { + if (visible > 0 && changedp) { + restore_bg (); + draw(); + } +} + +void MouseCursor::move(int newx, int newy) { + x = newx; + y = newy; + changedp = true; +} + +void MouseCursor::show () { + if (++visible == 1) { + init_bg(); + changedp=true; + } +} + +void MouseCursor::hide () { + if (--visible == 0) { + changedp = true; + restore_bg(); + delete background; + background=0; + } +} + +Rect MouseCursor::get_rect() const { + int scrx=x-hotx; + int scry=y-hoty; + return Rect(scrx,scry,cursor->width(), cursor->height()); +} + +Rect MouseCursor::get_oldrect() const { + int scrx=oldx-hotx; + int scry=oldy-hoty; + return Rect(scrx,scry,cursor->width(), cursor->height()); +} + +void MouseCursor::init_bg() { + assert (visible > 0); + assert (cursor != 0); + + if (background != 0) { + delete background; + } + + background = ecl::MakeSurfaceLike (cursor->width(), + cursor->height(), + SCREEN->get_surface()); + grab_bg(); +} + +void MouseCursor::grab_bg () { + assert (background != 0); + + GC gc(background); + blit (gc, 0,0, SCREEN->get_surface(), get_rect()); + + oldx=x; + oldy=y; +} + +void MouseCursor::restore_bg () { + if (background) { + GC gc(SCREEN->get_surface()); + blit (gc, oldx-hotx, oldy-hoty, background); + SCREEN->update_rect (get_oldrect()); + } +} + + +/* -------------------- Local Variables -------------------- */ +namespace +{ + Video_SDL *video_engine = 0; + MouseCursor *cursor = 0; + Surface *back_buffer = 0; + + /*! List of available video modes. */ + video::VMInfo video_modes[] = { + { + VM_640x480, 640, 480, 32, "640x480", + "models-32.lua", "gfx32/", + 120, 78, "thumbs32", // thumbnail size/directory + Rect (0, 0, 640, 416), // game area + Rect (0, 416, 640, 64), // statusbar area + Rect (10, 425, 117, 43), // time area + Rect (100, 425, 30, 43), // moves area + Rect (152, 433, 490, 52),// inventory area + Rect (150, 434, 475, 35),// text area + VM_None, true, + }, + { + VM_640x512, 640, 512, 32, "640x512", + "models-32.lua", "gfx32/", + 120, 78, "thumbs32", // thumbnail size/directory + Rect (0, 0, 640, 416), // game area + Rect (0, 416, 640, 64), // statusbar area + Rect (15, 420, 110, 40), // time area + Rect (100, 420, 30, 40), // moves area + Rect (152, 433, 490, 52),// inventory area + Rect (150, 434, 475, 35), // text area + VM_640x480, + false, // 640x512 is deprecated! + }, + { + VM_800x600, 800, 600, 40, "800x600", + "models-40.lua", "gfx40/", +// 160, 104, "thumbs40", // thumbnail size/directory + 120, 78, "thumbs32", + Rect (0, 0, 800, 520), // game area + Rect (0, 520, 800, 80), // statusbar area + Rect (15, 540, 140, 40), // time area + Rect (125, 540, 30, 40), // moves area + Rect (192, 539, 610, 46),// inventory area + Rect (185, 545, 600, 39), // text area + VM_640x480, true, + }, + { + VM_1024x768, 1024, 768, 48, "1024x768", + "models-48.lua", "gfx48/", +// 160, 104, "thumbs40", // thumbnail size/directory + 120, 78, "thumbs32", + Rect (32, 0, 960, 624), // game area + Rect (32, 624, 960, 96), // statusbar area + Rect (50, 640, 170, 60), // time area + Rect (185, 640, 30, 60), // moves area + Rect (260, 650, 710, 46),// inventory area + Rect (260, 655, 710, 40), // text area + VM_640x480, true, + }, + }; + + VideoModes current_video_mode = VM_None; +} + + +/* -------------------- Auxiliary functions -------------------- */ + +namespace +{ + bool vm_available (int w, int h, int &bpp, bool &fullscreen) + { + Uint32 flags = SDL_HWSURFACE; + if (fullscreen) + flags |= SDL_FULLSCREEN; + + int newbpp = SDL_VideoModeOK (w, h, bpp, flags); + if (newbpp != 0) { + bpp = newbpp; + return true; + } + return false; + } + +/*! This function is installed as an event filter by video::Init. It + intercepts mouse motions, which are used to update the position of + the mouse cursor (but passed on to the event queue) */ + int event_filter(const SDL_Event *e) + { + if (e->type == SDL_MOUSEMOTION) { + cursor->move(e->motion.x, e->motion.y); + cursor->redraw(); + } + return 1; + } + +} + + +/* -------------------- Functions -------------------- */ + + + +void video::SetMouseCursor(ecl::Surface *s, int hotx, int hoty) { + cursor->set_image(s, hotx, hoty); + cursor->redraw(); +} + +void video::HideMouse() { + cursor->hide(); + cursor->redraw(); +} + +void video::ShowMouse() { + cursor->show(); + cursor->redraw(); +} + +int video::Mousex() { + return cursor->get_x(); +} + +int video::Mousey() { + return cursor->get_y(); +} + +/* -------------------- Input grabbing -------------------- */ + + +TempInputGrab::TempInputGrab (bool onoff) +{ + old_onoff = SetInputGrab (onoff); +} + +TempInputGrab::~TempInputGrab() +{ + SetInputGrab (old_onoff); +} + +bool video::GetInputGrab() +{ + return SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_ON; +} + +bool video::SetInputGrab (bool onoff) +{ + bool old_onoff = GetInputGrab (); + if (onoff) { + Screen *screen = GetScreen(); + SDL_WarpMouse (screen->width()/2, screen->height()/2); + SDL_Event e; + while (SDL_PollEvent(&e)) { + ; // swallow mouse motion event + } + SDL_WM_GrabInput (SDL_GRAB_ON); + } + else + SDL_WM_GrabInput (SDL_GRAB_OFF); + return old_onoff; +} + + +Surface* video::BackBuffer() { + if (back_buffer==0) { + back_buffer= Duplicate(SCREEN->get_surface()); + } + return back_buffer; +} + +const video::VMInfo *video::GetInfo (VideoModes vm) +{ + return &video_modes[vm]; +} + +const video::VMInfo *video::GetInfo () +{ + return GetInfo (current_video_mode); +} + +bool video::ModeAvailable (VideoModes vm) +{ + const VMInfo *vminfo = GetInfo (vm); + string fname; + return (vminfo->available && app.systemFS->findFile (vminfo->initscript, fname)); +} + + +void video::Init() +{ + assert (NUMENTRIES(video_modes) == VM_COUNT); + + int vidmode = Clamp (options::GetInt("VideoMode"), 0, VM_COUNT-1); + int oldvidmode = vidmode; + + video_engine = new Video_SDL(); + + int bpp = 16; //options::BitsPerPixel; + assert(bpp==16 || bpp==32); + + while (true) { + VMInfo *vminfo = &video_modes[vidmode]; + int w = vminfo->width; + int h = vminfo->height; + bool fullscreen = options::GetBool("FullScreen"); + + if (ModeAvailable (static_cast (vidmode)) + && vm_available (w, h, bpp, fullscreen) + && video_engine->init (w, h, bpp, fullscreen)) + { + // Success! + break; + } + + // Video mode not available? Try the fallback video mode + vidmode = vminfo->fallback_videomode; + if (vidmode == VM_None) { + // Give up :-( + fprintf(stderr, "Couldn't open screen: %s\n", SDL_GetError()); + exit(1); + } + } + + current_video_mode = static_cast(vidmode); + if (vidmode != oldvidmode) { + options::SetOption ("VideoMode", vidmode); + } + + +#ifndef MACOSX + Surface *icn = enigma::GetImage("enigma_marble"); + if(icn) + SDL_WM_SetIcon(icn->get_surface(), NULL); +#endif + + cursor = new MouseCursor; + int x, y; + SDL_GetMouseState(&x, &y); + cursor->move(x,y); + + SDL_SetEventFilter(event_filter); + + + UpdateGamma(); +} + +void video::Shutdown() +{ + SDL_SetEventFilter(0); + delete video_engine; + delete cursor; + delete back_buffer; + video_engine = 0; + cursor = 0; + back_buffer = 0; +} + +void video::ChangeVideoMode() +{ + MouseCursor *oldcursor = cursor; + cursor = 0; + Shutdown(); + Init(); + delete cursor; + cursor = oldcursor; +} + +ecl::Screen * video::GetScreen() { + return SCREEN; +} + +VideoModes video::GetVideoMode() { + return current_video_mode; +} + +bool video::IsFullScreen() +{ + return video_engine->is_fullscreen(); +} + +int video::GetColorDepth() { + return SCREEN->get_surface()->bipp(); +} + +bool video::SetFullscreen(bool onoff) +{ + video_engine->set_fullscreen(onoff); + bool is_fullscreen = video_engine->is_fullscreen(); + if (onoff == is_fullscreen) { + options::SetOption("FullScreen", is_fullscreen); + } + return is_fullscreen; +} + +bool video::ToggleFullscreen() +{ + return SetFullscreen (!video_engine->is_fullscreen()); +} + +void video::SetCaption(const char *str) +{ + video_engine->set_caption(str); +} + +const string& video::GetCaption() +{ + return video_engine->get_caption(); +} + +void video::UpdateGamma() +{ + float gamma = static_cast (options::GetDouble ("Gamma")); + if (gamma < 0.25) + gamma = 0.25; // Windows does not set gamma for values < 0.2271 + SDL_SetGamma (gamma, gamma, gamma); +} + +void video::Screenshot (const std::string &fname) +{ + // auto-create the directory if necessary + string directory; + if (ecl::split_path (fname, &directory, 0) && !ecl::FolderExists(directory)) { + ecl::FolderCreate (directory); + } + + ecl::SavePNG (SCREEN->get_surface(), fname); + enigma::Log << "Wrote screenshot to '" << fname << "\n"; +} + + +/* -------------------- Special Effects -------------------- */ + +void video::FX_Fade(FadeMode mode) +{ + ecl::Screen *screen = ecl::Screen::get_instance(); + Surface *d = screen->get_surface(); + const double fadesec = 0.6; + double v = 255/fadesec; + + ecl::Surface *buffer = Duplicate(d); + double dt; + + double a = mode==FADEIN ? 0 : 255; + + GC gc (d); + + while (true) { + Uint32 otime = SDL_GetTicks(); + + box(gc, d->size()); + buffer->set_alpha(int(a)); + blit(gc, 0,0,buffer); + screen->update_all(); + screen->flush_updates(); + + dt = (SDL_GetTicks()-otime)/1000.0; + if (mode==FADEIN && (a+=v*dt) > 255) + break; + else if (mode==FADEOUT && (a-=v*dt) < 0) + break; + } + + if (mode==FADEIN) { + buffer->set_alpha(255); + blit(gc, 0,0,buffer); + } else + box (gc, d->size()); + screen->update_all(); + screen->flush_updates(); + delete buffer; +} + +void video::FX_Fly (Surface *newscr, int originx, int originy) +{ + double rest_time = 0.5; + + double velx = -originx / rest_time; + double vely = -originy / rest_time; + + double origx = originx; + double origy = originy; + + Screen *scr = SCREEN; + GC scrgc(scr->get_surface()); + + while (rest_time > 0) + { + Uint32 otime = SDL_GetTicks(); + + Rect r(static_cast(origx), + static_cast(origy), + scr->width(), scr->height()); + blit (scrgc, r.x, r.y, newscr); + + scr->update_rect(r); + scr->flush_updates(); + + double dt = (SDL_GetTicks()-otime)/1000.0; + if (dt > rest_time) + dt = rest_time; + rest_time -= dt; + origx += velx * dt; + origy += vely * dt; + } +} + +namespace +{ + class Effect_Push : public TransitionEffect { + public: + Effect_Push(ecl::Surface *newscr, int originx, int originy); + void tick (double dtime); + bool finished() const; + private: + double rest_time; + ecl::Surface *newscr; + std::auto_ptr oldscr; + int originx, originy; + double velx, vely; + double accx, accy; + double x, y; + double t; + }; +} + +Effect_Push::Effect_Push(ecl::Surface *newscr_, int originx_, int originy_) +: rest_time (0.7), + newscr (newscr_), + oldscr (Duplicate(SCREEN->get_surface())), + originx (originx_), + originy (originy_), + velx (-2 * originx / rest_time), + vely (-2 * originy / rest_time), + accx (-0.5*velx/rest_time), + accy (-0.5*vely/rest_time), + x (originx), + y (originy), + t (0) +{ +} + +void Effect_Push::tick (double dtime) +{ + Screen *scr = SCREEN; + GC scrgc(scr->get_surface()); + + if (rest_time > 0) { + if (dtime > rest_time) + dtime = rest_time; + rest_time -= dtime; + t+=dtime; + + x = (accx*t + velx)*t + originx; + y = (accy*t + vely)*t + originy; + + blit (scrgc, (int)x-originx, (int)y, oldscr.get()); + blit (scrgc, (int)x, (int)y-originy, oldscr.get()); + blit (scrgc, (int)x-originx, (int)y-originy, oldscr.get()); + + blit (scrgc, (int)x, (int) y, newscr); + + scr->update_all(); + scr->flush_updates(); + } + else { + blit(scrgc, 0,0, newscr); + scr->update_all(); + scr->flush_updates(); + } +} + +bool Effect_Push::finished() const +{ + return rest_time <= 0; +} + + + + +TransitionEffect * +video::MakeEffect (TransitionModes tm, ecl::Surface *newscr) +{ + int scrw = SCREEN->width(); + int scrh = SCREEN->height(); + + switch (tm) { + case TM_PUSH_RANDOM: { + int xo=0, yo=0; + while (xo==0 && yo==0) { + xo = enigma::IntegerRand(-1,1)*scrw; + yo = enigma::IntegerRand(-1,1)*scrh; + } + return new Effect_Push(newscr, xo, yo); + } + case TM_PUSH_N: return new Effect_Push (newscr, 0, -scrh); + case TM_PUSH_S: return new Effect_Push (newscr, 0, +scrh); + case TM_PUSH_E: return new Effect_Push (newscr, +scrw, 0); + case TM_PUSH_W: return new Effect_Push (newscr, -scrw, 0); + default: + return 0; + }; +} + + +void video::ShowScreen (TransitionModes tm, Surface *newscr) { + int scrw = SCREEN->width(); + int scrh = SCREEN->height(); + + switch (tm) { + case TM_RANDOM: + break; + case TM_FADEOUTIN: + break; + case TM_SQUARES: + break; + case TM_FLY_N: FX_Fly (newscr, 0, -scrh); break; + case TM_FLY_S: FX_Fly (newscr, 0, +scrh); break; + case TM_FLY_E: FX_Fly (newscr, +scrw, 0); break; + case TM_FLY_W: FX_Fly (newscr, -scrw, 0); break; + + default: + break; + } +} + diff --git a/project/jni/application/enigma/src/video.hh b/project/jni/application/enigma/src/video.hh new file mode 100644 index 000000000..c556c6798 --- /dev/null +++ b/project/jni/application/enigma/src/video.hh @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef VIDEO_HH_INCLUDED +#define VIDEO_HH_INCLUDED + +#include +#include "SDL.h" +#include "ecl_fwd.hh" +#include "ecl_geom.hh" + +namespace video +{ + + enum VideoModes { + VM_None = -1, + VM_640x480 = 0, + VM_640x512 = 1, + VM_800x600 = 2, + VM_1024x768 = 3, + VM_COUNT + }; + + struct VMInfo { + VideoModes videomode; + int width, height; // Screen width and height in pixels + int tile_size; // Tile size in pixels + const char *name; // Menu text + const char *initscript; // Lua initialization script + const char *gfxdir; // Directory that contains the graphics + int thumbw, thumbh; // Width and height of thumbnails + const char *thumbsdir; // Directory that contains the thumbnails + ecl::Rect gamearea; + ecl::Rect statusbararea; + ecl::Rect sb_timearea; + ecl::Rect sb_movesarea; + ecl::Rect sb_itemarea; + ecl::Rect sb_textarea; + VideoModes fallback_videomode; + bool available; // Is this video mode available? + }; + + void Init(); + void Shutdown(); + + void ChangeVideoMode(); + bool SetFullscreen(bool onoff); + + /*! Switch between windowed and fullscreen mode. Return true if + fullscreen mode is active afterwards. */ + bool ToggleFullscreen(); + + bool IsFullScreen(); + + /*! Return information about arbitrary video mode. */ + const VMInfo *GetInfo (VideoModes vm); + + /*! Return information about current video mode. */ + const VMInfo *GetInfo (); + + bool ModeAvailable (VideoModes vm); + + //! Return the current video mode + VideoModes GetVideoMode(); + + /*! Return the number of bits per pixel in the current video + mode. [currently always 16] */ + int GetColorDepth(); + + ecl::Screen *GetScreen(); + + /*! The backbuffer is surface that has the same size and pixel + format as the screen. This surface is used by ShowScreen() and + FX_* functions below. */ + ecl::Surface *BackBuffer(); + + /*! Update gamma correction using current options. */ + void UpdateGamma(); + + void SetCaption (const char *str); + const std::string& GetCaption(); + + /*! Take a screenshot and save it as a PNG to file FNAME. */ + void Screenshot (const std::string &fname); + +/* -------------------- Input grabbing -------------------- */ + + class TempInputGrab { + public: + TempInputGrab (bool onoff); + ~TempInputGrab(); + private: + bool old_onoff; + }; + + bool GetInputGrab(); + bool SetInputGrab (bool onoff); + +/* -------------------- Mouse cursor -------------------- */ + + void SetMouseCursor(ecl::Surface *s, int hotx, int hoty); + void HideMouse(); + void ShowMouse(); + int Mousex(); + int Mousey(); + +/* -------------------- Visual effects -------------------- */ + + class TransitionEffect { + public: + virtual ~TransitionEffect() {} + virtual void tick (double dtime) = 0; + virtual bool finished() const = 0; + }; + + enum FadeMode { FADEIN, FADEOUT }; + void FX_Fade (FadeMode mode); + void FX_Fly (ecl::Surface *newscr, int originx, int originy); + + enum TransitionModes + { + TM_RANDOM, + TM_FADEOUTIN, + TM_SQUARES, + TM_FLY_N, TM_FLY_S, TM_FLY_W, TM_FLY_E, + TM_FLY_NW, TM_FLY_NE, TM_FLY_SE, TM_FLY_SW, + TM_PUSH_RANDOM, TM_PUSH_N, TM_PUSH_S, TM_PUSH_W, TM_PUSH_E + }; + void ShowScreen (TransitionModes tm, ecl::Surface *newscr); + + TransitionEffect *MakeEffect (TransitionModes tm, ecl::Surface *newscr); +} + +#endif diff --git a/project/jni/application/enigma/src/world.cpp b/project/jni/application/enigma/src/world.cpp new file mode 100644 index 000000000..5f9e8f5c4 --- /dev/null +++ b/project/jni/application/enigma/src/world.cpp @@ -0,0 +1,2114 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include "errors.hh" +#include "laser.hh" +#include "player.hh" +#include "sound.hh" +#include "options.hh" +#include "server.hh" +#include "lua.hh" +#include "client.hh" +#include "main.hh" +#include "stones_internal.hh" + +#include +#include +#include +#include +#include + + + +// remove comment from define below to switch on verbose messaging +// note: VERBOSE_MESSAGES is defined in multiple source files! +// #define VERBOSE_MESSAGES + +using namespace std; +using namespace world; +using namespace ecl; + +#include "world_internal.hh" + +// Size of one time step -- do not change! +const double ActorTimeStep = 0.0025; + + +/* -------------------- Auxiliary functions -------------------- */ + +namespace { + + /*! Enigma's stones have rounded corners; this leads to realistic + behaviour when a marble rebounds from the corner of a single + stone. But it's annoying when there are two adjacent stones and + a marble rebounds from any one (or even both) corners, because + it changes the direction of the marble quite unpredictably. + + This function modifies the contact information for two adjacent + stones in such a way that the two collisions are treated as a + single collision with a flat surface. */ + void maybe_join_contacts (StoneContact &a, StoneContact &b) + { +// double maxd = 4.0/32; // Max distance between joinable collisions + + if (a.is_contact && b.is_contact + && a.is_collision && b.is_collision + && a.response==STONE_REBOUND && b.response==STONE_REBOUND) + // && length(a.contact_point - b.contact_point) <= maxd) + { + b.ignore = true; // Don't rebound from `b' + + DirectionBits fa = contact_faces(a); + DirectionBits fb = contact_faces(b); + + switch (fa & fb) { + case NORTHBIT: a.normal = V2(0,-1); break; + case EASTBIT: a.normal = V2(1,0); break; + case SOUTHBIT: a.normal = V2(0,1); break; + case WESTBIT: a.normal = V2(-1,0); break; + case NODIRBIT: + //fprintf(stderr, "Strange: contacts have no direction in common\n"); + break; + default: + //fprintf(stderr, "Strange: contacts have multiple directions in common\n"); + break; + } + } + } + +/*! Find an already existing contact point in the ContactList that is + similar to the second argument. */ + bool has_nearby_contact (const ContactList &cl, const Contact &c) + { + double posdelta = 0.2; + double normaldelta = 0.1; + for (size_t i=0; isource; + Object *dst = GetObject(s->destloc); + +#if defined(VERBOSE_MESSAGES) + src->warning("emit_from: msg='%s'", // dest=%i/%i obj=%p", + s->message.c_str() +// destloc.pos.x, destloc.pos.y, +// dst); + ); +#endif + if (GridObject *go = dynamic_cast(src)) + SendMessage (dst, Message (s->message, value, go->get_pos())); + else + SendMessage (dst, Message (s->message, value)); + } + + void emit_from (const SignalList &sl, Object *source, int value) + { + size_t size = sl.size(); + for (unsigned i=0; i targets; + +// for (unsigned i=0; i= 0, XLevelRuntime, "RubberBand: length negative"); +} + +RubberBand::RubberBand (Actor *a1, Stone *st, const RubberBandData &d) +: actor(a1), actor2(0), stone(st), model(0), + data (d) +{ + ASSERT(actor, XLevelRuntime, "RubberBand: no actor defined"); + ASSERT(d.length >= 0, XLevelRuntime, "RubberBand: length negative"); + model = display::AddRubber(get_p1(), get_p2()); +} + +RubberBand::~RubberBand() { + model.kill(); +} + +void RubberBand::apply_forces () +{ + V2 v = get_p2()-get_p1(); + double vv = ecl::length(v); + + if (vv > data.length) { + V2 force = v * data.strength*(vv-data.length)/vv; + force /= 6; + actor->add_force(force); + if (actor2) + actor2->add_force(-force); + } + else if (vv < data.minlength) { + V2 force = v * data.strength * (vv-data.minlength) / vv; + force /= 6; + actor->add_force(force); + if (actor2) + actor2->add_force(-force); + } + +} + +V2 RubberBand::get_p1() const +{ + return V2(actor->get_pos()[0], actor->get_pos()[1]); +} + +V2 RubberBand::get_p2() const +{ + if (!stone) + return V2(actor2->get_pos()[0], actor2->get_pos()[1]); + else + return stone->get_pos().center(); +} + +void RubberBand::tick (double /* dtime */) +{ + model.update_first (get_p1()); + model.update_second (get_p2()); +} + + +/* -------------------- Field -------------------- */ + +Field::Field() +{ + floor=0; + item=0; + stone=0; +} + +Field::~Field() +{ + DisposeObject(floor); + DisposeObject(item); + DisposeObject(stone); +} + + +/* -------------------- StoneContact -------------------- */ + +StoneContact::StoneContact(Actor *a, GridPos p, + const V2 &cp, const V2 &n) +: actor(a), stonepos(p), + response(STONE_PASS), + contact_point(cp), + normal(n), + is_collision(false), + ignore (false), + new_collision(false), + is_contact(true) +{} + +StoneContact::StoneContact() +: is_collision(false), + ignore (false), + new_collision(false), + is_contact(false) +{} + +DirectionBits +world::contact_faces(const StoneContact &sc) +{ + using namespace enigma; + + int dirs = NODIRBIT; + + if (sc.normal[0] < 0) + dirs |= WESTBIT; + else if (sc.normal[0] > 0) + dirs |= EASTBIT; + if (sc.normal[1] < 0) + dirs |= NORTHBIT; + else if (sc.normal[1] > 0) + dirs |= SOUTHBIT; + + return DirectionBits(dirs); +} + +Direction +world::contact_face(const StoneContact &sc) +{ + using namespace enigma; + if (sc.normal == V2(-1,0)) + return WEST; + else if (sc.normal == V2(1,0)) + return EAST; + else if (sc.normal == V2(0,-1)) + return NORTH; + else if (sc.normal == V2(0,1)) + return SOUTH; + else + return NODIR; +} + + +/* -------------------- Global variables -------------------- */ + +namespace +{ + auto_ptr level; +} + +enigma::Timer world::GameTimer; +bool world::TrackMessages; +Actor *world::CurrentCollisionActor = 0; + + + +/* -------------------- Layer implementation -------------------- */ + +template +T *Layer::get(GridPos p) { + if (Field *f=level->get_field(p)) + return raw_get(*f); + else + return defaultval; +} + +template +T *Layer::yield(GridPos p) { + if (Field *f=level->get_field(p)) { + T *x = raw_get(*f); + if (x) { + raw_set(*f, 0); + x->removal(p); + } + return x; + } else + return defaultval; +} + +template +void Layer::set(GridPos p, T *x) { + if (x) { + if (Field *f=level->get_field(p)) { + if (T *old = raw_get (*f)) { + old->removal(p); + dispose (old); + } + raw_set(*f, x); + x->creation(p); + } + else + dispose(x); + } +} + + + + +/* -------------------- World -------------------- */ + +World::World(int ww, int hh) +: fields(ww,hh), + preparing_level(true) +{ + w = ww; + h = hh; + + scrambleIntensity = server::GetDifficulty() == DIFFICULTY_EASY ? 3 : 10; +} + +World::~World() +{ + fields = FieldArray(0,0); + for_each(actorlist.begin(), actorlist.end(), mem_fun(&Actor::dispose)); + delete_sequence (m_rubberbands.begin(), m_rubberbands.end()); +} + +bool World::is_border (GridPos p) { + return(p.x==0 || p.y==0 || p.x==w-1 || p.y==h-1); +} + +void World::remove (ForceField *ff) +{ + ForceList::iterator i=find (forces.begin(), forces.end(), ff); + if (i != forces.end()) + forces.erase(i); +} + +Object *World::get_named (const string &name) +{ + ecl::Dict::iterator found = m_objnames.find(name); + if (found != m_objnames.end()) + return found->second; + Log << "Did not find named object: " << name << '\n'; + return 0; +} + +void World::name_object (Object *obj, const std::string &name) +{ + m_objnames.insert(name, obj); // [name] = obj; + obj->set_attrib("name", name); +} + +void World::unname (Object *obj) +{ + ASSERT(obj, XLevelRuntime, "unname: no object given"); + string name; + if (obj->string_attrib("name", &name)) { + m_objnames.remove(name); + obj->set_attrib("name", ""); + } +} + +void World::add_actor (Actor *a) +{ + add_actor (a, a->get_pos()); +} + +void World::add_actor (Actor *a, const V2 &pos) +{ + actorlist.push_back(a); + a->get_actorinfo()->pos = pos; + if (!preparing_level) { + // if game is already running, call on_creation() from here + a->on_creation(pos); + } +} + +void World::tick (double dtime) +{ + // dtime is always 0.01 (cf. server.cc) + + move_actors (dtime); + handle_delayed_impulses (dtime); + tick_sound_dampings(); + + // Tell floors and items about new stones. + for (unsigned i=0; i::iterator i = scrambles.begin(); + list::iterator e = scrambles.end(); + + for (; i != e; ++i) { + Stone *puzz = GetStone(i->pos); + if (puzz && i->intensity) { + SendMessage(puzz, "scramble", Value(double(i->dir))); + --i->intensity; + } + else { + fprintf(stderr, "no stone found for scramble at %i/%i\n", i->pos.x, i->pos.y); + i->intensity = 0; + } + } + + scrambles.remove_if(mem_fun_ref(&Scramble::expired)); + } +} + +//---------------------------------------------------------------------- +// Physics simulation +//---------------------------------------------------------------------- + + +/* -------------------- Force calculation -------------------- */ + +#ifndef M_PI +#define M_PI 3.1415926535 +#endif + +void World::add_mouseforce (Actor *a, Floor *floor, V2 &mforce) +{ + if (a->get_controllers() != 0) { + V2 f = floor->process_mouseforce(a, m_mouseforce.get_force(a)); + if (a->is_drunken()) { + // rotate mouse force by random angle + double maxangle = M_PI * 0.7; + double angle = DoubleRand (-maxangle, maxangle); + f = V2(f[0]*cos(angle) - f[1]*sin(angle), + f[0]*sin(angle) + f[1]*cos(angle)); + } + mforce += f; + } +} + +/*! Calculate the total force on an actor A at time TIME. The + actor's current position X and velocity V are also passed. [Note + that the position and velocity entries in ActorInfo will be updated + only after a *successful* time step, so they cannot be used + here.] */ +V2 World::get_local_force (Actor *a) +{ + V2 f; + + if (a->is_on_floor()) { + const Field *field = GetField (a->get_gridpos()); + if (Floor *floor = field->floor) { + // Constant force + m_flatforce.add_force(a, f); + + // Mouse force + add_mouseforce (a, floor, f); + + // Friction + double friction = floor->friction(); + if (a->has_spikes()) + friction += 7.0; + + V2 v = a->get_vel(); + double vv=length(v); + if (vv > 0) { + V2 frictionf = v * (server::FrictionFactor*friction); + frictionf /= vv; + frictionf *= pow(vv, 0.8); + f -= frictionf; + } + + floor->add_force(a, f); + } + + if (Item *item = field->item) + item->add_force(a, f); + } + + return f; +} + +/* Global forces are calculated less often than local ones, namely + only once every four time steps, cf. move_actors(). They are used + for forces that are more time consuming to calculate, i.e., + actor-actor interactions and external force fields. */ +V2 World::get_global_force (Actor *a) +{ + V2 f; + // Electrostatic forces between actors. + if (double q = get_charge(a)) { + for (ActorList::iterator i=actorlist.begin(); + i != actorlist.end(); ++i) + { + Actor *a2 = *i; + if (a2 == a) continue; + if (double q2 = get_charge(a2)) { + V2 distv = a->get_pos() - a2->get_pos(); + if (double dist = distv.normalize()) + f += server::ElectricForce * q * q2 / (dist) * distv; + } + } + } + + // All other force fields. + for (ForceList::iterator i=forces.begin(); i != forces.end(); ++i) + (*i)->add_force(a, f); + + return f; +} + +/* -------------------- Collision handling -------------------- */ + +struct Ball { + ecl::V2 c; // center + double r; // radius +}; + +struct Oblong { + ecl::V2 c; // center + double w; // width + double h; // height + double erad; // edge radius +}; + +// static void contact_ball_oblong () +// { +// } + +/* Determine whether an actor is in contact with a stone at position + `p'. The result is returned in `c'. Three situations can occur: + + 1) There is no stone at `p'. In this case, `is_contact' is set to + false and nothing else is done. + + 2) The stone and the actor are in contact. In this case, `c' is + filled with the contact information and `is_contact'p is set to + true. + + 3) The stone and the actor are _not_ in contact. In this case, `c' + is filled is filled with information about the closest feature + on the stone and `is_contact' is set to false. +*/ +void World::find_contact_with_stone (Actor *a, GridPos p, StoneContact &c) +{ + c.is_contact = false; + + const ActorInfo &ai = *a->get_actorinfo(); + double r = ai.radius; +// Ball b (a->get_pos(), ai.radius); +// Oblong o (p.center(), 1, 1, 2.0 / 32); + + V2 centerdist = a->get_pos() - p.center(); + if (square (centerdist) > 1.0) + return; + + Stone *stone = world::GetStone(p); + if (!stone) + return; + + int x = p.x, y = p.y; + + double ax = ai.pos[0]; + double ay = ai.pos[1]; + const double contact_e = 0.02; + const double erad = 2.0/32; // edge radius + + // Closest feature == north or south face of the stone? + if (ax>x+erad && axy+1) { + c.contact_point = V2(ax, y+1); + c.normal = V2(0,+1); + dist = ay-(y+1); + } + // north + else if (ayy+erad && ayx+1) { // east + c.contact_point = V2(x+1,ay); + c.normal = V2(+1,0); + dist = ax-(x+1); + } + else if (ax= x+1-erad); + int ycorner=(ay >= y+1-erad); + double cx[2] = {erad, -erad}; + + V2 corner(x+xcorner+cx[xcorner], y+ycorner+cx[ycorner]); + V2 b=V2(ax,ay) - corner; + + c.is_contact = (length(b)-r-erad < contact_e); + c.normal = normalize(b); + c.contact_point = corner + c.normal*erad; + } + + if (c.is_contact) { + // treat this as a collision only if actor not inside the stone + // and velocity towards stone + if (ax >= x && ax < x+1 && ay >= y && ay < y+1) + c.is_collision = false; + else + c.is_collision = c.normal*ai.vel < 0; + + c.ignore = false; + c.actor = a; + c.stonepos = p; + c.stoneid = stone->get_traits().id; + c.response = stone->collision_response(c); + c.sound = stone->collision_sound(); + } +} + +void World::find_stone_contacts (Actor *a, StoneContactList &cl) +{ + ActorInfo &ai = *a->get_actorinfo(); + ecl::Rect r(round_down(ai.pos[0]-0.5), round_down(ai.pos[1]-0.5), 1, 1); + + static StoneContact contacts[2][2]; + + // Test for collisions with the nearby stones + int ncontacts = 0; + for (int i=r.w; i>=0; --i) { + for (int j=r.h; j>=0; --j) { + GridPos p (r.x + i, r.y + j); + find_contact_with_stone(a, p, contacts[i][j]); + ncontacts += contacts[i][j].is_contact; + } + } + if (ncontacts > 0) { + maybe_join_contacts (contacts[0][0], contacts[1][0]); + maybe_join_contacts (contacts[0][0], contacts[0][1]); + maybe_join_contacts (contacts[1][0], contacts[1][1]); + maybe_join_contacts (contacts[0][1], contacts[1][1]); + + for (int i=0; i<=r.w; i++) + for (int j=0; j<=r.h; j++) + if (contacts[i][j].is_contact) + cl.push_back(contacts[i][j]); + } +} + +/*! This function is called for every contact between an actor and a + stone. It handles: (1) informing stones about contacts, (2) + collision response, (3) updating the actor's contact list. Note + that a stone may kill itself during a call to actor_hit() or + actor_contact() so we must not refer to it after having called these + functions. (Incidentally, this is why StoneContact only contains a + 'stonepos' and no 'stone' entry.) */ +void World::handle_stone_contact (StoneContact &sc) +{ + Actor *a = sc.actor; + ActorInfo &ai = *a->get_actorinfo(); + double restitution = 1.0; //0.85; + + if (server::NoCollisions && (sc.stoneid != st_borderstone)) + return; + + Contact contact (sc.contact_point, sc.normal); + + if (sc.is_contact && sc.response == STONE_REBOUND) { + ai.new_contacts.push_back (contact); + } + + if (sc.is_collision) { + if (!sc.ignore && sc.response == STONE_REBOUND) { + bool slow_collision = length (ai.vel) < 0.3; + if (!has_nearby_contact(ai.contacts, contact)) { + if (Stone *stone = world::GetStone(sc.stonepos)) { + CurrentCollisionActor = a; + if (slow_collision) stone->actor_touch(sc); + else stone->actor_hit(sc); + CurrentCollisionActor = 0; + + if (!slow_collision) { + client::Msg_Sparkle (sc.contact_point); + double volume = std::max (0.25, length(ai.vel)/8); + volume = std::min (1.0, volume); + volume = getVolume(sc.sound.c_str(), a, volume); + sound::EmitSoundEvent (sc.sound.c_str(), sc.contact_point, volume); + } + } + } + + double dt = ActorTimeStep; + ai.collforce -= (1 + restitution)*(ai.vel*sc.normal)*sc.normal / dt * ai.mass; + } + } + else if (sc.is_contact) { + if (Stone *stone = world::GetStone(sc.stonepos)) + stone->actor_contact(sc.actor); + } +} + +namespace { + struct ActorEntry { + double pos; + size_t idx; + + ActorEntry () { pos = 0; idx = 0; } + ActorEntry (double pos_, size_t idx_) { pos = pos_; idx = idx_; } + + bool operator < (const ActorEntry &x) const { + return pos < x.pos; + } + }; +}; + +void World::handle_actor_contacts () { + size_t nactors = actorlist.size(); + vector xlist (nactors); + + for (size_t i=0; iget_actorinfo(); + xlist[i] = ActorEntry (ai->pos[0], i); + } + sort (xlist.begin(), xlist.end()); + for (size_t i=0; iget_actorinfo(); + double r1 = ai1.radius; + double x1 = ai1.pos[0]; + for (size_t j=i+1; jget_actorinfo(); + Actor *actor2 = actorlist[j]; + ActorInfo &a2 = *actor2->get_actorinfo(); + + if (a1.ignore_contacts || a2.ignore_contacts) + return; + + V2 n = a1.pos - a2.pos; // normal to contact surface + double dist = n.normalize(); + double overlap = a1.radius + a2.radius - dist; + if (overlap > 0 && !a2.grabbed) { + double relspeed = (a2.vel-a1.vel)*n; + + if (relspeed < 0) // not moving towards each other + return; + + actor1->on_collision (actor2); + actor2->on_collision (actor1); + + bool reboundp = (actor1->is_movable() && actor2->is_movable() && + (actor1->is_on_floor() == actor2->is_on_floor())); + + if (reboundp) { + Contact contact (a2.pos + n*a2.radius, -n); + a2.new_contacts.push_back (contact); + contact.normal = n; + a1.new_contacts.push_back (contact); + + double restitution = 1.0; //0.95; + double mu = a1.mass*a2.mass / (a1.mass + a2.mass); // reduced mass + + V2 force = (restitution * 2 * mu * relspeed / ActorTimeStep) * n; + a1.collforce += force; + a2.collforce -= force; + + if (!has_nearby_contact (a1.contacts, contact)) { + double volume = length (force) * ActorTimeStep; + volume = std::min(1.0, volume); + if (volume > 0.4) { + volume = getVolume("ballcollision", NULL, volume); + sound::EmitSoundEvent ("ballcollision", contact.pos, volume); + } + } + } + } +} + +void World::handle_contacts (unsigned actoridx) +{ + Actor *actor1 = actorlist[actoridx]; + ActorInfo &a1 = *actor1->get_actorinfo(); + + if (a1.ignore_contacts) // used by the cannonball for example + return; + + // Handle contacts with stones + StoneContactList cl; + find_stone_contacts(actor1, cl); + for (StoneContactList::iterator i=cl.begin(); i != cl.end(); ++i) + handle_stone_contact (*i); +} + +/* -------------------- Actor Motion -------------------- */ + +void World::move_actors (double dtime) +{ + const double dt = ActorTimeStep; + + static double rest_time = 0; + rest_time += dtime; + + size_t nactors = actorlist.size(); + vector global_forces (nactors); + for (unsigned i=0; i 0) { + for (unsigned i=0; iget_actorinfo(); + + // the "6" is a historical accident, don't change it! + ai.force = ai.forceacc * 6; + ai.force += global_forces[i]; + ai.force += get_local_force (a); + + ai.forceacc = V2(); + ai.collforce = V2(); + ai.last_pos = ai.pos; + ai.new_contacts.clear(); + } + + handle_actor_contacts(); + for (unsigned i=0; iget_actorinfo(); + + swap (ai.contacts, ai.new_contacts); + + if (!a->can_move()) { + if (length(ai.force) > 30) + client::Msg_Sparkle (ai.pos); + ai.vel = V2(); + } + else if (!a->is_dead() && a->is_movable() && !ai.grabbed) { + advance_actor(a, dt); + } + a->move (); // 'move' nevertheless, to pick up items etc + a->think (dt); + } + for_each (m_rubberbands.begin(), m_rubberbands.end(), + mem_fun(&RubberBand::apply_forces)); + + rest_time -= dt; + } +} + +/* This function performs one step in the numerical integration of an + actor's equation of motion. TIME ist the current absolute time and + H the size of the integration step. */ +void World::advance_actor (Actor *a, double dtime) +{ + const double MAXVEL = 70; + + ActorInfo &ai = *a->get_actorinfo(); + V2 force = ai.force; + + // If the actor is currently in contact with other objects, remove + // the force components in the direction of these objects. + for (unsigned i=0; i 1) + ai.vel /= q; +} + + + +void World::handle_delayed_impulses (double dtime) +{ + // Handle delayed impulses + ImpulseList::iterator i = delayed_impulses.begin(), + end = delayed_impulses.end(); + while (i != end) { + // shall the impulse take effect now ? + if (i->tick(dtime)) { + i->mark_referenced(true); + if (Stone *st = GetStone(i->destination())) + i->send_impulse(st); // may delete stones and revoke delayed impuleses! + i = delayed_impulses.erase(i); + } + else + ++i; + } +} + +void World::revoke_delayed_impulses(const Stone *target) { + // Revokes delayed impulses to and from target + ImpulseList::iterator i = delayed_impulses.begin(), + end = delayed_impulses.end(); + while (i != end) { + if (i->is_receiver(target) || i->is_sender(target)) { + if (i->is_referenced()) { + i->mark_obsolete(); + ++i; + } else { + i = delayed_impulses.erase(i); + } + } else { + ++i; + } + } +} + +void World::tick_sound_dampings () +{ + // See sound.hh and sound.cc for details. + static int counter = 0; + ++counter; + + if (counter > 9) { + counter = 0; + SoundDampingList::iterator i = level->sound_dampings.begin(), + end = level->sound_dampings.end(); + int count = 0; + while (i != end) { + if(i->tick()) // return true if damping entry should be deleted + i = level->sound_dampings.erase(i); + else + ++i; + } + } +} + +void World::stone_change(GridPos p) +{ + if (const Field *f = GetField (p)) { + Stone *st = f->stone; + if (st) + st->on_floor_change(); + + if (Item *it = f->item) + it->stone_change(st); + + if (Floor *fl = f->floor) + fl->stone_change(st); + + lasers::MaybeRecalcLight(p); + } +} + + + + +/* -------------------- Functions -------------------- */ + +void world::Resize (int w, int h) +{ + level.reset (new World(w,h)); + display::NewWorld(w, h); +} + +void world::PrepareLevel () +{ + GameTimer.clear(); + CurrentCollisionActor = 0; + Resize (20, 13); +} + +bool world::InitWorld() +{ + level->scramble_puzzles(); + + lasers::RecalcLight(); + lasers::RecalcLightNow(); // recalculate laser beams if necessary + + bool seen_player0 = false; + + for (ActorList::iterator i=level->actorlist.begin(); + i != level->actorlist.end(); ++i) + { + Actor *a = *i; + a->on_creation(a->get_actorinfo()->pos); + a->message ("init", Value()); + + int iplayer; + if (a->int_attrib("player", &iplayer)) { + player::AddActor(iplayer,a); + if (iplayer == 0) seen_player0 = true; + } else { + player::AddUnassignedActor(a); + } + } + + level->changed_stones.clear(); + + if (!seen_player0) + throw XLevelLoading("Error: No player 0 defined!"); + + world::BroadcastMessage("init", Value(), + GridLayerBits(GRID_ITEMS_BIT | GRID_STONES_BIT | GRID_FLOOR_BIT)); + + server::InitMoveCounter(); + STATUSBAR->show_move_counter (server::ShowMoves); + + display::FocusReferencePoint(); + + level->preparing_level = false; + + return true; +} + +void world::SetMouseForce(V2 f) +{ + level->m_mouseforce.add_force(f); +} + +void world::NameObject(Object *obj, const std::string &name) +{ + string old_name; + if (obj->string_attrib("name", &old_name)) { + obj->warning("name '%s' overwritten by '%s'", + old_name.c_str(), name.c_str()); + UnnameObject(obj); + } + level->name_object (obj, name); +} + +void world::UnnameObject(Object *obj) +{ + level->unname(obj); +} + +void world::TransferObjectName (Object *source, Object *target) +{ + string name; + if (source->string_attrib("name", &name)) { + UnnameObject(source); + string targetName; + if (target->string_attrib("name", &targetName)) { + target->warning("name '%s' overwritten by '%s'", + targetName.c_str(), name.c_str()); + UnnameObject(target); + } + NameObject(target, name); + } +} + +Object * world::GetNamedObject (const std::string &name) +{ + return level->get_named (name); +} + +bool world::IsLevelBorder(GridPos p) +{ + return level->is_border(p); +} + +bool world::IsInsideLevel(GridPos p) +{ + return level->contains(p); +} + + +/* -------------------- Force fields -------------------- */ + +void world::AddForceField(ForceField *ff) +{ + level->forces.push_back(ff); +} + +void world::RemoveForceField(ForceField *ff) { + level->remove (ff); +} + +void world::SetConstantForce (V2 force) { + level->m_flatforce.set_force(force); +} + + + +/* -------------------- Rubber bands -------------------- */ + +void world::AddRubberBand (Actor *a, Stone *st, const RubberBandData &d) +{ + level->m_rubberbands.push_back(new RubberBand (a, st, d)); +} + +void world::AddRubberBand (Actor *a, Actor *a2, const RubberBandData &d) +{ + RubberBandData rbd (d); + rbd.length = ecl::Max (d.length, get_radius(a) + get_radius(a2)); + level->m_rubberbands.push_back(new RubberBand (a, a2, rbd)); +} + +bool world::KillRubberBands (Actor *a) +{ + bool didKill = false; + for (unsigned i=0; im_rubberbands.size(); ) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_actor() == a || r.get_actor2() == a) { + delete &r; + level->m_rubberbands.erase(level->m_rubberbands.begin()+i); + didKill = true; + continue; // don't increment i + } + ++i; + } + return didKill; +} + + +void world::KillRubberBand (Actor *a, Stone *st) +{ + ASSERT(a, XLevelRuntime, "KillRubberBand: no actor attached"); + for (unsigned i=0; im_rubberbands.size(); ) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_actor() == a && r.get_stone() != 0) + if (r.get_stone()==st || st==0) { + delete &r; + level->m_rubberbands.erase(level->m_rubberbands.begin()+i); + continue; // don't increment i + } + ++i; + } +} + +void world::KillRubberBand (Actor *a, Actor *a2) +{ + ASSERT(a, XLevelRuntime, "KillRubberBand: no actor attached"); + for (unsigned i=0; im_rubberbands.size(); ) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_actor() == a && r.get_actor2() != 0) + if (r.get_actor2()==a2 || a2==0) { + delete &r; + level->m_rubberbands.erase(level->m_rubberbands.begin()+i); + continue; // don't increment i + } + ++i; + } +} + +void world::KillRubberBands (Stone *st) +{ + for (unsigned i=0; im_rubberbands.size(); ) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_stone() != 0 && r.get_stone()==st) { + delete &r; + level->m_rubberbands.erase(level->m_rubberbands.begin()+i); + continue; // don't increment i + } + ++i; + } +} + +void world::GiveRubberBands (Stone *st, vector &rubs) { + for (unsigned i=0; im_rubberbands.size(); ) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_stone() == st) { + Rubber_Band_Info rbi; + rbi.act = r.get_actor(); + rbi.data = r.get_data(); + rubs.push_back(rbi); + } + ++i; + } +} + +bool world::HasRubberBand (Actor *a, Stone *st) +{ + for (unsigned i=0; im_rubberbands.size(); ++i) { + RubberBand &r = *level->m_rubberbands[i]; + if (r.get_actor() == a && r.get_stone() == st) + return true; + } + return false; +} + + +/* -------------------- Signals -------------------- */ + +void world::AddSignal (const GridLoc &srcloc, + const GridLoc &dstloc, + const string &msg) +{ +#if defined(VERBOSE_MESSAGES) + fprintf(stderr, "AddSignal src=%i/%i dest=%i/%i msg='%s'\n", + srcloc.pos.x, srcloc.pos.y, dstloc.pos.x, dstloc.pos.y, msg.c_str()); +#endif // VERBOSE_MESSAGES + + if (Object *src = GetObject(srcloc)) { + src->set_attrib("action", "signal"); + level->m_signals.push_back (Signal (src, dstloc, msg)); + } + else { + Log << "AddSignal: Invalid signal source\n"; + } +} + +bool world::HaveSignals (Object *src) +{ + SignalList::const_iterator i=level->m_signals.begin(), + end = level->m_signals.end(); + for (; i != end; ++i) + if (i->source == src) + return true; + return false; +} + + +bool world::EmitSignalByIndex (Object *src, int signalidx, int value) +{ + return emit_by_index (level->m_signals, src, signalidx, value); +} + +bool world::GetSignalTargetPos (Object *src, GridPos &pos, int signalidx) +{ + SignalList::const_iterator i = level->m_signals.begin(), + end = level->m_signals.end(); + int idx = 0; + for (; i != end; ++i) { + if (i->source == src) { + if (idx == signalidx) { + pos = i->destloc.pos; + return true; + } + idx += 1; + } + } + return false; +} + + +Value world::SendMessage(Object *o, const std::string &msg) +{ + return SendMessage (o, Message (msg, Value())); +} + +Value world::SendMessage(Object *o, const std::string &msg, const Value& value) +{ + return SendMessage (o, Message (msg, value)); +} + +Value world::SendMessage (Object *o, const Message &m) +{ + if (o) { + if (TrackMessages) + o->warning("will be sent message '%s' (with Value)", m.message.c_str()); + return o->on_message(m); + } + else if (TrackMessages) { + fprintf(stderr, "Sending message '%s' to NULL-object\n", m.message.c_str()); + return Value(); + } + return Value(); +} + + +void world::BroadcastMessage (const std::string& msg, + const Value& value, + GridLayerBits grids) +{ + int width = level->w; + int height = level->h; + bool to_floors = (grids & GRID_FLOOR_BIT) != 0; + bool to_items = (grids & GRID_ITEMS_BIT) != 0; + bool to_stones = (grids & GRID_STONES_BIT) != 0; + + for (int y = 0; yget_field(p); + if (to_floors && f->floor) SendMessage (f->floor, msg, value); + if (to_items && f->item) SendMessage (f->item, msg, value); + if (to_stones && f->stone) SendMessage (f->stone, msg, value); + } + } +} + + +void world::PerformAction (Object *o, bool onoff) +{ + string action = "idle"; + string target; + + o->string_attrib("action", &action); + o->string_attrib("target", &target); + +#if defined(VERBOSE_MESSAGES) + o->warning("PerformAction action=%s target=%s", action.c_str(), target.c_str()); +#endif // VERBOSE_MESSAGES + + if (action == "callback") { + if (lua::CallFunc(lua::LevelState(), target.c_str(), Value(onoff), o) != 0) { + throw XLevelRuntime(string("callback '")+target+"' failed:\n"+lua::LastError(lua::LevelState())); + } + } + else if (action == "signal") { + emit_from (level->m_signals, o, onoff); + } + else if (Object *t = GetNamedObject(target)) { + if (GridObject *go = dynamic_cast(o)) + SendMessage (t, Message (action, Value(onoff), go->get_pos())); + else + SendMessage (t, Message (action, Value(onoff))); + } + else if (action != "idle") { + fprintf (stderr, "Unknown target '%s' for action '%s'\n", + target.c_str(), action.c_str()); + } +} + + +namespace +{ + void explosion (GridPos p, ItemID explosion_item) + { + if (Stone *stone = GetStone(p)) + SendMessage(stone, "expl"); + if (Item *item = GetItem(p)) { + if (has_flags(item, itf_indestructible)) + SendMessage(item, "expl"); + else + SetItem(p, explosion_item); + } + else + SetItem(p, explosion_item); + if (Floor *floor = GetFloor(p)) + SendMessage(floor, "expl"); + } +} + +void world::SendExplosionEffect(GridPos center, ExplosionType type) +{ + const int AFFECTED_FIELDS = 8; + + for (int a = 0; aget_field(p); +} + + +/* -------------------- Floor manipulation -------------------- */ + +void world::KillFloor(GridPos p) +{ + level->fl_layer.kill(p); +} + +Floor *world::GetFloor(GridPos p) +{ + return level->fl_layer.get(p); +} + +void world::SetFloor(GridPos p, Floor* fl) +{ + level->fl_layer.set(p,fl); + if (!level->preparing_level) + if (Stone *st = GetStone(p)) + st->on_floor_change(); +} + + +/* -------------------- Stone manipulation -------------------- */ + +Stone * world::GetStone(GridPos p) { + if (Field *f = level->get_field (p)) + return f->stone; + else + return level->st_layer.get(p); +} + +void world::KillStone(GridPos p) { + level->st_layer.kill(p); + level->changed_stones.push_back(p); +} + +Stone * world::YieldStone(GridPos p) { + Stone *st = level->st_layer.yield(p); + level->changed_stones.push_back(p); + return st; +} + +void world::SetStone(GridPos p, Stone* st) { + level->st_layer.set(p,st); + level->changed_stones.push_back(p); +} + +void world::ReplaceStone (GridPos p, Stone* st) { + Stone *old = level->st_layer.get(p); + if (old) { + TransferObjectName(old, st); + level->st_layer.kill(p); + } + SetStone(p, st); +} + +void world::MoveStone (GridPos oldPos, GridPos newPos) { + SetStone(newPos, YieldStone(oldPos)); +} + +void world::SetScrambleIntensity (int intensity) { + level->scrambleIntensity = intensity; +} + +void world::AddScramble(GridPos p, Direction d) { + level->add_scramble(p, d); +} + + +/* -------------------- Item manipulation -------------------- */ + +void world::KillItem(GridPos p) +{ + lasers::MaybeRecalcLight(p); + level->it_layer.kill(p); +} + +Item *world::GetItem(GridPos p) { + return level->it_layer.get(p); +} + +Item *world::YieldItem(GridPos p) { + lasers::MaybeRecalcLight(p); + return level->it_layer.yield(p); +} + +void world::SetItem (GridPos p, Item* it) +{ + lasers::MaybeRecalcLight(p); + level->it_layer.set(p,it); +} + +void world::SetItem (GridPos p, ItemID id) +{ + SetItem (p, MakeItem (id)); +} + + +/* -------------------- Actor manipulation -------------------- */ + +void world::AddActor(double x, double y, Actor* a) +{ + level->add_actor (a, V2(x, y)); +} + +void world::AddActor (Actor *a) +{ + level->add_actor (a); +} + +Actor * world::YieldActor(Actor *a) +{ + ActorList::iterator i=find(level->actorlist.begin(), level->actorlist.end(), a); + if (i != level->actorlist.end()) { + level->actorlist.erase(i); + GrabActor(a); + return a; + } + return 0; +} + +void world::KillActor (Actor *a) { + delete YieldActor (a); +} + +void world::WarpActor(Actor *a, double newx, double newy, bool keep_velocity) +{ + V2 newpos = V2(newx, newy); + ASSERT(IsInsideLevel(GridPos(newpos)), XLevelRuntime, + "WarpActor: Tried to warp actor out of level grid. (And hyperspace travel is not implemented yet, sorry.)"); + if (!keep_velocity) + a->get_actorinfo()->vel = V2(); + a->warp(newpos); +} + +void world::FastRespawnActor(Actor *a, bool keep_velocity) { + a->find_respawnpos(); + const V2& p = a->get_respawnpos(); + WarpActor(a, p[0], p[1], keep_velocity); +} + + +void world::RespawnActor(Actor *a) { + a->find_respawnpos(); + a->respawn(); +} + +Actor *FindActorByID (ActorID id) +{ + ActorList::iterator i = level->actorlist.begin(), + end = level->actorlist.end(); + for (; i != end; ++i) { + Actor *a = *i; + if (get_id(a) == id) + return a; + } + return 0; +} + +unsigned world::CountActorsOfKind (ActorID id) +{ + unsigned count = 0; + ActorList::iterator i = level->actorlist.begin(), + end = level->actorlist.end(); + for (; i != end; ++i) { + Actor *a = *i; + if (get_id(a) == id) + ++count; + } + return count; +} + +Actor *world::FindOtherMarble(Actor *thisMarble) +{ + if (!thisMarble) + return 0; + + switch (get_id(thisMarble)) { + case ac_blackball: return FindActorByID (ac_whiteball); + case ac_whiteball: return FindActorByID (ac_blackball); + default: + return 0; + } +} + +bool world::ExchangeMarbles(Actor *marble1) { + Actor *marble2 = FindOtherMarble(marble1); + if (marble2) { + ActorInfo *info1 = marble1->get_actorinfo(); + ActorInfo *info2 = marble2->get_actorinfo(); + + swap(info1->pos, info2->pos); + swap(info1->oldpos, info2->oldpos); + + return true; + } + return false; +} + + +void world::GrabActor(Actor *a) +{ + a->get_actorinfo()->grabbed = true; +} + +void world::ReleaseActor(Actor *a) +{ + a->get_actorinfo()->grabbed = false; +} + +bool world::GetActorsInRange (ecl::V2 center, double range, + vector& actors) +{ + ActorList &al = level->actorlist; + for (ActorList::iterator i=al.begin(); i!=al.end(); ++i) { + Actor *a = *i; + if (length(a->get_pos() - center) < range) + actors.push_back(a); + } + return !actors.empty(); +} + +bool world::GetActorsInsideField (const GridPos& pos, vector& actors) +{ + ActorList &al = level->actorlist; + for (ActorList::iterator i=al.begin(); i!=al.end(); ++i) { + Actor *a = *i; + if (a->get_gridpos() == pos) + actors.push_back(a); + } + return !actors.empty(); +} + +void world::ShatterActorsInsideField (const GridPos &p) +{ + vector actors; + GetActorsInsideField (p, actors); + vector::iterator i=actors.begin(), + end = actors.end(); + for (; i != end; ++i) + SendMessage(*i, "shatter"); +} + + +/* -------------------- Functions -------------------- */ + +void world::addDelayedImpulse (const Impulse& impulse, double delay, + const Stone *estimated_receiver) +{ + // @@@ FIXME: is a special handling necessary if several impulses hit same destination ? + + level->delayed_impulses.push_back(DelayedImpulse(impulse, delay, estimated_receiver)); +} + +void world::revokeDelayedImpulses(const Stone *target) { + // Any stone may call this function on deletion. + // When the repository shuts down no world is existing thus check + // world first. + if (level.get() != NULL) + level->revoke_delayed_impulses(target); +} + +float world::getVolume(const char *name, Object *obj, float def_volume) +{ + // See sound.hh and sound.cc for details. + SoundDampingList::iterator i = level->sound_dampings.begin(), + end = level->sound_dampings.end(); + while (i != end) { + if (i->is_equal(name, obj)) + return i->get_volume(def_volume); + ++i; + } + // No entry found for this object. Create a new one. + level->sound_dampings.push_back(sound::SoundDamping(name, obj)); + return def_volume; +} + +void world::Tick(double dtime) { + level->tick (dtime); +} + +void world::TickFinished () { + for (unsigned i=0; iactorlist.size(); ++i) { + level->actorlist[i]->move_screen(); + } + + // + for (unsigned i=0; im_rubberbands.size();++i) + level->m_rubberbands[i]->tick (0.0); +} + +void world::Init() +{ + InitActors(); + lasers::Init(); + InitItems(); + stones::Init(); + InitFloors(); +} + +void world::Shutdown() +{ + level.reset(); + Repos_Shutdown(); +} + + +/* -------------------- Object repository -------------------- */ +namespace +{ + class ObjectRepos : public ecl::Nocopy { + public: + ObjectRepos(); + ~ObjectRepos(); + void add_templ(Object *o); + void add_templ (const string &name, Object *o); + bool has_templ(const string &name); + Object *make(const string &name); + Object *get_template(const string &name); + + void dump_info(); + private: + typedef std::map ObjectMap; + ObjectMap objmap; // repository of object templates + int stonecount, floorcount, itemcount; + }; +} + +ObjectRepos::ObjectRepos() { + stonecount = floorcount = itemcount = 0; +} + +ObjectRepos::~ObjectRepos() +{ + ecl::delete_map(objmap.begin(), objmap.end()); +} + + +void ObjectRepos::add_templ (const string &kind, Object *o) +{ + if (has_templ(kind)) + enigma::Log << "add_templ: redefinition of object `" <get_kind(); + if (has_templ(kind)) + enigma::Log << "add_templ: redefinition of object `" <second->clone(); +} + +Object * ObjectRepos::get_template(const string &name) { + if (objmap.find (name) != objmap.end()) + return objmap[name]; + else + return 0; +} + +/* Generate a list of all available objects and their attributes. */ +void ObjectRepos::dump_info() +{ + ObjectMap::iterator iter = objmap.begin(); + for (; iter != objmap.end(); ++iter) { + cout << iter->first << "( "; + Object *obj = iter->second; + const Object::AttribMap &a = obj->get_attribs(); + for (Object::AttribMap::const_iterator j=a.begin(); j!=a.end(); ++j) + { + if (j->first != "kind") + cout << j->first << " "; + } + cout << ")\n"; + } +} + + +namespace +{ + ObjectRepos *repos; + vector actor_repos(ac_COUNT); + vector stone_repos(st_COUNT); + vector item_repos(it_COUNT); +} + +void world::Register (const string &kind, Object *obj) { + if (!repos) + repos = new ObjectRepos; + if (kind.empty()) + repos->add_templ(obj->get_kind(), obj); + else + repos->add_templ(kind, obj); +} + + +void world::Register (Object *obj) { + Register (obj->get_kind(), obj); +} + +void world::Register (const string &kind, Floor *obj) +{ + Object *o = obj; + Register(kind, o); +} + +void world::Register (const string &kind, Stone *obj) { + Object *o = obj; + Register(kind, o); +} + +void world::RegisterStone (Stone *stone) +{ + Register(static_cast(stone)); + StoneID id = get_id(stone); + ASSERT (id != st_INVALID, XLevelRuntime, + "RegisterStone: trying to register with invalid ID"); + stone_repos[id] = stone; +} + +void world::RegisterActor (Actor *actor) +{ + Register(static_cast(actor)); + ActorID id = get_id(actor); + ASSERT (id != ac_INVALID, XLevelRuntime, + "RegisterActor: trying to register with invalid ID"); + actor_repos[id] = actor; +} + +void world::Repos_Shutdown() { + delete repos; +} + +Object * world::MakeObject(const char *kind) { + static Object *last_templ = 0; + static string last_kind; + + if (last_kind != kind) { + last_kind = kind; + last_templ = repos->get_template(kind); + } + + Object *o = 0; + if (last_templ) + o=last_templ->clone(); + if (!o) + fprintf(stderr, "MakeObject: unknown object name `%s'\n",kind); + return o; +} + +Object * world::GetObjectTemplate(const std::string &kind) { + if (!repos->has_templ(kind)) { + cerr << "GetObjectTemplate: unknown object name `" <get_template(kind); +} + +Floor* world::MakeFloor(const char *kind) { + return dynamic_cast(MakeObject(kind)); +} + +Stone * world::MakeStone (const char *kind) { + return dynamic_cast(MakeObject(kind)); +} + +Actor * world::MakeActor (const char *kind) { + return dynamic_cast(MakeObject(kind)); +} + +Actor *world::MakeActor (ActorID id) +{ + if (Actor *ac = actor_repos[id]) + return ac->clone(); + else + ASSERT(0, XLevelRuntime, "MakeActor: no actor for ID defined"); + return 0; +} + +Stone *world::MakeStone (StoneID id) +{ + if (Stone *st = stone_repos[id]) + return st->clone(); + else + ASSERT(0, XLevelRuntime, "MakeStone: no stone for ID defined"); + return 0; +} + + +void world::DisposeObject(Object *o) { + if (o != 0) { + UnnameObject(o); + o->dispose(); + } +} + +void world::DefineSimpleFloor(const std::string &kind, double friction, + double mousefactor, bool burnable, + const std::string &firetransform) +{ + Register(new Floor(kind.c_str(), friction, mousefactor, + flf_default, burnable ? flft_burnable : flft_default, + firetransform.c_str(), "")); +} + +void world::DumpObjectInfo() { + repos->dump_info(); +} + +/* ------------------- Item repository ------------------- */ + +void world::Register (const string &kind, Item *obj) +{ + Object *o = obj; + world::Register(kind, o); +} + +void world::RegisterItem (Item *item) +{ + Register(static_cast(item)); + ItemID id = get_id(item); + ASSERT(id != it_INVALID, XLevelRuntime, + "RegisterItem: trying to register with invalid ID"); + item_repos[id] = item; +} + +Item *world::MakeItem (ItemID id) +{ + if (Item *it = item_repos[id]) + return it->clone(); + else + ASSERT(0, XLevelRuntime, "MakeItem: no item for ID defined"); + return 0; +} + +Item * world::MakeItem(const char *kind) { + return dynamic_cast(MakeObject(kind)); +} + diff --git a/project/jni/application/enigma/src/world.hh b/project/jni/application/enigma/src/world.hh new file mode 100644 index 000000000..623e73300 --- /dev/null +++ b/project/jni/application/enigma/src/world.hh @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2002,2003,2004,2005 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef WORLD_HH_INCLUDED +#define WORLD_HH_INCLUDED + +#include "objects.hh" +#include "util.hh" + +namespace world +{ + using namespace enigma; + +/* -------------------- Types -------------------- */ + + using ecl::V2; + + using std::string; + + /*! + * Impulse is used to transfer force from one Object to another + * Object. Currently only Stones can be the destination of + * Impulses. + */ + struct Impulse { + // Variables + Object *sender; + GridPos dest; + Direction dir; + + // Constructors + Impulse(Object *sender_, const GridPos& dest_, Direction dir_) + : sender(sender_), dest(dest_), dir(dir_) + {} + }; + + struct Message { + // Variables + std::string message; + enigma::Value value; + GridPos gridpos; + + // Constructors + Message (); + Message (const std::string &message, + const enigma::Value &value, + GridPos gridpos = GridPos()); + + }; + + + /*! The different kinds of materials objects in Enigma can be made + of. */ + enum MaterialType { + MATERIAL_GLASS, + MATERIAL_METAL, + MATERIAL_STONE, + }; + + /*! + * This structure stores all the information that is necessary to + * handle collisions between stones and actors. + */ + struct StoneContact + { + // Variables. + Actor *actor; + GridPos stonepos; + StoneID stoneid; + StoneResponse response; + + V2 contact_point; // Where do the shapes meet? (world coordinates) + V2 normal; // The surface normal at the contact point + bool is_collision; // Actor moves towards the stone, not away + bool ignore; // Ignore this contact + bool new_collision; // True if actor did not touch the stone before + bool is_contact; // if false, contact_point is closest feature + std::string sound; + + // Constructor. + StoneContact (Actor *a, + GridPos stonepos, + const V2 & contact_point, + const V2 & surface_normal); + StoneContact(); + }; + + // Which of the four faces of the stone does the actor touch? + Direction contact_face (const StoneContact &sc); + DirectionBits contact_faces (const StoneContact &sc); + +/* -------------------- Force Fields -------------------- */ + + class ForceField { + public: + virtual ~ForceField() {} + virtual void add_force(Actor *a, V2 &force) = 0; + virtual void tick(double /*dtime*/) {} + }; + + class ConstantForce : public ForceField { + public: + ConstantForce(V2 f=V2()) : force(f) {} + void add_force(Actor * /*a*/, V2 &f) { + f += force; + } + void set_force (const V2 &force_) { force = force_; } + private: + V2 force; + }; + +//---------------------------------------------------------------------- +// GLOBAL VARIABLES +//---------------------------------------------------------------------- + + /* The global timer for all objects that need to be notified at + regular intervals. */ + extern enigma::Timer GameTimer; + + /* Output a message whenever a message is being sent. */ + extern bool TrackMessages; + + /* A hack to implement BlackBallsStone and WhiteBallsStone. */ + extern Actor *CurrentCollisionActor; + +//---------------------------------------------------------------------- +// FUNCTIONS +//---------------------------------------------------------------------- + +/* -------------------- World Management -------------------- */ + + void Init(); + + void PrepareLevel (); + + /* Create a new, empty world with width `w' and height `h`. */ + void Resize (int w, int h); + + /* Initialize the world after loading it. Call this after loading + the world to force laser beams to be recalculated etc. */ + bool InitWorld(); + + void Tick(double dtime); + void TickFinished (); + + // Destroy all objects and the complete object repository + void Shutdown(); + + + bool IsLevelBorder(GridPos p); + bool IsInsideLevel(GridPos p); + Object *GetObject (const GridLoc &l); + +/* -------------------- Named Objects -------------------- */ + + void NameObject (Object *obj, const string &name); + void UnnameObject (Object *obj); + void TransferObjectName(Object *source, Object *target); + Object *GetNamedObject (const string &name); + +/* -------------------- Force Fields -------------------- */ + + void AddForceField (ForceField *ff); + void RemoveForceField (ForceField *ff); + void SetMouseForce (V2 f); + + void SetConstantForce (V2 force); + + +/* -------------------- Rubbers Bands -------------------- */ + + struct RubberBandData { + double strength; + double length; + double minlength; + RubberBandData (); + RubberBandData (const RubberBandData &); + }; + + struct Rubber_Band_Info { + Actor *act; + RubberBandData data; + }; + typedef std::vector RBI_vector; + + + /*! Add a rubber band that connects an actor with either a stone + or another actor. `strength' is the force constant, and + `length' is the natural length of the elastic: if it is shorter + than `length' it will exert no force on the actor(s). */ + void AddRubberBand (Actor *a, Stone *st, const RubberBandData &d); + void AddRubberBand (Actor *a, Actor *a2, const RubberBandData &d); + + /*! Remove all rubber bands connected to `a'. */ + bool KillRubberBands (Actor *a); + + /*! Remove the rubber band between `a' and `st'. If `st' is 0, + all rubber bands connecting `a' to a stone will be cut. */ + void KillRubberBand (Actor *a, Stone *st); + + /*! Remove the rubber band between `a' and `a2'. If `a2' is 0, + all rubber bands connecting `a' to other actors will be cut. */ + void KillRubberBand (Actor *a, Actor *a2); + + /*! Remove all rubber bands attached to stone ST. */ + void KillRubberBands (Stone *st); + + /*! Fills given vector with basic info about rubbers attached to + given stone */ + void GiveRubberBands (Stone *st, RBI_vector &rubbers); + + /*! Returns true if there already is a rubber band connecting `a' + and `st'. */ + bool HasRubberBand (Actor *a, Stone *st); + +/* -------------------- Puzzle Stone Scrambling -------------------- */ + + void SetScrambleIntensity(int intensity); + void AddScramble(GridPos p, Direction d); + + + +/* -------------------- Signals & Messages -------------------- */ + + void AddSignal (const GridLoc &src, + const GridLoc &dst, + const string &msg); + + bool HaveSignals (Object *src); + + /*! Return true if suitable signal was found. */ + bool EmitSignalByIndex (Object *src, int signalidx, int value=0); + + /* Signal indices start at 0 */ + bool GetSignalTargetPos (Object *src, GridPos &pos, int signalidx = 0); + + void BroadcastMessage (const std::string& msg, const enigma::Value& value, + GridLayerBits grids); + + Value SendMessage (Object *o, const string &msg); + Value SendMessage (Object *o, const string &msg, const enigma::Value& value); + Value SendMessage (Object *o, const Message &m); + + /*! This function is used by all triggers, switches etc. that + perform some particular action when activated (like opening + doors or switching lasers on and off). It interprets the + "action" and "target" attributes of `o'. */ + void PerformAction (Object *o, bool onoff); + + +/* -------------------- Actors -------------------- */ + + void AddActor (Actor *a); + void AddActor (double x, double y, Actor* a); + + Actor *YieldActor(Actor *a); + void KillActor (Actor *a); + + void WarpActor (Actor *a, double newx, double newy, bool keep_velocity); + + /*! Move `a' to its respawnpos immediately; do not run an + animation like `RespawnActor(a)' would. */ + void FastRespawnActor (Actor *a, bool keep_velocity); + + /*! Like FastRespawnActor but marble 'appears' by running an + animation. */ + void RespawnActor (Actor *a); + + /*! Find the marble of the other color (ac-whiteball <-> + ac-blackball). */ + Actor *FindOtherMarble (Actor *thisMarble); + + /*! Searches for other marble and exchanges their positions. + Returns false if no other marble could be found. */ + bool ExchangeMarbles (Actor *marble1); + + void GrabActor (Actor *a); + void ReleaseActor (Actor *a); + + unsigned CountActorsOfKind (ActorID id); + + /*! Find all actors at most RANGE units away from CENTER. Returns + false if none were found. */ + bool GetActorsInRange (ecl::V2 center, double range, std::vector &actors); + + /*! Find all actors that are inside 'pos'. Returns false if none + were found. */ + bool GetActorsInsideField (const GridPos& pos, std::vector& actors); + + /*! Shatter all actors inside 'pos'. */ + void ShatterActorsInsideField (const GridPos &pos); + + +/* -------------------- Field -------------------- */ + + struct Field { + // Variables + Floor *floor; + Item *item; + Stone *stone; + + // Constructor and Destructor + Field(); + ~Field(); + }; + + const Field *GetField (GridPos p); + +/* -------------------- Stones -------------------- */ + + void SetStone(GridPos p, Stone* st); + void ReplaceStone(GridPos p, Stone *st); + Stone *GetStone(GridPos p); + Stone *YieldStone(GridPos p); + void KillStone(GridPos p); + + void MoveStone(GridPos oldPos, GridPos newPos); + +/* -------------------- Items -------------------- */ + + void SetItem (GridPos p, Item* it); + void SetItem (GridPos p, ItemID id); + Item *GetItem (GridPos p); + Item *YieldItem (GridPos p); + void KillItem (GridPos p); + +/* -------------------- Floors -------------------- */ + + void SetFloor (GridPos p, Floor* st); + Floor *GetFloor (GridPos p); + void KillFloor (GridPos p); + +/* -------------------- Impulses -------------------- */ + + void addDelayedImpulse(const Impulse& impulse, double delay, + const Stone *estimated_receiver); + + /*! Revoke all delayed impulses with TARGET as sender or receiver. */ + void revokeDelayedImpulses(const Stone *target); + + +/* -------------------- Explosions -------------------- */ + + enum ExplosionType { + EXPLOSION_DYNAMITE, + EXPLOSION_BLACKBOMB, + EXPLOSION_WHITEBOMB, + EXPLOSION_BOMBSTONE, + EXPLOSION_SPITTER, + }; + void SendExplosionEffect (GridPos p, ExplosionType type); + +/* -------------------- Sound Events and Damping -------------------- */ + + float getVolume(const char *name, Object *obj, float def_volume = 1.0); + +/* -------------------- Creation/Definition of objects -------------------- */ + + Object *MakeObject (const char *kind); + Floor *MakeFloor (const char *kind); + Stone *MakeStone (const char *kind); + Stone *MakeStone (StoneID id); + Actor *MakeActor (const char *kind); + Actor *MakeActor (ActorID id); + + void DisposeObject(Object *o); + + void DefineSimpleStone (const string &kind, const string &sound, + int hollow, int glass); + + void DefineSimpleStoneMovable (const string &kind, const string &sound, + int glass); + + void DefineSimpleFloor(const string &kind, double friction, + double mousefactor, bool burnable, + const string &firetransform); + + /* Register a new object. */ + void Register (Object *obj); + void Register (const string &kind, Object *obj); + void Register (const string &kind, Floor *obj); + void Register (const string &kind, Stone *obj); + void RegisterStone (Stone *st); + void RegisterActor (Actor *ac); + + + + void Register (const string &kind, Item *obj); + void RegisterItem (Item *it); + Item *MakeItem (const char *kind); + Item *MakeItem (ItemID id); + + /* Shutdown object repository */ + void Repos_Shutdown(); + + Object *GetObjectTemplate(const string &kind); + + /* Print information about all registered objects to stdout. */ + void DumpObjectInfo(); +} + +#endif diff --git a/project/jni/application/enigma/src/world_internal.hh b/project/jni/application/enigma/src/world_internal.hh new file mode 100644 index 000000000..bccea2dc7 --- /dev/null +++ b/project/jni/application/enigma/src/world_internal.hh @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2002,2003,2004 Daniel Heck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include + +namespace world +{ + struct Field; + struct Signal; + + typedef ecl::Array2 FieldArray; + + typedef vector ForceList; + typedef vector StoneContactList; + typedef vector ActorList; + typedef vector SignalList; + + +/* -------------------- Signals -------------------- */ + + struct Signal { + // Variables + Object *source; + GridLoc destloc; + string message; + + // Constructor + Signal (Object *src, GridLoc dstloc, const string &msg) + : source (src), destloc(dstloc), message(msg) + {} + }; + +/* -------------------- RubberBand -------------------- */ + + /*! Stores the physical information about a rubber band (to which + object it is attached, its length and force, etc.) */ + class RubberBand { + public: + RubberBand (Actor *a1, Actor *a2, const RubberBandData &d); + RubberBand (Actor *a1, Stone *st, const RubberBandData &d); + ~RubberBand(); + + void apply_forces (); + void tick (double dtime); + + Actor *get_actor() const { return actor; } + Actor *get_actor2() const { return actor2; } + Stone *get_stone() const { return stone; } + + const RubberBandData get_data () const { return data; } + private: + V2 get_p1() const; + V2 get_p2() const; + + // Variables. + Actor *actor, *actor2; + Stone *stone; + display::RubberHandle model; + RubberBandData data; + }; + +/* -------------------- MouseForce -------------------- */ + + /*! This class implements the "force field" that accelerates + objects when the mouse is moved. Only objects that have the + "mouseforce" and the "controllers" attributes set are affected + by this force field. */ + class MouseForce { + public: + void set_force(V2 f) { force=f; } + void add_force(V2 f) { force+=f; } + + V2 get_force(Actor *a) { + if (a->is_flying() || a->is_dead()) + return V2(); + else + return force * a->get_mouseforce(); + } + + void tick (double /*dtime*/) { + force=V2(); + } + private: + V2 force; + }; + + +/* -------------------- Scramble -------------------- */ + + /*! Stores all positions plus direction where puzzle scrambling + should be performed */ + struct Scramble { + // Variables + GridPos pos; + Direction dir; + int intensity; + + Scramble(GridPos p, Direction d, int i) : pos(p), dir(d), intensity(i) {} + + bool expired() const { return intensity<1; } + }; + +/* -------------------- DelayedImpulse -------------------- */ + + class DelayedImpulse { + Impulse impulse; + double delay; + const Stone *receiver; // to test if stone has changed + bool isReferenced; // an itereator references this impulse + bool isObsolete; // the impulse should be deleted + + DelayedImpulse& operator = (const DelayedImpulse& other); // forbidden + public: + DelayedImpulse(const Impulse& impulse_, double delay_, const Stone *receiver_) + : impulse(impulse_), delay(delay_), receiver(receiver_), + isReferenced(false), isObsolete(false) {} + + DelayedImpulse(const DelayedImpulse& other) + : impulse(other.impulse), delay(other.delay), receiver(other.receiver), + isReferenced(other.isReferenced), isObsolete(other.isObsolete) {} + + bool tick(double dtime) { // returns true if Impulse has to be sent NOW + delay -= dtime; + return delay <= 0; + } + + const GridPos& destination() const { return impulse.dest; } + + bool is_receiver(const Stone *target) const { + return target == receiver; + } + + bool is_sender(const Stone *target) const { + return target == impulse.sender; + } + + bool is_referenced() const { + return isReferenced; + } + + void mark_referenced(bool state) { + isReferenced = state; + } + + bool is_obsolete() const { + return isObsolete; + } + + void mark_obsolete() { + isObsolete = true; + } + + void send_impulse(Stone *target) const { + + // @@@ FIXME: the test for equality of Stones sometimes fails + // (e.g. with turnstiles rotated by rotators) + // + // I guess this happens, because when the turnstile-pivot + // removes and add arms during turn, it may happen, that + // the Stone receives the same memory address + // + // Possible fix : add unique ID to all objects + + if (is_receiver(target)) { + // if object did not change since impulse was initiated + target->on_impulse(impulse); + } + } + }; + + typedef list ImpulseList; + +/* -------------------- Layer -------------------- */ + + template + class Layer { + T *defaultval; + public: + Layer(T* deflt = 0) : defaultval(deflt) {} + virtual ~Layer() {} + + T *get(GridPos p); + T *yield(GridPos p); + void set(GridPos p, T *x); + void kill(GridPos p) { dispose(yield(p)); } + + virtual T* raw_get (Field &) = 0; + virtual void raw_set (Field &, T *) = 0; + + private: + virtual void dispose(T *x) { if (x) DisposeObject(x); } + }; + + /* + ** Floor layer + */ + class FloorLayer : public Layer { + private: + Floor *raw_get (Field &f) { return f.floor; } + void raw_set (Field &f, Floor *x) { f.floor = x;} + }; + + + /* + ** Item layer + */ + class ItemLayer : public Layer { + private: + Item *raw_get (Field &f) { return f.item; } + void raw_set (Field &f, Item *x) { f.item = x;} + }; + + /* + ** Stone layer + */ + class StoneLayer : public Layer + { + public: + StoneLayer() : Layer(&borderstone) {} + private: + Stone *raw_get (Field &f) { return f.stone; } + void raw_set (Field &f, Stone *st) { f.stone = st;} + void dispose (Stone *st) { + if (st) { + KillRubberBands(st); + DisposeObject(st); + } + } + + /* This stone is used as the virtual border of the playing area. + It is immovable and indestructible and makes sure the player's + marble cannot leave the level. */ + class BorderStone : public Stone { + public: + BorderStone() : Stone("borderstone") {} + Stone *clone() { return this; } + void dispose() {} + virtual const StoneTraits &get_traits() const { + static StoneTraits border_traits = { + "INVALID", st_borderstone, stf_none, material_stone, 1.0 + }; + return border_traits; + } + }; + + BorderStone borderstone; + }; + +/* ------------- Sound Damping List -------------- */ + +typedef list SoundDampingList; + +/* -------------------- World -------------------- */ + + /*! Contains the level information (in theory everything that is + local to the current level; in practice a lot of things are + still stored in global variables). */ + class World { + public: + World (int ww, int hh); + ~World(); + + bool contains (GridPos p) { + return (p.x>=0 && p.y>=0 && p.xcontains(p)) + return &fields(p.x,p.y); + else + return 0; + } + + void name_object (Object *obj, const string &name); + void unname (Object *); + Object *get_named (const string &); + + void tick (double dtime); + void remove (ForceField *ff); + + void add_scramble(GridPos p, Direction dir); + void scramble_puzzles(); + + void add_actor (Actor *a); + void add_actor (Actor *a, const V2 &pos); + + void tick_actor(Actor *a, double dtime); + + void revoke_delayed_impulses(const Stone *target); + + private: + + /* ---------- Private methods ---------- */ + + void add_mouseforce (Actor *a, Floor *floor, V2 &mforce); + V2 get_local_force (Actor *a); + V2 get_global_force (Actor *a); + + void advance_actor (Actor *a, double h); + void move_actors (double dtime); + void find_contact_with_stone (Actor *a, GridPos p, StoneContact &c); + void find_stone_contacts (Actor *a, StoneContactList &cl); + void handle_stone_contact (StoneContact &sc); + void handle_actor_contacts (); + void handle_actor_contact (size_t a1, size_t a2); + void handle_contacts (unsigned actoridx); + void handle_delayed_impulses (double dtime); + void stone_change (GridPos p); + void tick_sound_dampings (); + + public: + + /* ---------- Variables ---------- */ + + FieldArray fields; // Contains floors, items, etc. + int w, h; // Width and height of the level + ForceList forces; + ActorList actorlist; // List of movable, dynamic objects + vector m_rubberbands; + SignalList m_signals; + MouseForce m_mouseforce; + ConstantForce m_flatforce; + int scrambleIntensity; + + //! True if game is not running yet + bool preparing_level; + + ImpulseList delayed_impulses; + vector changed_stones; + + SoundDampingList sound_dampings; // see sound.hh for details + + FloorLayer fl_layer; + ItemLayer it_layer; + StoneLayer st_layer; + + private: + ecl::Dict m_objnames; // Name -> object mapping + + list scrambles; + }; +} + diff --git a/project/jni/application/src b/project/jni/application/src index 4de17cd97..c8b486527 120000 --- a/project/jni/application/src +++ b/project/jni/application/src @@ -1 +1 @@ -alienblaster \ No newline at end of file +enigma \ No newline at end of file diff --git a/project/res/values/strings.xml b/project/res/values/strings.xml index b0908ee46..baac56d7f 100644 --- a/project/res/values/strings.xml +++ b/project/res/values/strings.xml @@ -1,6 +1,6 @@ - Alien Blaster + enigma Initializing diff --git a/project/src/Accelerometer.java b/project/src/Accelerometer.java index 8218593d2..85bb7f6f5 100644 --- a/project/src/Accelerometer.java +++ b/project/src/Accelerometer.java @@ -1,5 +1,5 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import android.app.Activity; import android.content.Context; diff --git a/project/src/AssetExtract.java b/project/src/AssetExtract.java index 72f535f7e..91623c035 100644 --- a/project/src/AssetExtract.java +++ b/project/src/AssetExtract.java @@ -1,6 +1,6 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change // spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import java.util.zip.*; import java.io.*; diff --git a/project/src/Audio.java b/project/src/Audio.java index a7a3bf744..ba74bb618 100644 --- a/project/src/Audio.java +++ b/project/src/Audio.java @@ -1,5 +1,5 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import android.app.Activity; diff --git a/project/src/GLSurfaceView_SDL.java b/project/src/GLSurfaceView_SDL.java index ecb423fcf..613821999 100644 --- a/project/src/GLSurfaceView_SDL.java +++ b/project/src/GLSurfaceView_SDL.java @@ -18,7 +18,7 @@ fixed with a hammer and rasp to work with libSDL port */ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import java.io.Writer; import java.util.ArrayList; diff --git a/project/src/Globals.java b/project/src/Globals.java index 7b4fb0b18..33739bdad 100644 --- a/project/src/Globals.java +++ b/project/src/Globals.java @@ -1,14 +1,14 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount anywhere -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import android.app.Activity; import android.content.Context; class Globals { - public static String ApplicationName = "AlienBlaster"; + public static String ApplicationName = "enigma"; // Should be zip file - public static String DataDownloadUrl = "Data size is 2 Mb|http://sites.google.com/site/xpelyax/Home/alienblaster110_data.zip?attredirects=0%26d=1|http://sitesproxy.goapk.com/site/xpelyax/Home/alienblaster110_data.zip"; + public static String DataDownloadUrl = "Enigma Game Data (16 MiB)|https://sites.google.com/site/xpelyax/Home/enigma-data.zip?attredirects=0%26d=1"; // Set this value to true if you're planning to render 3D using OpenGL - it eats some GFX resources, so disabled for 2D public static boolean NeedDepthBuffer = false; @@ -17,21 +17,21 @@ class Globals { public static boolean HorizontalOrientation = true; // Readme text to be shown on download page - public static String ReadmeText = "^You can press \"Home\" now - the data will be downloaded in background^In game press \"Menu\" for secondary fire, \"Volume Up/Down\" to cycle weapons".replace("^","\n"); + public static String ReadmeText = "^You can press \"Home\" now - the data will be downloaded in background^Have fun playing enigma!^".replace("^","\n"); - public static boolean AppUsesMouse = false; + public static boolean AppUsesMouse = true; - public static boolean AppNeedsArrowKeys = true; + public static boolean AppNeedsArrowKeys = false; - public static boolean AppUsesJoystick = false; + public static boolean AppUsesJoystick = true; public static boolean AppUsesMultitouch = false; public static boolean NonBlockingSwapBuffers = false; - public static int AppTouchscreenKeyboardKeysAmount = 4; + public static int AppTouchscreenKeyboardKeysAmount = 0; - public static int AppTouchscreenKeyboardKeysAmountAutoFire = 1; + public static int AppTouchscreenKeyboardKeysAmountAutoFire = 0; // Phone-specific config // It will download app data to /sdcard/alienblaster if set to true, @@ -50,5 +50,5 @@ class Globals { } class LoadLibrary { - public LoadLibrary() { System.loadLibrary("sdl-1.3"); System.loadLibrary("sdl_mixer"); System.loadLibrary("sdl_image"); }; + public LoadLibrary() { System.loadLibrary("sdl-1.2"); System.loadLibrary("sdl_mixer"); System.loadLibrary("sdl_image"); System.loadLibrary("sdl_ttf"); System.loadLibrary("intl"); System.loadLibrary("lua"); }; } diff --git a/project/src/MainActivity.java b/project/src/MainActivity.java index be53b5dd9..3aab35e7f 100644 --- a/project/src/MainActivity.java +++ b/project/src/MainActivity.java @@ -1,5 +1,5 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import android.app.Activity; import android.content.Context; diff --git a/project/src/Settings.java b/project/src/Settings.java index 2bdf841d8..a29901cbe 100644 --- a/project/src/Settings.java +++ b/project/src/Settings.java @@ -1,5 +1,5 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import android.app.Activity; import android.content.Context; diff --git a/project/src/Video.java b/project/src/Video.java index b70c5f7ea..29a2789bb 100644 --- a/project/src/Video.java +++ b/project/src/Video.java @@ -1,5 +1,5 @@ // This string is autogenerated by ChangeAppSettings.sh, do not change spaces amount -package de.schwardtnet.alienblaster; +package org.enigmagame.enigma; import javax.microedition.khronos.opengles.GL10;