From 38bceffddd0d96fa57d79fc53354118afa1d0edc Mon Sep 17 00:00:00 2001 From: pelya Date: Tue, 14 Dec 2010 09:47:29 +0000 Subject: [PATCH] Added GemRB sources --- .../application/gemrb/AndroidAppSettings.cfg | 33 + .../application/gemrb/AndroidData/data.zip | Bin 0 -> 1141120 bytes project/jni/application/gemrb/icon.png | Bin 0 -> 2467 bytes project/jni/application/gemrb/src/AUTHORS | 17 + project/jni/application/gemrb/src/COPYING | 340 + project/jni/application/gemrb/src/GemRB.cpp | 71 + project/jni/application/gemrb/src/NEWS | 381 + project/jni/application/gemrb/src/README | 77 + project/jni/application/gemrb/src/TODO | 101 + .../application/gemrb/src/core/ActorMgr.cpp | 29 + .../jni/application/gemrb/src/core/ActorMgr.h | 41 + .../application/gemrb/src/core/Ambient.cpp | 36 + .../jni/application/gemrb/src/core/Ambient.h | 77 + .../application/gemrb/src/core/AmbientMgr.cpp | 62 + .../application/gemrb/src/core/AmbientMgr.h | 48 + .../gemrb/src/core/AnimStructures.h | 29 + .../application/gemrb/src/core/Animation.cpp | 261 + .../application/gemrb/src/core/Animation.h | 72 + .../gemrb/src/core/AnimationFactory.cpp | 168 + .../gemrb/src/core/AnimationFactory.h | 58 + .../gemrb/src/core/AnimationMgr.cpp | 29 + .../application/gemrb/src/core/AnimationMgr.h | 47 + .../gemrb/src/core/ArchiveImporter.cpp | 29 + .../gemrb/src/core/ArchiveImporter.h | 40 + .../jni/application/gemrb/src/core/Audio.cpp | 35 + .../jni/application/gemrb/src/core/Audio.h | 82 + .../jni/application/gemrb/src/core/Bitmap.cpp | 29 + .../jni/application/gemrb/src/core/Bitmap.h | 55 + .../application/gemrb/src/core/CMakeLists.txt | 49 + .../jni/application/gemrb/src/core/Cache.cpp | 311 + .../jni/application/gemrb/src/core/Cache.h | 93 + .../application/gemrb/src/core/Calendar.cpp | 96 + .../jni/application/gemrb/src/core/Calendar.h | 40 + .../application/gemrb/src/core/Callback.cpp | 33 + .../jni/application/gemrb/src/core/Callback.h | 35 + .../gemrb/src/core/CharAnimations.cpp | 2359 ++++ .../gemrb/src/core/CharAnimations.h | 231 + .../application/gemrb/src/core/Compressor.cpp | 37 + .../application/gemrb/src/core/Compressor.h | 41 + .../gemrb/src/core/ControlAnimation.cpp | 138 + .../gemrb/src/core/ControlAnimation.h | 51 + .../jni/application/gemrb/src/core/Core.cpp | 323 + .../gemrb/src/core/DataFileMgr.cpp | 29 + .../application/gemrb/src/core/DataFileMgr.h | 58 + .../jni/application/gemrb/src/core/Dialog.cpp | 100 + .../jni/application/gemrb/src/core/Dialog.h | 82 + .../gemrb/src/core/DialogHandler.cpp | 473 + .../gemrb/src/core/DialogHandler.h | 51 + .../application/gemrb/src/core/DialogMgr.cpp | 29 + .../application/gemrb/src/core/DialogMgr.h | 36 + .../gemrb/src/core/DisplayMessage.cpp | 230 + .../gemrb/src/core/DisplayMessage.h | 70 + .../jni/application/gemrb/src/core/Effect.h | 139 + .../application/gemrb/src/core/EffectMgr.cpp | 29 + .../application/gemrb/src/core/EffectMgr.h | 56 + .../gemrb/src/core/EffectQueue.cpp | 1980 +++ .../application/gemrb/src/core/EffectQueue.h | 296 + .../application/gemrb/src/core/Factory.cpp | 65 + .../jni/application/gemrb/src/core/Factory.h | 42 + .../gemrb/src/core/FactoryObject.cpp | 33 + .../gemrb/src/core/FactoryObject.h | 35 + .../jni/application/gemrb/src/core/Font.cpp | 560 + project/jni/application/gemrb/src/core/Font.h | 111 + .../application/gemrb/src/core/GUI/Button.cpp | 726 ++ .../application/gemrb/src/core/GUI/Button.h | 219 + .../gemrb/src/core/GUI/Console.cpp | 221 + .../application/gemrb/src/core/GUI/Console.h | 93 + .../gemrb/src/core/GUI/Control.cpp | 274 + .../application/gemrb/src/core/GUI/Control.h | 149 + .../gemrb/src/core/GUI/EventMgr.cpp | 439 + .../application/gemrb/src/core/GUI/EventMgr.h | 142 + .../gemrb/src/core/GUI/GameControl.cpp | 2718 ++++ .../gemrb/src/core/GUI/GameControl.h | 252 + .../application/gemrb/src/core/GUI/Label.cpp | 152 + .../application/gemrb/src/core/GUI/Label.h | 83 + .../gemrb/src/core/GUI/MapControl.cpp | 540 + .../gemrb/src/core/GUI/MapControl.h | 110 + .../gemrb/src/core/GUI/Progressbar.cpp | 184 + .../gemrb/src/core/GUI/Progressbar.h | 89 + .../gemrb/src/core/GUI/ScrollBar.cpp | 297 + .../gemrb/src/core/GUI/ScrollBar.h | 103 + .../application/gemrb/src/core/GUI/Slider.cpp | 296 + .../application/gemrb/src/core/GUI/Slider.h | 106 + .../gemrb/src/core/GUI/TextArea.cpp | 958 ++ .../application/gemrb/src/core/GUI/TextArea.h | 175 + .../gemrb/src/core/GUI/TextEdit.cpp | 231 + .../application/gemrb/src/core/GUI/TextEdit.h | 101 + .../application/gemrb/src/core/GUI/Window.cpp | 442 + .../application/gemrb/src/core/GUI/Window.h | 190 + .../gemrb/src/core/GUI/WorldMapControl.cpp | 399 + .../gemrb/src/core/GUI/WorldMapControl.h | 115 + .../jni/application/gemrb/src/core/Game.cpp | 1816 +++ project/jni/application/gemrb/src/core/Game.h | 424 + .../application/gemrb/src/core/GameData.cpp | 490 + .../jni/application/gemrb/src/core/GameData.h | 121 + .../gemrb/src/core/GameScript/Actions.cpp | 7109 +++++++++++ .../gemrb/src/core/GameScript/GSUtils.cpp | 2332 ++++ .../gemrb/src/core/GameScript/GSUtils.h | 142 + .../gemrb/src/core/GameScript/GameScript.cpp | 2337 ++++ .../gemrb/src/core/GameScript/GameScript.h | 1532 +++ .../gemrb/src/core/GameScript/Matching.cpp | 685 + .../gemrb/src/core/GameScript/Matching.h | 47 + .../gemrb/src/core/GameScript/Objects.cpp | 1158 ++ .../gemrb/src/core/GameScript/Triggers.cpp | 4443 +++++++ .../gemrb/src/core/GlobalTimer.cpp | 339 + .../application/gemrb/src/core/GlobalTimer.h | 78 + .../jni/application/gemrb/src/core/Holder.h | 95 + .../jni/application/gemrb/src/core/Image.cpp | 47 + .../jni/application/gemrb/src/core/Image.h | 61 + .../gemrb/src/core/ImageFactory.cpp | 42 + .../application/gemrb/src/core/ImageFactory.h | 40 + .../application/gemrb/src/core/ImageMgr.cpp | 92 + .../jni/application/gemrb/src/core/ImageMgr.h | 68 + .../gemrb/src/core/ImageWriter.cpp | 27 + .../application/gemrb/src/core/ImageWriter.h | 35 + .../application/gemrb/src/core/IniSpawn.cpp | 716 ++ .../jni/application/gemrb/src/core/IniSpawn.h | 168 + .../application/gemrb/src/core/Interface.cpp | 5382 ++++++++ .../application/gemrb/src/core/Interface.h | 786 ++ .../application/gemrb/src/core/Inventory.cpp | 1833 +++ .../application/gemrb/src/core/Inventory.h | 351 + .../jni/application/gemrb/src/core/Item.cpp | 247 + project/jni/application/gemrb/src/core/Item.h | 266 + .../application/gemrb/src/core/ItemMgr.cpp | 29 + .../jni/application/gemrb/src/core/ItemMgr.h | 48 + .../application/gemrb/src/core/LRUCache.cpp | 219 + .../jni/application/gemrb/src/core/LRUCache.h | 59 + .../application/gemrb/src/core/Makefile.am | 116 + .../jni/application/gemrb/src/core/Map.cpp | 3617 ++++++ project/jni/application/gemrb/src/core/Map.h | 493 + .../jni/application/gemrb/src/core/MapMgr.cpp | 29 + .../jni/application/gemrb/src/core/MapMgr.h | 52 + .../gemrb/src/core/MoviePlayer.cpp | 31 + .../application/gemrb/src/core/MoviePlayer.h | 50 + .../application/gemrb/src/core/MusicMgr.cpp | 33 + .../jni/application/gemrb/src/core/MusicMgr.h | 49 + .../application/gemrb/src/core/Palette.cpp | 243 + .../jni/application/gemrb/src/core/Palette.h | 99 + .../gemrb/src/core/PalettedImageMgr.cpp | 29 + .../gemrb/src/core/PalettedImageMgr.h | 49 + .../application/gemrb/src/core/Particles.cpp | 387 + .../application/gemrb/src/core/Particles.h | 137 + .../application/gemrb/src/core/PathFinder.h | 52 + .../jni/application/gemrb/src/core/Plugin.cpp | 29 + .../jni/application/gemrb/src/core/Plugin.h | 65 + .../application/gemrb/src/core/PluginMgr.cpp | 321 + .../application/gemrb/src/core/PluginMgr.h | 153 + .../application/gemrb/src/core/Polygon.cpp | 387 + .../jni/application/gemrb/src/core/Polygon.h | 81 + .../gemrb/src/core/PolymorphCache.h | 34 + .../application/gemrb/src/core/Projectile.cpp | 1503 +++ .../application/gemrb/src/core/Projectile.h | 357 + .../gemrb/src/core/ProjectileMgr.cpp | 29 + .../gemrb/src/core/ProjectileMgr.h | 36 + .../gemrb/src/core/ProjectileServer.cpp | 325 + .../gemrb/src/core/ProjectileServer.h | 97 + .../jni/application/gemrb/src/core/Region.cpp | 224 + .../jni/application/gemrb/src/core/Region.h | 96 + .../application/gemrb/src/core/Resource.cpp | 35 + .../jni/application/gemrb/src/core/Resource.h | 57 + .../gemrb/src/core/ResourceDesc.cpp | 50 + .../application/gemrb/src/core/ResourceDesc.h | 60 + .../gemrb/src/core/ResourceManager.cpp | 150 + .../gemrb/src/core/ResourceManager.h | 68 + .../gemrb/src/core/ResourceSource.cpp | 29 + .../gemrb/src/core/ResourceSource.h | 47 + .../jni/application/gemrb/src/core/SaveGame.h | 85 + .../gemrb/src/core/SaveGameIterator.cpp | 634 + .../gemrb/src/core/SaveGameIterator.h | 51 + .../gemrb/src/core/SaveGameMgr.cpp | 29 + .../application/gemrb/src/core/SaveGameMgr.h | 38 + .../gemrb/src/core/ScriptEngine.cpp | 29 + .../application/gemrb/src/core/ScriptEngine.h | 40 + .../gemrb/src/core/Scriptable/Actor.cpp | 6945 +++++++++++ .../gemrb/src/core/Scriptable/Actor.h | 718 ++ .../gemrb/src/core/Scriptable/ActorBlock.cpp | 2364 ++++ .../gemrb/src/core/Scriptable/ActorBlock.h | 569 + .../src/core/Scriptable/PCStatStruct.cpp | 201 + .../gemrb/src/core/Scriptable/PCStatStruct.h | 116 + .../gemrb/src/core/ScriptedAnimation.cpp | 719 ++ .../gemrb/src/core/ScriptedAnimation.h | 133 + .../application/gemrb/src/core/SoundMgr.cpp | 32 + .../jni/application/gemrb/src/core/SoundMgr.h | 64 + .../jni/application/gemrb/src/core/Spell.cpp | 241 + .../jni/application/gemrb/src/core/Spell.h | 171 + .../application/gemrb/src/core/SpellMgr.cpp | 29 + .../jni/application/gemrb/src/core/SpellMgr.h | 47 + .../application/gemrb/src/core/Spellbook.cpp | 982 ++ .../application/gemrb/src/core/Spellbook.h | 241 + .../application/gemrb/src/core/Sprite2D.cpp | 151 + .../jni/application/gemrb/src/core/Sprite2D.h | 82 + .../gemrb/src/core/SpriteCover.cpp | 49 + .../application/gemrb/src/core/SpriteCover.h | 39 + .../jni/application/gemrb/src/core/Store.cpp | 283 + .../jni/application/gemrb/src/core/Store.h | 178 + .../application/gemrb/src/core/StoreMgr.cpp | 29 + .../jni/application/gemrb/src/core/StoreMgr.h | 51 + .../application/gemrb/src/core/StringMgr.cpp | 29 + .../application/gemrb/src/core/StringMgr.h | 62 + .../application/gemrb/src/core/SymbolMgr.cpp | 29 + .../application/gemrb/src/core/SymbolMgr.h | 56 + .../src/core/System/CachedFileStream.cpp | 213 + .../gemrb/src/core/System/CachedFileStream.h | 45 + .../gemrb/src/core/System/DataStream.cpp | 185 + .../gemrb/src/core/System/DataStream.h | 77 + .../gemrb/src/core/System/FileStream.cpp | 281 + .../gemrb/src/core/System/FileStream.h | 63 + .../gemrb/src/core/System/MemoryStream.cpp | 110 + .../gemrb/src/core/System/MemoryStream.h | 58 + .../application/gemrb/src/core/System/VFS.cpp | 538 + .../application/gemrb/src/core/System/VFS.h | 164 + .../application/gemrb/src/core/System/swab.c | 33 + .../application/gemrb/src/core/System/swab.h | 35 + .../application/gemrb/src/core/TableMgr.cpp | 84 + .../jni/application/gemrb/src/core/TableMgr.h | 99 + .../jni/application/gemrb/src/core/Tile.cpp | 42 + project/jni/application/gemrb/src/core/Tile.h | 42 + .../application/gemrb/src/core/TileMap.cpp | 645 + .../jni/application/gemrb/src/core/TileMap.h | 96 + .../application/gemrb/src/core/TileMapMgr.cpp | 29 + .../application/gemrb/src/core/TileMapMgr.h | 46 + .../gemrb/src/core/TileOverlay.cpp | 132 + .../application/gemrb/src/core/TileOverlay.h | 46 + .../application/gemrb/src/core/TileSetMgr.cpp | 29 + .../application/gemrb/src/core/TileSetMgr.h | 37 + .../jni/application/gemrb/src/core/TypeID.h | 29 + .../application/gemrb/src/core/Variables.cpp | 500 + .../application/gemrb/src/core/Variables.h | 127 + .../jni/application/gemrb/src/core/Video.cpp | 228 + .../jni/application/gemrb/src/core/Video.h | 237 + .../application/gemrb/src/core/VideoMode.h | 51 + .../application/gemrb/src/core/WindowMgr.cpp | 28 + .../application/gemrb/src/core/WindowMgr.h | 52 + .../application/gemrb/src/core/WorldMap.cpp | 593 + .../jni/application/gemrb/src/core/WorldMap.h | 205 + .../gemrb/src/core/WorldMapMgr.cpp | 29 + .../application/gemrb/src/core/WorldMapMgr.h | 50 + .../jni/application/gemrb/src/core/damages.h | 53 + .../gemrb/src/includes/Makefile.am | 2 + .../gemrb/src/includes/RGBAColor.h | 31 + .../application/gemrb/src/includes/SClassID.h | 89 + .../gemrb/src/includes/defsounds.h | 49 + .../application/gemrb/src/includes/errors.h | 27 + .../application/gemrb/src/includes/exports.h | 48 + .../application/gemrb/src/includes/globals.h | 241 + .../application/gemrb/src/includes/ie_feats.h | 94 + .../application/gemrb/src/includes/ie_stats.h | 524 + .../application/gemrb/src/includes/ie_types.h | 70 + .../application/gemrb/src/includes/iless.h | 31 + .../application/gemrb/src/includes/logging.h | 99 + .../gemrb/src/includes/opcode_params.h | 197 + .../gemrb/src/includes/operatorbool.h | 12 + .../application/gemrb/src/includes/overlays.h | 60 + .../gemrb/src/includes/plugindef.h | 204 + .../application/gemrb/src/includes/strrefs.h | 197 + .../application/gemrb/src/includes/win32def.h | 92 + .../src/plugins/2DAImporter/2DAImporter.cpp | 120 + .../src/plugins/2DAImporter/2DAImporter.h | 169 + .../src/plugins/2DAImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/2DAImporter/Makefile.am | 3 + .../gemrb/src/plugins/ACMReader/ACMReader.cpp | 117 + .../gemrb/src/plugins/ACMReader/ACMReader.h | 73 + .../src/plugins/ACMReader/CMakeLists.txt | 3 + .../gemrb/src/plugins/ACMReader/Makefile.am | 3 + .../gemrb/src/plugins/ACMReader/decoder.cpp | 177 + .../gemrb/src/plugins/ACMReader/decoder.h | 48 + .../gemrb/src/plugins/ACMReader/general.h | 36 + .../gemrb/src/plugins/ACMReader/unpacker.cpp | 454 + .../gemrb/src/plugins/ACMReader/unpacker.h | 93 + .../src/plugins/AREImporter/AREImporter.cpp | 2314 ++++ .../src/plugins/AREImporter/AREImporter.h | 92 + .../src/plugins/AREImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/AREImporter/Makefile.am | 3 + .../src/plugins/BAMImporter/BAMImporter.cpp | 395 + .../src/plugins/BAMImporter/BAMImporter.h | 90 + .../src/plugins/BAMImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/BAMImporter/Makefile.am | 3 + .../src/plugins/BIFImporter/BIFImporter.cpp | 348 + .../src/plugins/BIFImporter/BIFImporter.h | 66 + .../src/plugins/BIFImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/BIFImporter/Makefile.am | 3 + .../gemrb/src/plugins/BIKPlayer/BIKPlayer.cpp | 1631 +++ .../gemrb/src/plugins/BIKPlayer/BIKPlayer.h | 235 + .../src/plugins/BIKPlayer/CMakeLists.txt | 1 + .../src/plugins/BIKPlayer/GetBitContext.cpp | 333 + .../src/plugins/BIKPlayer/GetBitContext.h | 75 + .../gemrb/src/plugins/BIKPlayer/Makefile.am | 16 + .../gemrb/src/plugins/BIKPlayer/binkdata.h | 612 + .../gemrb/src/plugins/BIKPlayer/common.h | 78 + .../gemrb/src/plugins/BIKPlayer/dct.cpp | 97 + .../gemrb/src/plugins/BIKPlayer/dsputil.h | 233 + .../gemrb/src/plugins/BIKPlayer/fft.cpp | 358 + .../gemrb/src/plugins/BIKPlayer/mem.cpp | 68 + .../gemrb/src/plugins/BIKPlayer/rational.cpp | 83 + .../gemrb/src/plugins/BIKPlayer/rational.h | 126 + .../gemrb/src/plugins/BIKPlayer/rdft.cpp | 130 + .../src/plugins/BMPImporter/BMPImporter.cpp | 295 + .../src/plugins/BMPImporter/BMPImporter.h | 56 + .../src/plugins/BMPImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/BMPImporter/Makefile.am | 3 + .../gemrb/src/plugins/BMPWriter/BMPWriter.cpp | 77 + .../gemrb/src/plugins/BMPWriter/BMPWriter.h | 9 + .../src/plugins/BMPWriter/CMakeLists.txt | 1 + .../gemrb/src/plugins/BMPWriter/Makefile.am | 3 + .../src/plugins/CHUImporter/CHUImporter.cpp | 518 + .../src/plugins/CHUImporter/CHUImporter.h | 48 + .../src/plugins/CHUImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/CHUImporter/Makefile.am | 3 + .../gemrb/src/plugins/CMakeLists.txt | 45 + .../src/plugins/CREImporter/CMakeLists.txt | 1 + .../src/plugins/CREImporter/CREImporter.cpp | 2967 +++++ .../src/plugins/CREImporter/CREImporter.h | 112 + .../gemrb/src/plugins/CREImporter/Makefile.am | 3 + .../src/plugins/DLGImporter/CMakeLists.txt | 1 + .../src/plugins/DLGImporter/DLGImporter.cpp | 391 + .../src/plugins/DLGImporter/DLGImporter.h | 85 + .../gemrb/src/plugins/DLGImporter/Makefile.am | 3 + .../plugins/DirectoryImporter/CMakeLists.txt | 1 + .../DirectoryImporter/DirectoryImporter.cpp | 96 + .../DirectoryImporter/DirectoryImporter.h | 46 + .../src/plugins/DirectoryImporter/Makefile.am | 3 + .../src/plugins/EFFImporter/CMakeLists.txt | 1 + .../src/plugins/EFFImporter/EFFImporter.cpp | 243 + .../src/plugins/EFFImporter/EFFImporter.h | 47 + .../gemrb/src/plugins/EFFImporter/Makefile.am | 3 + .../src/plugins/FXOpcodes/CMakeLists.txt | 1 + .../gemrb/src/plugins/FXOpcodes/FXOpcodes.cpp | 6472 ++++++++++ .../gemrb/src/plugins/FXOpcodes/Makefile.am | 3 + .../src/plugins/GAMImporter/CMakeLists.txt | 1 + .../src/plugins/GAMImporter/GAMImporter.cpp | 1192 ++ .../src/plugins/GAMImporter/GAMImporter.h | 79 + .../gemrb/src/plugins/GAMImporter/Makefile.am | 3 + .../src/plugins/GUIScript/CMakeLists.txt | 5 + .../gemrb/src/plugins/GUIScript/GUIScript.cpp | 10355 ++++++++++++++++ .../gemrb/src/plugins/GUIScript/GUIScript.h | 66 + .../gemrb/src/plugins/GUIScript/Makefile.am | 5 + .../src/plugins/GUIScript/PythonHelpers.cpp | 80 + .../src/plugins/GUIScript/PythonHelpers.h | 112 + .../src/plugins/IDSImporter/CMakeLists.txt | 1 + .../src/plugins/IDSImporter/IDSImporter.cpp | 158 + .../src/plugins/IDSImporter/IDSImporter.h | 53 + .../src/plugins/IDSImporter/IDSImporterDefs.h | 37 + .../gemrb/src/plugins/IDSImporter/Makefile.am | 3 + .../src/plugins/INIImporter/CMakeLists.txt | 1 + .../src/plugins/INIImporter/INIImporter.cpp | 165 + .../src/plugins/INIImporter/INIImporter.h | 212 + .../gemrb/src/plugins/INIImporter/Makefile.am | 3 + .../src/plugins/ITMImporter/CMakeLists.txt | 1 + .../src/plugins/ITMImporter/ITMImporter.cpp | 255 + .../src/plugins/ITMImporter/ITMImporter.h | 51 + .../gemrb/src/plugins/ITMImporter/Makefile.am | 3 + .../src/plugins/IWDOpcodes/CMakeLists.txt | 1 + .../src/plugins/IWDOpcodes/IWDOpcodes.cpp | 3371 +++++ .../gemrb/src/plugins/IWDOpcodes/Makefile.am | 3 + .../src/plugins/KEYImporter/CMakeLists.txt | 1 + .../src/plugins/KEYImporter/Dictionary.cpp | 226 + .../src/plugins/KEYImporter/Dictionary.h | 84 + .../src/plugins/KEYImporter/KEYImporter.cpp | 286 + .../src/plugins/KEYImporter/KEYImporter.h | 68 + .../gemrb/src/plugins/KEYImporter/Makefile.am | 3 + .../src/plugins/MOSImporter/CMakeLists.txt | 1 + .../src/plugins/MOSImporter/MOSImporter.cpp | 155 + .../src/plugins/MOSImporter/MOSImporter.h | 39 + .../gemrb/src/plugins/MOSImporter/Makefile.am | 3 + .../src/plugins/MUSImporter/CMakeLists.txt | 1 + .../src/plugins/MUSImporter/MUSImporter.cpp | 335 + .../src/plugins/MUSImporter/MUSImporter.h | 79 + .../gemrb/src/plugins/MUSImporter/Makefile.am | 3 + .../src/plugins/MVEPlayer/CMakeLists.txt | 1 + .../gemrb/src/plugins/MVEPlayer/MVEPlayer.cpp | 187 + .../gemrb/src/plugins/MVEPlayer/MVEPlayer.h | 57 + .../gemrb/src/plugins/MVEPlayer/Makefile.am | 12 + .../gemrb/src/plugins/MVEPlayer/gstmvedemux.h | 141 + .../gemrb/src/plugins/MVEPlayer/mve.h | 64 + .../src/plugins/MVEPlayer/mve_player.cpp | 473 + .../gemrb/src/plugins/MVEPlayer/mve_player.h | 93 + .../src/plugins/MVEPlayer/mveaudiodec.cpp | 82 + .../src/plugins/MVEPlayer/mvevideodec16.cpp | 849 ++ .../src/plugins/MVEPlayer/mvevideodec8.cpp | 802 ++ .../application/gemrb/src/plugins/Makefile.am | 44 + .../src/plugins/NullSound/CMakeLists.txt | 1 + .../gemrb/src/plugins/NullSound/Makefile.am | 3 + .../gemrb/src/plugins/NullSound/NullSound.cpp | 133 + .../gemrb/src/plugins/NullSound/NullSound.h | 55 + .../src/plugins/OGGReader/CMakeLists.txt | 11 + .../gemrb/src/plugins/OGGReader/Makefile.am | 6 + .../gemrb/src/plugins/OGGReader/OGGReader.cpp | 128 + .../gemrb/src/plugins/OGGReader/OGGReader.h | 55 + .../src/plugins/OpenALAudio/AmbientMgrAL.cpp | 312 + .../src/plugins/OpenALAudio/AmbientMgrAL.h | 89 + .../src/plugins/OpenALAudio/CMakeLists.txt | 7 + .../gemrb/src/plugins/OpenALAudio/Makefile.am | 4 + .../src/plugins/OpenALAudio/OpenALAudio.cpp | 925 ++ .../src/plugins/OpenALAudio/OpenALAudio.h | 148 + .../src/plugins/OpenALAudio/StackLock.cpp | 55 + .../gemrb/src/plugins/OpenALAudio/StackLock.h | 38 + .../src/plugins/PLTImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/PLTImporter/Makefile.am | 3 + .../src/plugins/PLTImporter/PLTImporter.cpp | 110 + .../src/plugins/PLTImporter/PLTImporter.h | 37 + .../src/plugins/PNGImporter/CMakeLists.txt | 5 + .../gemrb/src/plugins/PNGImporter/Makefile.am | 4 + .../src/plugins/PNGImporter/PNGImporter.cpp | 217 + .../src/plugins/PNGImporter/PNGImporter.h | 45 + .../src/plugins/PROImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/PROImporter/Makefile.am | 3 + .../src/plugins/PROImporter/PROImporter.cpp | 161 + .../src/plugins/PROImporter/PROImporter.h | 47 + .../src/plugins/PSTOpcodes/CMakeLists.txt | 1 + .../gemrb/src/plugins/PSTOpcodes/Makefile.am | 3 + .../src/plugins/PSTOpcodes/PSTOpcodes.cpp | 596 + .../gemrb/src/plugins/SDLAudio/CMakeLists.txt | 6 + .../gemrb/src/plugins/SDLAudio/Makefile.am | 5 + .../gemrb/src/plugins/SDLAudio/SDLAudio.cpp | 388 + .../gemrb/src/plugins/SDLAudio/SDLAudio.h | 81 + .../gemrb/src/plugins/SDLVideo/CMakeLists.txt | 4 + .../gemrb/src/plugins/SDLVideo/Makefile.am | 5 + .../gemrb/src/plugins/SDLVideo/SDLVideo.cpp | 2549 ++++ .../gemrb/src/plugins/SDLVideo/SDLVideo.h | 173 + .../src/plugins/SDLVideo/SDLVideoDriver.inl | 539 + .../src/plugins/SDLVideo/TileRenderer.inl | 122 + .../src/plugins/SPLImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/SPLImporter/Makefile.am | 3 + .../src/plugins/SPLImporter/SPLImporter.cpp | 242 + .../src/plugins/SPLImporter/SPLImporter.h | 48 + .../src/plugins/STOImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/STOImporter/Makefile.am | 3 + .../src/plugins/STOImporter/STOImporter.cpp | 418 + .../src/plugins/STOImporter/STOImporter.h | 62 + .../src/plugins/TISImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/TISImporter/Makefile.am | 3 + .../src/plugins/TISImporter/TISImporter.cpp | 145 + .../src/plugins/TISImporter/TISImporter.h | 42 + .../src/plugins/TLKImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/TLKImporter/Makefile.am | 3 + .../src/plugins/TLKImporter/TLKImporter.cpp | 572 + .../src/plugins/TLKImporter/TLKImporter.h | 68 + .../src/plugins/TLKImporter/TlkOverride.cpp | 342 + .../src/plugins/TLKImporter/TlkOverride.h | 83 + .../src/plugins/WAVReader/CMakeLists.txt | 3 + .../gemrb/src/plugins/WAVReader/Makefile.am | 3 + .../gemrb/src/plugins/WAVReader/WAVReader.cpp | 193 + .../gemrb/src/plugins/WAVReader/WAVReader.h | 59 + .../src/plugins/WEDImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/WEDImporter/Makefile.am | 3 + .../src/plugins/WEDImporter/WEDImporter.cpp | 348 + .../src/plugins/WEDImporter/WEDImporter.h | 66 + .../src/plugins/WMPImporter/CMakeLists.txt | 1 + .../gemrb/src/plugins/WMPImporter/Makefile.am | 3 + .../src/plugins/WMPImporter/WMPImporter.cpp | 441 + .../src/plugins/WMPImporter/WMPImporter.h | 61 + .../src/plugins/ZLibManager/CMakeLists.txt | 3 + .../gemrb/src/plugins/ZLibManager/Makefile.am | 3 + .../src/plugins/ZLibManager/ZLibManager.cpp | 159 + .../src/plugins/ZLibManager/ZLibManager.h | 36 + 455 files changed, 137680 insertions(+) create mode 100644 project/jni/application/gemrb/AndroidAppSettings.cfg create mode 100644 project/jni/application/gemrb/AndroidData/data.zip create mode 100644 project/jni/application/gemrb/icon.png create mode 100644 project/jni/application/gemrb/src/AUTHORS create mode 100644 project/jni/application/gemrb/src/COPYING create mode 100644 project/jni/application/gemrb/src/GemRB.cpp create mode 100644 project/jni/application/gemrb/src/NEWS create mode 100644 project/jni/application/gemrb/src/README create mode 100644 project/jni/application/gemrb/src/TODO create mode 100644 project/jni/application/gemrb/src/core/ActorMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/ActorMgr.h create mode 100644 project/jni/application/gemrb/src/core/Ambient.cpp create mode 100644 project/jni/application/gemrb/src/core/Ambient.h create mode 100644 project/jni/application/gemrb/src/core/AmbientMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/AmbientMgr.h create mode 100644 project/jni/application/gemrb/src/core/AnimStructures.h create mode 100644 project/jni/application/gemrb/src/core/Animation.cpp create mode 100644 project/jni/application/gemrb/src/core/Animation.h create mode 100644 project/jni/application/gemrb/src/core/AnimationFactory.cpp create mode 100644 project/jni/application/gemrb/src/core/AnimationFactory.h create mode 100644 project/jni/application/gemrb/src/core/AnimationMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/AnimationMgr.h create mode 100644 project/jni/application/gemrb/src/core/ArchiveImporter.cpp create mode 100644 project/jni/application/gemrb/src/core/ArchiveImporter.h create mode 100644 project/jni/application/gemrb/src/core/Audio.cpp create mode 100644 project/jni/application/gemrb/src/core/Audio.h create mode 100644 project/jni/application/gemrb/src/core/Bitmap.cpp create mode 100644 project/jni/application/gemrb/src/core/Bitmap.h create mode 100644 project/jni/application/gemrb/src/core/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/core/Cache.cpp create mode 100644 project/jni/application/gemrb/src/core/Cache.h create mode 100644 project/jni/application/gemrb/src/core/Calendar.cpp create mode 100644 project/jni/application/gemrb/src/core/Calendar.h create mode 100644 project/jni/application/gemrb/src/core/Callback.cpp create mode 100644 project/jni/application/gemrb/src/core/Callback.h create mode 100644 project/jni/application/gemrb/src/core/CharAnimations.cpp create mode 100644 project/jni/application/gemrb/src/core/CharAnimations.h create mode 100644 project/jni/application/gemrb/src/core/Compressor.cpp create mode 100644 project/jni/application/gemrb/src/core/Compressor.h create mode 100644 project/jni/application/gemrb/src/core/ControlAnimation.cpp create mode 100644 project/jni/application/gemrb/src/core/ControlAnimation.h create mode 100644 project/jni/application/gemrb/src/core/Core.cpp create mode 100644 project/jni/application/gemrb/src/core/DataFileMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/DataFileMgr.h create mode 100644 project/jni/application/gemrb/src/core/Dialog.cpp create mode 100644 project/jni/application/gemrb/src/core/Dialog.h create mode 100644 project/jni/application/gemrb/src/core/DialogHandler.cpp create mode 100644 project/jni/application/gemrb/src/core/DialogHandler.h create mode 100644 project/jni/application/gemrb/src/core/DialogMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/DialogMgr.h create mode 100644 project/jni/application/gemrb/src/core/DisplayMessage.cpp create mode 100644 project/jni/application/gemrb/src/core/DisplayMessage.h create mode 100644 project/jni/application/gemrb/src/core/Effect.h create mode 100644 project/jni/application/gemrb/src/core/EffectMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/EffectMgr.h create mode 100644 project/jni/application/gemrb/src/core/EffectQueue.cpp create mode 100644 project/jni/application/gemrb/src/core/EffectQueue.h create mode 100644 project/jni/application/gemrb/src/core/Factory.cpp create mode 100644 project/jni/application/gemrb/src/core/Factory.h create mode 100644 project/jni/application/gemrb/src/core/FactoryObject.cpp create mode 100644 project/jni/application/gemrb/src/core/FactoryObject.h create mode 100644 project/jni/application/gemrb/src/core/Font.cpp create mode 100644 project/jni/application/gemrb/src/core/Font.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Button.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Button.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Console.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Console.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Control.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Control.h create mode 100644 project/jni/application/gemrb/src/core/GUI/EventMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/EventMgr.h create mode 100644 project/jni/application/gemrb/src/core/GUI/GameControl.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/GameControl.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Label.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Label.h create mode 100644 project/jni/application/gemrb/src/core/GUI/MapControl.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/MapControl.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Progressbar.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Progressbar.h create mode 100644 project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/ScrollBar.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Slider.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Slider.h create mode 100644 project/jni/application/gemrb/src/core/GUI/TextArea.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/TextArea.h create mode 100644 project/jni/application/gemrb/src/core/GUI/TextEdit.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/TextEdit.h create mode 100644 project/jni/application/gemrb/src/core/GUI/Window.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/Window.h create mode 100644 project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp create mode 100644 project/jni/application/gemrb/src/core/GUI/WorldMapControl.h create mode 100644 project/jni/application/gemrb/src/core/Game.cpp create mode 100644 project/jni/application/gemrb/src/core/Game.h create mode 100644 project/jni/application/gemrb/src/core/GameData.cpp create mode 100644 project/jni/application/gemrb/src/core/GameData.h create mode 100644 project/jni/application/gemrb/src/core/GameScript/Actions.cpp create mode 100644 project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp create mode 100644 project/jni/application/gemrb/src/core/GameScript/GSUtils.h create mode 100644 project/jni/application/gemrb/src/core/GameScript/GameScript.cpp create mode 100644 project/jni/application/gemrb/src/core/GameScript/GameScript.h create mode 100644 project/jni/application/gemrb/src/core/GameScript/Matching.cpp create mode 100644 project/jni/application/gemrb/src/core/GameScript/Matching.h create mode 100644 project/jni/application/gemrb/src/core/GameScript/Objects.cpp create mode 100644 project/jni/application/gemrb/src/core/GameScript/Triggers.cpp create mode 100644 project/jni/application/gemrb/src/core/GlobalTimer.cpp create mode 100644 project/jni/application/gemrb/src/core/GlobalTimer.h create mode 100644 project/jni/application/gemrb/src/core/Holder.h create mode 100644 project/jni/application/gemrb/src/core/Image.cpp create mode 100644 project/jni/application/gemrb/src/core/Image.h create mode 100644 project/jni/application/gemrb/src/core/ImageFactory.cpp create mode 100644 project/jni/application/gemrb/src/core/ImageFactory.h create mode 100644 project/jni/application/gemrb/src/core/ImageMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/ImageMgr.h create mode 100644 project/jni/application/gemrb/src/core/ImageWriter.cpp create mode 100644 project/jni/application/gemrb/src/core/ImageWriter.h create mode 100644 project/jni/application/gemrb/src/core/IniSpawn.cpp create mode 100644 project/jni/application/gemrb/src/core/IniSpawn.h create mode 100644 project/jni/application/gemrb/src/core/Interface.cpp create mode 100644 project/jni/application/gemrb/src/core/Interface.h create mode 100644 project/jni/application/gemrb/src/core/Inventory.cpp create mode 100644 project/jni/application/gemrb/src/core/Inventory.h create mode 100644 project/jni/application/gemrb/src/core/Item.cpp create mode 100644 project/jni/application/gemrb/src/core/Item.h create mode 100644 project/jni/application/gemrb/src/core/ItemMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/ItemMgr.h create mode 100644 project/jni/application/gemrb/src/core/LRUCache.cpp create mode 100644 project/jni/application/gemrb/src/core/LRUCache.h create mode 100644 project/jni/application/gemrb/src/core/Makefile.am create mode 100644 project/jni/application/gemrb/src/core/Map.cpp create mode 100644 project/jni/application/gemrb/src/core/Map.h create mode 100644 project/jni/application/gemrb/src/core/MapMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/MapMgr.h create mode 100644 project/jni/application/gemrb/src/core/MoviePlayer.cpp create mode 100644 project/jni/application/gemrb/src/core/MoviePlayer.h create mode 100644 project/jni/application/gemrb/src/core/MusicMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/MusicMgr.h create mode 100644 project/jni/application/gemrb/src/core/Palette.cpp create mode 100644 project/jni/application/gemrb/src/core/Palette.h create mode 100644 project/jni/application/gemrb/src/core/PalettedImageMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/PalettedImageMgr.h create mode 100644 project/jni/application/gemrb/src/core/Particles.cpp create mode 100644 project/jni/application/gemrb/src/core/Particles.h create mode 100644 project/jni/application/gemrb/src/core/PathFinder.h create mode 100644 project/jni/application/gemrb/src/core/Plugin.cpp create mode 100644 project/jni/application/gemrb/src/core/Plugin.h create mode 100644 project/jni/application/gemrb/src/core/PluginMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/PluginMgr.h create mode 100644 project/jni/application/gemrb/src/core/Polygon.cpp create mode 100644 project/jni/application/gemrb/src/core/Polygon.h create mode 100644 project/jni/application/gemrb/src/core/PolymorphCache.h create mode 100644 project/jni/application/gemrb/src/core/Projectile.cpp create mode 100644 project/jni/application/gemrb/src/core/Projectile.h create mode 100644 project/jni/application/gemrb/src/core/ProjectileMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/ProjectileMgr.h create mode 100644 project/jni/application/gemrb/src/core/ProjectileServer.cpp create mode 100644 project/jni/application/gemrb/src/core/ProjectileServer.h create mode 100644 project/jni/application/gemrb/src/core/Region.cpp create mode 100644 project/jni/application/gemrb/src/core/Region.h create mode 100644 project/jni/application/gemrb/src/core/Resource.cpp create mode 100644 project/jni/application/gemrb/src/core/Resource.h create mode 100644 project/jni/application/gemrb/src/core/ResourceDesc.cpp create mode 100644 project/jni/application/gemrb/src/core/ResourceDesc.h create mode 100644 project/jni/application/gemrb/src/core/ResourceManager.cpp create mode 100644 project/jni/application/gemrb/src/core/ResourceManager.h create mode 100644 project/jni/application/gemrb/src/core/ResourceSource.cpp create mode 100644 project/jni/application/gemrb/src/core/ResourceSource.h create mode 100644 project/jni/application/gemrb/src/core/SaveGame.h create mode 100644 project/jni/application/gemrb/src/core/SaveGameIterator.cpp create mode 100644 project/jni/application/gemrb/src/core/SaveGameIterator.h create mode 100644 project/jni/application/gemrb/src/core/SaveGameMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/SaveGameMgr.h create mode 100644 project/jni/application/gemrb/src/core/ScriptEngine.cpp create mode 100644 project/jni/application/gemrb/src/core/ScriptEngine.h create mode 100644 project/jni/application/gemrb/src/core/Scriptable/Actor.cpp create mode 100644 project/jni/application/gemrb/src/core/Scriptable/Actor.h create mode 100644 project/jni/application/gemrb/src/core/Scriptable/ActorBlock.cpp create mode 100644 project/jni/application/gemrb/src/core/Scriptable/ActorBlock.h create mode 100644 project/jni/application/gemrb/src/core/Scriptable/PCStatStruct.cpp create mode 100644 project/jni/application/gemrb/src/core/Scriptable/PCStatStruct.h create mode 100644 project/jni/application/gemrb/src/core/ScriptedAnimation.cpp create mode 100644 project/jni/application/gemrb/src/core/ScriptedAnimation.h create mode 100644 project/jni/application/gemrb/src/core/SoundMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/SoundMgr.h create mode 100644 project/jni/application/gemrb/src/core/Spell.cpp create mode 100644 project/jni/application/gemrb/src/core/Spell.h create mode 100644 project/jni/application/gemrb/src/core/SpellMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/SpellMgr.h create mode 100644 project/jni/application/gemrb/src/core/Spellbook.cpp create mode 100644 project/jni/application/gemrb/src/core/Spellbook.h create mode 100644 project/jni/application/gemrb/src/core/Sprite2D.cpp create mode 100644 project/jni/application/gemrb/src/core/Sprite2D.h create mode 100644 project/jni/application/gemrb/src/core/SpriteCover.cpp create mode 100644 project/jni/application/gemrb/src/core/SpriteCover.h create mode 100644 project/jni/application/gemrb/src/core/Store.cpp create mode 100644 project/jni/application/gemrb/src/core/Store.h create mode 100644 project/jni/application/gemrb/src/core/StoreMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/StoreMgr.h create mode 100644 project/jni/application/gemrb/src/core/StringMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/StringMgr.h create mode 100644 project/jni/application/gemrb/src/core/SymbolMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/SymbolMgr.h create mode 100644 project/jni/application/gemrb/src/core/System/CachedFileStream.cpp create mode 100644 project/jni/application/gemrb/src/core/System/CachedFileStream.h create mode 100644 project/jni/application/gemrb/src/core/System/DataStream.cpp create mode 100644 project/jni/application/gemrb/src/core/System/DataStream.h create mode 100644 project/jni/application/gemrb/src/core/System/FileStream.cpp create mode 100644 project/jni/application/gemrb/src/core/System/FileStream.h create mode 100644 project/jni/application/gemrb/src/core/System/MemoryStream.cpp create mode 100644 project/jni/application/gemrb/src/core/System/MemoryStream.h create mode 100644 project/jni/application/gemrb/src/core/System/VFS.cpp create mode 100644 project/jni/application/gemrb/src/core/System/VFS.h create mode 100644 project/jni/application/gemrb/src/core/System/swab.c create mode 100644 project/jni/application/gemrb/src/core/System/swab.h create mode 100644 project/jni/application/gemrb/src/core/TableMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/TableMgr.h create mode 100644 project/jni/application/gemrb/src/core/Tile.cpp create mode 100644 project/jni/application/gemrb/src/core/Tile.h create mode 100644 project/jni/application/gemrb/src/core/TileMap.cpp create mode 100644 project/jni/application/gemrb/src/core/TileMap.h create mode 100644 project/jni/application/gemrb/src/core/TileMapMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/TileMapMgr.h create mode 100644 project/jni/application/gemrb/src/core/TileOverlay.cpp create mode 100644 project/jni/application/gemrb/src/core/TileOverlay.h create mode 100644 project/jni/application/gemrb/src/core/TileSetMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/TileSetMgr.h create mode 100644 project/jni/application/gemrb/src/core/TypeID.h create mode 100644 project/jni/application/gemrb/src/core/Variables.cpp create mode 100644 project/jni/application/gemrb/src/core/Variables.h create mode 100644 project/jni/application/gemrb/src/core/Video.cpp create mode 100644 project/jni/application/gemrb/src/core/Video.h create mode 100644 project/jni/application/gemrb/src/core/VideoMode.h create mode 100644 project/jni/application/gemrb/src/core/WindowMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/WindowMgr.h create mode 100644 project/jni/application/gemrb/src/core/WorldMap.cpp create mode 100644 project/jni/application/gemrb/src/core/WorldMap.h create mode 100644 project/jni/application/gemrb/src/core/WorldMapMgr.cpp create mode 100644 project/jni/application/gemrb/src/core/WorldMapMgr.h create mode 100644 project/jni/application/gemrb/src/core/damages.h create mode 100644 project/jni/application/gemrb/src/includes/Makefile.am create mode 100644 project/jni/application/gemrb/src/includes/RGBAColor.h create mode 100644 project/jni/application/gemrb/src/includes/SClassID.h create mode 100644 project/jni/application/gemrb/src/includes/defsounds.h create mode 100644 project/jni/application/gemrb/src/includes/errors.h create mode 100644 project/jni/application/gemrb/src/includes/exports.h create mode 100644 project/jni/application/gemrb/src/includes/globals.h create mode 100644 project/jni/application/gemrb/src/includes/ie_feats.h create mode 100644 project/jni/application/gemrb/src/includes/ie_stats.h create mode 100644 project/jni/application/gemrb/src/includes/ie_types.h create mode 100644 project/jni/application/gemrb/src/includes/iless.h create mode 100644 project/jni/application/gemrb/src/includes/logging.h create mode 100644 project/jni/application/gemrb/src/includes/opcode_params.h create mode 100644 project/jni/application/gemrb/src/includes/operatorbool.h create mode 100644 project/jni/application/gemrb/src/includes/overlays.h create mode 100644 project/jni/application/gemrb/src/includes/plugindef.h create mode 100644 project/jni/application/gemrb/src/includes/strrefs.h create mode 100644 project/jni/application/gemrb/src/includes/win32def.h create mode 100644 project/jni/application/gemrb/src/plugins/2DAImporter/2DAImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/2DAImporter/2DAImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/2DAImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/2DAImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/ACMReader.cpp create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/ACMReader.h create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/decoder.cpp create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/decoder.h create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/general.h create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/unpacker.cpp create mode 100644 project/jni/application/gemrb/src/plugins/ACMReader/unpacker.h create mode 100644 project/jni/application/gemrb/src/plugins/AREImporter/AREImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/AREImporter/AREImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/AREImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/AREImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/BAMImporter/BAMImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BAMImporter/BAMImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/BAMImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/BAMImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/BIFImporter/BIFImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIFImporter/BIFImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/BIFImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/BIFImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/BIKPlayer.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/BIKPlayer.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/GetBitContext.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/GetBitContext.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/binkdata.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/common.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/dct.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/dsputil.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/fft.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/mem.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/rational.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/rational.h create mode 100644 project/jni/application/gemrb/src/plugins/BIKPlayer/rdft.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BMPImporter/BMPImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BMPImporter/BMPImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/BMPImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/BMPImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/BMPWriter/BMPWriter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/BMPWriter/BMPWriter.h create mode 100644 project/jni/application/gemrb/src/plugins/BMPWriter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/BMPWriter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/CHUImporter/CHUImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/CHUImporter/CHUImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/CHUImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/CHUImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/CREImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/CREImporter/CREImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/CREImporter/CREImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/CREImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/DLGImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/DLGImporter/DLGImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/DLGImporter/DLGImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/DLGImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/DirectoryImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/DirectoryImporter/DirectoryImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/DirectoryImporter/DirectoryImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/DirectoryImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/EFFImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/EFFImporter/EFFImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/EFFImporter/EFFImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/EFFImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/FXOpcodes/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/FXOpcodes/FXOpcodes.cpp create mode 100644 project/jni/application/gemrb/src/plugins/FXOpcodes/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/GAMImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/GAMImporter/GAMImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/GAMImporter/GAMImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/GAMImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/GUIScript.cpp create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/GUIScript.h create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/PythonHelpers.cpp create mode 100644 project/jni/application/gemrb/src/plugins/GUIScript/PythonHelpers.h create mode 100644 project/jni/application/gemrb/src/plugins/IDSImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/IDSImporter/IDSImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/IDSImporter/IDSImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/IDSImporter/IDSImporterDefs.h create mode 100644 project/jni/application/gemrb/src/plugins/IDSImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/INIImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/INIImporter/INIImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/INIImporter/INIImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/INIImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/ITMImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/ITMImporter/ITMImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/ITMImporter/ITMImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/ITMImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/IWDOpcodes/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/IWDOpcodes/IWDOpcodes.cpp create mode 100644 project/jni/application/gemrb/src/plugins/IWDOpcodes/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/Dictionary.cpp create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/Dictionary.h create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/KEYImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/KEYImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/KEYImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/MOSImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/MOSImporter/MOSImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MOSImporter/MOSImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/MOSImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/MUSImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/MUSImporter/MUSImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MUSImporter/MUSImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/MUSImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/MVEPlayer.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/MVEPlayer.h create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/gstmvedemux.h create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mve.h create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mve_player.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mve_player.h create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mveaudiodec.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mvevideodec16.cpp create mode 100644 project/jni/application/gemrb/src/plugins/MVEPlayer/mvevideodec8.cpp create mode 100644 project/jni/application/gemrb/src/plugins/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/NullSound/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/NullSound/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/NullSound/NullSound.cpp create mode 100644 project/jni/application/gemrb/src/plugins/NullSound/NullSound.h create mode 100644 project/jni/application/gemrb/src/plugins/OGGReader/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/OGGReader/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/OGGReader/OGGReader.cpp create mode 100644 project/jni/application/gemrb/src/plugins/OGGReader/OGGReader.h create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/AmbientMgrAL.cpp create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/AmbientMgrAL.h create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/OpenALAudio.cpp create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/OpenALAudio.h create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/StackLock.cpp create mode 100644 project/jni/application/gemrb/src/plugins/OpenALAudio/StackLock.h create mode 100644 project/jni/application/gemrb/src/plugins/PLTImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/PLTImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/PLTImporter/PLTImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/PLTImporter/PLTImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/PNGImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/PNGImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/PNGImporter/PNGImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/PNGImporter/PNGImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/PROImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/PROImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/PROImporter/PROImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/PROImporter/PROImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/PSTOpcodes/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/PSTOpcodes/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/PSTOpcodes/PSTOpcodes.cpp create mode 100644 project/jni/application/gemrb/src/plugins/SDLAudio/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/SDLAudio/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/SDLAudio/SDLAudio.cpp create mode 100644 project/jni/application/gemrb/src/plugins/SDLAudio/SDLAudio.h create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/SDLVideo.cpp create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/SDLVideo.h create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/SDLVideoDriver.inl create mode 100644 project/jni/application/gemrb/src/plugins/SDLVideo/TileRenderer.inl create mode 100644 project/jni/application/gemrb/src/plugins/SPLImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/SPLImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/SPLImporter/SPLImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/SPLImporter/SPLImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/STOImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/STOImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/STOImporter/STOImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/STOImporter/STOImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/TISImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/TISImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/TISImporter/TISImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/TISImporter/TISImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/TLKImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/TLKImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/TlkOverride.cpp create mode 100644 project/jni/application/gemrb/src/plugins/TLKImporter/TlkOverride.h create mode 100644 project/jni/application/gemrb/src/plugins/WAVReader/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/WAVReader/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/WAVReader/WAVReader.cpp create mode 100644 project/jni/application/gemrb/src/plugins/WAVReader/WAVReader.h create mode 100644 project/jni/application/gemrb/src/plugins/WEDImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/WEDImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/WEDImporter/WEDImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/WEDImporter/WEDImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/WMPImporter/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/WMPImporter/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/WMPImporter/WMPImporter.cpp create mode 100644 project/jni/application/gemrb/src/plugins/WMPImporter/WMPImporter.h create mode 100644 project/jni/application/gemrb/src/plugins/ZLibManager/CMakeLists.txt create mode 100644 project/jni/application/gemrb/src/plugins/ZLibManager/Makefile.am create mode 100644 project/jni/application/gemrb/src/plugins/ZLibManager/ZLibManager.cpp create mode 100644 project/jni/application/gemrb/src/plugins/ZLibManager/ZLibManager.h diff --git a/project/jni/application/gemrb/AndroidAppSettings.cfg b/project/jni/application/gemrb/AndroidAppSettings.cfg new file mode 100644 index 000000000..f65c94933 --- /dev/null +++ b/project/jni/application/gemrb/AndroidAppSettings.cfg @@ -0,0 +1,33 @@ +# The application settings for Android libSDL port +AppSettingVersion=15 +LibSdlVersion=1.2 +AppName="GemRB" +AppFullName=net.sourceforge.gemrb +ScreenOrientation=h +InhibitSuspend=n +AppDataDownloadUrl="GemRB data(local)|data.zip" +SdlVideoResize=y +SdlVideoResizeKeepAspect=y +NeedDepthBuffer=n +AppUsesMouse=y +AppNeedsTwoButtonMouse=y +AppNeedsArrowKeys=n +AppNeedsTextInput=y +AppUsesJoystick=y +AppHandlesJoystickSensitivity=n +AppUsesMultitouch=n +NonBlockingSwapBuffers=n +RedefinedKeys="LCTRL c p o e" +AppTouchscreenKeyboardKeysAmount=0 +AppTouchscreenKeyboardKeysAmountAutoFire=0 +MultiABI=y +AppVersionCode=063 +AppVersionName="0.6.3" +CompiledLibraries="sdl_mixer ogg vorbis openal png python" +CustomBuildScript=n +AppCflags='-fexceptions -finline-functions -O2 -DSTATIC_LINK=Yes' +AppLdflags='' +AppSubdirsBuild='' +AppUseCrystaXToolchain=y +AppCmdline='GemRB' +ReadmeText='^You may press "Home" now - the data will be downloaded in background' diff --git a/project/jni/application/gemrb/AndroidData/data.zip b/project/jni/application/gemrb/AndroidData/data.zip new file mode 100644 index 0000000000000000000000000000000000000000..8b60a6777465c9a0075457334a9c99ee6531d4e4 GIT binary patch literal 1141120 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�Mvv|*abKc;2W?x;ZrrfD$<$b&)^Uusx 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�fjpx_^<=s!`xUr@n6apCr7 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

&AvF|xX=>ep2XM$%}cg@Uv!)mIRBqG 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%0Aty+6cze~I_AF{matswO)wJy20_v%=%1OUBQX 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%0A(lal$)G(<4*c>$TR&7b{Ol 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

&$sg8uxukyI`-qIQ7AM$A6)| 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 BBsBm4 literal 0 HcmV?d00001 diff --git a/project/jni/application/gemrb/icon.png b/project/jni/application/gemrb/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8ef0eba2da72367a0a215d71ba61a98f69667474 GIT binary patch literal 2467 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 +Avenger +Balrog994 +Brian Tanedo +Dark-Star +Divide +Edheldil +GuidoJ +Jaka Kranjc +Lotana +Marshall Mattingly III +Thuy Nguyen +Tom Prince +Willem Jan Palenstijn +Zefklop diff --git a/project/jni/application/gemrb/src/COPYING b/project/jni/application/gemrb/src/COPYING new file mode 100644 index 000000000..5b6e7c66c --- /dev/null +++ b/project/jni/application/gemrb/src/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/project/jni/application/gemrb/src/GemRB.cpp b/project/jni/application/gemrb/src/GemRB.cpp new file mode 100644 index 000000000..fecabd6e2 --- /dev/null +++ b/project/jni/application/gemrb/src/GemRB.cpp @@ -0,0 +1,71 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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. + * + * + */ + +// GemRB.cpp : Defines the entry point for the application. + +#include "win32def.h" // logging + +#include + +#include "Interface.h" + + +//this supposed to convince SDL to work on OS/X +//WARNING: commenting this out will cause SDL 1.2.x to crash +#ifdef __APPLE_CC__ // we need startup SDL here +#include +#endif + +#ifdef ANDROID +#include +#include "audio.h" + +// pause audio playing if app goes in background +static void appPutToBackground() +{ + core->GetAudioDrv()->Pause(); +} +// resume audio playing if app return to foreground +static void appPutToForeground() +{ + core->GetAudioDrv()->Resume(); +} + +#endif + +int main(int argc, char* argv[]) +{ +#ifdef ANDROID + SDL_ANDROID_SetApplicationPutToBackgroundCallback(&appPutToBackground, &appPutToForeground); +#endif + Interface::SanityCheck(VERSION_GEMRB); + core = new Interface( argc, argv ); + if (core->Init() == GEM_ERROR) { + delete( core ); + printf("Press enter to continue..."); + textcolor(DEFAULT); + getc(stdin); + return -1; + } + core->Main(); + delete( core ); + textcolor(DEFAULT); + return 0; +} diff --git a/project/jni/application/gemrb/src/NEWS b/project/jni/application/gemrb/src/NEWS new file mode 100644 index 000000000..961e2a864 --- /dev/null +++ b/project/jni/application/gemrb/src/NEWS @@ -0,0 +1,381 @@ +GemRB git (2bd6d0e): + New features: + - + + Improved features: + - + - bugfixes + + Applied patches: + +GemRB V0.6.3 git (2010-11-21) + New features: + - IWD:HoW is now completable! + - casting sounds and footsteps + - autodetection of secret doors, detect illusions + - basic bardsong support and selective magic resistance (bg2-style) + - proper store economics, ergonomics and dragging + - custom blood color (creature-dependant) + - new actions, iwd effects and triggers + - Importing a SoA game into ToB + + Improved features: + - actor selection and action bar (for summons and illusions too!) + - door bashing and traps + - loading screens, ambushes, worldmap + - sparkles, panic and other effects + - actions, dialogs, object matching + - personal items support (swap/equip/remove) + - bugfixes + + Applied patches: + iwd regression fix from Eggert Jón Magnússon + +GemRB V0.6.2 (2010-08-21): + New features: + - a basic SDL_mixer plugin for faster, but lower-quality audio + - dualclassing for bg1 and iwd + - new triggers, actions, infravision + - feet circle flickering on portrait hover, coloration in dialog + - wisdom xp bonus (pst) + + Improved features: + - actions, triggers, object matching + - item loading and ability selection, inventory + - projectiles, effects, subtitles, verbal constants + - the core and guiscript design was cleaned up in many places + - bugfixes + + Applied patches: + backslash check patch from anthiste + bg1 character generation patch from Maighstir + a crosscompiling fix from F.Fischer + +GemRB V0.6.1 (2010-06-16): + New features: + - a minimal dataset + - reputation penalties on death or injury + - casting level bonus/malus (wild mages, clerics) + - tinting for different times of the day and weather effects + - a BI(n)K player plugin for the IWD2 movies + - new actions, turn undead + + Improved features: + - the internal design was cleaned up in many places + - game saving, modal actions, combat, effects, spawns + - magic missiles are now drawn properly + - various guiscripts (no more flickering!) + - bugfixes + + Applied patches: + two patches from Brendan Molloy + +GemRB V0.6.0 (2009-11-03): + New features: + - BG1 and IWD are roughly completable! + - levelup support for bg1 and iwd, dream cutscenes in ToB + - more hardcoded projectiles and avatar animations + - evasion, backstabbing and basic hide in shadows + - compatibility with the widescreen mod (unreleased) allows for multiple + custom resolutions + - contingency and sequencer spells, beginnings of wild magic support + + Improved features: + - combat, travelling and feedback + - better spellcasting timing + - actions, effects and triggers + - various guiscripts + - bugfixes + + Applied patches: + a few patches from nugrud for how/totl support + +GemRB V0.5.1 (2009-08-27): + New features: + - BG2:SoA is roughly completable! + - almost all missing IE's hardcoded projectiles, spell hit projectiles, + projectile trails, projectile failure (spell), projectile effectlists + - auto-reloading of projectile weapons in case the ammo stack runs out + - damage resistance + - sorcerer style spellbooks, reading of iwd2 spellbooks + - target following to other areas + - the null sound plugin is now always loaded last by default; for old + installs see the provided configuration example (DelayPlugin) + - intelligence and wisdom dictated lore bonus + - a GUIEnhancements config option (on by default) that enables a few + extra controls (for convenience and larger mods) + - PST death counters (don't anger the Lady) + - initial support for targetting by portrait + + Improved features: + - actions, effects and triggers + - pathfinding, feet circles, fog of war and worldmap travel + - combat and spellcasting (especially summoning) + - projectiles + - config and default table value parsing is smarter about spaces + - various guiscripts + - bugfixes + + Applied patches: + various patches from nugrud for bg2 gui enhancements + fix compilation (with cmake) on OS X, by hanicka + +GemRB V0.5.0 (2009-06-25): + New features: + - SoA, ToB and PST are roughly playable beyond their first levels + - combat: dual-wielding, APR, proficiency and style boni, dexterity + bonus, initiatitive and speed factor, individual combat rounds + - many IE's hardcoded projectiles and support for projectile sounds + - IWD2 GUI now works after chargen too + - bg2 chargen now levels to the correct level + - summoned and charmed creatures can be ordered around + - actor tooltips (name and injury status) + - running, initial variable values and portal animations in PST + - hardcoded monk bonuses + + Improved features: + - dialog, actions and triggers + - combat mechanics, animation, feedback, ranged combat + - matters of time and matter + - levelup, dual classing, multiclass handling + - focus: scrolling while paused is now possible + - animations (projectile, creature) + - pathfinding + - area music restarts when there's no music playing + - disarm trap checks skills + - various guiscripts + - bugfixes + + Applied patches: + #2802190 jbmetz (improve the rpm spec handling) + #2802437 danamin (patch bomb sanitizing bg1 chargen + bg2 code share) + +GemRB V0.4.0 (2009-05-25): + New features: + - level up support in bg2 + - basic party reordering + - bashing of containers and doors + - persistent area effects (cloudkill, stinking cloud, web, etc.) + - item amount window for stack splitting (shift+click or doubleclick) + - depletion of item charges + - opcodes: disable spellcasting, cutscene2 (pocketplane travel), knock, + clear air, polymorph, disable button + - dynamic scrollbar creation (display of more than 10 kits, 24 spells) + - portrait effect icons + - item ability selection + - character customization + + Improved features: + - fog of war + - party reformation + - iwd and how guiscripts have been merged + - traps + - pst dialogs + - regeneration, hp bonuses, healing + - animations and projectiles + - rewritten MVE player + - ranged combat + - various guiscripts + - bugfixes + + Applied patches: + #2770564 Whiteclone (pst options window bug) + numerous patches from mattinm finishing the level up support + a few patches from ape fixing and extending iwd + #2579743 jbmetz added RPM spec files + +GemRB V0.3.2 (2009-02-16): + New features: + - default cancel button, bound to the escape key + - tooltip animations and a shortcut (tab) + - wrapper python classes that simplified the GUIScripts + - trap detection, removal, triggering, xp, feedback, autopause + - modal effects + - proper xp award for dual- and multiclass actors + - double click (used in the map window) + - click-and-hold incrementing/decrementing + - accumulate kill statistics + - characters can move while the map is open + - sound on item equip + - arbitrary feat prerequisites in iwd2 + - hard pause for all games (originally a ToB feature); triggered with 'h' + - extended night areas (originally a bg2 feature) + + Improved features: + - walking animation timing + - formations (arbitrary sizes, rotation, cursor) + - ppc support (no more crashes) + - container/door/infopoint cursor and highlight handling + - various guiscripts + - cmake build system (now really works on *nix) + - magic item exclusion + - stores and bags + - fixed attack loop when target dies + - bugfixes + + Applied patches: + #2159734 Zefklop (Mouse activity during movies) + #2243323 Zefklop (correct Openal cleanup) + #2263333 Whiteclone (bg1 guiinv) + #2380891 Amikrop (iwd1 guicommonwindows) + +GemRB V0.3.1 (2008-09-25): + New features: + - mouse scroll support + - starting tob inventory + - character import in iwd and how + - spritecover for area animations + - proper XP bonus for thieving and learning spells + + Improved features: + - gcc 4.3 compatibility + - PST bestiary + - bg2 and tob game modes have been merged + - bg2 and iwd2 character generation was simplified and improved + - stricter dualclassing prerequisites + - the cmake build system is available for other platforms too + - pathfinding + - starting time is now at day 0 + - less memory leaks + - bugfixes + +GemRB V0.3.0 (2008-02-17): + New features: + - TLK override handling (custom biographies and map notes) + - weapon immunities + - party AI + - expansion playmode + - more actions, triggers and effects + - loading of projectile explosion animations + - kit information window + - optional CMake build system (windows only) + + Improved features: + - sound (now perfect!) + - character generation + - opcodes + - character record window + - pathfinding + - tooltip delay + - bugfixes + +GemRB V0.2.9 (2007-07-06): + New features: + - thieving + - tracking + - graphical feedback (color pulse, blur, mirror image, vvc overlays etc) + - projectiles + - spell casting + - item use + - challenge rating calculation + + Improved features: + - more opcodes + - bugfixes + - shop/inventory gui + +GemRB V0.2.8 (2006-12-24): + New features: + - equipment is rendered both on paperdoll and avatar + - weather (snow/rain) is now rendered + + Improved features: + - action menus + - game scripting (actions/triggers) + +GemRB V0.2.7 (2006-08-30): + New features: + - large animations + - worldmap travel + - dialogue portraits + - translucent shadows option + - personal space of actors + - combat + - many new effects + - overlay animation + + Improved features: + - Script fixes + - Action menus + - TextScreen + - doors + - animated overlays + - new actions + +GemRB V0.2.6 (2005-12-06): + New features: + - Effects are in a different plugin + - DoxyGen docs + - Wallgroup covers + - Door triggers + - Action menus (talk/attack) + - party/protagonist death handled + + Improved features: + - Textscreen graphic fixed + - script workflow + - compilation and running on different systems (MacOSX, PPC Linux) + - various leaks/instabilities fixed + - Saving games + - inventory screens in many games + +GemRB V0.2.5 (2005-08-22): + New features: + - Save game + - Effects are now loaded + - Equipping effects in items + - Spawn points in areas + - Textscreen (scrolled text between chapters) + + Improved features: + - GameScript is now much more reliable: Action override works, triggers fire once and then get cleared + - fully working Store screen + - fixed padding of message window rows (in dialogs) + +GemRB V0.2.4 (2005-05-29): + New features: + - Store dialogs (Temple, Inn, Container, Tavern, Store) + - Fog of war with line of sight + - Doors block path and line of sight + - Window frames at higher resolutions + - Animated buttons (PST portraits, Donation window) + - Store opens when appropriate + - Containers + + Improved features: + - Fixed dialogs + - new GUIScript functions with documentation + - Fog of war/door/store related gamescript actions + - fixed object distance and area variable handling in gamescript + - other new gamescript actions/triggers + - Implemented PCs fidget animations + + Documentation: + - Introduction to writing GUIScript scripts + +GemRB v0.2.3 (2005-02-13): + New features: + - GUI for most of the games, especially interactive Inventory and Spellbook + - Map and WorldMap + - Load screen interstitials with progress bar + - Spell and item cache to speed up object management + - Added gamescript actions/triggers + - Selection of spells during character generation + - First attempt on effects code + - First attempt on Fog-Of-War + - Tooltips + - Overhead text + - Ambient sounds + - Volume control + - Manual page gemrb(1) + - Documentation for GemRB Python API and our custom override files + + Improved features: + - Character generation + - GUI + - Build infrastructure on Linux and Un*x systems + - Progress towards portability to 64 bit and big endian machines + - Many bugfixes and new bugs as well ;-) + - Shortened version numbers + - Simplified user configuration, game specific settings are now + in gemrb/override dir diff --git a/project/jni/application/gemrb/src/README b/project/jni/application/gemrb/src/README new file mode 100644 index 000000000..8d3b71955 --- /dev/null +++ b/project/jni/application/gemrb/src/README @@ -0,0 +1,77 @@ +Introduction +------------ +GemRB (Game Engine Made with preRendered Background) is a "port" +(actually a new implementation) of the original Infinity Engine (the one +of Baldur's Gate, Icewind Dale, Planescape: Torment, ...) to +Linux/Unix, MacOS X and Windows with some enhancements. Would you like to +create a game like Baldur's Gate? + +It means that you either need some of the original game's data +somewhere on your harddisk, or you can try to use the data from the +Dragonlance Total Conversion project - see the link below. + +The original game data has to be installed on a windows +partition and mounted to your Linux/Unix filesystem, installed on +windows and then copied to your filesystem, installed with WINE or +extracted manually from the CDs using the tool `unshield'. + +What little documentation exists is mostly in gemrb/docs/en/ and +subdirectories, the gemrb.6 man page and this file. + +Supported platforms +------------------- +Supported (i.e. we got reports about successfully running GemRB) systems: +Linux x86, x86-64, ppc +FreeBSD x86 +MS Windows (98, XP or Vista) +various Macintosh systems (even pre x86) also should work ... +some smart phones (Symbian, Android) +some consoles (OpenPandora, Dingoo) +some exotic OSes (ReactOS, SyllableOS, Haiku) + +Requirements +------------ +See the INSTALL file. + +Contacts +-------- +Our homepage: +http://gemrb.sourceforge.net + +Our project at sourceforge.net: +http://sourceforge.net/projects/gemrb + +New GemRB forum (users): +http://forums.gibberlings3.net/index.php?showforum=91 + +IRC channel: +The best way to talk with us is by joining the #GemRB channel +on the FreeNode IRC network. There's somebody to talk with most of +the time. + + +Useful links +------------ +IESDP, documentation for the Infinity Engine file formats and more: +http://iesdp.gibberlings3.net/ + +Near Infinity, Java viewer and editor for data files of the original games: +http://www.idi.ntnu.no/~joh/ni/index.html + +DLTCEP, MS Windows viewer and editor for data files of the original games: +http://forums.gibberlings3.net/index.php?showforum=137 + +Unshield, extractor for .CAB files created by InstallShield +http://synce.sourceforge.net/synce/unshield.php + +Valgrind, a powerful developer tool to fix programmer errors (leaks, buffer overflows and all the like that happen) +http://valgrind.org/ + +SDL, Simple Directmedia Layer, the graphical library used for GemRB +http://www.libsdl.org/index.php + +OpenAL, Cross-Platform 3D audio libraries, the sound library used for GemRB +http://openal.org/ + +WINE, Open Source implementation of the Windows API, useful for installing the games +http://www.winehq.org diff --git a/project/jni/application/gemrb/src/TODO b/project/jni/application/gemrb/src/TODO new file mode 100644 index 000000000..2f1af016a --- /dev/null +++ b/project/jni/application/gemrb/src/TODO @@ -0,0 +1,101 @@ +Scripts: +1. (DONE?) ToB specific actions/triggers, like pocketplane (so ToB will work) +2. (PARTLY) Properly detect the play mode (sp/mp, normal/extended) + +Strings: +1. fix (finish implementation of) talk table override +2. implement feature: "" (or even more ambitious stat specific strref tokens) + +Combat: +1. (PARTLY) fix combat round timings +2. implement customisable combat calculation, it should be general enough to +simulate all games, without any hardcoded parts + +Items: +1. (DONE-almost) Break items, count charges +2. (PARTLY) Implement switching weapon abilities +3. (DONE-almost) Implement item usage (similar to spells) + +Effects: +1. (DONE-almost) Implement common effects +2. (PARTLY) Implement IWD2 effects +3. (DONE-almost) Implement TOB effects +4. (PARTLY) Implement PST effects +5. (DONE-almost) Implement area/non-living affecting effects + +Area: +1. (PARTLY) Don't load scripts for pile items? (research when a script is unused) +2. (DONE) Create real streaming ambients (do not preload them, just use them when needed) +3. (DONE) in pathfinder, calculate with the actor's feet circle size (npc blocking still needed) +4. (DONE-almost) fix overlaid tiles - bug #1623839 + +Store: +1. Store caching (especially bags) + +Animation: +1. (PARTLY) fix char animation sequences +2. stanceID is still fuzzy. Fix it. (FIXED?) +3. (PARTLY) Implement projectile animations (area, cone, fragments, hardcoded features) + +Actor: +1. (PARTLY) Use the character sheet (Actor.cpp) itself to store attributes of the character + during character generation. + Benefits: data is already stored in the destination, data constraints and relations + are easily implemented. + How to: replace GemRB.SetVar with GemRB.SetPlayerStat. Don't forget to create + the character first. - see bg1 chargen for a complete solution +2. Move position of actor (and ground circle) to the center of a searchmap cell +3. (PARTLY) Actually handle the iwd2 spelllists. Exporter is still needed. + +Game GUI: +1. (PARTLY) implement class based (but customisable) action button bar. Generally port + the IWD2 system to all engines +2. implement grabbing mouse pointer by a control to fix dragging of PST Float menu window +3. (PARTLY) Fix drop capitals (initials) Calculate text height/width correctly, display + it correctly too. +4. (PARTLY) Fix unwanted screen shake (especially when on bottom of area) +5. (DONE-almost) Level up code, this should be written mostly in GuiScript!!! +6. (PARTLY) Contingency, spell trigger setup windows +7. (PARTLY) Customize windows (part of character record window) + +General: +1. The Cache and Variables classes could be rewritten to incorporate the release + function more smoothly (use templates?) +2. various directories (GemRB override, game override ...) should be resolved + right after loading config files and remain static afterwards. Maybe define + some PATH variable describing all the directories searched for files +3. valgrind reports a big heap of unreleased python objects +4. Implement at least all the options accessible from the GUI options setup, + rewrite baldur|torment|icewind.ini + +Graphics: +1. use scaling in Video::SpriteScaleDown() instead of in Video::GetPreview() + and in BMPImporter +2. move SDLVideoDriver sprite functions to their own file, rename them to + SpriteIsPixelTransparent etc. +3. (PARTLY) Add PNG support? (still image done) +4. Fog of war: fully visible squares with one corner neighbour invisible need alpha of the adjacent corner to the invisible square tuned down (uh, i hope it is clear what to do, look for artifacts in the fog of war edge) + +Sound: +1. valgrind reports invalid memory access due to Unqueueing buffers and using + them in another thread (openal weirdness?) +2. (PARTLY) sounds get sometimes distorted, might be connected to problem #1 +3. (PARTLY) Separate OpenAL interface from ACM loader and MVE player +4. implement sound handles so looping, moving sound, stopping sound is possible +5. fix sound settings (currently the volumes get reset on area change, for example) +6. implement and use as much from EAX (echo, damping, etc) as possible + +Release: +2. Get a sample game with some free license which could be distributed + with GemRB. + + +Documentation: +1. (PARTLY) make tool to scan source files for those with non-standard + copyright notices +2. Add Doxygen doc comments to more objects +3. Write GemRB overview, structure and high-level flow docs + + +Community: +2. Move this todo to bug/task tracker at Sourceforge :-) diff --git a/project/jni/application/gemrb/src/core/ActorMgr.cpp b/project/jni/application/gemrb/src/core/ActorMgr.cpp new file mode 100644 index 000000000..ebcee7124 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ActorMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "ActorMgr.h" + +ActorMgr::ActorMgr(void) +{ +} + +ActorMgr::~ActorMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ActorMgr.h b/project/jni/application/gemrb/src/core/ActorMgr.h new file mode 100644 index 000000000..7afcfbaf8 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ActorMgr.h @@ -0,0 +1,41 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ACTORMGR_H +#define ACTORMGR_H + +#include "Plugin.h" +#include "Scriptable/Actor.h" +#include "System/DataStream.h" + +class GEM_EXPORT ActorMgr : public Plugin { +public: + ActorMgr(void); + virtual ~ActorMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual Actor* GetActor(unsigned char is_in_party) = 0; + + //returns saved size, updates internal offsets before save + virtual int GetStoredFileSize(Actor *ac) = 0; + //saves file + virtual int PutActor(DataStream *stream, Actor *actor, bool chr=false) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Ambient.cpp b/project/jni/application/gemrb/src/core/Ambient.cpp new file mode 100644 index 000000000..db543f474 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Ambient.cpp @@ -0,0 +1,36 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * 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 "Ambient.h" + +Ambient::Ambient() +{ +} + +Ambient::~Ambient() +{ + unsigned int i=sounds.size(); + while(i--) { + free(sounds[i]); + } +} + +void Ambient::setActive() { flags |= IE_AMBI_ENABLED; } +void Ambient::setInactive() { flags &= ~IE_AMBI_ENABLED; } diff --git a/project/jni/application/gemrb/src/core/Ambient.h b/project/jni/application/gemrb/src/core/Ambient.h new file mode 100644 index 000000000..ec4f9be22 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Ambient.h @@ -0,0 +1,77 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * 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 AMBIENT_H +#define AMBIENT_H + +#include "exports.h" +#include "globals.h" +#include "ie_types.h" + +#include +#include +#include + +#define IE_AMBI_ENABLED 1 +#define IE_AMBI_POINT 2 +#define IE_AMBI_MAIN 4 +#define IE_AMBI_AREA 8 + +class GEM_EXPORT Ambient { +public: + Ambient(); + ~Ambient(); + + /* there is a good reason to have these in the header: + * they are automatically inlined, so we have + * no roundtrips and no overhead for accessors --Divide */ + const char *getName() const { return name; } + const Point &getOrigin() const { return origin; } + ieWord getRadius() const { return radius; } + ieWord getHeight() const { return height; } + ieWord getGain() const { return gain; } + char *getSound(ieDword i) const + { + if(i sounds; + ieDword interval; // no pauses if zero + ieDword perset; + ieDword appearance; + ieDword flags; + +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AmbientMgr.cpp b/project/jni/application/gemrb/src/core/AmbientMgr.cpp new file mode 100644 index 000000000..a998eb86a --- /dev/null +++ b/project/jni/application/gemrb/src/core/AmbientMgr.cpp @@ -0,0 +1,62 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * 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 "AmbientMgr.h" + +#include "Ambient.h" + +AmbientMgr::AmbientMgr() +{ +} + +AmbientMgr::~AmbientMgr() +{ + reset(); +} + +void AmbientMgr::activate(const std::string &name) +{ + for (std::vector::iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + (*it) -> setActive(); + break; + } + } +} + +void AmbientMgr::deactivate(const std::string &name) +{ + for (std::vector::iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + (*it) -> setInactive(); + break; + } + } +} + +bool AmbientMgr::isActive(const std::string &name) const +{ + for (std::vector::const_iterator it = ambients.begin(); it != ambients.end(); ++it) { + if ((*it) -> getName() == name) { + return (*it) -> getFlags() & IE_AMBI_ENABLED; + } + } + return false; +} diff --git a/project/jni/application/gemrb/src/core/AmbientMgr.h b/project/jni/application/gemrb/src/core/AmbientMgr.h new file mode 100644 index 000000000..0b6daf122 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AmbientMgr.h @@ -0,0 +1,48 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * 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 AMBIENTMGR_H +#define AMBIENTMGR_H + +#include "exports.h" +#include "win32def.h" + +#include +#include + +class Ambient; + +class GEM_EXPORT AmbientMgr { +public: + AmbientMgr(); + virtual ~AmbientMgr(); + virtual void reset() { ambients = std::vector (); } + virtual void setAmbients(const std::vector &a) { reset(); ambients = a; activate(); } + virtual void activate(const std::string &name); + virtual void activate() { active = true; } // hard play ;-) + virtual void deactivate(const std::string &name); + virtual void deactivate() { active = false; } // hard stop + virtual bool isActive(const std::string &name) const; +protected: + std::vector ambients; + bool active; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimStructures.h b/project/jni/application/gemrb/src/core/AnimStructures.h new file mode 100644 index 000000000..9692cf947 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimStructures.h @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ANIMSTRUCTURES_H +#define ANIMSTRUCTURES_H + +struct CycleEntry { + ieWord FramesCount; + ieWord FirstFrame; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Animation.cpp b/project/jni/application/gemrb/src/core/Animation.cpp new file mode 100644 index 000000000..6ec1caa1f --- /dev/null +++ b/project/jni/application/gemrb/src/core/Animation.cpp @@ -0,0 +1,261 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "Animation.h" + +#include "win32def.h" + +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "Video.h" + +Animation::Animation(int count) +{ + frames = (Sprite2D **) calloc(count, sizeof(Sprite2D *)); + indicesCount = count; + if (count) { + pos = rand() % count; + } + else { + pos = 0; + } + starttime = 0; + x = 0; + y = 0; + Flags = A_ANI_ACTIVE; + fps = 15; + endReached = false; + //behaviour flags + playReversed = false; + gameAnimation = false; +} + +Animation::~Animation(void) +{ + Video *video = core->GetVideoDriver(); + + for (unsigned int i = 0; i < indicesCount; i++) { + video->FreeSprite( frames[i] ); + } + free(frames); +} + +void Animation::SetPos(unsigned int index) +{ + if (index=indicesCount) { + printf("You tried to write past a buffer in animation, BAD!\n"); + abort(); + } + core->GetVideoDriver()->FreeSprite(frames[index]); + frames[index]=frame; + + int x = -frame->XPos; + int y = -frame->YPos; + int w = frame->Width; + int h = frame->Height; + if (x < animArea.x) { + animArea.w += (animArea.x - x); + animArea.x = x; + } + if (y < animArea.y) { + animArea.h += (animArea.y - y); + animArea.y = y; + } + if (x+w > animArea.x+animArea.w) { + animArea.w = x+w-animArea.x; + } + if (y+h > animArea.y+animArea.h) { + animArea.h = y+h-animArea.y; + } +} + +unsigned int Animation::GetCurrentFrame() const +{ + if (playReversed) + return indicesCount-pos-1; + return pos; +} + +Sprite2D* Animation::LastFrame(void) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + if (gameAnimation) { + starttime = core->GetGame()->Ticks; + } else { + GetTime( starttime ); + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + return ret; +} + +Sprite2D* Animation::NextFrame(void) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + if (starttime == 0) { + if (gameAnimation) { + starttime = core->GetGame()->Ticks; + } else { + GetTime( starttime ); + } + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + + if (endReached && (Flags&A_ANI_PLAYONCE) ) + return ret; + + unsigned long time; + if (gameAnimation) { + time = core->GetGame()->Ticks; + } else { + GetTime(time); + } + + //it could be that we skip more than one frame in case of slow rendering + //large, composite animations (dragons, multi-part area anims) require synchronisation + if (( time - starttime ) >= ( unsigned long ) ( 1000 / fps )) { + int inc = (time-starttime)*fps/1000; + pos += inc; + starttime += inc*1000/fps; + } + if (pos >= indicesCount ) { + if (indicesCount) { + if (Flags&A_ANI_PLAYONCE) { + pos = indicesCount-1; + endReached = true; + } else { + pos = pos%indicesCount; + endReached = false; //looping, there is no end + } + } else { + pos = 0; + endReached = true; + } + starttime = 0; + } + return ret; +} + +Sprite2D* Animation::GetSyncedNextFrame(Animation* master) +{ + if (!Flags&A_ANI_ACTIVE) { + printf("Frame fetched while animation is inactive!\n"); + return NULL; + } + Sprite2D* ret; + if (playReversed) + ret = frames[indicesCount-pos-1]; + else + ret = frames[pos]; + + starttime = master->starttime; + pos = master->pos; + endReached = master->endReached; + + return ret; +} + + +void Animation::release(void) +{ + delete this; +} +/** Gets the i-th frame */ +Sprite2D* Animation::GetFrame(unsigned int i) +{ + if (i >= indicesCount) { + return NULL; + } + return frames[i]; +} + +void Animation::MirrorAnimation() +{ + Video *video = core->GetVideoDriver(); + + for (size_t i = 0; i < indicesCount; i++) { + Sprite2D * tmp = frames[i]; + frames[i] = video->MirrorSpriteHorizontal( tmp, true ); + video->FreeSprite(tmp); + } + + // flip animArea horizontally as well + animArea.x = -animArea.w - animArea.x; +} + +void Animation::MirrorAnimationVert() +{ + Video *video = core->GetVideoDriver(); + + for (size_t i = 0; i < indicesCount; i++) { + Sprite2D * tmp = frames[i]; + frames[i] = video->MirrorSpriteVertical( tmp, true ); + video->FreeSprite(tmp); + } + + // flip animArea vertically as well +// animArea.y = -animArea.h - animArea.y; +} + +void Animation::AddAnimArea(Animation* slave) +{ + int x = slave->animArea.x; + int y = slave->animArea.y; + int w = slave->animArea.w; + int h = slave->animArea.h; + if (x < animArea.x) { + animArea.w += (animArea.x - x); + animArea.x = x; + } + if (y < animArea.y) { + animArea.h += (animArea.y - y); + animArea.y = y; + } + if (x+w > animArea.x+animArea.w) { + animArea.w = x+w-animArea.x; + } + if (y+h > animArea.y+animArea.h) { + animArea.h = y+h-animArea.y; + } +} diff --git a/project/jni/application/gemrb/src/core/Animation.h b/project/jni/application/gemrb/src/core/Animation.h new file mode 100644 index 000000000..fcf9a7d13 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Animation.h @@ -0,0 +1,72 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ANIMATION_H +#define ANIMATION_H + +#include "RGBAColor.h" +#include "exports.h" +#include "globals.h" + +#include "Region.h" +#include "Sprite2D.h" + +#include + +class GEM_EXPORT Animation { +private: + Sprite2D **frames; + unsigned int indicesCount; + unsigned long starttime; +public: + bool endReached; + unsigned int pos; + int x, y; + unsigned char fps; + bool playReversed; + bool gameAnimation; + Region animArea; + ieDword Flags; + Animation(int count); + ~Animation(void); + void AddFrame(Sprite2D* frame, unsigned int index); + Sprite2D* LastFrame(void); + Sprite2D* NextFrame(void); + Sprite2D* GetSyncedNextFrame(Animation* master); + void release(void); + /** Gets the i-th frame */ + Sprite2D* GetFrame(unsigned int i); + /** Mirrors all the frames vertically */ + void MirrorAnimationVert(); + /** Mirrors all the frames horizontally */ + void MirrorAnimation(); + /** sets frame index */ + void SetPos(unsigned int index); + /** Sets ScriptName for area animation */ + void SetScriptName(const char *name); + /** returns the frame count */ + unsigned int GetFrameCount() const { return indicesCount; } + /** returns the current frame's index */ + unsigned int GetCurrentFrame() const; + /** add other animation's animarea to self */ + void AddAnimArea(Animation* slave); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimationFactory.cpp b/project/jni/application/gemrb/src/core/AnimationFactory.cpp new file mode 100644 index 000000000..e6637394c --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationFactory.cpp @@ -0,0 +1,168 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "AnimationFactory.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +AnimationFactory::AnimationFactory(const char* ResRef) + : FactoryObject( ResRef, IE_BAM_CLASS_ID ) +{ + FLTable = NULL; + FrameData = NULL; + datarefcount = 0; +} + +AnimationFactory::~AnimationFactory(void) +{ + for (unsigned int i = 0; i < frames.size(); i++) { + core->GetVideoDriver()->FreeSprite( frames[i] ); + } + if (FLTable) + free( FLTable); + + // FIXME: track down where sprites are being leaked + if (datarefcount) { + fprintf(stderr, "AnimationFactory %s has refcount %d\n", ResRef, datarefcount); + //assert(datarefcount == 0); + } + if (FrameData) + free( FrameData); +} + +void AnimationFactory::AddFrame(Sprite2D* frame) +{ + frames.push_back( frame ); +} + +void AnimationFactory::AddCycle(CycleEntry cycle) +{ + cycles.push_back( cycle ); +} + +void AnimationFactory::LoadFLT(unsigned short* buffer, int count) +{ + if (FLTable) { + free( FLTable ); + } + //FLTable = new unsigned short[count]; + FLTable = (unsigned short *) malloc(count * sizeof( unsigned short ) ); + memcpy( FLTable, buffer, count * sizeof( unsigned short ) ); +} + +void AnimationFactory::SetFrameData(unsigned char* FrameData) +{ + this->FrameData = FrameData; +} + + +Animation* AnimationFactory::GetCycle(unsigned char cycle) +{ + if (cycle >= cycles.size()) { + return NULL; + } + int ff = cycles[cycle].FirstFrame; + int lf = ff + cycles[cycle].FramesCount; + Animation* anim = new Animation( cycles[cycle].FramesCount ); + int c = 0; + for (int i = ff; i < lf; i++) { + frames[FLTable[i]]->acquire(); + anim->AddFrame( frames[FLTable[i]], c++ ); + } + return anim; +} + +/* returns the required frame of the named cycle, cycle defaults to 0 */ +Sprite2D* AnimationFactory::GetFrame(unsigned short index, unsigned char cycle) const +{ + if (cycle >= cycles.size()) { + return NULL; + } + int ff = cycles[cycle]. FirstFrame, fc = cycles[cycle].FramesCount; + if(index >= fc) { + return NULL; + } + Sprite2D* spr = frames[FLTable[ff+index]]; + spr->acquire(); + return spr; +} + +Sprite2D* AnimationFactory::GetFrameWithoutCycle(unsigned short index) const +{ + if(index >= frames.size()) { + return NULL; + } + Sprite2D* spr = frames[index]; + spr->acquire(); + return spr; +} + +Sprite2D* AnimationFactory::GetPaperdollImage(ieDword *Colors, + Sprite2D *&Picture2, unsigned int type) const +{ + if (frames.size()<2) { + return NULL; + } + + Video* video = core->GetVideoDriver(); + Picture2 = video->DuplicateSprite(frames[1]); + if (!Picture2) { + return NULL; + } + if (Colors) { + Palette* palette = Picture2->GetPalette(); + palette->SetupPaperdollColours(Colors, type); + Picture2->SetPalette(palette); + palette->Release(); + } + + Picture2->XPos = (short)frames[1]->XPos; + Picture2->YPos = (short)frames[1]->YPos - 80; + + + Sprite2D* spr = core->GetVideoDriver()->DuplicateSprite(frames[0]); + if (Colors) { + Palette* palette = spr->GetPalette(); + palette->SetupPaperdollColours(Colors, type); + spr->SetPalette(palette); + palette->Release(); + } + + spr->XPos = (short)frames[0]->XPos; + spr->YPos = (short)frames[0]->YPos; + + //don't free pixels, createsprite stores it in spr + + return spr; +} + +void AnimationFactory::IncDataRefCount() +{ + ++datarefcount; +} + +void AnimationFactory::DecDataRefCount() +{ + assert(datarefcount > 0); + --datarefcount; +} diff --git a/project/jni/application/gemrb/src/core/AnimationFactory.h b/project/jni/application/gemrb/src/core/AnimationFactory.h new file mode 100644 index 000000000..b385362eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationFactory.h @@ -0,0 +1,58 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ANIMATIONFACTORY_H +#define ANIMATIONFACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "Animation.h" +#include "FactoryObject.h" + +class GEM_EXPORT AnimationFactory : public FactoryObject { +private: + std::vector< Sprite2D*> frames; + std::vector< CycleEntry> cycles; + unsigned short* FLTable; // Frame Lookup Table + unsigned char* FrameData; + int datarefcount; +public: + AnimationFactory(const char* ResRef); + ~AnimationFactory(void); + void AddFrame(Sprite2D* frame); + void AddCycle(CycleEntry cycle); + void LoadFLT(unsigned short* buffer, int count); + void SetFrameData(unsigned char* FrameData); + Animation* GetCycle(unsigned char cycle); + /** No descriptions */ + Sprite2D* GetFrame(unsigned short index, unsigned char cycle=0) const; + Sprite2D* GetFrameWithoutCycle(unsigned short index) const; + size_t GetCycleCount() const { return cycles.size(); } + size_t GetFrameCount() const { return frames.size(); } + int GetCycleSize(int idx) const { return cycles[idx].FramesCount; } + Sprite2D* GetPaperdollImage(ieDword *Colors, Sprite2D *&Picture2, + unsigned int type) const; + + void IncDataRefCount(); + void DecDataRefCount(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/AnimationMgr.cpp b/project/jni/application/gemrb/src/core/AnimationMgr.cpp new file mode 100644 index 000000000..7ddece946 --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "AnimationMgr.h" + +AnimationMgr::AnimationMgr(void) +{ +} + +AnimationMgr::~AnimationMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/AnimationMgr.h b/project/jni/application/gemrb/src/core/AnimationMgr.h new file mode 100644 index 000000000..d161aff4b --- /dev/null +++ b/project/jni/application/gemrb/src/core/AnimationMgr.h @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ANIMATIONMGR_H +#define ANIMATIONMGR_H + +#include "globals.h" + +#include "Animation.h" +#include "AnimationFactory.h" +#include "Font.h" +#include "Plugin.h" + +class GEM_EXPORT AnimationMgr : public Plugin { +public: + AnimationMgr(void); + virtual ~AnimationMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual int GetCycleSize(unsigned char Cycle) = 0; + virtual AnimationFactory* GetAnimationFactory(const char* ResRef, + unsigned char mode = IE_NORMAL) = 0; + /** This function will load the Animation as a Font */ + virtual Font* GetFont() = 0; + /** Debug Function: Returns the Global Animation Palette as a Sprite2D Object. + If the Global Animation Palette is NULL, returns NULL. */ + virtual Sprite2D* GetPalette() = 0; + virtual int GetCycleCount() = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ArchiveImporter.cpp b/project/jni/application/gemrb/src/core/ArchiveImporter.cpp new file mode 100644 index 000000000..2c8524ff0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ArchiveImporter.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "ArchiveImporter.h" + +ArchiveImporter::ArchiveImporter(void) +{ +} + +ArchiveImporter::~ArchiveImporter(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ArchiveImporter.h b/project/jni/application/gemrb/src/core/ArchiveImporter.h new file mode 100644 index 000000000..076d6795c --- /dev/null +++ b/project/jni/application/gemrb/src/core/ArchiveImporter.h @@ -0,0 +1,40 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ARCHIVEIMPORTER_H +#define ARCHIVEIMPORTER_H + +#include "globals.h" + +#include "Plugin.h" + +class GEM_EXPORT ArchiveImporter : public Plugin { +public: + ArchiveImporter(void); + virtual ~ArchiveImporter(void); + virtual int OpenArchive(const char* filename) = 0; + virtual int CreateArchive(DataStream *stream) = 0; + //decompressing a .sav file similar to CBF + virtual int DecompressSaveGame(DataStream *compressed) = 0; + virtual int AddToSaveGame(DataStream *str, DataStream *uncompressed) = 0; + virtual DataStream* GetStream(unsigned long Resource, unsigned long Type) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Audio.cpp b/project/jni/application/gemrb/src/core/Audio.cpp new file mode 100644 index 000000000..fa114c4bd --- /dev/null +++ b/project/jni/application/gemrb/src/core/Audio.cpp @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2004 The GemRB Project + * + * 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 "Audio.h" + +const TypeID Audio::ID = { "Audio" }; + +Audio::Audio(void) +{ +} + +Audio::~Audio(void) +{ +} + +SoundHandle::~SoundHandle() +{ +} diff --git a/project/jni/application/gemrb/src/core/Audio.h b/project/jni/application/gemrb/src/core/Audio.h new file mode 100644 index 000000000..640083160 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Audio.h @@ -0,0 +1,82 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2004 The GemRB Project + * + * 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 AUDIO_H_INCLUDED +#define AUDIO_H_INCLUDED + +#include "globals.h" +#include "win32def.h" + +#include "Plugin.h" +#include "Holder.h" + +#define GEM_SND_RELATIVE 1 +#define GEM_SND_LOOPING 2 +#define GEM_SND_SPEECH IE_STR_SPEECH // 4 +#define GEM_SND_VOL_MUSIC 1 +#define GEM_SND_VOL_AMBIENTS 2 + +class AmbientMgr; +class SoundMgr; + +class GEM_EXPORT SoundHandle : public Held { +public: + virtual bool Playing() = 0; + virtual void SetPos(int XPos, int YPos) = 0; + virtual void Stop() = 0; + virtual void StopLooping() = 0; + virtual ~SoundHandle(); +}; + +class GEM_EXPORT Audio : public Plugin { +public: + static const TypeID ID; +public: + Audio(void); + virtual ~Audio(); + virtual bool Init(void) = 0; + virtual Holder Play(const char* ResRef, int XPos, int YPos, unsigned int flags = 0, unsigned int *length = 0) = 0; + virtual Holder Play(const char* ResRef, unsigned int *length = 0) { return Play(ResRef, 0, 0, GEM_SND_RELATIVE, length); } + virtual bool IsSpeaking() = 0; + virtual AmbientMgr* GetAmbientMgr() { return ambim; } + virtual void UpdateVolume(unsigned int flags = GEM_SND_VOL_MUSIC | GEM_SND_VOL_AMBIENTS) = 0; + virtual bool CanPlay() = 0; + virtual void ResetMusics() = 0; + virtual bool Play() = 0; + virtual bool Stop() = 0; + virtual bool Pause() = 0; + virtual bool Resume() = 0; + virtual int CreateStream(Holder) = 0; + virtual void UpdateListenerPos(int XPos, int YPos ) = 0; + virtual void GetListenerPos(int &XPos, int &YPos ) = 0; + virtual bool ReleaseStream(int stream, bool HardStop=false ) = 0; + virtual int SetupNewStream( ieWord x, ieWord y, ieWord z, + ieWord gain, bool point, bool Ambient) = 0; + virtual int QueueAmbient(int stream, const char* sound) = 0; + virtual void SetAmbientStreamVolume(int stream, int volume) = 0; + virtual void QueueBuffer(int stream, unsigned short bits, + int channels, short* memory, int size, int samplerate) = 0; + +protected: + AmbientMgr* ambim; + +}; + +#endif // AUDIO_H_INCLUDED diff --git a/project/jni/application/gemrb/src/core/Bitmap.cpp b/project/jni/application/gemrb/src/core/Bitmap.cpp new file mode 100644 index 000000000..2a55fa4cf --- /dev/null +++ b/project/jni/application/gemrb/src/core/Bitmap.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 "Bitmap.h" + +Bitmap::Bitmap(unsigned int w, unsigned int h) + : height(h), width(w), data(new unsigned char[height*width]) +{ +} + +Bitmap::~Bitmap() +{ + delete[] data; +} diff --git a/project/jni/application/gemrb/src/core/Bitmap.h b/project/jni/application/gemrb/src/core/Bitmap.h new file mode 100644 index 000000000..2c37bdd87 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Bitmap.h @@ -0,0 +1,55 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 BITMAP_H +#define BITMAP_H + +#include "exports.h" + +class GEM_EXPORT Bitmap { +public: + Bitmap(unsigned int height, unsigned int width); + ~Bitmap(); + unsigned char GetAt(unsigned int x, unsigned int y) const + { + if (x >= width || y >= height) + return 0; + return data[width*y+x]; + + } + void SetAt(unsigned int x, unsigned int y, unsigned char idx) + { + if (x >= width || y >= height) + return; + data[width*y+x] = idx; + + } + unsigned int GetHeight() const + { + return height; + } + unsigned int GetWidth() const + { + return width; + } +private: + unsigned int height, width; + unsigned char *data; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/CMakeLists.txt b/project/jni/application/gemrb/src/core/CMakeLists.txt new file mode 100644 index 000000000..aafa67645 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CMakeLists.txt @@ -0,0 +1,49 @@ +ADD_DEFINITIONS(-DGEM_BUILD_DLL) + +FILE(GLOB gemrb_core_LIB_SRCS "*.cpp" + GameScript/Actions.cpp + GameScript/GSUtils.cpp + GameScript/GameScript.cpp + GameScript/Matching.cpp + GameScript/Objects.cpp + GameScript/Triggers.cpp + GUI/Button.cpp + GUI/Console.cpp + GUI/Control.cpp + GUI/EventMgr.cpp + GUI/GameControl.cpp + GUI/Label.cpp + GUI/MapControl.cpp + GUI/Progressbar.cpp + GUI/ScrollBar.cpp + GUI/Slider.cpp + GUI/TextArea.cpp + GUI/TextEdit.cpp + GUI/Window.cpp + GUI/WorldMapControl.cpp + Scriptable/Actor.cpp + Scriptable/ActorBlock.cpp + Scriptable/PCStatStruct.cpp + System/CachedFileStream.cpp + System/DataStream.cpp + System/FileStream.cpp + System/MemoryStream.cpp + System/VFS.cpp + System/snprintf.cpp + ) + +if (STATIC_LINK) + ADD_LIBRARY(gemrb_core STATIC ${gemrb_core_LIB_SRCS}) +else (STATIC_LINK) + ADD_LIBRARY(gemrb_core SHARED ${gemrb_core_LIB_SRCS}) + IF(WIN32) + INSTALL(TARGETS gemrb_core RUNTIME DESTINATION ${LIB_DIR}) + ELSE(WIN32) + INSTALL(TARGETS gemrb_core LIBRARY DESTINATION ${LIB_DIR}) + ENDIF(WIN32) +endif (STATIC_LINK) + +SET_TARGET_PROPERTIES(gemrb_core PROPERTIES + COMPILE_DEFINITIONS + "PLUGINDIR=\"${PLUGIN_DIR}\";DATADIR=\"${DATA_DIR}\";SYSCONFDIR=\"${SYSCONF_DIR}\"" + ) diff --git a/project/jni/application/gemrb/src/core/Cache.cpp b/project/jni/application/gemrb/src/core/Cache.cpp new file mode 100644 index 000000000..647b0cb93 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Cache.cpp @@ -0,0 +1,311 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "Cache.h" + +#include + +// private inlines +inline unsigned int Cache::MyHashKey(const char* key) const +{ + int nHash = tolower(key[0]); + for (int i=1;(i 0 ); + assert( nHashTableSize > 16 ); + + m_pHashTable = NULL; + m_nHashTableSize = nHashTableSize; // default size + m_nCount = 0; + m_pFreeList = NULL; + m_pBlocks = NULL; + m_nBlockSize = nBlockSize; +} + +void Cache::InitHashTable(unsigned int nHashSize, bool bAllocNow) + //Used to force allocation of a hash table or to override the default + //hash table size of (which is fairly small) +{ + assert( m_nCount == 0 ); + assert( nHashSize > 16 ); + + if (m_pHashTable != NULL) { + // free hash table + free( m_pHashTable); + m_pHashTable = NULL; + } + + if (bAllocNow) { + m_pHashTable = (Cache::MyAssoc **) malloc( sizeof( Cache::MyAssoc * ) * nHashSize ); + memset( m_pHashTable, 0, sizeof( Cache::MyAssoc * ) * nHashSize ); + } + m_nHashTableSize = nHashSize; +} + +void Cache::RemoveAll(ReleaseFun fun) +{ + if (m_pHashTable) { + for (unsigned int nHash = 0; nHash < m_nHashTableSize; nHash++) + { + MyAssoc* pAssoc; + for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; + pAssoc = pAssoc->pNext) + { + if (fun) + fun(pAssoc->data); + pAssoc->MyAssoc::~MyAssoc(); + } + } + // free hash table + free( m_pHashTable ); + m_pHashTable = NULL; + } + + m_nCount = 0; + m_pFreeList = NULL; + + // free memory blocks + MemBlock* p = m_pBlocks; + while (p != NULL) { + MemBlock* pNext = p->pNext; + free( p ); + p = pNext; + } + + m_pBlocks = NULL; +} + +Cache::~Cache() +{ + RemoveAll(NULL); +} + +Cache::MyAssoc* Cache::NewAssoc() +{ + if (m_pFreeList == NULL) { + // add another block + Cache::MemBlock* newBlock = ( Cache::MemBlock* ) malloc(m_nBlockSize * sizeof( Cache::MyAssoc ) + sizeof( Cache::MemBlock )); + assert( newBlock != NULL ); // we must have something + + newBlock->pNext = m_pBlocks; + m_pBlocks = newBlock; + + // chain them into free list + Cache::MyAssoc* pAssoc = ( Cache::MyAssoc* ) + ( newBlock + 1 ); + for (int i = 0; i < m_nBlockSize; i++) { + pAssoc->pNext = m_pFreeList; + m_pFreeList = pAssoc++; + } + } + + Cache::MyAssoc* pAssoc = m_pFreeList; + m_pFreeList = m_pFreeList->pNext; + m_nCount++; + assert( m_nCount > 0 ); // make sure we don't overflow +#ifdef _DEBUG + pAssoc->key[0] = 0; + pAssoc->data = 0; +#endif + pAssoc->nRefCount=1; + return pAssoc; +} + +void Cache::FreeAssoc(Cache::MyAssoc* pAssoc) +{ + if(pAssoc->pNext) { + pAssoc->pNext->pPrev=pAssoc->pPrev; + } + *pAssoc->pPrev = pAssoc->pNext; + pAssoc->pNext = m_pFreeList; + m_pFreeList = pAssoc; + m_nCount--; + assert( m_nCount >= 0 ); // make sure we don't underflow + + // if no more elements, cleanup completely + if (m_nCount == 0) { + RemoveAll(NULL); + } +} + +Cache::MyAssoc *Cache::GetNextAssoc(Cache::MyAssoc *Position) const +{ + if (m_pHashTable == NULL || m_nCount==0) { + return NULL; + } + + Cache::MyAssoc* pAssocRet = (Cache::MyAssoc*)Position; + + if (pAssocRet == NULL) + { + // find the first association + for (unsigned int nBucket = 0; nBucket < m_nHashTableSize; nBucket++) + if ((pAssocRet = m_pHashTable[nBucket]) != NULL) + break; + return pAssocRet; + } + Cache::MyAssoc* pAssocNext = pAssocRet->pNext; + if (pAssocNext == NULL) + { + // go to next bucket + for (unsigned int nBucket = MyHashKey(pAssocRet->key) + 1; + nBucket < m_nHashTableSize; nBucket++) + if ((pAssocNext = m_pHashTable[nBucket]) != NULL) + break; + } + + return pAssocNext; +} + +Cache::MyAssoc* Cache::GetAssocAt(const ieResRef key) const + // find association (or return NULL) +{ + if (m_pHashTable == NULL) { + return NULL; + } + + unsigned int nHash = MyHashKey( key ); + + // see if it exists + Cache::MyAssoc* pAssoc; + for (pAssoc = m_pHashTable[nHash]; + pAssoc != NULL; + pAssoc = pAssoc->pNext) { + if (!strnicmp( pAssoc->key, key, KEYSIZE )) { + return pAssoc; + } + } + return NULL; +} + +void *Cache::GetResource(const ieResRef key) const +{ + Cache::MyAssoc* pAssoc = GetAssocAt( key ); + if (pAssoc == NULL) { + return NULL; + } // not in map + + pAssoc->nRefCount++; + return pAssoc->data; +} + +//returns true if it was successful +bool Cache::SetAt(const ieResRef key, void *rValue) +{ + int i; + + if (m_pHashTable == NULL) { + InitHashTable( m_nHashTableSize ); + } + + Cache::MyAssoc* pAssoc=GetAssocAt( key ); + + if (pAssoc) { + //already exists, but we return true if it is the same + return (pAssoc->data==rValue); + } + + // it doesn't exist, add a new Association + pAssoc = NewAssoc(); + for (i=0;ikey[i]=tolower(key[i]); + } + for (;ikey[i]=0; + } + pAssoc->data=rValue; + // put into hash table + unsigned int nHash = MyHashKey(pAssoc->key); + pAssoc->pNext = m_pHashTable[nHash]; + pAssoc->pPrev = &m_pHashTable[nHash]; + if (pAssoc->pNext) { + pAssoc->pNext->pPrev = &pAssoc->pNext; + } + m_pHashTable[nHash] = pAssoc; + return true; +} + +int Cache::RefCount(const ieResRef key) const +{ + Cache::MyAssoc* pAssoc=GetAssocAt( key ); + if (pAssoc) { + return pAssoc->nRefCount; + } + return -1; +} + +int Cache::DecRef(void *data, const ieResRef key, bool remove) +{ + Cache::MyAssoc* pAssoc; + + if (key) { + pAssoc=GetAssocAt( key ); + if (pAssoc && (pAssoc->data==data) ) { + if (!pAssoc->nRefCount) { + return -1; + } + --pAssoc->nRefCount; + if (remove && !pAssoc->nRefCount) { + FreeAssoc(pAssoc); + return 0; + } + return pAssoc->nRefCount; + } + return -1; + } + + pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL); + + while (pAssoc) { + if (pAssoc->data == data) { + if (!pAssoc->nRefCount) { + return -1; + } + --pAssoc->nRefCount; + if (remove && !pAssoc->nRefCount) { + FreeAssoc(pAssoc); + return 0; + } + return pAssoc->nRefCount; + } + pAssoc=GetNextAssoc(pAssoc); + } + return -1; +} + +void Cache::Cleanup() +{ + Cache::MyAssoc* pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL); + + while (pAssoc) + { + Cache::MyAssoc* nextAssoc = GetNextAssoc(pAssoc); + if (pAssoc->nRefCount == 0) { + FreeAssoc(pAssoc); + } + pAssoc=nextAssoc; + } +} diff --git a/project/jni/application/gemrb/src/core/Cache.h b/project/jni/application/gemrb/src/core/Cache.h new file mode 100644 index 000000000..b83e57641 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Cache.h @@ -0,0 +1,93 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 |Avenger| + * + * 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 CACHE_H +#define CACHE_H + +#include "globals.h" +#include "win32def.h" + +#define KEYSIZE 8 + +#ifndef ReleaseFun +typedef void (*ReleaseFun)(void *); +#endif + +class Cache +{ +protected: + // Association + struct MyAssoc { + MyAssoc* pNext; + MyAssoc** pPrev; + char key[KEYSIZE]; //not ieResRef! + ieDword nRefCount; + void* data; + }; + struct MemBlock { + MemBlock* pNext; + }; + +public: + // Construction + Cache(int nBlockSize = 10, int nHashTableSize = 129); + + // Attributes + // number of elements + inline int GetCount() const + { + return m_nCount; + } + inline bool IsEmpty() const + { + return m_nCount==0; + } + // Lookup + void *GetResource(const ieResRef key) const; + // Operations + bool SetAt(const ieResRef key, void *rValue); + // decreases refcount or drops data + //if name is supplied it is faster, it will use rValue to validate the request + int DecRef(void *rValue, const ieResRef name, bool free); + int RefCount(const ieResRef key) const; + void RemoveAll(ReleaseFun fun);//removes all refcounts + void Cleanup(); //removes only zero refcounts + void InitHashTable(unsigned int hashSize, bool bAllocNow = true); + + // Implementation +protected: + MyAssoc** m_pHashTable; + unsigned int m_nHashTableSize; + int m_nCount; + MyAssoc* m_pFreeList; + MemBlock* m_pBlocks; + int m_nBlockSize; + + Cache::MyAssoc* NewAssoc(); + void FreeAssoc(Cache::MyAssoc*); + Cache::MyAssoc* GetAssocAt(const ieResRef) const; + Cache::MyAssoc *GetNextAssoc(Cache::MyAssoc * rNextPosition) const; + unsigned int MyHashKey(const ieResRef) const; + +public: + ~Cache(); +}; + +#endif //CACHE_H diff --git a/project/jni/application/gemrb/src/core/Calendar.cpp b/project/jni/application/gemrb/src/core/Calendar.cpp new file mode 100644 index 000000000..673bdb040 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Calendar.cpp @@ -0,0 +1,96 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2009 The GemRB Project + * + * 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 "Calendar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "TableMgr.h" +#include "Variables.h" + +Calendar::Calendar(void) +{ + int i; + AutoTable tab("months"); + if (!tab) { + monthnamecount=-1; + monthnames = NULL; + days = NULL; + return; + } + monthnamecount = tab->GetRowCount(); + monthnames = (int *) malloc(sizeof(int) * monthnamecount); + days = (int *) malloc(sizeof(int) * monthnamecount); + daysinyear=0; + for(i=0;iQueryField(i,0)); + daysinyear+=days[i]; + monthnames[i]=atoi(tab->QueryField(i,1)); + } +} + +Calendar::~Calendar(void) +{ + if (monthnames) free(monthnames); + if (days) free(days); +} + +void Calendar::GetMonthName(int dayandmonth) const +{ + int month=1; + + for(int i=0;iGetTokenDictionary()->SetAtCopy("DAY", dayandmonth+1); + + tmp = core->GetString( monthnames[i] ); + core->GetTokenDictionary()->SetAt("MONTHNAME",tmp); + //must not free tmp, SetAt doesn't copy the pointer! + + core->GetTokenDictionary()->SetAtCopy("MONTH", month); + return; + } + dayandmonth-=days[i]; + //ignoring single days (they are not months) + if (days[i]!=1) month++; + } +} + +int Calendar::GetCalendarDay(int date) const +{ + int dayandmonth; + int month=1; + + if (!daysinyear) return 0; + dayandmonth = date%daysinyear; + for(int i=0;i { +public: + virtual ~Callback(); + virtual bool call (); + virtual bool call (int); +}; + +typedef Holder EventHandler; + +#endif diff --git a/project/jni/application/gemrb/src/core/CharAnimations.cpp b/project/jni/application/gemrb/src/core/CharAnimations.cpp new file mode 100644 index 000000000..979094834 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CharAnimations.cpp @@ -0,0 +1,2359 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "CharAnimations.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Map.h" +#include "Palette.h" +#include "Video.h" + +static int AvatarsCount = 0; +static AvatarStruct *AvatarTable = NULL; +static const ieByte SixteenToNine[16]={0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1}; +static const ieByte SixteenToFive[16]={0,0,1,1,2,2,3,3,4,4,3,3,2,2,1,1}; + +static const int zOrder_Mirror16[16][4] = { + { 0, 3, 2, 1 }, + { 0, 3, 2, 1 }, + { 0, 3, 1, 2 }, + { 0, 3, 1, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 0, 3, 1, 2 }, + { 0, 3, 1, 2 }, + { 0, 3, 2, 1 } +}; + +static const int zOrder_8[8][4] = { + { 0, 3, 2, 1 }, + { 0, 3, 1, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 1, 0, 3, 2 }, + { 2, 0, 3, 1 }, + { 2, 0, 3, 1 }, + { 2, 0, 3, 1 } +}; + +struct EquipResRefData { + char Suffix[9]; + unsigned char Cycle; +}; + + +void CharAnimations::ReleaseMemory() +{ + if (AvatarTable) { + free(AvatarTable); + AvatarTable=NULL; + } +} + +int CharAnimations::GetAvatarsCount() +{ + return AvatarsCount; +} + +AvatarStruct *CharAnimations::GetAvatarStruct(int RowNum) +{ + return AvatarTable+RowNum; +} + +unsigned int CharAnimations::GetAnimationID() const +{ + if (AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].AnimID; +} + +int CharAnimations::GetCircleSize() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].CircleSize; +} +int CharAnimations::NoPalette() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].PaletteType; +} + +int CharAnimations::GetAnimType() const +{ + if (AvatarsRowNum==~0u) return -1; + return AvatarTable[AvatarsRowNum].AnimationType; +} + +int CharAnimations::GetSize() const +{ + if (AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].Size; +} + +int CharAnimations::GetBloodColor() const +{ + if(AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].BloodColor; +} + +static ieResRef EmptySound={0}; + +const ieResRef &CharAnimations::GetWalkSound() const +{ + if(AvatarsRowNum==~0u) return EmptySound; + return AvatarTable[AvatarsRowNum].WalkSound; +} + +int CharAnimations::GetWalkSoundCount() const +{ + if(AvatarsRowNum==~0u) return 0; + return AvatarTable[AvatarsRowNum].WalkSoundCount; +} + +int CharAnimations::GetActorPartCount() const +{ + if (AvatarsRowNum==~0u) return -1; + switch (AvatarTable[AvatarsRowNum].AnimationType) { + case IE_ANI_NINE_FRAMES: //dragon animations + return 9; + case IE_ANI_FOUR_FRAMES: //wyvern animations + return 4; + case IE_ANI_PST_GHOST: //special pst anims + if (AvatarTable[AvatarsRowNum].Prefixes[1][0]=='*') { + return 1; + } + if (AvatarTable[AvatarsRowNum].Prefixes[2][0]=='*') { + return 2; + } + if (AvatarTable[AvatarsRowNum].Prefixes[3][0]=='*') { + return 3; + } + return 4; + default: + return 1; + } +} + +int CharAnimations::GetTotalPartCount() const +{ + if (AvatarsRowNum==~0u) return -1; + switch (AvatarTable[AvatarsRowNum].AnimationType) { + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + return GetActorPartCount() + 1; // only weapon + case IE_ANI_CODE_MIRROR: + return GetActorPartCount() + 3; // equipment + case IE_ANI_TWENTYTWO: + return GetActorPartCount() + 3; // equipment + default: + return GetActorPartCount(); + } +} + +void CharAnimations::SetArmourLevel(int ArmourLevel) +{ + if (AvatarsRowNum==~0u) return; + //ignore ArmourLevel for the static pst anims (all sprites are displayed) + if (AvatarTable[AvatarsRowNum].AnimationType == IE_ANI_PST_GHOST) { + ArmourLevel = 0; + } + strncpy( ResRef, AvatarTable[AvatarsRowNum].Prefixes[ArmourLevel], 8 ); + ResRef[8]=0; + DropAnims(); +} + +//RangedType could be weird, reducing its value to 0,1,2 +void CharAnimations::SetRangedType(int rt) +{ + if ((unsigned int) rt<2) { + RangedType=(ieByte) rt; + } else { + RangedType=2; + } +} + +void CharAnimations::SetWeaponType(int wt) +{ + if (wt != WeaponType) { + WeaponType = wt; + DropAnims(); + } +} + +void CharAnimations::SetHelmetRef(const char* ref) +{ + HelmetRef[0] = ref[0]; + HelmetRef[1] = ref[1]; + + // TODO: Only drop helmet anims? + // Note: this doesn't happen "often", so this isn't a performance + // bottleneck. (wjp) + DropAnims(); + gamedata->FreePalette(palette[PAL_HELMET], 0); + gamedata->FreePalette(modifiedPalette[PAL_HELMET], 0); +} + +void CharAnimations::SetWeaponRef(const char* ref) +{ + WeaponRef[0] = ref[0]; + WeaponRef[1] = ref[1]; + + // TODO: Only drop weapon anims? + DropAnims(); + gamedata->FreePalette(palette[PAL_WEAPON], 0); + gamedata->FreePalette(modifiedPalette[PAL_WEAPON], 0); +} + +void CharAnimations::SetOffhandRef(const char* ref) +{ + OffhandRef[0] = ref[0]; + OffhandRef[1] = ref[1]; + + // TODO: Only drop shield/offhand anims? + DropAnims(); + gamedata->FreePalette(palette[PAL_OFFHAND], 0); + gamedata->FreePalette(modifiedPalette[PAL_OFFHAND], 0); +} + +void CharAnimations::LockPalette(const ieDword *gradients) +{ + if (lockPalette) return; + //cannot lock colors for PST animations + if (GetAnimType() >= IE_ANI_PST_ANIMATION_1) + { + return; + } + //force initialisation of animation + SetColors( gradients ); + GetAnimation(0,0); + if (palette[PAL_MAIN]) { + lockPalette=true; + } +} + +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 +static const char *StancePrefix[]={"3","2","5","5","4","4","2","2","5","4","1","3","3","3","4","1","4","4","4"}; +static const char *CyclePrefix[]= {"0","0","1","1","1","1","0","0","1","1","0","1","1","1","1","1","1","1","1"}; +static const unsigned int CycleOffset[] = {0, 0, 0, 0, 0, 9, 0, 0, 0, 18, 0, 0, 9, 18, 0, 0, 0, 0, 0}; + +void CharAnimations::SetColors(const ieDword *arg) +{ + Colors = arg; + SetupColors(PAL_MAIN); + SetupColors(PAL_WEAPON); + SetupColors(PAL_OFFHAND); + SetupColors(PAL_HELMET); +} + +void CharAnimations::SetupColors(PaletteType type) +{ + Palette* pal = palette[(int)type]; + + if (!pal) { + return; + } + + if (!Colors) { + return; + } + + if (GetAnimType() >= IE_ANI_PST_ANIMATION_1) { + // Only do main palette + if (type != PAL_MAIN) { + return; + } + // TODO: handle equipment colour glows + + // Colors[6] is the COLORCOUNT stat in PST. + // It tells how many customisable color slots we have. + // The color slots start from the end of the palette and go + // backwards. There are 6 available slots with a size of 32 each. + // Actually, the slots seem to be written in the cre file + // but we ignore them, i'm not sure this is correct + int colorcount = Colors[6]; + int size = 32; + //the color count shouldn't be more than 6! + if (colorcount>6) colorcount=6; + int dest = 256-colorcount*size; + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } + if ((colorcount == 0) && (needmod==false) ) { + gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef); + PaletteResRef[0]=0; + return; + } + for (int i = 0; i < colorcount; i++) { + core->GetPalette( Colors[i]&255, size, + &palette[PAL_MAIN]->col[dest] ); + dest +=size; + } + + if (needmod) { + if (!modifiedPalette[PAL_MAIN]) + modifiedPalette[PAL_MAIN] = new Palette(); + modifiedPalette[PAL_MAIN]->SetupGlobalRGBModification(palette[PAL_MAIN], GlobalColorMod); + } else { + gamedata->FreePalette(modifiedPalette[PAL_MAIN], 0); + } + return; + } + + int PType = NoPalette(); + if ( PType && (type == PAL_MAIN) ) { + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } + if (!needmod && PaletteResRef[0]) { + gamedata->FreePalette(palette[PAL_MAIN], PaletteResRef); + } + PaletteResRef[0]=0; + //handling special palettes like MBER_BL (black bear) + if (PType!=1) { + if (GetAnimType()==IE_ANI_NINE_FRAMES) { + snprintf(PaletteResRef,9,"%.4s_%-.2s%s",ResRef, (char *) &PType, StancePrefix[StanceID]); + } else { + snprintf(PaletteResRef,9,"%.4s_%-.2s",ResRef, (char *) &PType); + } + strlwr(PaletteResRef); + Palette *tmppal = gamedata->GetPalette(PaletteResRef); + if (tmppal) { + palette[PAL_MAIN] = tmppal; + } else { + PaletteResRef[0]=0; + } + } + if (needmod) { + if (!modifiedPalette[PAL_MAIN]) + modifiedPalette[PAL_MAIN] = new Palette(); + modifiedPalette[PAL_MAIN]->SetupGlobalRGBModification(palette[PAL_MAIN], GlobalColorMod); + } else { + gamedata->FreePalette(modifiedPalette[PAL_MAIN], 0); + } + return; + } + + pal->SetupPaperdollColours(Colors, (int)type); + if (lockPalette) { + return; + } + + int i; + bool needmod = false; + if (GlobalColorMod.type != RGBModifier::NONE) { + needmod = true; + } else { + for (i = 0; i < 7; ++i) { + if (ColorMods[i+8*((int)type)].type != RGBModifier::NONE) + needmod = true; + } + } + + + if (needmod) { + if (!modifiedPalette[(int)type]) + modifiedPalette[(int)type] = new Palette(); + + if (GlobalColorMod.type != RGBModifier::NONE) { + modifiedPalette[(int)type]->SetupGlobalRGBModification(palette[(int)type], GlobalColorMod); + } else { + modifiedPalette[(int)type]->SetupRGBModification(palette[(int)type],ColorMods, (int)type); + } + } else { + gamedata->FreePalette(modifiedPalette[(int)type], 0); + } + +} + +Palette* CharAnimations::GetPartPalette(int part) +{ + int actorPartCount = GetActorPartCount(); + PaletteType type = PAL_MAIN; + + if (part == actorPartCount) type = PAL_WEAPON; + if (part == actorPartCount+1) type = PAL_OFFHAND; + if (part == actorPartCount+2) type = PAL_HELMET; + + if (modifiedPalette[(int)type]) + return modifiedPalette[(int)type]; + + return palette[(int)type]; +} + +static int compare_avatars(const void *a, const void *b) +{ + unsigned int aa = ((AvatarStruct *)a)->AnimID; + unsigned int bb = ((AvatarStruct *)b)->AnimID; + return (int) (aa-bb); +} + +void CharAnimations::InitAvatarsTable() +{ + AutoTable Avatars("avatars"); + if (!Avatars) { + printMessage("CharAnimations", "A critical animation file is missing!\n", LIGHT_RED); + abort(); + } + AvatarTable = (AvatarStruct *) calloc ( AvatarsCount = Avatars->GetRowCount(), sizeof(AvatarStruct) ); + int i=AvatarsCount; + DataFileMgr *resdata = core->GetResDataINI(); + while(i--) { + AvatarTable[i].AnimID=(unsigned int) strtol(Avatars->GetRowName(i),NULL,0 ); + strnlwrcpy(AvatarTable[i].Prefixes[0],Avatars->QueryField(i,AV_PREFIX1),8); + strnlwrcpy(AvatarTable[i].Prefixes[1],Avatars->QueryField(i,AV_PREFIX2),8); + strnlwrcpy(AvatarTable[i].Prefixes[2],Avatars->QueryField(i,AV_PREFIX3),8); + strnlwrcpy(AvatarTable[i].Prefixes[3],Avatars->QueryField(i,AV_PREFIX4),8); + AvatarTable[i].AnimationType=(ieByte) atoi(Avatars->QueryField(i,AV_ANIMTYPE) ); + AvatarTable[i].CircleSize=(ieByte) atoi(Avatars->QueryField(i,AV_CIRCLESIZE) ); + const char *tmp = Avatars->QueryField(i,AV_USE_PALETTE); + //QueryField will always return a zero terminated string + //so tmp[0] must exist + if ( isalpha (tmp[0]) ) { + //this is a hack, we store 2 letters on an integer + //it was allocated with calloc, so don't bother erasing it + strncpy( (char *) &AvatarTable[i].PaletteType, tmp, 3); + } + else { + AvatarTable[i].PaletteType=atoi(Avatars->QueryField(i,AV_USE_PALETTE) ); + } + char size = Avatars->QueryField(i,AV_SIZE)[0]; + if (size == '*') { + size = 0; + } + AvatarTable[i].Size = size; + + AvatarTable[i].WalkScale = 0; + AvatarTable[i].RunScale = 0; + AvatarTable[i].Bestiary = -1; + + if (resdata) { + char section[12]; + snprintf(section,10,"%d", i); + + if (!resdata->GetKeysCount(section)) continue; + + float walkscale = resdata->GetKeyAsFloat(section, "walkscale", 0.0f); + if (walkscale != 0.0f) AvatarTable[i].WalkScale = (int)(1000.0f / walkscale); + float runscale = resdata->GetKeyAsFloat(section, "runscale", 0.0f); + if (runscale != 0.0f) AvatarTable[i].RunScale = (int)(1000.0f / runscale); + AvatarTable[i].Bestiary = resdata->GetKeyAsInt(section, "bestiary", -1); + } + } + qsort(AvatarTable, AvatarsCount, sizeof(AvatarStruct), compare_avatars); + + + AutoTable blood("bloodclr"); + if (blood) { + int rows = blood->GetRowCount(); + for(int i=0;iQueryField(i,0), (long &)value); + valid_number(blood->QueryField(i,1), (long &)rmin); + valid_number(blood->QueryField(i,2), (long &)rmax); + if (value>255 || rmin>0xffff || rmax>0xffff) { + printMessage("CharAnimations", "bloodclr entry:", LIGHT_RED); + printf("%02x %04x-%04x ", (unsigned int) value, (unsigned int) rmin, (unsigned int) rmax); + printStatus("Invalid value!", LIGHT_RED); + continue; + } + for(int j=0;jAvatarTable[j].AnimID) continue; + AvatarTable[j].BloodColor = value; + } + } + } + + AutoTable walk("walksnd"); + if (walk) { + int rows = walk->GetRowCount(); + for(int i=0;iQueryField(i,0), 8); + valid_number(walk->QueryField(i,1), (long &)rmin); + valid_number(walk->QueryField(i,2), (long &)rmax); + valid_number(walk->QueryField(i,3), (long &)range); + if (value[0]=='*') { + value[0]=0; + range = 0; + } + for(int j=0;jAvatarTable[j].AnimID) continue; + memcpy(AvatarTable[j].WalkSound, value, sizeof(ieResRef) ); + AvatarTable[j].WalkSoundCount = range; + } + } + } +} + +CharAnimations::CharAnimations(unsigned int AnimID, ieDword ArmourLevel) +{ + Colors = NULL; + int i,j; + for (i = 0; i < 4; ++i) { + modifiedPalette[i] = NULL; + palette[i] = NULL; + } + nextStanceID = 0; + StanceID = 0; + autoSwitchOnEnd = false; + lockPalette = false; + if (!AvatarsCount) { + InitAvatarsTable(); + } + + for (i = 0; i < MAX_ANIMS; i++) { + for (j = 0; j < MAX_ORIENT; j++) { + Anims[i][j] = NULL; + } + } + ArmorType = 0; + RangedType = 0; + WeaponType = 0; + PaletteResRef[0] = 0; + WeaponRef[0] = 0; + HelmetRef[0] = 0; + OffhandRef[0] = 0; + for (i = 0; i < 32; ++i) { + ColorMods[i].type = RGBModifier::NONE; + ColorMods[i].speed = 0; + // make initial phase depend on location to make the pulse appear + // less even + ColorMods[i].phase = 5*i; + } + GlobalColorMod.type = RGBModifier::NONE; + GlobalColorMod.speed = 0; + GlobalColorMod.phase = 0; + lastModUpdate = 0; + + AvatarsRowNum=AvatarsCount; + if (core->HasFeature(GF_ONE_BYTE_ANIMID) ) { + ieDword tmp = AnimID&0xf000; + if (tmp==0x6000 || tmp==0xe000) { + AnimID&=0xff; + } + } + + while (AvatarsRowNum--) { + if (AvatarTable[AvatarsRowNum].AnimID<=AnimID) { + SetArmourLevel( ArmourLevel ); + return; + } + } + ResRef[0]=0; + printMessage("CharAnimations", " ", LIGHT_RED); + printf("Invalid or nonexistent avatar entry:%04X\n", AnimID); +} + +//we have to drop them when armourlevel changes +void CharAnimations::DropAnims() +{ + Animation** tmppoi; + int partCount = GetTotalPartCount(); + for (int StanceID = 0; StanceID < MAX_ANIMS; StanceID++) { + for (int i = 0; i < MAX_ORIENT; i++) { + if (Anims[StanceID][i]) { + tmppoi = Anims[StanceID][i]; + for (int j = 0; j < partCount; j++) + delete Anims[StanceID][i][j]; + delete[] tmppoi; + + // anims can only be duplicated at the Animation** level + for (int IDb=StanceID;IDb < MAX_ANIMS; IDb++) { + for (int i2 = 0; i2FreePalette(palette[PAL_MAIN], PaletteResRef); + int i; + for (i = 1; i < 4; ++i) + gamedata->FreePalette(palette[i], 0); + for (i = 0; i < 4; ++i) + gamedata->FreePalette(modifiedPalette[i], 0); +} +/* +This is a simple Idea of how the animation are coded + +There are the following animation types: + +IE_ANI_CODE_MIRROR: The code automatically mirrors the needed frames + (as in the example above) + + These Animations are stores using the following template: + [NAME][ARMORTYPE][ACTIONCODE] + + Each BAM File contains only 9 Orientations, the missing 7 Animations + are created by Horizontally Mirroring the 1-7 Orientations. + +IE_ANI_CODE_MIRROR_2: another mirroring type with more animations + [NAME]g[1,11-15,2,21-26] + +IE_ANI_CODE_MIRROR_3: Almost identical to IE_ANI_CODE_MIRROR_2, but with fewer cycles in g26 + +IE_ANI_ONE_FILE: The whole animation is in one file, no mirroring needed. + Each animation group is 16 Cycles. + +IE_ANI_TWO_FILES: The whole animation is in 2 files. The East and West part are in 2 BAM Files. + Example: + ACHKg1 + ACHKg1E + + Each BAM File contains many animation groups, each animation group + stores 5 Orientations, the missing 3 are stored in East BAM Files. + + +IE_ANI_FOUR_FILES: The Animation is coded in Four Files. Probably it is an old Two File animation with + additional frames added in a second time. + +IE_ANI_FOUR_FILES_2: Like IE_ANI_FOUR_FILES but with only 16 cycles per frame. + +IE_ANI_TWENTYTWO: This Animation Type stores the Animation in the following format + [NAME][ACTIONCODE][/E] + ACTIONCODE=A1-6, CA, SX, SA (sling is A1) + The g1 file contains several animation states. See MHR + Probably it could use A7-9 files too, bringing the file numbers to 28. + This is the original bg1 format. + +IE_ANI_SIX_FILES: The layout for these files is: + [NAME][g1-3][/E] + Each state contains 16 Orientations, but the last 6 are stored in the East file. + g1 contains only the walking animation. + G2 contains stand, ready, get hit, die and twitch. + g3 contains 3 attacks. + +IE_ANI_SIX_FILES_2: Similar to SIX_FILES, but the orientation numbers are reduced like in FOUR_FILES. Only one animation uses it: MOGR + +IE_ANI_TWO_FILES_2: Animations using this type are stored using the following template: + [NAME]g1[/E] + Each state contains 8 Orientations, but the second 4 are stored in the East file. + From the standard animations, only AHRS and ACOW belong to this type. + +IE_ANI_TWO_FILES_3: Animations using this type are stored using the following template: + [NAME][ACTIONTYPE][/E] + + Example: + MBFI* + MBFI*E + + Each BAM File contains one animation group, each animation group + stores 5 Orientations though the files contain all 8 Cycles, the missing 3 are stored in East BAM Files in Cycle: Stance*8+ (5,6,7). + This is the standard IWD animation, but BG2 also has it. + See MMR + +IE_ANI_TWO_FILES_3B: Animations using this type are stored using the following template: + [NAME][ACTIONTYPE][/E] + + Example: + MBFI* + MBFI*E + + This is a cut down version of IE_ANI_TWO_FILES_3. A2, CA and SP suffixes are missing. + This is the standard IWD animation, but BG2 also has it. + See MOR2 + +IE_ANI_FOUR_FRAMES: These animations are large, four bams make a frame. + + +IE_ANI_NINE_FRAMES: These animations are huge, nine bams make a frame. + + +IE_ANI_FRAGMENT: These animations are used for projectile graphics. + A single file contains 5 cycles (code mirror for east animation) + +IE_ANI_PST_ANIMATION_1: +IE_ANI_PST_ANIMATION_2: +IE_ANI_PST_ANIMATION_3: + Planescape: Torment Animations are stored in a different + way than the other games. This format uses the following template: + [C/D][ACTIONTYPE][NAME][B] + + Example: + CAT1MRTB + + Each Animation stores 5 Orientations, which are automatically mirrored + to form an 8 Orientation Animation. PST Animations have a different Palette + format. This Animation Type handles the PST Palette format too. + + NOTE: Walking/Running animations store 9 Orientations. + The second variation is missing the resting stance (STD) and the transitions. + These creatures are always in combat stance (don't rest). + Animation_3 is without STC (combat stance), they are always standing + +IE_ANI_PST_STAND: This is a modified PST animation, it contains only a + Standing image for every orientations, it follows the + [C/D]STD[NAME][B] standard. + +IE_ANI_PST_GHOST: This is a special static animation with no standard + All armourlevels are drawn simultaneously. There is no orientation or stance. + + + WEST PART | EAST PART + | + NW NNW N NNE NE + NW 006 007 008 009 010 NE +WNW 005 | 011 ENE + W 004 xxx 012 E +WSW 003 | 013 ESE + SW 002 001 000 015 014 SE + SW SSW S SSE SE + | + | + +*/ + +Animation** CharAnimations::GetAnimation(unsigned char Stance, unsigned char Orient) +{ + if (StanceID>=MAX_ANIMS) { + printf("Illegal stance ID\n"); + abort(); + } + + //for paletted dragon animations, we need the stance id + StanceID = nextStanceID = Stance; + int AnimType = GetAnimType(); + + //alter stance here if it is missing and you know a substitute + //probably we should feed this result back to the actor? + switch (AnimType) { + case -1: //invalid animation + return NULL; + + case IE_ANI_PST_STAND: + StanceID=IE_ANI_AWAKE; + break; + case IE_ANI_PST_GHOST: + StanceID=IE_ANI_AWAKE; + Orient=0; + break; + case IE_ANI_PST_ANIMATION_3: //stc->std + if (StanceID==IE_ANI_READY) { + StanceID=IE_ANI_AWAKE; + } + break; + case IE_ANI_PST_ANIMATION_2: //std->stc + if (StanceID==IE_ANI_AWAKE) { + StanceID=IE_ANI_READY; + } + break; + } + //pst animations don't have separate animation for sleep/die + if (AnimType >= IE_ANI_PST_ANIMATION_1) { + if (StanceID==IE_ANI_DIE) { + StanceID=IE_ANI_TWITCH; + } + } + + //TODO: Implement Auto Resource Loading + //setting up the sequencing of animation cycles + autoSwitchOnEnd = false; + switch (StanceID) { + case IE_ANI_DAMAGE: + nextStanceID = IE_ANI_READY; + autoSwitchOnEnd = true; + break; + case IE_ANI_SLEEP: //going to sleep + nextStanceID = IE_ANI_TWITCH; + autoSwitchOnEnd = true; + break; + case IE_ANI_TWITCH: //dead, sleeping + autoSwitchOnEnd = false; + break; + case IE_ANI_DIE: //going to die + nextStanceID = IE_ANI_TWITCH; + autoSwitchOnEnd = true; + break; + case IE_ANI_WALK: + case IE_ANI_RUN: + case IE_ANI_CAST: // looping + case IE_ANI_READY: + break; + case IE_ANI_AWAKE: + break; + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_HEAD_TURN: + case IE_ANI_PST_START: + nextStanceID = IE_ANI_AWAKE; + autoSwitchOnEnd = true; + break; + case IE_ANI_CONJURE: //ending + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + nextStanceID = IE_ANI_READY; + autoSwitchOnEnd = true; + break; + default: + printf ("Invalid Stance: %d\n", StanceID); + break; + } + Animation** anims = Anims[StanceID][Orient]; + + if (anims) { + return anims; + } + + int partCount = GetTotalPartCount(); + int actorPartCount = GetActorPartCount(); + if (partCount < 0) return 0; + anims = new Animation*[partCount]; + + EquipResRefData* equipdat = 0; + for (int part = 0; part < partCount; ++part) + { + anims[part] = 0; + + //newresref is based on the prefix (ResRef) and various + // other things. + //this is longer than expected so it won't overflow + char NewResRef[12]; + unsigned char Cycle = 0; + if (part < actorPartCount) { + // Character animation parts + + if (equipdat) delete equipdat; + + //we need this long for special anims + strncpy( NewResRef, ResRef, 8 ); + GetAnimResRef( StanceID, Orient, NewResRef, Cycle, part, equipdat); + } else { + // Equipment animation parts + + anims[part] = 0; + if (GetSize() == 0) continue; + + if (part == actorPartCount) { + if (WeaponRef[0] == 0) continue; + // weapon + GetEquipmentResRef(WeaponRef,false,NewResRef,Cycle,equipdat); + } else if (part == actorPartCount+1) { + if (OffhandRef[0] == 0) continue; + if (WeaponType == IE_ANI_WEAPON_2H) continue; + // off-hand + if (WeaponType == IE_ANI_WEAPON_1H) { + GetEquipmentResRef(OffhandRef,false,NewResRef,Cycle, + equipdat); + } else { // IE_ANI_WEAPON_2W + GetEquipmentResRef(OffhandRef,true,NewResRef,Cycle, + equipdat); + } + } else if (part == actorPartCount+2) { + if (HelmetRef[0] == 0) continue; + // helmet + GetEquipmentResRef(HelmetRef,false,NewResRef,Cycle,equipdat); + } + } + NewResRef[8]=0; //cutting right to size + + AnimationFactory* af = ( AnimationFactory* ) + gamedata->GetFactoryResource( NewResRef, + IE_BAM_CLASS_ID, IE_NORMAL ); + + if (!af) { + if (part < actorPartCount) { + char warnbuf[200]; + snprintf(warnbuf, 200, + "Couldn't create animationfactory: %s (%04x)\n", NewResRef, GetAnimationID()); + printMessage("CharAnimations",warnbuf,LIGHT_RED); + for (int i = 0; i < part; ++i) + delete anims[i]; + delete[] anims; + delete equipdat; + return 0; + } else { + // not fatal if animation for equipment is missing + continue; + } + } + + Animation* a = af->GetCycle( Cycle ); + anims[part] = a; + + if (!a) { + if (part < actorPartCount) { + char warnbuf[200]; + snprintf(warnbuf, 200, + "Couldn't load animation: %s, cycle %d\n", + NewResRef, Cycle); + printMessage("CharAnimations",warnbuf,LIGHT_RED); + for (int i = 0; i < part; ++i) + delete anims[i]; + delete[] anims; + delete equipdat; + return 0; + } else { + // not fatal if animation for equipment is missing + continue; + } + } + + if (part < actorPartCount) { + //if you need to revert this change, consider true paletted + //animations which need a GlobalColorMod (mgir for example) + + //if (!palette[PAL_MAIN] && ((GlobalColorMod.type!=RGBModifier::NONE) || (NoPalette()!=1)) ) { + if(!palette[PAL_MAIN]) { + // This is the first time we're loading an Animation. + // We copy the palette of its first frame into our own palette + palette[PAL_MAIN] = a->GetFrame(0)->GetPalette()->Copy(); + // ...and setup the colours properly + SetupColors(PAL_MAIN); + } + } else if (part == actorPartCount) { + if (!palette[PAL_WEAPON]) { + palette[PAL_WEAPON] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_WEAPON); + } + } else if (part == actorPartCount+1) { + if (!palette[PAL_OFFHAND]) { + palette[PAL_OFFHAND] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_OFFHAND); + } + } else if (part == actorPartCount+2) { + if (!palette[PAL_HELMET]) { + palette[PAL_HELMET] = a->GetFrame(0)->GetPalette()->Copy(); + SetupColors(PAL_HELMET); + } + } + + //animation is affected by game flags + a->gameAnimation = true; + a->SetPos( 0 ); + + //setting up the sequencing of animation cycles + switch (StanceID) { + case IE_ANI_DAMAGE: + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + case IE_ANI_DIE: + case IE_ANI_PST_START: + case IE_ANI_HEAD_TURN: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + a->Flags |= A_ANI_PLAYONCE; + break; + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + a->playReversed = true; + a->Flags |= A_ANI_PLAYONCE; + break; + } + switch (GetAnimType()) { + case IE_ANI_NINE_FRAMES: //dragon animations + case IE_ANI_FOUR_FRAMES: //wyvern animations + case IE_ANI_BIRD: + case IE_ANI_CODE_MIRROR: + case IE_ANI_CODE_MIRROR_2: //9 orientations + case IE_ANI_CODE_MIRROR_3: + case IE_ANI_PST_ANIMATION_3: //no stc just std + case IE_ANI_PST_ANIMATION_2: //no std just stc + case IE_ANI_PST_ANIMATION_1: + case IE_ANI_FRAGMENT: + if (Orient > 8) { + a->MirrorAnimation( ); + } + break; + default: + break; + } + + // make animarea of part 0 encompass the animarea of the other parts + if (part > 0) + anims[0]->AddAnimArea(a); + + } + + switch (GetAnimType()) { + case IE_ANI_NINE_FRAMES: //dragon animations + case IE_ANI_FOUR_FRAMES: //wyvern animations + case IE_ANI_BIRD: + case IE_ANI_CODE_MIRROR: + case IE_ANI_SIX_FILES: //16 anims some are stored elsewhere + case IE_ANI_ONE_FILE: //16 orientations + case IE_ANI_CODE_MIRROR_2: //9 orientations + case IE_ANI_CODE_MIRROR_3: + Anims[StanceID][Orient] = anims; + break; + case IE_ANI_TWO_FILES: + case IE_ANI_TWENTYTWO: + case IE_ANI_TWO_FILES_2: + case IE_ANI_TWO_FILES_3: + case IE_ANI_TWO_FILES_3B: + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + case IE_ANI_SIX_FILES_2: + case IE_ANI_FRAGMENT: + Orient&=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient + 1] = anims; + break; + + case IE_ANI_PST_ANIMATION_3: //no stc just std + case IE_ANI_PST_ANIMATION_2: //no std just stc + case IE_ANI_PST_ANIMATION_1: + switch (StanceID) { + case IE_ANI_WALK: + case IE_ANI_RUN: + case IE_ANI_PST_START: + Anims[StanceID][Orient] = anims; + break; + default: + Orient &=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient + 1] = anims; + break; + } + break; + + case IE_ANI_PST_STAND: + Orient &=~1; + Anims[StanceID][Orient] = anims; + Anims[StanceID][Orient+1] = anims; + break; + case IE_ANI_PST_GHOST: + Orient = 0; + StanceID = IE_ANI_AWAKE; + Anims[StanceID][0] = anims; + break; + default: + printMessage("CharAnimations","Unknown animation type\n",LIGHT_RED); + abort(); + } + delete equipdat; + + return Anims[StanceID][Orient]; +} + +static const int one_file[19]={2, 1, 0, 0, 2, 3, 0, 1, 0, 4, 1, 0, 0, 0, 3, 1, 4, 4, 4}; + +void CharAnimations::GetAnimResRef(unsigned char StanceID, + unsigned char Orient, + char* NewResRef, unsigned char& Cycle, + int Part, EquipResRefData*& EquipData) +{ + char tmp[256]; + EquipData = 0; + Orient &= 15; + switch (GetAnimType()) { + case IE_ANI_FOUR_FRAMES: + AddFFSuffix( NewResRef, StanceID, Cycle, Orient, Part ); + break; + + case IE_ANI_NINE_FRAMES: + AddNFSuffix( NewResRef, StanceID, Cycle, Orient, Part ); + break; + + case IE_ANI_CODE_MIRROR: + AddVHRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_BIRD: + Cycle = (ieByte) ((StanceID&1) * 9 + SixteenToNine[Orient]); + break; + + case IE_ANI_FRAGMENT: + Cycle = SixteenToFive[Orient]; + break; + + case IE_ANI_ONE_FILE: + Cycle = (ieByte) (one_file[StanceID] * 16 + Orient); + break; + + case IE_ANI_SIX_FILES: + AddSixSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWENTYTWO: //5+3 animations + AddMHRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_TWO_FILES_2: //4+4 animations + AddLR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES_3: //IWD style anims + AddMMRSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES_3B: //IWD style anims + AddMMR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_TWO_FILES: + AddTwoFileSuffix(NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_FOUR_FILES: + AddLRSuffix( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_FOUR_FILES_2: + AddLRSuffix2( NewResRef, StanceID, Cycle, Orient, EquipData ); + break; + + case IE_ANI_SIX_FILES_2: //MOGR (variant of FOUR_FILES) + AddLR3Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_CODE_MIRROR_2: //9 orientations + AddVHR2Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_CODE_MIRROR_3: // like IE_ANI_CODE_MIRROR_2 but with fewer cycles in g26 + AddVHR3Suffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_PST_ANIMATION_1: + case IE_ANI_PST_ANIMATION_2: + case IE_ANI_PST_ANIMATION_3: + AddPSTSuffix( NewResRef, StanceID, Cycle, Orient ); + break; + + case IE_ANI_PST_STAND: + sprintf(NewResRef,"%cSTD%4s",ResRef[0], ResRef+1); + Cycle = (ieByte) SixteenToFive[Orient]; + break; + case IE_ANI_PST_GHOST: // pst static animations + //still doesn't handle the second cycle of the golem anim + Cycle = 0; + strnlwrcpy(NewResRef, AvatarTable[AvatarsRowNum].Prefixes[Part], 8); + break; + default: + sprintf (tmp,"Unknown animation type in avatars.2da row: %d\n", AvatarsRowNum); + printMessage ("CharAnimations",tmp, LIGHT_RED); + abort(); + } +} + +void CharAnimations::GetEquipmentResRef(const char* equipRef, bool offhand, + char* ResRef, unsigned char& Cycle, EquipResRefData* equip) +{ + switch (GetAnimType()) { + case IE_ANI_FOUR_FILES: + case IE_ANI_FOUR_FILES_2: + GetLREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + case IE_ANI_CODE_MIRROR: + GetVHREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + case IE_ANI_TWENTYTWO: + GetMHREquipmentRef( ResRef, Cycle, equipRef, offhand, equip ); + break; + default: + printMessage ("CharAnimations", "Unsupported animation type for equipment animation.\n", LIGHT_RED); + abort(); + break; + } +} + +const int* CharAnimations::GetZOrder(unsigned char Orient) +{ + switch (GetAnimType()) { + case IE_ANI_CODE_MIRROR: + return zOrder_Mirror16[Orient]; + case IE_ANI_TWENTYTWO: + return zOrder_8[Orient/2]; + case IE_ANI_FOUR_FILES: + return 0; // FIXME + default: + return 0; + } +} + + +void CharAnimations::AddPSTSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + const char *Prefix; + + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_ATTACK_BACKSLASH: + Cycle=SixteenToFive[Orient]; + Prefix="at1"; break; + case IE_ANI_DAMAGE: + Cycle=SixteenToFive[Orient]; + Prefix="hit"; break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + Cycle=SixteenToFive[Orient]; + Prefix="gup"; break; + case IE_ANI_AWAKE: + Cycle=SixteenToFive[Orient]; + Prefix="std"; break; + case IE_ANI_READY: + Cycle=SixteenToFive[Orient]; + Prefix="stc"; break; + case IE_ANI_DIE: + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle=SixteenToFive[Orient]; + Prefix="dfb"; break; + case IE_ANI_RUN: + Cycle=SixteenToNine[Orient]; + Prefix="run"; break; + case IE_ANI_WALK: + Cycle=SixteenToNine[Orient]; + Prefix="wlk"; break; + case IE_ANI_HEAD_TURN: + Cycle=SixteenToFive[Orient]; + if (rand()&1) { + Prefix="sf2"; + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); + if (gamedata->Exists(ResRef, IE_BAM_CLASS_ID) ) { + return; + } + } + Prefix="sf1"; + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); + if (gamedata->Exists(ResRef, IE_BAM_CLASS_ID) ) { + return; + } + Prefix = "stc"; + break; + case IE_ANI_PST_START: + Cycle=0; + Prefix="ms1"; break; + default: //just in case + Cycle=SixteenToFive[Orient]; + Prefix="stc"; break; + } + sprintf(ResRef,"%c%3s%4s",this->ResRef[0], Prefix, this->ResRef+1); +} + +void CharAnimations::AddVHR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Cycle=SixteenToNine[Orient]; + + switch (StanceID) { + case IE_ANI_ATTACK: //temporarily + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g21" ); + break; + + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g26" ); + Cycle+=45; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "g25" ); + Cycle+=45; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "g26" ); + Cycle+=54; + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "g24" ); + Cycle+=27; + break; + + case IE_ANI_HEAD_TURN: + case IE_ANI_AWAKE: + strcat( ResRef, "g12" ); + Cycle+=18; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g15" ); + Cycle+=45; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g14" ); + Cycle+=45; + break; + + case IE_ANI_DIE: + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g14" ); + Cycle+=36; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g13" ); + Cycle+=27; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle+=9; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + break; + default: + printf("VHR2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } +} + +void CharAnimations::AddVHR3Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Cycle=SixteenToNine[Orient]; + + switch (StanceID) { + case IE_ANI_ATTACK: //temporarily + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g21" ); + break; + + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g26" ); + Cycle+=18; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "g25" ); + Cycle+=45; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "g26" ); + Cycle+=36; + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "g24" ); + Cycle+=27; + break; + + case IE_ANI_HEAD_TURN: + case IE_ANI_AWAKE: + strcat( ResRef, "g12" ); + Cycle+=18; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g15" ); + Cycle+=45; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g14" ); + Cycle+=45; + break; + + case IE_ANI_DIE: + case IE_ANI_EMERGE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g14" ); + Cycle+=36; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g13" ); + Cycle+=27; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle+=9; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + break; + default: + printf("VHR3 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } +} + +// Note: almost like SixSuffix +void CharAnimations::AddFFSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, int Part) +{ + Cycle=SixteenToNine[Orient]; + switch (StanceID) { + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + break; + + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g3" ); + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g3" ); + Cycle += 16; + break; + + case IE_ANI_ATTACK_JAB: + case IE_ANI_CAST: + case IE_ANI_CONJURE: + strcat( ResRef, "g3" ); + Cycle += 32; + break; + + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g2" ); + break; + + case IE_ANI_READY: + strcat( ResRef, "g2" ); + Cycle += 16; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g2" ); + Cycle += 32; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g2" ); + Cycle += 48; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g2" ); + Cycle += 64; + break; + + default: + printf("Four frames Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + + } + ResRef[6]=(char) (Part+'1'); + ResRef[7]=0; +} + +void CharAnimations::AddNFSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, int Part) +{ + char prefix[10]; + + Cycle = SixteenToNine[Orient]; + snprintf(prefix, 9, "%s%s%d%s%d", ResRef, StancePrefix[StanceID], Part+1, + CyclePrefix[StanceID], Cycle); + strnlwrcpy(ResRef,prefix,8); + Cycle=(ieByte) (Cycle+CycleOffset[StanceID]); +} + +//Attack +//h1, h2, w2 +//static const char *SlashPrefix[]={"a1","a4","a7"}; +//static const char *BackPrefix[]={"a2","a5","a8"}; +//static const char *JabPrefix[]={"a3","a6","a9"}; +static const char *SlashPrefix[]={"a1","a2","a7"}; +static const char *BackPrefix[]={"a3","a4","a8"}; +static const char *JabPrefix[]={"a5","a6","a9"}; +static const char *RangedPrefix[]={"sa","sx","ss"}; +static const char *RangedPrefixOld[]={"sa","sx","a1"}; + +void CharAnimations::AddVHRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& EquipData) +{ + Cycle = SixteenToNine[Orient]; + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, SlashPrefix[WeaponType] ); + strcpy( EquipData->Suffix, SlashPrefix[WeaponType] ); + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, BackPrefix[WeaponType] ); + strcpy( EquipData->Suffix, BackPrefix[WeaponType] ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, JabPrefix[WeaponType] ); + strcpy( EquipData->Suffix, JabPrefix[WeaponType] ); + break; + + case IE_ANI_AWAKE: + strcat( ResRef, "g17" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 63; + break; + + case IE_ANI_CAST: //looping + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + break; + + case IE_ANI_CONJURE: //ending + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle += 9; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g14" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 36; + break; + + case IE_ANI_DIE: + strcat( ResRef, "g15" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 45; + break; + //I cannot find an emerge animation... + //Maybe is Die reversed + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g19" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 81; + break; + + case IE_ANI_HEAD_TURN: + if (rand()&1) { + strcat( ResRef, "g12" ); + Cycle += 18; + } else { + strcat( ResRef, "g18" ); + Cycle += 72; + } + strcpy( EquipData->Suffix, "g1" ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_READY: + if ( WeaponType == IE_ANI_WEAPON_2H ) { + strcat( ResRef, "g13" ); + Cycle += 27; + } else { + strcat( ResRef, "g1" ); + Cycle += 9; + } + strcpy( EquipData->Suffix, "g1" ); + break; + //This depends on the ranged weapon equipped + case IE_ANI_SHOOT: + strcat( ResRef, RangedPrefix[RangedType] ); + strcpy( EquipData->Suffix, RangedPrefix[RangedType] ); + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g16" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 54; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g16" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle += 54; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g11" ); + strcpy( EquipData->Suffix, "g1" ); + break; + + default: + printf("VHR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetVHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + if (offhand) { + sprintf( ResRef, "wq%c%c%co%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } else { + sprintf( ResRef, "wq%c%c%c%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } +} + +void CharAnimations::AddSixSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g3" ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g3" ); + Cycle = 16 + Orient; + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g3" ); + Cycle = 32 + Orient; + break; + + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g2" ); + Cycle = 0 + Orient; + break; + + case IE_ANI_READY: + strcat( ResRef, "g2" ); + Cycle = 16 + Orient; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g2" ); + Cycle = 32 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "g2" ); + Cycle = 48 + Orient; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g2" ); + Cycle = 64 + Orient; + break; + + default: + printf("Six Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + + } + if (Orient>9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddLR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + Orient /= 2; + + switch (StanceID) { + case IE_ANI_READY: + case IE_ANI_CAST: //looping + case IE_ANI_CONJURE://ending + case IE_ANI_HIDE: + case IE_ANI_WALK: + case IE_ANI_AWAKE: + Cycle = 0 + Orient; + break; + + case IE_ANI_SHOOT: + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_HEAD_TURN: + Cycle = 8 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + Cycle = 24 + Orient; + break; + + case IE_ANI_DAMAGE: + Cycle = 16 + Orient; + break; + + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle = 32 + Orient; + break; + default: + printf("LR2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient>=4) { + strcat( ResRef, "g1e" ); + } else { + strcat( ResRef, "g1" ); + } +} + +void CharAnimations::AddMHRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& EquipData) +{ + Orient /= 2; + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + strcat (ResRef, SlashPrefix[WeaponType]); + strcpy( EquipData->Suffix, SlashPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_BACKSLASH: + strcat (ResRef, BackPrefix[WeaponType]); + strcpy( EquipData->Suffix, BackPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_ATTACK_JAB: + strcat (ResRef, JabPrefix[WeaponType]); + strcpy( EquipData->Suffix, JabPrefix[WeaponType] ); + Cycle = Orient; + break; + + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + if ( WeaponType == IE_ANI_WEAPON_2W ) { + Cycle = 24 + Orient; + } else { + Cycle = 8 + Orient; + } + break; + + case IE_ANI_CAST://looping + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle = 8 + Orient; + break; + + case IE_ANI_CONJURE://ending + strcat( ResRef, "ca" ); + strcpy( EquipData->Suffix, "ca" ); + Cycle = Orient; + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient; + break; + + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_PST_START: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 48 + Orient; + break; + + //I cannot find an emerge animation... + //Maybe is Die reversed + case IE_ANI_EMERGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 48 + Orient; + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient; + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient; + break; + + //This depends on the ranged weapon equipped + case IE_ANI_SHOOT: + strcat (ResRef, RangedPrefixOld[RangedType]); + strcpy( EquipData->Suffix, RangedPrefixOld[RangedType] ); + Cycle = Orient; + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 64 + Orient; + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 56 + Orient; + break; + + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient; + break; + default: + printf("MHR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient>=5) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e" ); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetMHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + if (offhand) { + //i think there is no offhand stuff for bg1, lets use the bg2 equivalent here? + sprintf( ResRef, "wq%c%c%co%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } else { + sprintf( ResRef, "wp%c%c%c%s", GetSize(), equipRef[0], equipRef[1], equip->Suffix ); + } +} + +void CharAnimations::AddTwoFileSuffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch(StanceID) { + case IE_ANI_HEAD_TURN: + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + Cycle = 24 + Orient / 2; + break; + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + Cycle = 40 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_DIE: + case IE_ANI_PST_START: + Cycle = 32 + Orient / 2; + break; + case IE_ANI_WALK: + Cycle = Orient / 2; + break; + default: + Cycle = 8 + Orient / 2; + break; + } + strcat( ResRef, "g1" ); + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddLRSuffix2( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData) +{ + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 24 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_DIE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient / 2; + break; + case IE_ANI_SLEEP: + case IE_ANI_TWITCH: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient / 2; + break; + default: + printf("LRSuffix2 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e"); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::AddLRSuffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData) +{ + EquipData = new EquipResRefData; + EquipData->Suffix[0] = 0; + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + //these animations are missing + strcat( ResRef, "g2" ); + strcpy( EquipData->Suffix, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 24 + Orient / 2; + break; + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_DIE: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 32 + Orient / 2; + break; + case IE_ANI_TWITCH: + case IE_ANI_SLEEP: + strcat( ResRef, "g1" ); + strcpy( EquipData->Suffix, "g1" ); + Cycle = 40 + Orient / 2; + break; + default: + printf("LR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + strcat( EquipData->Suffix, "e"); + } + EquipData->Cycle = Cycle; +} + +void CharAnimations::GetLREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool /*offhand*/, + EquipResRefData* equip) +{ + Cycle = equip->Cycle; + //hackhackhack + sprintf( ResRef, "%4s%c%s", this->ResRef, equipRef[0], equip->Suffix ); +} + +//Only for the ogre animation (MOGR) +void CharAnimations::AddLR3Suffix( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "g2" ); + Cycle = Orient / 2; + break; + case IE_ANI_ATTACK_SLASH: + strcat( ResRef, "g2" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "g2" ); + Cycle = 8 + Orient / 2; //there is no third attack animation + break; + case IE_ANI_CAST: + case IE_ANI_CONJURE: + case IE_ANI_SHOOT: + strcat( ResRef, "g3" ); + Cycle = Orient / 2; + break; + case IE_ANI_WALK: + strcat( ResRef, "g1" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_READY: + strcat( ResRef, "g1" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_HEAD_TURN: //could be wrong + case IE_ANI_AWAKE: + strcat( ResRef, "g1" ); + Cycle = Orient / 2; + break; + case IE_ANI_DAMAGE: + strcat( ResRef, "g3" ); + Cycle = 8 + Orient / 2; + break; + case IE_ANI_DIE: + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + case IE_ANI_SLEEP: + strcat( ResRef, "g3" ); + Cycle = 16 + Orient / 2; + break; + case IE_ANI_TWITCH: + strcat( ResRef, "g3" ); + Cycle = 24 + Orient / 2; + break; + default: + printf("LR3 Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddMMR2Suffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + case IE_ANI_ATTACK_JAB: + case IE_ANI_CONJURE: + case IE_ANI_CAST: + strcat( ResRef, "a1" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "a4" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_AWAKE: + case IE_ANI_READY: + strcat( ResRef, "sd" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "sc" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "gh" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DIE: + strcat( ResRef, "de" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "gu" ); + Cycle = ( Orient / 2 ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "sl" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "tw" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_WALK: + strcat( ResRef, "wk" ); + Cycle = ( Orient / 2 ); + break; + default: + printf("MMR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::AddMMRSuffix(char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient) +{ + switch (StanceID) { + case IE_ANI_ATTACK: + case IE_ANI_ATTACK_SLASH: + case IE_ANI_ATTACK_BACKSLASH: + strcat( ResRef, "a1" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_SHOOT: + strcat( ResRef, "a4" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_ATTACK_JAB: + strcat( ResRef, "a2" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_AWAKE: + case IE_ANI_READY: + strcat( ResRef, "sd" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_CONJURE: + strcat( ResRef, "ca" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_CAST: + strcat( ResRef, "sp" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_HEAD_TURN: + strcat( ResRef, "sc" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DAMAGE: + strcat( ResRef, "gh" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_DIE: + strcat( ResRef, "de" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_GET_UP: + case IE_ANI_EMERGE: + case IE_ANI_PST_START: + strcat( ResRef, "gu" ); + Cycle = ( Orient / 2 ); + break; + + //Unknown... maybe only a transparency effect apply + case IE_ANI_HIDE: + break; + + case IE_ANI_SLEEP: + strcat( ResRef, "sl" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_TWITCH: + strcat( ResRef, "tw" ); + Cycle = ( Orient / 2 ); + break; + + case IE_ANI_WALK: + strcat( ResRef, "wk" ); + Cycle = ( Orient / 2 ); + break; + default: + printf("MMR Animation: unhandled stance: %s %d\n", ResRef, StanceID); + abort(); + break; + } + if (Orient > 9) { + strcat( ResRef, "e" ); + } +} + +void CharAnimations::PulseRGBModifiers() +{ + unsigned long time = core->GetGame()->Ticks; + + if (time - lastModUpdate <= 40) + return; + + if (time - lastModUpdate > 400) lastModUpdate = time - 40; + + int inc = (time - lastModUpdate)/40; + bool change[4] = { false, false, false, false }; + if (GlobalColorMod.type != RGBModifier::NONE && + GlobalColorMod.speed > 0) + { + GlobalColorMod.phase += inc; + change[0] = change[1] = change[2] = change[3] = true; + + // reset if done + if (GlobalColorMod.phase > 2*GlobalColorMod.speed) { + GlobalColorMod.type = RGBModifier::NONE; + GlobalColorMod.phase = 0; + GlobalColorMod.speed = 0; + } + } + + for (int i = 0; i < 32; ++i) { + if (ColorMods[i].type != RGBModifier::NONE && + ColorMods[i].speed > 0) + { + ColorMods[i].phase += inc; + change[i>>3] = true; + } + } + + if (change[0]) SetupColors(PAL_MAIN); + if (change[1]) SetupColors(PAL_WEAPON); + if (change[2]) SetupColors(PAL_OFFHAND); + if (change[3]) SetupColors(PAL_HELMET); + + lastModUpdate += inc*40; +} diff --git a/project/jni/application/gemrb/src/core/CharAnimations.h b/project/jni/application/gemrb/src/core/CharAnimations.h new file mode 100644 index 000000000..c963663a9 --- /dev/null +++ b/project/jni/application/gemrb/src/core/CharAnimations.h @@ -0,0 +1,231 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 CHARANIMATIONS_H +#define CHARANIMATIONS_H + +#include "RGBAColor.h" +#include "exports.h" + +#include "Animation.h" +#include "Palette.h" +#include "TableMgr.h" + +#include + +#define AV_PREFIX1 0 +#define AV_PREFIX2 1 +#define AV_PREFIX3 2 +#define AV_PREFIX4 3 +#define AV_ANIMTYPE 4 +#define AV_CIRCLESIZE 5 +#define AV_USE_PALETTE 6 +#define AV_SIZE 7 + +#define MAX_ANIMS 19 + +#define IE_ANI_ATTACK 0 +#define IE_ANI_AWAKE 1 +#define IE_ANI_CAST 2 +#define IE_ANI_CONJURE 3 +#define IE_ANI_DAMAGE 4 +#define IE_ANI_DIE 5 +#define IE_ANI_HEAD_TURN 6 +#define IE_ANI_READY 7 +#define IE_ANI_SHOOT 8 +#define IE_ANI_TWITCH 9 +#define IE_ANI_WALK 10 +#define IE_ANI_ATTACK_SLASH 11 +#define IE_ANI_ATTACK_BACKSLASH 12 +#define IE_ANI_ATTACK_JAB 13 +#define IE_ANI_EMERGE 14 +#define IE_ANI_HIDE 15 +#define IE_ANI_RUN 15 //pst has no hide, i hope +#define IE_ANI_SLEEP 16 +#define IE_ANI_GET_UP 17 +#define IE_ANI_PST_START 18 + +//BG2, IWD animation types +#define IE_ANI_CODE_MIRROR 0 +#define IE_ANI_ONE_FILE 1 +#define IE_ANI_FOUR_FILES 2 +#define IE_ANI_TWO_FILES 3 +#define IE_ANI_CODE_MIRROR_2 4 +#define IE_ANI_SIX_FILES_2 5 //MOGR +#define IE_ANI_TWENTYTWO 6 +#define IE_ANI_BIRD 7 +#define IE_ANI_SIX_FILES 8 //MCAR/MWYV +#define IE_ANI_TWO_FILES_3 9 //iwd animations +#define IE_ANI_TWO_FILES_2 10 //low res bg1 anim +#define IE_ANI_FOUR_FRAMES 11 //wyvern anims +#define IE_ANI_NINE_FRAMES 12 //dragon anims +#define IE_ANI_FRAGMENT 13 //fragment animation +#define IE_ANI_FOUR_FILES_2 14 //METT +#define IE_ANI_CODE_MIRROR_3 15 //MSPS +#define IE_ANI_TWO_FILES_3B 16 //iwd animations (eg. MBBM) + +//PST animation types +#define IE_ANI_PST_ANIMATION_1 56 //full animation +#define IE_ANI_PST_GHOST 57 //no orientations +#define IE_ANI_PST_STAND 58 //has orientations +#define IE_ANI_PST_ANIMATION_2 59 //full animation std-->stc +#define IE_ANI_PST_ANIMATION_3 60 //full animation stc-->std + +//armour levels +#define IE_ANI_NO_ARMOR 0 +#define IE_ANI_LIGHT_ARMOR 1 +#define IE_ANI_MEDIUM_ARMOR 2 +#define IE_ANI_HEAVY_ARMOR 3 + +#define IE_ANI_WEAPON_1H 0 +#define IE_ANI_WEAPON_2H 1 +#define IE_ANI_WEAPON_2W 2 + +#define IE_ANI_RANGED_BOW 0 +#define IE_ANI_RANGED_XBOW 1 +#define IE_ANI_RANGED_THROW 2 + +struct AvatarStruct { + /* entries from avatars.2da */ + unsigned int AnimID; + unsigned int PaletteType; + ieResRef Prefixes[4]; + unsigned char AnimationType; + unsigned char CircleSize; + char Size; + + /* comes from bloodclr.2da */ + char BloodColor; + + /* resdata.ini entries */ + unsigned int WalkScale; /* 1000 / walkscale */ + unsigned int RunScale; /* 1000 / runscale */ + int Bestiary; + + /* comes from walksnd.2da */ + ieResRef WalkSound; + ieByte WalkSoundCount; +}; + +struct EquipResRefData; + +class GEM_EXPORT CharAnimations { +private: + Animation** Anims[MAX_ANIMS][MAX_ORIENT]; + char HelmetRef[2]; + char WeaponRef[2]; + char OffhandRef[2]; +public: + const ieDword *Colors; //these are the custom color indices + RGBModifier ColorMods[32]; // color modification effects + unsigned long lastModUpdate; + RGBModifier GlobalColorMod; // global color modification effect + + Palette* palette[4]; + Palette* modifiedPalette[4]; + unsigned int AvatarsRowNum; + unsigned char ArmorType, WeaponType, RangedType; + ieResRef ResRef; + ieResRef PaletteResRef; + unsigned char nextStanceID, StanceID; + bool autoSwitchOnEnd; + bool lockPalette; +public: + CharAnimations(unsigned int AnimID, ieDword ArmourLevel); + ~CharAnimations(void); + static void ReleaseMemory(); + void SetArmourLevel(int ArmourLevel); + void SetRangedType(int Ranged); + void SetWeaponType(int WeaponType); + void SetHelmetRef(const char* ref); + void SetWeaponRef(const char* ref); + void SetOffhandRef(const char* ref); + void SetupColors(PaletteType type); + void SetColors(const ieDword *Colors); + void LockPalette(const ieDword *Colors); + + // returns an array of animations of size GetTotalPartCount() + Animation** GetAnimation(unsigned char Stance, unsigned char Orient); + int GetTotalPartCount() const; + const int* GetZOrder(unsigned char Orient); + + // returns Palette for a given part (unlocked) + Palette* GetPartPalette(int part); // TODO: clean this up + +public: //attribute functions + static int GetAvatarsCount(); + static AvatarStruct *GetAvatarStruct(int RowNum); + unsigned int GetAnimationID() const; + int GetCircleSize() const; + int NoPalette() const; + int GetAnimType() const; + int GetSize() const; + int GetBloodColor() const; + const ieResRef &GetWalkSound() const; + int GetWalkSoundCount() const; + void PulseRGBModifiers(); + +private: + void DropAnims(); + void InitAvatarsTable(); + int GetActorPartCount() const; + void AddPSTSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddFFSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, int Part); + void AddNFSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, int Part); + void AddVHR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddVHRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void AddVHR3Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void GetVHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddSixSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddMHRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void GetMHREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddMMRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddMMR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddTwoFileSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddLRSuffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData*& equip); + void AddLRSuffix2( char* ResRef, unsigned char StanceID, + unsigned char& Cycle, unsigned char Orient, EquipResRefData *&EquipData); + void GetLREquipmentRef(char* ResRef, unsigned char& Cycle, + const char* equipRef, bool offhand, EquipResRefData* equip); + void AddLR2Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void AddLR3Suffix(char* ResRef, unsigned char AnimID, + unsigned char& Cycle, unsigned char Orient); + void GetAnimResRef(unsigned char AnimID, unsigned char Orient, + char* ResRef, unsigned char& Cycle, int Part, EquipResRefData*& equip); + void GetEquipmentResRef(const char* equipRef, bool offhand, + char* ResRef, unsigned char& Cycle, EquipResRefData* equip); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Compressor.cpp b/project/jni/application/gemrb/src/core/Compressor.cpp new file mode 100644 index 000000000..e7a675be3 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Compressor.cpp @@ -0,0 +1,37 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "Compressor.h" + +#include "globals.h" + +Compressor::Compressor(void) +{ +} + +Compressor::~Compressor(void) +{ +} + +// Initialization Function. Returns FALSE if there was an error during initialization, else returns TRUE. +int Compressor::Init(void) +{ + return GEM_OK; +} diff --git a/project/jni/application/gemrb/src/core/Compressor.h b/project/jni/application/gemrb/src/core/Compressor.h new file mode 100644 index 000000000..3409f8019 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Compressor.h @@ -0,0 +1,41 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 COMPRESSOR_H +#define COMPRESSOR_H + +#include "Plugin.h" +#include "System/DataStream.h" + +#include + +class GEM_EXPORT Compressor : public Plugin { +public: + Compressor(void); + virtual ~Compressor(void); + /** Initialization Function. Returns FALSE if there was an error during initialization, else returns TRUE. */ + virtual int Init(void); + /** decompresses a datastream (memory or file) to a FILE * stream */ + virtual int Decompress(FILE* dest, DataStream* source, unsigned int size_guess = 0) const = 0; + /** compresses a datastream (memory or file) to another DataStream */ + virtual int Compress(DataStream *dest, DataStream* source) const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ControlAnimation.cpp b/project/jni/application/gemrb/src/core/ControlAnimation.cpp new file mode 100644 index 000000000..6e618844a --- /dev/null +++ b/project/jni/application/gemrb/src/core/ControlAnimation.cpp @@ -0,0 +1,138 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "ControlAnimation.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" /* needed only for paperdoll palettes */ +#include "Video.h" /* needed only for paperdoll palettes */ +#include "GUI/Button.h" + +ControlAnimation::ControlAnimation(Control* ctl, const ieResRef ResRef, int Cycle) +{ + control = NULL; + bam = NULL; + cycle = Cycle; + frame = 0; + anim_phase = 0; + + bam = ( AnimationFactory* ) gamedata->GetFactoryResource( ResRef, + IE_BAM_CLASS_ID, IE_NORMAL ); + + if (! bam) + return; + + control = ctl; + control->animation = this; + has_palette = false; +} + +//freeing the bitmaps only once, but using an intelligent algorithm +ControlAnimation::~ControlAnimation(void) +{ + //removing from timer first + core->timer->RemoveAnimation( this ); + + bam = NULL; +} + +bool ControlAnimation::SameResource(const ieResRef ResRef, int Cycle) +{ + if (!control ) return false; + if (!bam) return false; + if (strnicmp(ResRef, bam->ResRef, sizeof(ieResRef) )) return false; + int c = cycle; + if (control->Flags&IE_GUI_BUTTON_PLAYRANDOM) { + c&=~1; + } + if (Cycle!=c) return false; + return true; +} + +void ControlAnimation::UpdateAnimation(void) +{ + unsigned long time; + int Cycle = cycle; + + if (control->Flags & IE_GUI_BUTTON_PLAYRANDOM) { + // simple Finite-State Machine + if (anim_phase == 0) { + frame = 0; + anim_phase = 1; + time = 500 + 500 * (rand() % 20); + cycle&=~1; + Cycle=cycle; + } else if (anim_phase == 1) { + if (rand() % 30 == 0) { + cycle|=1; + Cycle=cycle; + } + anim_phase = 2; + time = 100; + } else { + frame++; + time = 100; + } + } else { + frame ++; + if (has_palette) { + time = 100; //hack for slower movement + } else { + time = 15; + } + } + + Sprite2D* pic = bam->GetFrame( (unsigned short) frame, (unsigned char) Cycle ); + + if (pic == NULL) { + //stopping at end frame + if (control->Flags & IE_GUI_BUTTON_PLAYONCE) { + core->timer->RemoveAnimation( this ); + return; + } + anim_phase = 0; + frame = 0; + pic = bam->GetFrame( 0, (unsigned char) Cycle ); + } + + if (pic == NULL) { + return; + } + + if (has_palette) { + Palette* palette = pic->GetPalette(); + palette->SetupPaperdollColours(colors, 0); + pic->SetPalette(palette); + palette->Release(); + } + + control->SetAnimPicture( pic ); + core->timer->AddAnimation( this, time ); +} + +void ControlAnimation::SetPaletteGradients(ieDword *col) +{ + memcpy(colors, col, 8*sizeof(ieDword)); + has_palette = true; +} + diff --git a/project/jni/application/gemrb/src/core/ControlAnimation.h b/project/jni/application/gemrb/src/core/ControlAnimation.h new file mode 100644 index 000000000..c85870f67 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ControlAnimation.h @@ -0,0 +1,51 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 CONTROLANIMATIONS_H +#define CONTROLANIMATIONS_H + +#include "RGBAColor.h" +#include "exports.h" + +#include "AnimationFactory.h" +#include "Sprite2D.h" +#include "GUI/Control.h" + +#include + +class GEM_EXPORT ControlAnimation { +private: + AnimationFactory* bam; + Control* control; + unsigned int cycle; + unsigned int frame; + unsigned int anim_phase; + bool has_palette; + ieDword colors[8]; +public: + ControlAnimation(Control* ctl, const ieResRef ResRef, int Cycle = 0); + ~ControlAnimation(void); + void UpdateAnimation(); + //report if the current resource is the same as descripted by the params + bool SameResource(const ieResRef ResRef, int Cycle); + void SetPaletteGradients(ieDword *col); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Core.cpp b/project/jni/application/gemrb/src/core/Core.cpp new file mode 100644 index 000000000..f0c075366 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Core.cpp @@ -0,0 +1,323 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Core.cpp + * Some compatibility and utility functions + * @author The GemRB Project + */ + +#include "globals.h" +#include "exports.h" + +#include "Interface.h" +#include "Scriptable/Actor.h" + +#include +#include +#ifdef WIN32 +#include "win32def.h" +#ifdef _DEBUG +#include +#include +#endif + +BOOL WINAPI DllEntryPoint(HINSTANCE /*hinstDLL*/, DWORD /*fdwReason*/, + LPVOID /*lpvReserved*/) +{ + return true; +} +#endif + +//// Globally used functions + +ieByte pl_uppercase[256]; +ieByte pl_lowercase[256]; + +// these 3 functions will copy a string to a zero terminated string with a maximum length +void strnlwrcpy(char *dest, const char *source, int count) +{ + while(count--) { + *dest++ = pl_lowercase[(ieByte) *source]; + if(!*source++) { + while(count--) *dest++=0; + break; + } + } + *dest=0; +} + +void strnuprcpy(char* dest, const char *source, int count) +{ + while(count--) { + *dest++ = pl_uppercase[(ieByte) *source]; + if(!*source++) { + while(count--) *dest++=0; + break; + } + } + *dest=0; +} + +// this one also filters spaces +void strnspccpy(char* dest, const char *source, int count) +{ + memset(dest,0,count); + while(count--) { + char c = pl_lowercase[(ieByte) *source]; + if (c!=' ') { + *dest++=c; + } + if(!*source++) { + return; + } + } +} + +#ifndef HAVE_STRNLEN +int strnlen(const char* string, int maxlen) +{ + if (!string) { + return -1; + } + int i = 0; + while (maxlen-- > 0) { + if (!string[i]) + break; + i++; + } + return i; +} +#endif // ! HAVE_STRNLEN + +static const unsigned char orientations[25]={ +6,7,8,9,10, +5,6,8,10,11, +4,4,0,12,12, +3,2,0,14,13, +2,1,0,15,14 +}; + +/** Calculates the orientation of a character (or projectile) facing a point */ +unsigned char GetOrient(const Point &s, const Point &d) +{ + int deltaX = s.x - d.x; + int deltaY = s.y - d.y; + int div = Distance(s,d); + if(!div) return 0; //default + if(div>3) div/=2; + int aX=deltaX/div; + int aY=deltaY/div; + return orientations[(aY+2)*5+aX+2]; +} + +/** Calculates distance between 2 points */ +unsigned int Distance(Point p, Point q) +{ + long x = ( p.x - q.x ); + long y = ( p.y - q.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +/** Calculates distance squared from a point to a scriptable */ +unsigned int SquaredMapDistance(Point p, Scriptable *b) +{ + long x = ( p.x/16 - b->Pos.x/16 ); + long y = ( p.y/12 - b->Pos.y/12 ); + return (unsigned int)(x*x + y*y); +} + +/** Calculates distance between 2 points */ +unsigned int Distance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +unsigned int PersonalDistance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + int ret = (int) sqrt( ( double ) ( x* x + y* y ) ); + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*10; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +unsigned int SquaredPersonalDistance(Point p, Scriptable *b) +{ + long x = ( p.x - b->Pos.x ); + long y = ( p.y - b->Pos.y ); + int ret = x*x + y*y; + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*100; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +/** Calculates map distance between 2 scriptables */ +unsigned int SquaredMapDistance(Scriptable *a, Scriptable *b) +{ + long x = (a->Pos.x/16 - b->Pos.x/16 ); + long y = (a->Pos.y/12 - b->Pos.y/12 ); + return (unsigned int)(x*x + y*y); +} + +/** Calculates distance between 2 scriptables */ +unsigned int Distance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + return (unsigned int) sqrt( ( double ) ( x* x + y* y ) ); +} + +/** Calculates distance squared between 2 scriptables */ +unsigned int SquaredDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + return (unsigned int) ( x* x + y* y ); +} + +/** Calculates distance between 2 scriptables, including feet circle if applicable */ +unsigned int PersonalDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + int ret = (int) sqrt( ( double ) ( x* x + y* y ) ); + if (a->Type==ST_ACTOR) { + ret-=((Actor *)a)->size*10; + } + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*10; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +unsigned int SquaredPersonalDistance(Scriptable *a, Scriptable *b) +{ + long x = ( a->Pos.x - b->Pos.x ); + long y = ( a->Pos.y - b->Pos.y ); + int ret = x*x + y*y; + if (a->Type==ST_ACTOR) { + ret-=((Actor *)a)->size*100; + } + if (b->Type==ST_ACTOR) { + ret-=((Actor *)b)->size*100; + } + if (ret<0) return (unsigned int) 0; + return (unsigned int) ret; +} + +// returns EA relation between two scriptables (non actors are always enemies) +// it is used for protectile targeting/iwd ids targeting too! +int EARelation(Scriptable* Owner, Actor* target) +{ + ieDword eao = EA_ENEMY; + + if (Owner && Owner->Type==ST_ACTOR) { + eao = ((Actor *) Owner)->GetStat(IE_EA); + } + + ieDword eat = target->GetStat(IE_EA); + + if (eao<=EA_GOODCUTOFF) { + + if (eat<=EA_GOODCUTOFF) { + return EAR_FRIEND; + } + if (eat>=EA_EVILCUTOFF) { + return EAR_HOSTILE; + } + + return EAR_NEUTRAL; + } + + if (eao>=EA_EVILCUTOFF) { + + if (eat<=EA_GOODCUTOFF) { + return EAR_HOSTILE; + } + if (eat>=EA_EVILCUTOFF) { + return EAR_FRIEND; + } + + return EAR_NEUTRAL; + } + + return EAR_NEUTRAL; +} + +/** Returns the length of string (up to a delimiter) */ +GEM_EXPORT int strlench(const char* string, char ch) +{ + int i; + for (i = 0; string[i] && string[i] != ch; i++) + ; + return i; +} + +//// Compatibility functions +#ifndef HAVE_STRNDUP +GEM_EXPORT char* strndup(const char* s, size_t l) +{ + size_t len = strlen( s ); + if (len < l) { + l = len; + } + char* string = ( char* ) malloc( l + 1 ); + strncpy( string, s, l ); + string[l] = 0; + return string; +} +#endif + +#ifdef WIN32 + +#else + +char* strupr(char* string) +{ + char* s; + if (string) { + for (s = string; *s; ++s) + *s = toupper( *s ); + } + return string; +} + +char* strlwr(char* string) +{ + char* s; + if (string) { + for (s = string; *s; ++s) + *s = tolower( *s ); + } + return string; +} + + +#endif // ! WIN32 + diff --git a/project/jni/application/gemrb/src/core/DataFileMgr.cpp b/project/jni/application/gemrb/src/core/DataFileMgr.cpp new file mode 100644 index 000000000..2c95d6787 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DataFileMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "DataFileMgr.h" + +DataFileMgr::DataFileMgr(void) +{ +} + +DataFileMgr::~DataFileMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/DataFileMgr.h b/project/jni/application/gemrb/src/core/DataFileMgr.h new file mode 100644 index 000000000..8773767b7 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DataFileMgr.h @@ -0,0 +1,58 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 DataFileMgr.h + * Declares DataFileMgr class, abstract loader for .INI files + * @author The GemRB Project + */ + + +#ifndef DATAFILEMGR_H +#define DATAFILEMGR_H + +#include "Plugin.h" +#include "System/DataStream.h" + +/** + * @class DataFileMgr + * Abstract loader for .INI files + */ + +class GEM_EXPORT DataFileMgr : public Plugin { +public: + DataFileMgr(void); + virtual ~DataFileMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = false) = 0; + virtual int GetTagsCount() const = 0; + virtual const char* GetTagNameByIndex(int index) const = 0; + virtual int GetKeysCount(const char* Tag) const = 0; + virtual const char* GetKeyNameByIndex(const char* Tag, int index) const = 0; + virtual const char* GetKeyAsString(const char* Tag, const char* Key, + const char* Default) const = 0; + virtual int GetKeyAsInt(const char* Tag, const char* Key, + const int Default) const = 0; + virtual float GetKeyAsFloat(const char* Tag, const char* Key, + const float Default) const = 0; + virtual bool GetKeyAsBool(const char* Tag, const char* Key, + const bool Default) const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Dialog.cpp b/project/jni/application/gemrb/src/core/Dialog.cpp new file mode 100644 index 000000000..a8399ee59 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Dialog.cpp @@ -0,0 +1,100 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "Dialog.h" + +#include "win32def.h" + +#include "GameScript/GameScript.h" + +Dialog::Dialog(void) +{ + TopLevelCount = 0; +} + +Dialog::~Dialog(void) +{ + if (initialStates) { + for (unsigned int i = 0; i < TopLevelCount; i++) { + if (initialStates[i]) { + FreeDialogState( initialStates[i] ); + } + } + free(initialStates); + } + if (Order) free(Order); +} + +DialogState* Dialog::GetState(unsigned int index) +{ + if (index >= TopLevelCount) { + return NULL; + } + return initialStates[index]; +} + +void Dialog::FreeDialogState(DialogState* ds) +{ + for (unsigned int i = 0; i < ds->transitionsCount; i++) { + DialogTransition *trans = ds->transitions[i]; + for (size_t j = 0; j < trans->actions.size(); ++j) + trans->actions[j]->Release(); + if (trans->condition) + delete trans->condition; + delete( trans ); + } + free( ds->transitions ); + if (ds->condition) { + delete ds->condition; + } + delete( ds ); +} + +int Dialog::FindFirstState(Scriptable* target) +{ + for (unsigned int i = 0; i < TopLevelCount; i++) { + Condition *cond = GetState( Order[i] )->condition; + if (cond && cond->Evaluate(target)) { + return Order[i]; + } + } + return -1; +} + +int Dialog::FindRandomState(Scriptable* target) +{ + unsigned int i; + unsigned int max = TopLevelCount; + if (!max) return -1; + unsigned int pick = rand()%max; + for (i=pick; i < max; i++) { + Condition *cond = GetState(i)->condition; + if (cond && cond->Evaluate(target)) { + return i; + } + } + for (i=0; i < pick; i++) { + Condition *cond = GetState(i)->condition; + if (cond && cond->Evaluate(target)) { + return i; + } + } + return -1; +} diff --git a/project/jni/application/gemrb/src/core/Dialog.h b/project/jni/application/gemrb/src/core/Dialog.h new file mode 100644 index 000000000..4dc473b6a --- /dev/null +++ b/project/jni/application/gemrb/src/core/Dialog.h @@ -0,0 +1,82 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 DIALOG_H +#define DIALOG_H + +#include "exports.h" +#include "globals.h" + +#include "GameScript/GameScript.h" + +#include + +#define IE_DLG_TR_TEXT 0x01 +#define IE_DLG_TR_TRIGGER 0x02 +#define IE_DLG_TR_ACTION 0x04 +#define IE_DLG_TR_FINAL 0x08 +#define IE_DLG_TR_JOURNAL 0x10 +#define IE_DLG_UNSOLVED 0x40 +#define IE_DLG_SOLVED 0x100 +#define IE_DLG_QUEST_GROUP 0x4000 // this is a GemRB extension + +struct DialogTransition { + ieDword Flags; + ieStrRef textStrRef; + ieStrRef journalStrRef; + Condition* condition; + std::vector actions; + ieResRef Dialog; + ieDword stateIndex; +}; + +struct DialogState { + ieStrRef StrRef; + DialogTransition** transitions; + unsigned int transitionsCount; + Condition* condition; + unsigned int weight; +}; + +class GEM_EXPORT Dialog { +public: + Dialog(void); + ~Dialog(void); +private: + void FreeDialogState(DialogState* ds); +public: + void AddState(DialogState* ds); + DialogState* GetState(unsigned int index); + int FindFirstState(Scriptable* target); + int FindRandomState(Scriptable* target); + + void Release() + { + delete this; + } +public: + ieResRef ResRef; + ieDword Flags; //freeze flags (bg2) + unsigned int TopLevelCount; + ieDword* Order; + DialogState** initialStates; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DialogHandler.cpp b/project/jni/application/gemrb/src/core/DialogHandler.cpp new file mode 100644 index 000000000..5653a8096 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogHandler.cpp @@ -0,0 +1,473 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "DialogHandler.h" + +#include "strrefs.h" + +#include "DialogMgr.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Video.h" +#include "GUI/GameControl.h" + +//translate section values (journal, solved, unsolved, user) +static int sectionMap[4]={4,1,2,0}; +static const int bg2Sections[4]={4,1,2,0}; +static const int noSections[4]={0,0,0,0}; + +DialogHandler::DialogHandler(void) +{ + dlg = NULL; + targetID = 0; + originalTargetID = 0; + speakerID = 0; + if (core->HasFeature(GF_JOURNAL_HAS_SECTIONS) ) { + memcpy(sectionMap, bg2Sections, sizeof(sectionMap) ); + } else { + memcpy(sectionMap, noSections, sizeof(sectionMap) ); + } +} + +DialogHandler::~DialogHandler(void) +{ + if (dlg) { + delete dlg; + } +} + +//Try to start dialogue between two actors (one of them could be inanimate) +int DialogHandler::InitDialog(Scriptable* spk, Scriptable* tgt, const char* dlgref) +{ + if (dlg) { + delete dlg; + dlg = NULL; + } + + PluginHolder dm(IE_DLG_CLASS_ID); + dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true ); + dlg = dm->GetDialog(); + + if (!dlg) { + printMessage("GameControl", " ", LIGHT_RED); + printf( "Cannot start dialog: %s\n", dlgref ); + return -1; + } + + strnlwrcpy(dlg->ResRef, dlgref, 8); //this isn't handled by GetDialog??? + + //target is here because it could be changed when a dialog runs onto + //and external link, we need to find the new target (whose dialog was + //linked to) + + Actor *oldTarget = GetActorByGlobalID(targetID); + speakerID = spk->GetGlobalID(); + targetID = tgt->GetGlobalID(); + if (!originalTargetID) originalTargetID = tgt->GetGlobalID(); + if (tgt->Type==ST_ACTOR) { + Actor *tar = (Actor *) tgt; + spk->LastTalkedTo=targetID; + tar->LastTalkedTo=speakerID; + tar->SetCircleSize(); + } + if (oldTarget) oldTarget->SetCircleSize(); + + //check if we are already in dialog + if (core->GetGameControl()->GetDialogueFlags()&DF_IN_DIALOG) { + return 0; + } + + int si = dlg->FindFirstState( tgt ); + if (si < 0) { + return -1; + } + + //we need GUI for dialogs + core->GetGameControl()->UnhideGUI(); + + //no exploring while in dialogue + core->GetGameControl()->SetScreenFlags(SF_GUIENABLED|SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_OR); + core->GetGameControl()->SetDialogueFlags(DF_IN_DIALOG, BM_OR); + + if (tgt->Type==ST_ACTOR) { + Actor *tar = (Actor *) tgt; + tar->DialogInterrupt(); + } + + //allow mouse selection from dialog (even though screen is locked) + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + video->SetMouseEnabled(true); + core->timer->SetMoveViewPort( tgt->Pos.x, tgt->Pos.y, 0, true ); + video->MoveViewportTo( tgt->Pos.x-vp.w/2, tgt->Pos.y-vp.h/2 ); + //there are 3 bits, if they are all unset, the dialog freezes scripts + if (!(dlg->Flags&7) ) { + core->GetGameControl()->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + } + //opening control size to maximum, enabling dialog window + core->GetGame()->SetControlStatus(CS_HIDEGUI, BM_NAND); + core->GetGame()->SetControlStatus(CS_DIALOG, BM_OR); + core->SetEventFlag(EF_PORTRAIT); + return 0; +} + +/*try to break will only try to break it, false means unconditional stop*/ +void DialogHandler::EndDialog(bool try_to_break) +{ + if (try_to_break && (core->GetGameControl()->GetDialogueFlags()&DF_UNBREAKABLE) ) { + return; + } + + Actor *tmp = GetSpeaker(); + if (tmp) { + tmp->LeaveDialog(); + } + speakerID = 0; + Scriptable *tmp2 = GetTarget(); + if (tmp2 && tmp2->Type == ST_ACTOR) { + tmp = (Actor *)tmp2; + } else { + tmp = NULL; + } + if (tmp) { + tmp->LeaveDialog(); + } + targetID = 0; + if (tmp) tmp->SetCircleSize(); + originalTargetID = 0; + ds = NULL; + if (dlg) { + delete dlg; + dlg = NULL; + } + //restoring original size + core->GetGame()->SetControlStatus(CS_DIALOG, BM_NAND); + core->GetGameControl()->SetScreenFlags(SF_DISABLEMOUSE|SF_LOCKSCROLL, BM_NAND); + core->GetGameControl()->SetDialogueFlags(0, BM_SET); + core->SetEventFlag(EF_PORTRAIT); +} + + +void DialogHandler::DialogChoose(unsigned int choose) +{ + TextArea* ta = core->GetMessageTextArea(); + if (!ta) { + printMessage("GameControl","Dialog aborted???",LIGHT_RED); + EndDialog(); + return; + } + + Actor *speaker = GetSpeaker(); + if (!speaker) { + printMessage("GameControl","Speaker gone???",LIGHT_RED); + EndDialog(); + return; + } + + Scriptable *target = GetTarget(); + if (!target) { + printMessage("GameControl","Target gone???",LIGHT_RED); + EndDialog(); + return; + } + Actor *tgt = NULL; + if (target->Type == ST_ACTOR) { + tgt = (Actor *)target; + } + + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + video->SetMouseEnabled(true); + core->timer->SetMoveViewPort( target->Pos.x, target->Pos.y, 0, true ); + video->MoveViewportTo( target->Pos.x-vp.w/2, target->Pos.y-vp.h/2 ); + + if (choose == (unsigned int) -1) { + //increasing talkcount after top level condition was determined + + int si = dlg->FindFirstState( tgt ); + if (si<0) { + EndDialog(); + return; + } + + if (tgt) { + if (core->GetGameControl()->GetDialogueFlags()&DF_TALKCOUNT) { + core->GetGameControl()->SetDialogueFlags(DF_TALKCOUNT, BM_NAND); + tgt->TalkCount++; + } else if (core->GetGameControl()->GetDialogueFlags()&DF_INTERACT) { + core->GetGameControl()->SetDialogueFlags(DF_INTERACT, BM_NAND); + tgt->InteractCount++; + } + } + ds = dlg->GetState( si ); + } else { + if (ds->transitionsCount <= choose) { + return; + } + + DialogTransition* tr = ds->transitions[choose]; + + ta->PopMinRow(); + + if (tr->Flags&IE_DLG_TR_JOURNAL) { + int Section = 0; + if (tr->Flags&IE_DLG_UNSOLVED) { + Section |= 1; + } + if (tr->Flags&IE_DLG_SOLVED) { + Section |= 2; + } + if (core->GetGame()->AddJournalEntry(tr->journalStrRef, sectionMap[Section], tr->Flags>>16) ) { + displaymsg->DisplayConstantString(STR_JOURNALCHANGE,0xffff00); + char *string = core->GetString( tr->journalStrRef ); + //cutting off the strings at the first crlf + char *poi = strchr(string,'\n'); + if (poi) { + *poi='\0'; + } + displaymsg->DisplayString( string ); + free( string ); + } + } + + if (tr->textStrRef != 0xffffffff) { + //allow_zero is for PST (deionarra's text) + displaymsg->DisplayStringName( (int) (tr->textStrRef), 0x8080FF, speaker, IE_STR_SOUND|IE_STR_SPEECH|IE_STR_ALLOW_ZERO); + if (core->HasFeature( GF_DIALOGUE_SCROLLS )) { + ta->AppendText( "", -1 ); + } + } + + if (tr->actions.size()) { + // does this belong here? we must clear actions somewhere before + // we start executing them (otherwise queued actions interfere) + // executing actions directly does not work, because dialog + // needs to end before final actions are executed due to + // actions making new dialogs! + if (target->Type == ST_ACTOR) ((Movable *)target)->ClearPath(); // fuzzie added this + target->ClearActions(); + + for (unsigned int i = 0; i < tr->actions.size(); i++) { + target->AddAction(tr->actions[i]); + //GameScript::ExecuteAction( target, action ); + } + } + + int final_dialog = tr->Flags & IE_DLG_TR_FINAL; + + if (final_dialog) { + ta->SetMinRow( false ); + EndDialog(); + } + + // *** the commented-out line here should no longer be required, with instant handling *** + // all dialog actions must be executed immediately + //target->ProcessActions(true); + // (do not clear actions - final actions can involve waiting/moving) + + if (final_dialog) { + return; + } + + // avoid problems when dhjollde.dlg tries starting a cutscene in the middle of a dialog + // (it seems harmless doing it in non-HoW too, since other versions would just break in such a situation) + core->SetCutSceneMode( false ); + + //displaying dialog for selected option + int si = tr->stateIndex; + //follow external linkage, if required + if (tr->Dialog[0] && strnicmp( tr->Dialog, dlg->ResRef, 8 )) { + //target should be recalculated! + tgt = NULL; + if (originalTargetID) { + // always try original target first (sometimes there are multiple + // actors with the same dialog in an area, we want to pick the one + // we were talking to) + tgt = GetActorByGlobalID(originalTargetID); + if (tgt && strnicmp( tgt->GetDialog(GD_NORMAL), tr->Dialog, 8 ) != 0) { + tgt = NULL; + } + } + if (!tgt) { + // then just search the current area for an actor with the dialog + tgt = target->GetCurrentArea()->GetActorByDialog(tr->Dialog); + } + if (!tgt) { + // try searching for banter dialogue: the original engine seems to + // happily let you randomly switch between normal and banter dialogs + + // TODO: work out if this should go somewhere more central (such + // as GetActorByDialog), or if there's a less awful way to do this + // (we could cache the entries, for example) + // TODO: fix for ToB (see also the Interact action) + AutoTable pdtable("interdia"); + if (pdtable) { + int row = pdtable->FindTableValue( pdtable->GetColumnIndex("FILE"), tr->Dialog ); + tgt = target->GetCurrentArea()->GetActorByScriptName(pdtable->GetRowName(row)); + } + } + target = tgt; + if (!target) { + printMessage("Dialog","Can't redirect dialog\n",YELLOW); + ta->SetMinRow( false ); + EndDialog(); + return; + } + Actor *oldTarget = GetActorByGlobalID(targetID); + targetID = tgt->GetGlobalID(); + tgt->SetCircleSize(); + if (oldTarget) oldTarget->SetCircleSize(); + // we have to make a backup, tr->Dialog is freed + ieResRef tmpresref; + strnlwrcpy(tmpresref,tr->Dialog, 8); + if (target->GetInternalFlag()&IF_NOINT) { + // this whole check moved out of InitDialog by fuzzie, see comments + // for the IF_NOINT check in BeginDialog + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + ta->SetMinRow( false ); + EndDialog(); + return; + } + int ret = InitDialog( speaker, target, tmpresref); + if (ret<0) { + // error was displayed by InitDialog + ta->SetMinRow( false ); + EndDialog(); + return; + } + } + ds = dlg->GetState( si ); + if (!ds) { + printMessage("Dialog","Can't find next dialog\n",YELLOW); + ta->SetMinRow( false ); + EndDialog(); + return; + } + } + //displaying npc text + displaymsg->DisplayStringName( ds->StrRef, 0x70FF70, target, IE_STR_SOUND|IE_STR_SPEECH); + //adding a gap between options and npc text + ta->AppendText("",-1); + int i; + int idx = 0; + ta->SetMinRow( true ); + //first looking for a 'continue' opportunity, the order is descending (a la IE) + unsigned int x = ds->transitionsCount; + while(x--) { + if (ds->transitions[x]->Flags & IE_DLG_TR_FINAL) { + continue; + } + if (ds->transitions[x]->textStrRef != 0xffffffff) { + continue; + } + if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) { + if (ds->transitions[x]->condition && + !ds->transitions[x]->condition->Evaluate(target)) { + continue; + } + } + core->GetDictionary()->SetAt("DialogOption",x); + core->GetGameControl()->SetDialogueFlags(DF_OPENCONTINUEWINDOW, BM_OR); + goto end_of_choose; + } + for (x = 0; x < ds->transitionsCount; x++) { + if (ds->transitions[x]->Flags & IE_DLG_TR_TRIGGER) { + if (ds->transitions[x]->condition && + !ds->transitions[x]->condition->Evaluate(target)) { + continue; + } + } + idx++; + if (ds->transitions[x]->textStrRef == 0xffffffff) { + //dialogchoose should be set to x + //it isn't important which END option was chosen, as it ends + core->GetDictionary()->SetAt("DialogOption",x); + core->GetGameControl()->SetDialogueFlags(DF_OPENENDWINDOW, BM_OR); + } else { + char *string = ( char * ) malloc( 40 ); + sprintf( string, "[s=%d,ffffff,ff0000]%d - [p]", x, idx ); + i = ta->AppendText( string, -1 ); + free( string ); + string = core->GetString( ds->transitions[x]->textStrRef ); + ta->AppendText( string, i ); + free( string ); + ta->AppendText( "[/p][/s]", i ); + } + } + // this happens if a trigger isn't implemented or the dialog is wrong + if (!idx) { + printMessage("Dialog", "There were no valid dialog options!\n", YELLOW); + core->GetGameControl()->SetDialogueFlags(DF_OPENENDWINDOW, BM_OR); + } +end_of_choose: + //padding the rows so our text will be at the top + if (core->HasFeature( GF_DIALOGUE_SCROLLS )) { + ta->AppendText( "", -1 ); + } + else { + ta->PadMinRow(); + } +} + +// TODO: duplicate of the one in GameControl +Actor *DialogHandler::GetActorByGlobalID(ieDword ID) +{ + if (!ID) + return NULL; + Game* game = core->GetGame(); + if (!game) + return NULL; + + Map* area = game->GetCurrentArea( ); + if (!area) + return NULL; + return area->GetActorByGlobalID(ID); +} + +Scriptable *DialogHandler::GetTarget() +{ + // TODO: area GetScriptableByGlobalID? + + if (!targetID) return NULL; + + Game *game = core->GetGame(); + if (!game) return NULL; + + Map *area = game->GetCurrentArea(); + if (!area) return NULL; + + Actor *actor = area->GetActorByGlobalID(targetID); + if (actor) return actor; + + Door *door = area->GetDoorByGlobalID(targetID); + if (door) return door; + Container *container = area->GetContainerByGlobalID(targetID); + if (container) return container; + InfoPoint *ip = area->GetInfoPointByGlobalID(targetID); + if (ip) return ip; + + return NULL; +} + +Actor *DialogHandler::GetSpeaker() +{ + return GetActorByGlobalID(speakerID); +} + diff --git a/project/jni/application/gemrb/src/core/DialogHandler.h b/project/jni/application/gemrb/src/core/DialogHandler.h new file mode 100644 index 000000000..499fb0dea --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogHandler.h @@ -0,0 +1,51 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 DIALOGHANDLER_H +#define DIALOGHANDLER_H + +#include "exports.h" + +#include "Dialog.h" + +class GEM_EXPORT DialogHandler { +public: + DialogHandler(); + ~DialogHandler(); +private: + /** this function safely retrieves an Actor by ID */ + Actor *GetActorByGlobalID(ieDword ID); +private: + DialogState* ds; + Dialog* dlg; +public: + ieDword speakerID; + ieDword targetID; + ieDword originalTargetID; +public: + Scriptable *GetTarget(); + Actor *GetSpeaker(); + + int InitDialog(Scriptable* speaker, Scriptable* target, const char* dlgref); + void EndDialog(bool try_to_break=false); + void DialogChoose(unsigned int choose); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DialogMgr.cpp b/project/jni/application/gemrb/src/core/DialogMgr.cpp new file mode 100644 index 000000000..bc68aa544 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "DialogMgr.h" + +DialogMgr::DialogMgr(void) +{ +} + +DialogMgr::~DialogMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/DialogMgr.h b/project/jni/application/gemrb/src/core/DialogMgr.h new file mode 100644 index 000000000..125ae52a0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/DialogMgr.h @@ -0,0 +1,36 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 DIALOGMGR_H +#define DIALOGMGR_H + +#include "Dialog.h" +#include "Plugin.h" +#include "System/DataStream.h" + +class GEM_EXPORT DialogMgr : public Plugin { +public: + DialogMgr(void); + virtual ~DialogMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + virtual Dialog* GetDialog() const = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/DisplayMessage.cpp b/project/jni/application/gemrb/src/core/DisplayMessage.cpp new file mode 100644 index 000000000..1954acc3f --- /dev/null +++ b/project/jni/application/gemrb/src/core/DisplayMessage.cpp @@ -0,0 +1,230 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* 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 "DisplayMessage.h" + +#include "strrefs.h" + +#include "Interface.h" +#include "TableMgr.h" +#include "GUI/Label.h" +#include "GUI/TextArea.h" + +GEM_EXPORT DisplayMessage * displaymsg; + +static int strref_table[STRREF_COUNT]; + +#define PALSIZE 8 +static Color ActorColor[PALSIZE]; +static const char* DisplayFormatName = "[color=%lX]%s - [/color][p][color=%lX]%s[/color][/p]"; +static const char* DisplayFormatAction = "[color=%lX]%s - [/color][p][color=%lX]%s %s[/color][/p]"; +static const char* DisplayFormat = "[/color][p][color=%lX]%s[/color][/p]"; +static const char* DisplayFormatValue = "[/color][p][color=%lX]%s: %d[/color][/p]"; +static const char* DisplayFormatNameString = "[color=%lX]%s - [/color][p][color=%lX]%s: %s[/color][/p]"; + +DisplayMessage::DisplayMessage(void) { + ReadStrrefs(); +} + +bool DisplayMessage::ReadStrrefs() +{ + int i; + memset(strref_table,-1,sizeof(strref_table) ); + AutoTable tab("strings"); + if (!tab) { + return false; + } + for(i=0;iQueryField(i,0)); + } + return true; +} + +void DisplayMessage::DisplayString(const char* Text, Scriptable *target) const +{ + Label *l = core->GetMessageLabel(); + if (l) { + l->SetText(Text, 0); + } + TextArea *ta = core->GetMessageTextArea(); + if (ta) { + ta->AppendText( Text, -1 ); + } else { + if(target) { + char *tmp = strdup(Text); + + target->DisplayHeadText(tmp); + } + } +} + +ieStrRef DisplayMessage::GetStringReference(int stridx) const +{ + return strref_table[stridx]; +} + +bool DisplayMessage::HasStringReference(int stridx) const +{ + return strref_table[stridx] != -1; +} + +unsigned int DisplayMessage::GetSpeakerColor(const char *&name, const Scriptable *&speaker) const +{ + unsigned int speaker_color; + + if(!speaker) return 0; + switch (speaker->Type) { + case ST_ACTOR: + name = speaker->GetName(-1); + core->GetPalette( ((Actor *) speaker)->GetStat(IE_MAJOR_COLOR) & 0xFF, PALSIZE, ActorColor ); + speaker_color = (ActorColor[4].r<<16) | (ActorColor[4].g<<8) | ActorColor[4].b; + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + name = core->GetString( ((InfoPoint *) speaker)->DialogName ); + speaker_color = 0xc0c0c0; + break; + default: + name = ""; + speaker_color = 0x800000; + break; + } + return speaker_color; +} + + +//simply displaying a constant string +void DisplayMessage::DisplayConstantString(int stridx, unsigned int color, Scriptable *target) const +{ + if (stridx<0) return; + const char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + DisplayString(text, color, target); +} + +void DisplayMessage::DisplayString(int stridx, unsigned int color, ieDword flags) const +{ + if (stridx<0) return; + const char* text = core->GetString( stridx, flags); + DisplayString(text, color, NULL); +} + +void DisplayMessage::DisplayString(const char *text, unsigned int color, Scriptable *target) const +{ + if (!text) return; + int newlen = (int)(strlen( DisplayFormat) + strlen( text ) + 12); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormat, color, text ); + DisplayString( newstr, target ); + free( newstr ); +} + +// String format is +// blah : whatever +void DisplayMessage::DisplayConstantStringValue(int stridx, unsigned int color, ieDword value) const +{ + if (stridx<0) return; + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + int newlen = (int)(strlen( DisplayFormat ) + strlen( text ) + 28); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatValue, color, text, (int) value ); + core->FreeString( text ); + DisplayString( newstr ); + free( newstr ); +} + +// String format is +// - blah blah : whatever +void DisplayMessage::DisplayConstantStringNameString(int stridx, unsigned int color, int stridx2, const Scriptable *actor) const +{ + unsigned int actor_color; + const char *name = 0; + + if (stridx<0) return; + actor_color = GetSpeakerColor(name, actor); + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND ); + char* text2 = core->GetString( strref_table[stridx2], IE_STR_SOUND ); + int newlen = (int)(strlen( DisplayFormat ) + strlen(name) + strlen( text ) + strlen(text2) + 18); + char* newstr = ( char* ) malloc( newlen ); + if (strlen(text2)) { + snprintf( newstr, newlen, DisplayFormatNameString, actor_color, name, color, text, text2 ); + } else { + snprintf( newstr, newlen, DisplayFormatName, color, name, color, text ); + } + core->FreeString( text ); + core->FreeString( text2 ); + DisplayString( newstr ); + free( newstr ); +} + +// String format is +// - blah blah +void DisplayMessage::DisplayConstantStringName(int stridx, unsigned int color, const Scriptable *speaker) const +{ + if (stridx<0) return; + if(!speaker) return; + + const char* text = core->GetString( strref_table[stridx], IE_STR_SOUND|IE_STR_SPEECH ); + DisplayStringName(text, color, speaker); +} + +void DisplayMessage::DisplayConstantStringAction(int stridx, unsigned int color, const Scriptable *attacker, const Scriptable *target) const +{ + unsigned int attacker_color; + const char *name1 = 0; + const char *name2 = 0; + + if (stridx<0) return; + + GetSpeakerColor(name2, target); + attacker_color = GetSpeakerColor(name1, attacker); + + char* text = core->GetString( strref_table[stridx], IE_STR_SOUND|IE_STR_SPEECH ); + int newlen = (int)(strlen( DisplayFormatAction ) + strlen( name1 ) + + + strlen( name2 ) + strlen( text ) + 18); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatAction, attacker_color, name1, color, + text, name2); + core->FreeString( text ); + DisplayString( newstr ); + free( newstr ); +} + +void DisplayMessage::DisplayStringName(int stridx, unsigned int color, const Scriptable *speaker, ieDword flags) const +{ + if (stridx<0) return; + + const char* text = core->GetString( stridx, flags); + DisplayStringName(text, color, speaker); +} + +void DisplayMessage::DisplayStringName(const char *text, unsigned int color, const Scriptable *speaker) const +{ + unsigned int speaker_color; + const char *name = 0; + + if (!text) return; + speaker_color = GetSpeakerColor(name, speaker); + + int newlen = (int)(strlen( DisplayFormatName ) + strlen( name ) + + + strlen( text ) + 18); + char* newstr = ( char* ) malloc( newlen ); + snprintf( newstr, newlen, DisplayFormatName, speaker_color, name, color, text ); + DisplayString( newstr ); + free( newstr ); +} diff --git a/project/jni/application/gemrb/src/core/DisplayMessage.h b/project/jni/application/gemrb/src/core/DisplayMessage.h new file mode 100644 index 000000000..9fc781b5d --- /dev/null +++ b/project/jni/application/gemrb/src/core/DisplayMessage.h @@ -0,0 +1,70 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* 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 DisplayMessage.h + * Declaration of the DisplayMessage class used for displaying messages in + * game message window + */ + +#ifndef DISPLAYMESSAGE_H +#define DISPLAYMESSAGE_H + +#include "exports.h" + +#include "ActorMgr.h" + +class GEM_EXPORT DisplayMessage +{ +private: + bool ReadStrrefs(); + +public: + DisplayMessage(void); + + /** returns a string reference from a string reference index constant */ + ieStrRef GetStringReference(int stridx) const; + /** returns true if a string reference for a string reference index constant exists */ + bool HasStringReference(int stridx) const; + /** returns the speaker's color and name */ + unsigned int GetSpeakerColor(const char *&name, const Scriptable *&speaker) const; + /** displays any string in the textarea */ + void DisplayString(const char *txt, Scriptable *speaker=NULL) const; + /** displays a string constant in the textarea */ + void DisplayConstantString(int stridx, unsigned int color, Scriptable *speaker=NULL) const; + /** displays actor name - action : parameter */ + void DisplayConstantStringNameString(int stridx, unsigned int color, int stridx2, const Scriptable *actor) const; + /** displays a string constant followed by a number in the textarea */ + void DisplayConstantStringValue(int stridx, unsigned int color, ieDword value) const; + /** displays a string constant in the textarea, starting with speaker's name */ + void DisplayConstantStringName(int stridx, unsigned int color, const Scriptable *speaker) const; + /** displays a string constant in the textarea, starting with actor, and ending with target */ + void DisplayConstantStringAction(int stridx, unsigned int color, const Scriptable *actor, const Scriptable *target) const; + /** displays a string in the textarea */ + void DisplayString(int stridx, unsigned int color, ieDword flags) const; + void DisplayString(const char *text, unsigned int color, Scriptable *target) const; + /** displays a string in the textarea, starting with speaker's name */ + void DisplayStringName(int stridx, unsigned int color, const Scriptable *speaker, ieDword flags) const; + void DisplayStringName(const char *text, unsigned int color, const Scriptable *speaker) const; +}; + +extern GEM_EXPORT DisplayMessage * displaymsg; + +#endif diff --git a/project/jni/application/gemrb/src/core/Effect.h b/project/jni/application/gemrb/src/core/Effect.h new file mode 100644 index 000000000..400c6bcbd --- /dev/null +++ b/project/jni/application/gemrb/src/core/Effect.h @@ -0,0 +1,139 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Effect.h + * Declares Effect class implementing spell and spell-like effects + * and related defines + */ + +#ifndef EFFECT_H +#define EFFECT_H + +#include "ie_types.h" + +#include "Region.h" + +class Actor; + +//local variables in creatures are stored in fake opcodes +#define FAKE_VARIABLE_OPCODE 187 +#define FAKE_VARIABLE_MARKER 1 + +// Effect target types +#define FX_TARGET_UNKNOWN 0 +#define FX_TARGET_SELF 1 +#define FX_TARGET_PRESET 2 +#define FX_TARGET_PARTY 3 +#define FX_TARGET_ALL 4 +#define FX_TARGET_ALL_BUT_PARTY 5 +#define FX_TARGET_OWN_SIDE 6 +#define FX_TARGET_OTHER_SIDE 7 +#define FX_TARGET_ALL_BUT_SELF 8 +#define FX_TARGET_ORIGINAL 9 + +// Effect duration/timing types +#define FX_DURATION_INSTANT_LIMITED 0 +#define FX_DURATION_INSTANT_PERMANENT 1 +#define FX_DURATION_INSTANT_WHILE_EQUIPPED 2 +#define FX_DURATION_DELAY_LIMITED 3 //this contains a relative onset time (delay) also used as duration, transforms to 6 when applied +#define FX_DURATION_DELAY_PERMANENT 4 //this transforms to 9 (i guess) +#define FX_DURATION_DELAY_UNSAVED 5 //this transforms to 8 +#define FX_DURATION_DELAY_LIMITED_PENDING 6 //this contains an absolute onset time and a duration +#define FX_DURATION_AFTER_EXPIRES 7 //this is a delayed non permanent effect (resolves to JUST_EXPIRED) +#define FX_DURATION_PERMANENT_UNSAVED 8 +#define FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES 9//this is a special permanent +#define FX_DURATION_JUST_EXPIRED 10 +#define MAX_TIMING_MODE 11 +#define FX_DURATION_ABSOLUTE 0x1000 + +// Effect resistance types +#define FX_NO_RESIST_NO_DISPEL 0 +#define FX_CAN_RESIST_CAN_DISPEL 1 +//#define FX_CAN_RESIST_NO_DISPEL 2 //same as 0 (not resistable, not dispellable) +#define FX_NO_RESIST_CAN_DISPEL 3 +#define FX_CAN_DISPEL 1 +#define FX_CAN_RESIST 3 + +/** + * @class Effect + * Structure holding information about single spell or spell-like effect. + */ + +// the same as ITMFeature and SPLFeature +struct Effect { + ieDword Opcode; + ieDword Target; + ieDword Power; + ieDword Parameter1; + ieDword Parameter2; + ieWord TimingMode; //0x1000 -- no need of conversion + ieWord unknown2; + ieDword Resistance; + ieDword Duration; + ieWord Probability1; + ieWord Probability2; + //keep these four in one bunch, VariableName will + //spread across them + ieResRef Resource; + ieResRef Resource2; //vvc in a lot of effects + ieResRef Resource3; + ieResRef Resource4; + ieDword DiceThrown; + ieDword DiceSides; + ieDword SavingThrowType; + ieDword SavingThrowBonus; + ieWord IsVariable; + ieWord IsSaveForHalfDamage; + + // EFF V2.0 fields: + ieDword PrimaryType; //school + ieDword MinAffectedLevel; + ieDword MaxAffectedLevel; + ieDword Parameter3; + ieDword Parameter4; + ieDword PosX, PosY; + ieDword SourceType; //1-item, 2-spell + ieResRef Source; + ieDword SourceFlags; + ieDword Projectile; //9c + ieDwordSigned InventorySlot; //a0 + //Original engine had a VariableName here, but it is stored in the resource fields + ieDword CasterLevel; //c4 in both + ieDword FirstApply; //c8 in bg2, cc in iwd2 + ieDword SecondaryType; + ieDword SecondaryDelay; //still not sure about this + ieDword CasterID; //10c in bg2 (not saved?) + // These are not in the IE files, but are our precomputed values + ieDword random_value; +public: + //don't modify position in case it was already set + void SetPosition(const Point &p) { + if(PosX==0xffffffff && PosY==0xffffffff) { + PosX=p.x; + PosY=p.y; + } + } +}; + +// FIXME: what about area spells? They can have map & coordinates as target +//void AddEffect(Effect* fx, Actor* self, Actor* pretarget); + +#endif // ! EFFECT_H diff --git a/project/jni/application/gemrb/src/core/EffectMgr.cpp b/project/jni/application/gemrb/src/core/EffectMgr.cpp new file mode 100644 index 000000000..7ca50f42e --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectMgr.cpp @@ -0,0 +1,29 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "EffectMgr.h" + +EffectMgr::EffectMgr(void) +{ +} + +EffectMgr::~EffectMgr(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/EffectMgr.h b/project/jni/application/gemrb/src/core/EffectMgr.h new file mode 100644 index 000000000..689651c55 --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectMgr.h @@ -0,0 +1,56 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 EffectMgr.h + * Declares EffectMgr class, loader for Effect objects + * @author The GemRB Project + */ + + +#ifndef EFFECTMGR_H +#define EFFECTMGR_H + +#include "Effect.h" +#include "Plugin.h" +#include "System/DataStream.h" + +/** + * @class EffectMgr + * Abstract loader for Effect objects + */ + +class GEM_EXPORT EffectMgr : public Plugin { +public: + EffectMgr(void); + virtual ~EffectMgr(void); + virtual bool Open(DataStream* stream, bool autoFree = true) = 0; + + /** Fills fx with Effect data loaded from the stream */ + virtual Effect* GetEffect(Effect *fx) = 0; + /** Fills fx with Effect v1 data loaded from the stream*/ + virtual Effect* GetEffectV1(Effect *fx) = 0; + /** Fills fx with Effect v2.0 data loaded from the stream*/ + virtual Effect* GetEffectV20(Effect *fx) = 0; + /** Fills the stream with Effect v2 data loaded from the effect*/ + virtual void PutEffectV2(DataStream *stream, const Effect *fx) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/EffectQueue.cpp b/project/jni/application/gemrb/src/core/EffectQueue.cpp new file mode 100644 index 000000000..3828c85ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectQueue.cpp @@ -0,0 +1,1980 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "EffectQueue.h" + +#include "DisplayMessage.h" +#include "Effect.h" +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "SymbolMgr.h" +#include "Scriptable/Actor.h" +#include "Spell.h" //needs for the source flags bitfield + +#include + +static struct { + const char* Name; + EffectFunction Function; + int Strref; +} Opcodes[MAX_EFFECTS]; + +static int initialized = 0; +static EffectRef *effectnames = NULL; +static int effectnames_count = 0; +static int pstflags = false; + +bool EffectQueue::match_ids(Actor *target, int table, ieDword value) +{ + if( value == 0) { + return true; + } + + int a, stat; + + switch (table) { + case 2: //EA + stat = IE_EA; break; + case 3: //GENERAL + stat = IE_GENERAL; break; + case 4: //RACE + stat = IE_RACE; break; + case 5: //CLASS + stat = IE_CLASS; break; + case 6: //SPECIFIC + stat = IE_SPECIFIC; break; + case 7: //GENDER + stat = IE_SEX; break; + case 8: //ALIGNMENT + stat = target->GetStat(IE_ALIGNMENT); + a = value&15; + if( a) { + if( a != ( stat & 15 )) { + return false; + } + } + a = value & 0xf0; + if( a) { + if( a != ( stat & 0xf0 )) { + return false; + } + } + return true; + default: + return false; + } + if( target->GetStat(stat)==value) { + return true; + } + return false; +} + +static const bool fx_instant[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,true}; + +inline bool IsInstant(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_instant[timingmode]; +} + +static const bool fx_equipped[MAX_TIMING_MODE]={false,false,true,false,false,true,false,false,true,false,false}; + +inline bool IsEquipped(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_equipped[timingmode]; +} + +// 0 1 2 3 4 5 6 7 8 9 10 +static const bool fx_relative[MAX_TIMING_MODE]={true,false,false,true,true,true,false,false,false,false,false}; + +inline bool NeedPrepare(ieWord timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_relative[timingmode]; +} + +#define INVALID -1 +#define PERMANENT 0 +#define DELAYED 1 +#define DURATION 2 + +static const int fx_prepared[MAX_TIMING_MODE]={DURATION,PERMANENT,PERMANENT,DELAYED, //0-3 +DELAYED,DELAYED,DELAYED,DELAYED,PERMANENT,PERMANENT,PERMANENT}; //4-7 + +inline int DelayType(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return INVALID; + return fx_prepared[timingmode]; +} + +//which effects are removable +static const bool fx_removable[MAX_TIMING_MODE]={true,true,false,true,true,false,true,true,false,false,true}; + +inline int IsRemovable(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return INVALID; + return fx_removable[timingmode]; +} + +//change the timing method after the effect triggered +static const ieByte fx_triggered[MAX_TIMING_MODE]={FX_DURATION_JUST_EXPIRED,FX_DURATION_INSTANT_PERMANENT,//0,1 +FX_DURATION_INSTANT_WHILE_EQUIPPED,FX_DURATION_DELAY_LIMITED_PENDING,//2,3 +FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5 +FX_DURATION_INSTANT_LIMITED,FX_DURATION_JUST_EXPIRED,FX_DURATION_PERMANENT_UNSAVED,//6,8 +FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES,FX_DURATION_JUST_EXPIRED};//9,10 + +//change the timing method for effect that should trigger after this effect expired +static const ieDword fx_to_delayed[]={FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED, +FX_DURATION_PERMANENT_UNSAVED,FX_DURATION_DELAY_LIMITED_PENDING, +FX_DURATION_AFTER_EXPIRES,FX_DURATION_PERMANENT_UNSAVED, //4,5 +FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED,//6,8 +FX_DURATION_JUST_EXPIRED,FX_DURATION_JUST_EXPIRED};//9,10 + +inline ieByte TriggeredEffect(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_triggered[timingmode]; +} + +int compare_effects(const void *a, const void *b) +{ + return stricmp(((EffectRef *) a)->Name,((EffectRef *) b)->Name); +} + +int find_effect(const void *a, const void *b) +{ + return stricmp((const char *) a,((const EffectRef *) b)->Name); +} + +static EffectRef* FindEffect(const char* effectname) +{ + if( !effectname || !effectnames) { + return NULL; + } + void *tmp = bsearch(effectname, effectnames, effectnames_count, sizeof(EffectRef), find_effect); + if( !tmp) { + printMessage( "EffectQueue", "", YELLOW); + printf("Couldn't assign effect: %s\n", effectname ); + } + return (EffectRef *) tmp; +} + +static EffectRef fx_protection_from_display_string_ref={"Protection:String",NULL,-1}; + +//special effects without level check (but with damage dices precalculated) +static EffectRef diced_effects[] = { + //core effects + {"Damage",NULL,-1}, + {"CurrentHPModifier",NULL,-1}, + {"MaximumHPModifier",NULL,-1}, + //iwd effects + {"BurningBlood",NULL,-1}, //iwd + {"ColdDamage",NULL,-1}, + {"CrushingDamage",NULL,-1}, + {"VampiricTouch",NULL,-1}, + {"VitriolicSphere",NULL,-1}, + //pst effects + {"TransferHP",NULL,-1}, +{NULL,NULL,0} }; + +//special effects without level check (but with damage dices not precalculated) +static EffectRef diced_effects2[] = { + {"BurningBlood2",NULL,-1}, //how/iwd2 + {"StaticCharge",NULL,-1}, //how/iwd2 + {"SoulEater",NULL,-1}, //how/iwd2 + {"LichTouch",NULL,-1}, //how +{NULL,NULL,0} }; + +inline static void ResolveEffectRef(EffectRef &effect_reference) +{ + if( effect_reference.opcode==-1) { + EffectRef* ref = FindEffect(effect_reference.Name); + if( ref && ref->opcode>=0) { + effect_reference.opcode = ref->opcode; + return; + } + effect_reference.opcode = -2; + } +} + +bool Init_EffectQueue() +{ + int i; + + if( initialized) { + return true; + } + pstflags = !!core->HasFeature(GF_PST_STATE_FLAGS); + + memset( Opcodes, 0, sizeof( Opcodes ) ); + for(i=0;iLoadSymbol( "effects" ); + if( eT < 0) { + printMessage( "EffectQueue","A critical scripting file is missing!\n",LIGHT_RED ); + return false; + } + Holder effectsTable = core->GetSymbol( eT ); + if( !effectsTable) { + printMessage( "EffectQueue","A critical scripting file is damaged!\n",LIGHT_RED ); + return false; + } + + for (i = 0; i < MAX_EFFECTS; i++) { + const char* effectname = effectsTable->GetValue( i ); + if( efftextTable) { + int row = efftextTable->GetRowCount(); + while (row--) { + const char* ret = efftextTable->GetRowName( row ); + long val; + if( valid_number( ret, val ) && (i == val) ) { + Opcodes[i].Strref = atoi( efftextTable->QueryField( row, 1 ) ); + } + } + } + + EffectRef* poi = FindEffect( effectname ); + if( poi != NULL) { + Opcodes[i].Function = poi->Function; + Opcodes[i].Name = poi->Name; + //reverse linking opcode number + //using this unused field + if( (poi->opcode!=-1) && effectname[0]!='*') { + printf("Clashing Opcodes FN: %d vs. %d, %s\n", i, poi->opcode, effectname); + abort(); + } + poi->opcode = i; + } + //printf("-------- FN: %d, %s\n", i, effectname); + } + core->DelSymbol( eT ); + + //additional initialisations + for (i=0;diced_effects[i].Name;i++) { + ResolveEffectRef(diced_effects[i]); + } + for (i=0;diced_effects2[i].Name;i++) { + ResolveEffectRef(diced_effects2[i]); + } + + return true; +} + +void EffectQueue_ReleaseMemory() +{ + if( effectnames) { + free (effectnames); + } + effectnames_count = 0; + effectnames = NULL; +} + +void EffectQueue_RegisterOpcodes(int count, const EffectRef* opcodes) +{ + if( ! effectnames) { + effectnames = (EffectRef*) malloc( (count+1) * sizeof( EffectRef ) ); + } else { + effectnames = (EffectRef*) realloc( effectnames, (effectnames_count + count + 1) * sizeof( EffectRef ) ); + } + + memcpy( effectnames + effectnames_count, opcodes, count * sizeof( EffectRef )); + effectnames_count += count; + effectnames[effectnames_count].Name = NULL; + //if we merge two effect lists, then we need to sort their effect tables + //actually, we might always want to sort this list, so there is no + //need to do it manually (sorted table is needed if we use bsearch) + qsort(effectnames, effectnames_count, sizeof(EffectRef), compare_effects); +} + +EffectQueue::EffectQueue() +{ + Owner = NULL; +} + +EffectQueue::~EffectQueue() +{ + std::list< Effect* >::iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + delete (*f); + } +} + +Effect *EffectQueue::CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing) +{ + if( opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if( !fx) { + return NULL; + } + memset(fx,0,sizeof(Effect)); + fx->Target = FX_TARGET_SELF; + fx->Opcode = opcode; + //probability2 is the low number (by effectqueue 331) + fx->Probability1 = 100; + fx->Parameter1 = param1; + fx->Parameter2 = param2; + fx->TimingMode = timing; + fx->PosX = 0xffffffff; + fx->PosY = 0xffffffff; + return fx; +} + +//return the count of effects with matching parameters +//useful for effects where there is no separate stat to see +ieDword EffectQueue::CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *resource) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return 0; + } + return CountEffects(effect_reference.opcode, param1, param2, resource); +} + +//Change the location of an existing effect +//this is used when some external code needs to adjust the effect's location +//used when the gui sets the effect's final target +void EffectQueue::ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + ModifyEffectPoint(effect_reference.opcode, x, y); +} + +Effect *EffectQueue::CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing) +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return CreateEffect(effect_reference.opcode, param1, param2, timing); +} + +//copies the whole effectqueue (area projectiles use it) +EffectQueue *EffectQueue::CopySelf() const +{ + EffectQueue *effects; + + effects = new EffectQueue(); + std::list< Effect* >::const_iterator fxit = GetFirstEffect(); + Effect *fx; + + while( (fx = GetNextEffect(fxit))) { + effects->AddEffect(fx, false); + } + effects->SetOwner(GetOwner()); + return effects; +} + +//create a new effect with most of the characteristics of the old effect +//only opcode and parameters are changed +//This is used mostly inside effects, when an effect needs to spawn +//other effects with the same coordinates, source, duration, etc. +Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2) +{ + if( opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if( !fx) { + return NULL; + } + memcpy(fx,oldfx,sizeof(Effect) ); + fx->Opcode=opcode; + fx->Parameter1=param1; + fx->Parameter2=param2; + return fx; +} + +Effect *EffectQueue::CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2) +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return CreateEffectCopy(oldfx, effect_reference.opcode, param1, param2); +} + +static EffectRef fx_unsummon_creature_ref={"UnsummonCreature",NULL,-1}; + +Effect *EffectQueue::CreateUnsummonEffect(Effect *fx) +{ + Effect *newfx = NULL; + if( (fx->TimingMode&0xff) == FX_DURATION_INSTANT_LIMITED) { + newfx = CreateEffectCopy(fx, fx_unsummon_creature_ref, 0, 0); + newfx->TimingMode = FX_DURATION_DELAY_PERMANENT; + if( newfx->Resource3[0]) { + strnuprcpy(newfx->Resource,newfx->Resource3, sizeof(ieResRef)-1 ); + } else { + strnuprcpy(newfx->Resource,"SPGFLSH1", sizeof(ieResRef)-1 ); + } + if( fx->TimingMode == FX_DURATION_ABSOLUTE) { + //unprepare duration + newfx->Duration = (newfx->Duration-core->GetGame()->GameTime)/AI_UPDATE_TIME; + } + } + + return newfx; +} + +void EffectQueue::AddEffect(Effect* fx, bool insert) +{ + Effect* new_fx = new Effect; + memcpy( new_fx, fx, sizeof( Effect ) ); + if( insert) { + effects.insert( effects.begin(), new_fx ); + } else { + effects.push_back( new_fx ); + } +} + +//This method can remove an effect described by a pointer to it, or +//an exact matching effect +bool EffectQueue::RemoveEffect(Effect* fx) +{ + int invariant_size = offsetof( Effect, random_value ); + + for (std::list< Effect* >::iterator f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx2 = *f; + + if( (fx==fx2) || !memcmp( fx, fx2, invariant_size)) { + delete fx2; + effects.erase( f ); + return true; + } + } + return false; +} + +//this is where we reapply all effects when loading a saved game +//The effects are already in the fxqueue of the target +void EffectQueue::ApplyAllEffects(Actor* target) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + ApplyEffect( target, *f, 0 ); + } +} + +void EffectQueue::Cleanup() +{ + std::list< Effect* >::iterator f; + + for ( f = effects.begin(); f != effects.end(); ) { + if( (*f)->TimingMode == FX_DURATION_JUST_EXPIRED) { + delete *f; + effects.erase(f++); + } else { + f++; + } + } +} + +//Handle the target flag when the effect is applied first +int EffectQueue::AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const +{ + int i; + Game *game; + Map *map; + int flg; + ieDword spec = 0; + Actor *st = (self && (self->Type==ST_ACTOR)) ?(Actor *) self:NULL; + + switch (fx->Target) { + case FX_TARGET_ORIGINAL: + fx->SetPosition(self->Pos); + + flg = ApplyEffect( st, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( st) { + st->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + case FX_TARGET_SELF: + fx->SetPosition(dest); + + flg = ApplyEffect( st, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( st) { + st->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + + case FX_TARGET_ALL_BUT_SELF: + map=self->GetCurrentArea(); + i= map->GetActorCount(true); + while(i--) { + Actor* actor = map->GetActor( i, true ); + //don't pick ourselves + if( st==actor) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_OWN_SIDE: + if( !st || st->InParty) { + goto all_party; + } + map = self->GetCurrentArea(); + spec = st->GetStat(IE_SPECIFIC); + + //GetActorCount(false) returns all nonparty critters + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + if( actor->GetStat(IE_SPECIFIC)!=spec) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + case FX_TARGET_OTHER_SIDE: + if( !pretarget || pretarget->InParty) { + goto all_party; + } + map = self->GetCurrentArea(); + spec = pretarget->GetStat(IE_SPECIFIC); + + //GetActorCount(false) returns all nonparty critters + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + if( actor->GetStat(IE_SPECIFIC)!=spec) { + continue; + } + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + //GetActorCount can now return all nonparty critters + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + case FX_TARGET_PRESET: + fx->SetPosition(pretarget->Pos); + + flg = ApplyEffect( pretarget, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + if( pretarget) { + pretarget->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + break; + + case FX_TARGET_PARTY: +all_party: + game = core->GetGame(); + i = game->GetPartySize(true); + while(i--) { + Actor* actor = game->GetPC( i, true ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_ALL: + map = self->GetCurrentArea(); + i = map->GetActorCount(true); + while(i--) { + Actor* actor = map->GetActor( i, true ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_ALL_BUT_PARTY: + map = self->GetCurrentArea(); + i = map->GetActorCount(false); + while(i--) { + Actor* actor = map->GetActor( i, false ); + fx->SetPosition(actor->Pos); + + flg = ApplyEffect( actor, fx, 1 ); + //GetActorCount can now return all nonparty critters + if( fx->TimingMode != FX_DURATION_JUST_EXPIRED) { + actor->fxqueue.AddEffect( fx, flg==FX_INSERT ); + } + } + flg = FX_APPLIED; + break; + + case FX_TARGET_UNKNOWN: + default: + printf( "Unknown FX target type: %d\n", fx->Target); + flg = FX_ABORT; + break; + } + + return flg; +} + +//this is where effects from spells first get in touch with the target +//the effects are currently NOT in the target's fxqueue, those that stick +//will get copied (hence the fxqueue.AddEffect call) +//if this returns FX_NOT_APPLIED, then the whole stack was resisted +//or expired +int EffectQueue::AddAllEffects(Actor* target, const Point &destination) const +{ + int res = FX_NOT_APPLIED; + // pre-roll dice for fx needing them and stow them in the effect + ieDword random_value = core->Roll( 1, 100, -1 ); + + if( target) { + target->RollSaves(); + } + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + //handle resistances and saving throws here + (*f)->random_value = random_value; + //if applyeffect returns true, we stop adding the future effects + //this is to simulate iwd2's on the fly spell resistance + + int tmp = AddEffect(*f, Owner, target, destination); + //lets try without Owner, any crash? + //If yes, then try to fix the individual effect + //If you use target for Owner here, the wand in chateau irenicus will work + //the same way as Imoen's monster summoning, which is a BAD THING (TM) + //int tmp = AddEffect(*f, Owner?Owner:target, target, destination); + if( tmp == FX_ABORT) { + res = FX_NOT_APPLIED; + break; + } + if( tmp != FX_NOT_APPLIED) { + res = FX_APPLIED; + } + } + return res; +} + +//check if an effect has no level based resistance, but instead the dice sizes/count +//adjusts Parameter1 (like a damage causing effect) +inline static bool IsDicedEffect(int opcode) +{ + int i; + + for(i=0;diced_effects[i].Name;i++) { + if( diced_effects[i].opcode==opcode) { + return true; + } + } + return false; +} + +//there is no level based resistance, but Parameter1 cannot be precalculated +//these effects use the Dice fields in a special way +inline static bool IsDicedEffect2(int opcode) +{ + int i; + + for(i=0;diced_effects2[i].Name;i++) { + if( diced_effects2[i].opcode==opcode) { + return true; + } + } + return false; +} + +//resisted effect based on level +inline bool check_level(Actor *target, Effect *fx) +{ + //skip non level based effects + if( IsDicedEffect((int) fx->Opcode)) { + fx->Parameter1 = DICE_ROLL((signed)fx->Parameter1); + //this is a hack for PST style diced effects + if( core->HasFeature(GF_SAVE_FOR_HALF) ) { + if( memcmp(fx->Resource,"NEG",4) ) { + fx->IsSaveForHalfDamage=1; + } + } else { + if( (fx->Parameter2&3)==3) { + fx->IsSaveForHalfDamage=1; + } + } + return false; + } + if( IsDicedEffect2((int) fx->Opcode)) { + return false; + } + + if( !target) { + return false; + } + if(fx->Target == FX_TARGET_SELF) { + return false; + } + + ieDword level = (ieDword) target->GetXPLevel( true ); + //return true if resisted + if (fx->MinAffectedLevel > 0 || fx->MaxAffectedLevel > 0) { + if (level < fx->MinAffectedLevel || level > fx->MaxAffectedLevel) { + return true; + } + } + return false; +} + +//roll for the effect probability, there is a high and a low treshold, the d100 +//roll should hit in the middle +inline bool check_probability(Effect* fx) +{ + //watch for this, probability1 is the high number + //probability2 is the low number + //random value is 0-99 + if( fx->random_value<=fx->Probability2 || fx->random_value>fx->Probability1) { + return false; + } + return true; +} + +//immunity effects +static EffectRef fx_level_immunity_ref={"Protection:Spelllevel",NULL,-1}; +static EffectRef fx_opcode_immunity_ref={"Protection:Opcode",NULL,-1}; //bg2 +static EffectRef fx_opcode_immunity2_ref={"Protection:Opcode2",NULL,-1};//iwd +static EffectRef fx_spell_immunity_ref={"Protection:Spell",NULL,-1}; //bg2 +static EffectRef fx_spell_immunity2_ref={"Protection:Spell2",NULL,-1};//iwd +static EffectRef fx_store_spell_sequencer_ref={"Sequencer:Store",NULL,-1}; //bg2, works against sequencers +static EffectRef fx_school_immunity_ref={"Protection:School",NULL,-1}; +static EffectRef fx_secondary_type_immunity_ref={"Protection:SecondaryType",NULL,-1}; + +//decrementing immunity effects +static EffectRef fx_level_immunity_dec_ref={"Protection:SpellLevelDec",NULL,-1}; +static EffectRef fx_spell_immunity_dec_ref={"Protection:SpellDec",NULL,-1}; +static EffectRef fx_school_immunity_dec_ref={"Protection:SchoolDec",NULL,-1}; +static EffectRef fx_secondary_type_immunity_dec_ref={"Protection:SecondaryTypeDec",NULL,-1}; + +//bounce effects +static EffectRef fx_level_bounce_ref={"Bounce:SpellLevel",NULL,-1}; +//static EffectRef fx_opcode_bounce_ref={"Bounce:Opcode",NULL,-1}; +static EffectRef fx_spell_bounce_ref={"Bounce:Spell",NULL,-1}; +static EffectRef fx_school_bounce_ref={"Bounce:School",NULL,-1}; +static EffectRef fx_secondary_type_bounce_ref={"Bounce:SecondaryType",NULL,-1}; + +//decrementing bounce effects +static EffectRef fx_level_bounce_dec_ref={"Bounce:SpellLevelDec",NULL,-1}; +static EffectRef fx_spell_bounce_dec_ref={"Bounce:SpellDec",NULL,-1}; +static EffectRef fx_school_bounce_dec_ref={"Bounce:SchoolDec",NULL,-1}; +static EffectRef fx_secondary_type_bounce_dec_ref={"Bounce:SecondaryTypeDec",NULL,-1}; + +//spelltrap (multiple decrementing immunity) +static EffectRef fx_spelltrap={"SpellTrap", NULL,-1}; + +//this is for whole spell immunity/bounce +inline static void DecreaseEffect(Effect *efx) +{ + efx->Parameter1--; + if( (int) efx->Parameter1<1) { + //don't remove effects directly!!! + efx->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//lower decreasing immunities/bounces +static int check_type(Actor* actor, Effect* fx) +{ + //the protective effect (if any) + Effect *efx; + + ieDword bounce = actor->GetStat(IE_BOUNCE); + + //immunity checks +/*opcode immunity is in the per opcode checks + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) { + return 0; + } + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) { + return 0; + } +*/ + //spell level immunity + if(fx->Power && actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_ref, fx->Power, 0) ) { + return 0; + } + + //source immunity (spell name) + //if source is unspecified, don't resist it + if( fx->Source[0]) { + if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity_ref, fx->Source) ) { + return 0; + } + if( actor->fxqueue.HasEffectWithResource(fx_spell_immunity2_ref, fx->Source) ) { + return 0; + } + if (actor->fxqueue.HasEffectWithResource(fx_store_spell_sequencer_ref, fx->Source) ) { + //TODO: display strref 0x806C + return 0; + } + } + + //primary type immunity (school) + if( fx->PrimaryType) { + if( actor->fxqueue.HasEffectWithParam(fx_school_immunity_ref, fx->PrimaryType)) { + return 0; + } + } + + //secondary type immunity (usage) + if( fx->SecondaryType) { + if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_ref, fx->SecondaryType) ) { + return 0; + } + } + + //decrementing immunity checks + //decrementing level immunity + efx = actor->fxqueue.HasEffectWithParamPair(fx_level_immunity_dec_ref, fx->Power, 0); + if( efx ) { + DecreaseEffect(efx); + return 0; + } + + //decrementing spell immunity + if( fx->Source[0]) { + efx = actor->fxqueue.HasEffectWithResource(fx_spell_immunity_dec_ref, fx->Source); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + //decrementing primary type immunity (school) + if( fx->PrimaryType) { + efx = actor->fxqueue.HasEffectWithParam(fx_school_immunity_dec_ref, fx->PrimaryType); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + + //decrementing secondary type immunity (usage) + if( fx->SecondaryType) { + efx = actor->fxqueue.HasEffectWithParam(fx_secondary_type_immunity_dec_ref, fx->SecondaryType); + if( efx) { + DecreaseEffect(efx); + return 0; + } + } + + //spelltrap (absorb) + //FIXME: + //if the spelltrap effect already absorbed enough levels + //but still didn't get removed, it will absorb levels it shouldn't + //it will also absorb multiple spells in a single round + efx=actor->fxqueue.HasEffectWithParamPair(fx_spelltrap, 0, fx->Power); + if( efx) { + //storing the absorbed spell level + efx->Parameter3+=fx->Power; + //instead of a single effect, they had to create an effect for each level + //HOW DAMN LAME + //if decrease needs the spell level, use fx->Power here + actor->fxqueue.DecreaseParam1OfEffect(fx_spelltrap, 1); + //efx->Parameter1--; + return 0; + } + + //bounce checks + if( (bounce&BNC_LEVEL) && actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_ref, fx->Power, 0) ) { + return 0; + } + + if( fx->Source[0] && (bounce&BNC_RESOURCE) && actor->fxqueue.HasEffectWithResource(fx_spell_bounce_ref, fx->Source) ) { + return -1; + } + + if( fx->PrimaryType && (bounce&BNC_SCHOOL) ) { + if( actor->fxqueue.HasEffectWithParam(fx_school_bounce_ref, fx->PrimaryType)) { + return -1; + } + } + + if( fx->SecondaryType && (bounce&BNC_SECTYPE) ) { + if( actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_ref, fx->SecondaryType)) { + return -1; + } + } + //decrementing bounce checks + + //level decrementing bounce check + if( (bounce&BNC_LEVEL_DEC)) { + efx=actor->fxqueue.HasEffectWithParamPair(fx_level_bounce_dec_ref, fx->Power, 0); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->Source[0] && (bounce&BNC_RESOURCE_DEC)) { + efx=actor->fxqueue.HasEffectWithResource(fx_spell_bounce_dec_ref, fx->Resource); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->PrimaryType && (bounce&BNC_SCHOOL_DEC) ) { + efx=actor->fxqueue.HasEffectWithParam(fx_school_bounce_dec_ref, fx->PrimaryType); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + if( fx->SecondaryType && (bounce&BNC_SECTYPE_DEC) ) { + efx=actor->fxqueue.HasEffectWithParam(fx_secondary_type_bounce_dec_ref, fx->SecondaryType); + if( efx) { + DecreaseEffect(efx); + return -1; + } + } + + return 1; +} + +//check resistances, saving throws +static bool check_resistance(Actor* actor, Effect* fx) +{ + if( !actor) { + return false; + } + + //opcode immunity + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity_ref, fx->Opcode) ) { + printf ("immune to effect: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + if( actor->fxqueue.HasEffectWithParam(fx_opcode_immunity2_ref, fx->Opcode) ) { + printf ("immune2 to effect: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + +/* opcode bouncing isn't implemented? + //opcode bouncing + if( actor->fxqueue.HasEffectWithParam(fx_opcode_bounce_ref, fx->Opcode) ) { + return false; + } +*/ + + //not resistable (no saves either?) + if( fx->Resistance != FX_CAN_RESIST_CAN_DISPEL) { + return false; + } + + if (pstflags && (actor->GetSafeStat(IE_STATE_ID) & (STATE_ANTIMAGIC) ) ) { + return false; + } + + //don't resist self + bool selective_mr = core->HasFeature(GF_SELECTIVE_MAGIC_RES); + if (fx->Target==FX_TARGET_SELF) { + if (selective_mr) { + return false; + } + } + + //magic immunity + ieDword val = actor->GetStat(IE_RESISTMAGIC); + if( fx->random_value < val) { + // when using biased magic resistance non-hostile spells aren't resisted + if ((selective_mr && (fx->SourceFlags&SF_HOSTILE)) || !selective_mr) { + printf ("effect resisted: %s\n", (char*) Opcodes[fx->Opcode].Name); + return true; + } + } + + //saving throws + bool saved = false; + for (int i=0;i<5;i++) { + if( fx->SavingThrowType&(1<GetSavingThrow(i, fx->SavingThrowBonus); + if( saved) { + break; + } + } + } + if( saved) { + if( fx->IsSaveForHalfDamage) { + fx->Parameter1/=2; + } else { + printf ("%s saved against effect: %s\n", actor->GetName(1), (char*) Opcodes[fx->Opcode].Name); + return true; + } + } + return false; +} + +// this function is called two different ways +// when FirstApply is set, then the effect isn't stuck on the target +// this happens when a new effect comes in contact with the target. +// if the effect returns FX_DURATION_JUST_EXPIRED then it won't stick +// when first_apply is unset, the effect is already on the target +// this happens on load time too! +// returns FX_NOT_APPLIED if the process shouldn't be calling applyeffect anymore +// returns FX_ABORT if the whole spell this effect is in should be aborted +// it will disable all future effects of same source (only on first apply) + +int EffectQueue::ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieDword resistance) const +{ + //printf( "FX 0x%02x: %s(%d, %d)\n", fx->Opcode, effectnames[fx->Opcode].Name, fx->Parameter1, fx->Parameter2 ); + if( fx->Opcode >= MAX_EFFECTS) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + ieDword GameTime = core->GetGame()->GameTime; + + fx->FirstApply=first_apply; + if( first_apply) { + if( (fx->PosX==0xffffffff) && (fx->PosY==0xffffffff)) { + fx->PosX = target->Pos.x; + fx->PosY = target->Pos.y; + } + + //gemrb specific, stat based chance + if ((fx->Probability2 == 100) && Owner && (Owner->Type==ST_ACTOR) ) { + fx->Probability2 = 0; + fx->Probability1 = ((Actor *) Owner)->GetSafeStat(fx->Probability1); + } + + if (resistance) { + //the effect didn't pass the probability check + if( !check_probability(fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + //the effect didn't pass the target level check + if( check_level(target, fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + + //the effect didn't pass the resistance check + if( check_resistance(target, fx) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + return FX_NOT_APPLIED; + } + } + + //Same as in items and spells + if (fx->SourceFlags & SF_HOSTILE) { + if (target && (target != Owner) && Owner && (Owner->Type==ST_ACTOR) ) { + target->AttackedBy((Actor *) Owner); + } + } + + if( NeedPrepare(fx->TimingMode) ) { + //save delay for later + fx->SecondaryDelay = fx->Duration; + if( fx->TimingMode == FX_DURATION_INSTANT_LIMITED) { + fx->TimingMode = FX_DURATION_ABSOLUTE; + } + PrepareDuration(fx); + } + } + //check if the effect has triggered or expired + switch (DelayType(fx->TimingMode&0xff) ) { + case DELAYED: + if( fx->Duration>GameTime) { + return FX_NOT_APPLIED; + } + //effect triggered + //delayed duration (3) + if( NeedPrepare(fx->TimingMode) ) { + //prepare for delayed duration effects + fx->Duration = fx->SecondaryDelay; + PrepareDuration(fx); + } + fx->TimingMode=TriggeredEffect(fx->TimingMode); + break; + case DURATION: + if( fx->Duration<=GameTime) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + //add a return here, if 0 duration effects shouldn't work + } + break; + //permanent effect (so there is no warning) + case PERMANENT: + break; + //this shouldn't happen + default: + printf("Unknown delay type: %d (from %d)\n", DelayType(fx->TimingMode&0xff), fx->TimingMode); + abort(); + } + + EffectFunction fn = 0; + if( fx->OpcodeOpcode].Function; + } + int res = FX_ABORT; + if( fn) { + if( target && first_apply ) { + if( !target->fxqueue.HasEffectWithParamPair(fx_protection_from_display_string_ref, fx->Parameter1, 0) ) { + displaymsg->DisplayStringName( Opcodes[fx->Opcode].Strref, 0xf0f0f0, + target, IE_STR_SOUND); + } + } + + res=fn( Owner, target, fx ); + + //if there is no owner, we assume it is the target + switch( res ) { + case FX_APPLIED: + //normal effect with duration + break; + case FX_NOT_APPLIED: + //instant effect, pending removal + //for example, a damage effect + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + break; + case FX_INSERT: + //put this effect in the beginning of the queue + //all known insert effects are 'permanent' too + //that is the AC effect only + //actually, permanent effects seem to be + //inserted by the game engine too + case FX_PERMANENT: + //don't stick around if it was executed permanently + //for example, a permanent strength modifier effect + if( (fx->TimingMode == FX_DURATION_INSTANT_PERMANENT) ) { + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + } + break; + case FX_ABORT: + break; + default: + abort(); + } + } else { + //effect not found, it is going to be discarded + fx->TimingMode = FX_DURATION_JUST_EXPIRED; + } + return res; +} + +// looks for opcode with param2 + +#define MATCH_OPCODE() if((*f)->Opcode!=opcode) { continue; } + +// useful for: remove equipped item +#define MATCH_SLOTCODE() if((*f)->InventorySlot!=slotcode) { continue; } + +// useful for: remove projectile type +#define MATCH_PROJECTILE() if((*f)->Projectile!=projectile) { continue; } + +static const bool fx_live[MAX_TIMING_MODE]={true,true,true,false,false,false,false,false,true,true,false}; +inline bool IsLive(ieByte timingmode) +{ + if( timingmode>=MAX_TIMING_MODE) return false; + return fx_live[timingmode]; +} + +#define MATCH_LIVE_FX() if(!IsLive((*f)->TimingMode)) { continue; } +#define MATCH_PARAM1() if((*f)->Parameter1!=param1) { continue; } +#define MATCH_PARAM2() if((*f)->Parameter2!=param2) { continue; } +#define MATCH_RESOURCE() if( strnicmp( (*f)->Resource, resource, 8) ) { continue; } +#define MATCH_SOURCE() if( strnicmp( (*f)->Source, Removed, 8) ) { continue; } +#define MATCH_TIMING() if( (*f)->TimingMode!=timing) { continue; } + +//call this from an applied effect, after it returns, these effects +//will be killed along with it +void EffectQueue::RemoveAllEffects(ieDword opcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//removes all equipping effects that match slotcode +void EffectQueue::RemoveEquippingEffects(ieDwordSigned slotcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( !IsEquipped((*f)->TimingMode)) continue; + MATCH_SLOTCODE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//removes all effects that match projectile +void EffectQueue::RemoveAllEffectsWithProjectile(ieDword projectile) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_PROJECTILE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//remove effects belonging to a given spell +void EffectQueue::RemoveAllEffects(const ieResRef Removed) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_LIVE_FX(); + MATCH_SOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//remove effects belonging to a given spell, but only if they match timing method x +void EffectQueue::RemoveAllEffects(const ieResRef Removed, ieByte timing) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_TIMING(); + MATCH_SOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//this will modify effect reference +void EffectQueue::RemoveAllEffects(EffectRef &effect_reference) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + RemoveAllEffects(effect_reference.opcode); +} + +//Removes all effects with a matching resource field +void EffectQueue::RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_RESOURCE(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +void EffectQueue::RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + RemoveAllEffectsWithResource(effect_reference.opcode, resource); +} + +//This method could be used to remove stat modifiers that would lower a stat +//(works only if a higher stat means good for the target) +void EffectQueue::RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + switch((*f)->Parameter2) { + case 0:case 3: + if( ((signed) (*f)->Parameter1)>=0) continue; + break; + case 1:case 4: + if( ((signed) (*f)->Parameter1)>=(signed) current) continue; + break; + case 2:case 5: + if( ((signed) (*f)->Parameter1)>=100) continue; + break; + default: + break; + } + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//Removes all effects with a matching param2 +//param2 is usually an effect's subclass (quality) while param1 is more like quantity. +//So opcode+param2 usually pinpoints an effect better when not all effects of a given +//opcode need to be removed (see removal of portrait icon) +void EffectQueue::RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } +} + +//this function is called by FakeEffectExpiryCheck +//probably also called by rest +void EffectQueue::RemoveExpiredEffects(ieDword futuretime) const +{ + ieDword GameTime = core->GetGame()->GameTime; + if( GameTime+futuretime*AI_UPDATE_TIME::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + //FIXME: how this method handles delayed effects??? + //it should remove them as well, i think + if( DelayType( ((*f)->TimingMode) )!=PERMANENT ) { + if( (*f)->Duration<=GameTime) { + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } + } + } +} + +//this effect will expire all effects that are not truly permanent +//which i call permanent after death (iesdp calls it permanent after bonuses) +void EffectQueue::RemoveAllNonPermanentEffects() const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( IsRemovable((*f)->TimingMode) ) { + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + } + } +} + +//this will modify effect reference + +void EffectQueue::RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const +{ + ResolveEffectRef(effect_reference); + RemoveAllDetrimentalEffects(effect_reference.opcode, current); +} + +void EffectQueue::RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + RemoveAllEffectsWithParam(effect_reference.opcode, param2); +} + +//remove certain levels of effects, possibly matching school/secondary type +//this method removes whole spells (tied together by their source) +//FIXME: probably this isn't perfect +void EffectQueue::RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword Flags, ieDword match) const +{ + Removed[0]=0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( (*f)->Power>level) { + continue; + } + + if( Removed[0]) { + MATCH_SOURCE(); + } + if( Flags&RL_MATCHSCHOOL) { + if( (*f)->PrimaryType!=match) { + continue; + } + } + if( Flags&RL_MATCHSECTYPE) { + if( (*f)->SecondaryType!=match) { + continue; + } + } + //if dispellable was not set, or the effect is dispellable + //then remove it + if( Flags&RL_DISPELLABLE) { + if( !((*f)->Resistance&FX_CAN_DISPEL)) { + continue; + } + } + (*f)->TimingMode = FX_DURATION_JUST_EXPIRED; + if( Flags&RL_REMOVEFIRST) { + memcpy(Removed,(*f)->Source, sizeof(Removed)); + } + } +} + +Effect *EffectQueue::HasOpcode(ieDword opcode) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffect(EffectRef &effect_reference) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcode(effect_reference.opcode); +} + +Effect *EffectQueue::HasOpcodeWithParam(ieDword opcode, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcodeWithParam(effect_reference.opcode, param2); +} + +//looks for opcode with pairs of parameters (useful for protection against creature, extra damage or extra thac0 against creature) +//generally an IDS targeting + +Effect *EffectQueue::HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + //0 is always accepted as first parameter + if( param1) { + MATCH_PARAM1(); + } + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return NULL; + } + return HasOpcodeWithParamPair(effect_reference.opcode, param1, param2); +} + +// sums all the values of the specific damage bonus effects of the passed "damage type" +int EffectQueue::SpecificDamageBonus(ieDword opcode, ieDword param2) const +{ + int bonus = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_PARAM2(); + bonus += (signed) (*f)->Parameter1; + } + return bonus; +} + +static EffectRef fx_damage_bonus_modifier_ref={"DamageBonusModifier",NULL,-1}; +int EffectQueue::SpecificDamageBonus(ieDword damage_type) const +{ + ResolveEffectRef(fx_damage_bonus_modifier_ref); + if(fx_damage_bonus_modifier_ref.opcode < 0) { + return 0; + } + return SpecificDamageBonus(fx_damage_bonus_modifier_ref.opcode, damage_type); +} + +//this could be used for stoneskins and mirror images as well +void EffectQueue::DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + ieDword value = (*f)->Parameter1; + if( value>amount) value-=amount; + else value = 0; + (*f)->Parameter1=value; + } +} + +void EffectQueue::DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return; + } + DecreaseParam1OfEffect(effect_reference.opcode, amount); +} + + +//this function does IDS targeting for effects (extra damage/thac0 against creature) +static const int ids_stats[7]={IE_EA, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC, IE_SEX, IE_ALIGNMENT}; + +int EffectQueue::BonusAgainstCreature(ieDword opcode, Actor *actor) const +{ + int sum = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + if( (*f)->Parameter1) { + ieDword ids = (*f)->Parameter2; + if( ids<2 || ids>8) { + ids=2; + } + ieDword param1 = actor->GetStat(ids_stats[ids-2]); + MATCH_PARAM1(); + } + int val = (int) (*f)->Parameter3; + if( !val) val = 2; + sum += val; + } + return sum; +} + +int EffectQueue::BonusAgainstCreature(EffectRef &effect_reference, Actor *actor) const +{ + ResolveEffectRef(effect_reference); + if( effect_reference.opcode<0) { + return 0; + } + return BonusAgainstCreature(effect_reference.opcode, actor); +} + +bool EffectQueue::WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + // + int magic = (int) (*f)->Parameter1; + ieDword mask = (*f)->Parameter3; + ieDword value = (*f)->Parameter4; + if( magic==0) { + if( enchantment) continue; + } else if( magic>0) { + if( enchantment>magic) continue; + } + + if( (weapontype&mask) != value) { + continue; + } + return true; + } + return false; +} + +static EffectRef fx_weapon_immunity_ref={"Protection:Weapons",NULL,-1}; + +bool EffectQueue::WeaponImmunity(int enchantment, ieDword weapontype) const +{ + ResolveEffectRef(fx_weapon_immunity_ref); + if( fx_weapon_immunity_ref.opcode<0) { + return 0; + } + return WeaponImmunity(fx_weapon_immunity_ref.opcode, enchantment, weapontype); +} + +void EffectQueue::AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) const +{ + ResolveEffectRef(fx_ref); + if( fx_ref.opcode<0) { + return; + } + + ieDword opcode = fx_ref.opcode; + Point p(-1,-1); + + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + // + Effect *fx = core->GetEffect( (*f)->Resource, (*f)->Power, p); + if (!fx) continue; + fx->Target = FX_TARGET_PRESET; + fxqueue->AddEffect(fx, true); + } +} + +/* no longer needed, use IE_CASTING stat +static EffectRef fx_disable_spellcasting_ref={ "DisableCasting", NULL, -1 }; +int EffectQueue::DisabledSpellcasting(int types) const +{ + ResolveEffectRef(fx_disable_spellcasting_ref); + if( fx_disable_spellcasting_ref.opcode < 0) { + return 0; + } + + unsigned int spelltype_mask = 0; + bool iwd2 = !!core->HasFeature(GF_ENHANCED_EFFECTS); + ieDword opcode = fx_disable_spellcasting_ref.opcode; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + + if (iwd2) { + switch((*f)->Parameter2) { + case 0: // all + spelltype_mask |= 7; + break; + case 1: // mage and cleric + spelltype_mask |= 3; + break; + case 2: // mage + spelltype_mask |= 2; + break; + case 3: // cleric + spelltype_mask |= 1; + break; + case 4: // innate + spelltype_mask |= 4; + break; + } + } else { + switch((*f)->Parameter2) { + case 0: // mage + spelltype_mask |= 2; + break; + case 1: // cleric + spelltype_mask |= 1; + break; + case 2: // innate + spelltype_mask |= 4; + break; + } + } + } + return spelltype_mask & types; +} +*/ + +//useful for immunity vs spell, can't use item, etc. +Effect *EffectQueue::HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_RESOURCE(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + return HasOpcodeWithResource(effect_reference.opcode, resource); +} + +//used in contingency/sequencer code (cannot have the same contingency twice) +Effect *EffectQueue::HasOpcodeWithSource(ieDword opcode, const ieResRef Removed) const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + MATCH_LIVE_FX(); + MATCH_SOURCE(); + + return (*f); + } + return NULL; +} + +Effect *EffectQueue::HasEffectWithSource(EffectRef &effect_reference, const ieResRef resource) const +{ + ResolveEffectRef(effect_reference); + return HasOpcodeWithSource(effect_reference.opcode, resource); +} + +bool EffectQueue::HasAnyDispellableEffect() const +{ + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + if( (*f)->Resistance&FX_CAN_DISPEL) { + return true; + } + } + return false; +} + +void EffectQueue::dump() const +{ + printf( "EFFECT QUEUE:\n" ); + int i = 0; + std::list< Effect* >::const_iterator f; + for ( f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx = *f; + if( fx) { + char *Name = NULL; + if( fx->Opcode < MAX_EFFECTS) + Name = (char*) Opcodes[fx->Opcode].Name; + + printf( " %2d: 0x%02x: %s (%d, %d) S:%s\n", i++, fx->Opcode, Name, fx->Parameter1, fx->Parameter2, fx->Source ); + } + } +} +/* +Effect *EffectQueue::GetEffect(ieDword idx) const +{ + if( effects.size()<=idx) { + return NULL; + } + return effects[idx]; +} +*/ + +//returns true if the effect supports simplified duration +bool EffectQueue::HasDuration(Effect *fx) +{ + switch(fx->TimingMode) { + case FX_DURATION_INSTANT_LIMITED: //simple duration + case FX_DURATION_DELAY_LIMITED: //delayed duration + case FX_DURATION_DELAY_PERMANENT: //simple delayed + return true; + } + return false; +} + +static EffectRef fx_variable_ref={"Variable:StoreLocalVariable",NULL,-1}; + +//returns true if the effect must be saved +//variables are saved differently +bool EffectQueue::Persistent(Effect* fx) +{ + //we save this as variable + if( fx->Opcode==(ieDword) ResolveEffect(fx_variable_ref)) { + return false; + } + + switch (fx->TimingMode) { + //normal equipping fx of items + case FX_DURATION_INSTANT_WHILE_EQUIPPED: + //delayed effect not saved + case FX_DURATION_DELAY_UNSAVED: + //permanent effect not saved + case FX_DURATION_PERMANENT_UNSAVED: + //just expired effect + case FX_DURATION_JUST_EXPIRED: + return false; + } + return true; +} + +//alter the color effect in case the item is equipped in the shield slot +void EffectQueue::HackColorEffects(Actor *Owner, Effect *fx) +{ + if( fx->InventorySlot!=Owner->inventory.GetShieldSlot()) return; + + unsigned int gradienttype = fx->Parameter2 & 0xF0; + if( gradienttype == 0x10) { + gradienttype = 0x20; // off-hand + fx->Parameter2 &= ~0xF0; + fx->Parameter2 |= gradienttype; + } +} + +//iterate through saved effects +const Effect *EffectQueue::GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const +{ + while(f!=effects.end()) { + Effect *effect = *f; + f++; + if( Persistent(effect)) { + return effect; + } + } + return NULL; +} + +Effect *EffectQueue::GetNextEffect(std::list< Effect* >::const_iterator &f) const +{ + if( f!=effects.end()) return *f++; + return NULL; +} + +ieDword EffectQueue::CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *resource) const +{ + ieDword cnt = 0; + + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + if( param1!=0xffffffff) + MATCH_PARAM1(); + if( param2!=0xffffffff) + MATCH_PARAM2(); + if( resource) { + MATCH_RESOURCE(); + } + cnt++; + } + return cnt; +} + +void EffectQueue::ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const +{ + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + MATCH_OPCODE(); + (*f)->PosX=x; + (*f)->PosY=y; + (*f)->Parameter3=0; + return; + } +} + +//count effects that get saved +ieDword EffectQueue::GetSavedEffectsCount() const +{ + ieDword cnt = 0; + + std::list< Effect* >::const_iterator f; + + for ( f = effects.begin(); f != effects.end(); f++ ) { + Effect* fx = *f; + if( Persistent(fx)) + cnt++; + } + return cnt; +} + +void EffectQueue::TransformToDelay(ieByte &TimingMode) +{ + if( TimingModeImmuneToProjectile(fx->Projectile)) return 0; + + //don't resist item projectile payloads based on spell school, bounce, etc. + if( fx->InventorySlot) { + return 1; + } + + //check level resistances + //check specific spell immunity + //check school/sectype immunity + return check_type(target, fx); + } + return 0; +} + +void EffectQueue::AffectAllInRange(Map *map, const Point &pos, int idstype, int idsvalue, + unsigned int range, Actor *except) +{ + int cnt = map->GetActorCount(true); + while(cnt--) { + Actor *actor = map->GetActor(cnt,true); + if( except==actor) { + continue; + } + //distance + if( Distance(pos, actor)>range) { + continue; + } + //ids targeting + if( !match_ids(actor, idstype, idsvalue)) { + continue; + } + //line of sight + if( !map->IsVisible(actor->Pos, pos)) { + continue; + } + AddAllEffects(actor, actor->Pos); + } +} + diff --git a/project/jni/application/gemrb/src/core/EffectQueue.h b/project/jni/application/gemrb/src/core/EffectQueue.h new file mode 100644 index 000000000..52c9b6e6c --- /dev/null +++ b/project/jni/application/gemrb/src/core/EffectQueue.h @@ -0,0 +1,296 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 EffectQueue.h + * Declares EffectQueue class holding and processing all spell effects + * on a single Actor + * @author The GemRB Project + */ + +#ifndef EFFECTQUEUE_H +#define EFFECTQUEUE_H + +#include "exports.h" + +#include "Effect.h" +#include "Region.h" + +#include + +class Actor; +class Map; +class Scriptable; + +/** Maximum number of different Effect opcodes */ +#define MAX_EFFECTS 512 + +///** if the effect returns this, stop adding any other effect */ +#define FX_ABORT 0 +/** these effects don't stick around if used as permanent, + * in that case they modify a base stat like charisma modifier */ +#define FX_PERMANENT 2 +/** these effects never stick around, use them for instant effects like damage */ +#define FX_NOT_APPLIED 3 +/** these effects always stick around when applied as permanent or duration */ +#define FX_APPLIED 1 +///** insert the effect instead of push back */ +#define FX_INSERT 4 + +//remove level effects flags +#define RL_DISPELLABLE 1 //only dispellables +#define RL_MATCHSCHOOL 2 //match school +#define RL_MATCHSECTYPE 4 //match secondary type +#define RL_REMOVEFIRST 8 //remove only one spell (could be more effects) + +//bouncing immunities +#define BNC_PROJECTILE 1 +#define BNC_OPCODE 2 +#define BNC_LEVEL 4 +#define BNC_SCHOOL 8 +#define BNC_SECTYPE 0x10 +#define BNC_RESOURCE 0x20 +#define BNC_PROJECTILE_DEC 0x100 +#define BNC_OPCODE_DEC 0x200 +#define BNC_LEVEL_DEC 0x400 +#define BNC_SCHOOL_DEC 0x800 +#define BNC_SECTYPE_DEC 0x1000 +#define BNC_RESOURCE_DEC 0x2000 + +//normal immunities +#define IMM_PROJECTILE 1 +#define IMM_OPCODE 2 +#define IMM_LEVEL 4 +#define IMM_SCHOOL 8 +#define IMM_SECTYPE 16 +#define IMM_RESOURCE 32 +#define IMM_PROJECTILE_DEC 0x100 +#define IMM_OPCODE_DEC 0x200 +#define IMM_LEVEL_DEC 0x400 +#define IMM_SCHOOL_DEC 0x800 +#define IMM_SECTYPE_DEC 0x1000 +#define IMM_RESOURCE_DEC 0x2000 + +// FIXME: Dice roll should be probably done just once, e.g. when equipping +// the item, not each time the fx are applied +// the dice values are actually level limits, except in 3 hp modifier functions +// the damage function is an instant (the other 2 functions might be tricky with random values) +//#define DICE_ROLL(max_val) ((fx->DiceThrown && fx->DiceSides) ? ((max_val >=0) ? (MIN( core->Roll( fx->DiceThrown, fx->DiceSides, 0 ), max_val )) : (MAX( core->Roll( fx->DiceThrown, fx->DiceSides, 0 ), max_val ))) : max_val) + +//sometimes damage doesn't comply with the calculated value +#define DICE_ROLL(adjustment) (core->Roll( fx->DiceThrown, fx->DiceSides, adjustment) ) + +// You will need to get GameTime somehow to use this macro +#define PrepareDuration(fx) fx->Duration = (fx->Duration*AI_UPDATE_TIME + GameTime) + +// often used stat modifications, usually Parameter2 types 0, 1 and 2 +//these macros should work differently in permanent mode (modify base too) +#define STAT_GET(stat) (target->Modified[ stat ]) +#define STAT_ADD(stat, mod) target->SetStat( stat, STAT_GET( stat ) + ( mod ), 0 ) +#define STAT_SUB(stat, mod) target->SetStat( stat, STAT_GET( stat ) - ( mod ), 0 ) +#define STAT_BIT_OR(stat, mod) target->SetStat( stat, STAT_GET( stat ) | ( mod ), 0 ) +#define STAT_SET(stat, mod) target->SetStat( stat, ( mod ), 0 ) +#define STAT_SET_PCF(stat, mod) target->SetStat( stat, ( mod ), 1 ) +#define STAT_BIT_OR_PCF(stat, mod) target->SetStat( stat, STAT_GET( stat ) | ( mod ), 1 ) +#define STAT_MUL(stat, mod) target->SetStat( stat, STAT_GET(stat) * ( mod ) / 100, 0 ) +//if an effect sticks around +#define STATE_CURE( mod ) target->Modified[ IE_STATE_ID ] &= ~(ieDword) ( mod ) +#define STATE_SET( mod ) target->Modified[ IE_STATE_ID ] |= (ieDword) ( mod ) +#define EXTSTATE_SET( mod ) target->Modified[ IE_EXTSTATE_ID ] |= (ieDword) ( mod ) +#define STATE_GET( mod ) (target->Modified[ IE_STATE_ID ] & (ieDword) ( mod ) ) +#define EXTSTATE_GET( mod ) (target->Modified[ IE_EXTSTATE_ID ] & (ieDword) ( mod ) ) +#define STAT_MOD( stat ) target->NewStat(stat, fx->Parameter1, fx->Parameter2) +#define STAT_MOD_VAR( stat, mod ) target->NewStat(stat, ( mod ) , fx->Parameter2 ) +#define BASE_GET(stat) (target->BaseStats[ stat ]) +#define BASE_SET(stat, mod) target->SetBase( stat, ( mod ) ) +#define BASE_ADD(stat, mod) target->SetBase( stat, BASE_GET(stat)+ ( mod ) ) +#define BASE_SUB(stat, mod) target->SetBase( stat, BASE_GET(stat)- ( mod ) ) +#define BASE_MUL(stat, mod) target->SetBase( stat, BASE_GET(stat)* ( mod ) / 100 ) +#define BASE_MOD(stat) target->NewBase( stat, fx->Parameter1, fx->Parameter2) +#define BASE_MOD_VAR(stat, mod) target->NewBase( stat, (mod), fx->Parameter2 ) +//if an effect doesn't stick (and has permanent until cured effect) then +//it has to modify the base stat (which is saved) +//also use this one if the effect starts a cure effect automatically +#define BASE_STATE_SET( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), true ) +#define BASE_STATE_CURE( mod ) target->SetBaseBit( IE_STATE_ID, ( mod ), false ) + +/** Prototype of a function implementing a particular Effect opcode */ +typedef int (* EffectFunction)(Scriptable*, Actor*, Effect*); + + +/** Links Effect name to a function implementing the effect */ +struct EffectRef { + const char* Name; + EffectFunction Function; + int opcode; +}; + +/** Initializes table of available spell Effects used by all the queues. */ +/** The available effects should already be registered by the effect plugins */ +bool Init_EffectQueue(); + +/** Registers opcodes implemented by an effect plugin */ +void EffectQueue_RegisterOpcodes(int count, const EffectRef *opcodes); + +/** release effect list when Interface is destroyed */ +void EffectQueue_ReleaseMemory(); + +/** Check if opcode is for an effect that takes a color slot as parameter. */ +bool IsColorslotEffect(int opcode); + +/** + * @class EffectQueue + * Class holding and processing spell Effects on a single Actor + */ + +class GEM_EXPORT EffectQueue { +private: + /** List of Effects applied on the Actor */ + std::list< Effect* > effects; + /** Actor which is target of the Effects */ + Scriptable* Owner; + +public: + EffectQueue(); + virtual ~EffectQueue(); + + /** Sets Actor which is affected by these effects */ + void SetOwner(Scriptable* act) { Owner = act; } + /** Returns Actor affected by these effects */ + Scriptable* GetOwner() const { return Owner; } + + /** adds an effect to the queue, it could also insert it if flagged so + * fx should be freed by the caller + */ + void AddEffect(Effect* fx, bool insert=false); + /** Adds an Effect to the queue, subject to level and other checks. + * Returns FX_ABORT is unsuccessful. fx is just a reference, AddEffect() + * will malloc its own copy */ + int AddEffect(Effect* fx, Scriptable* self, Actor* pretarget, const Point &dest) const; + /** Removes first Effect matching fx from the queue. + * Effects are matched based on their contents */ + bool RemoveEffect(Effect* fx); + + int AddAllEffects(Actor* target, const Point &dest) const; + void ApplyAllEffects(Actor* target) const; + /** remove effects marked for removal */ + void Cleanup(); + + /* directly removes effects with specified opcode, use effect_reference when you can */ + void RemoveAllEffects(ieDword opcode) const; + void RemoveAllEffectsWithResource(ieDword opcode, const ieResRef resource) const; + + /* removes any effects (delayed or not) which were using projectile */ + void RemoveAllEffectsWithProjectile(ieDword projectile) const; + + /* removes equipping effects with specified inventory slot code */ + void RemoveEquippingEffects(ieDwordSigned slotcode) const; + + /* removes all effects of a given spell */ + void RemoveAllEffects(const ieResRef Removed) const; + void RemoveAllEffects(const ieResRef Removed, ieByte timing) const; + /* removes all effects of type */ + void RemoveAllEffects(EffectRef &effect_reference) const; + /* removes expired or to be expired effects */ + void RemoveExpiredEffects(ieDword futuretime) const; + /* removes all effects except timing mode 9 */ + void RemoveAllNonPermanentEffects() const; + void RemoveAllDetrimentalEffects(EffectRef &effect_reference, ieDword current) const; + void RemoveAllEffectsWithParam(EffectRef &effect_reference, ieDword param2) const; + void RemoveAllEffectsWithResource(EffectRef &effect_reference, const ieResRef resource) const; + void RemoveLevelEffects(ieResRef &Removed, ieDword level, ieDword flags, ieDword match) const; + + /* returns true if the timing method supports simplified duration */ + static bool HasDuration(Effect *fx); + /* returns true if the effect should be saved */ + static bool Persistent(Effect* fx); + /* returns next saved effect, increases index */ + std::list< Effect* >::const_iterator GetFirstEffect() const + { + return effects.begin(); + } + const Effect *GetNextSavedEffect(std::list< Effect* >::const_iterator &f) const; + Effect *GetNextEffect(std::list< Effect* >::const_iterator &f) const; + ieDword CountEffects(EffectRef &effect_reference, ieDword param1, ieDword param2, const char *ResRef) const; + void ModifyEffectPoint(EffectRef &effect_reference, ieDword x, ieDword y) const; + /* returns the number of saved effects */ + ieDword GetSavedEffectsCount() const; + size_t GetEffectsCount() const { return effects.size(); } + /* this method hacks the offhand weapon color effects */ + static void HackColorEffects(Actor *Owner, Effect *fx); + static Effect *CreateEffect(EffectRef &effect_reference, ieDword param1, ieDword param2, ieWord timing); + EffectQueue *CopySelf() const; + static Effect *CreateEffectCopy(Effect *oldfx, EffectRef &effect_reference, ieDword param1, ieDword param2); + static Effect *CreateUnsummonEffect(Effect *fx); + //locating opcodes + Effect *HasEffect(EffectRef &effect_reference) const; + Effect *HasEffectWithParam(EffectRef &effect_reference, ieDword param2) const; + Effect *HasEffectWithParamPair(EffectRef &effect_reference, ieDword param1, ieDword param2) const; + Effect *HasEffectWithResource(EffectRef &effect_reference, const ieResRef resource) const; + Effect *HasEffectWithSource(EffectRef &effect_reference, const ieResRef source) const; + void DecreaseParam1OfEffect(EffectRef &effect_reference, ieDword amount) const; + int SpecificDamageBonus(ieDword damage_type) const; + bool HasAnyDispellableEffect() const; + //transforming timing modes + static void TransformToDelay(ieByte &TimingMode); + //getting summarised effects + int BonusAgainstCreature(EffectRef &effect_reference, Actor *actor) const; + //getting weapon immunity flag + bool WeaponImmunity(int enchantment, ieDword weapontype) const; + //melee and ranged effects + void AddWeaponEffects(EffectQueue *fxqueue, EffectRef &fx_ref) const; + // checks if spells of type "types" are disabled (usually by armor) + // returns a bitfield of disabled spelltypes + // it is no longer used + //int DisabledSpellcasting(int types) const; + + // returns -1 if bounced, 0 if resisted, 1 if accepted spell + int CheckImmunity(Actor *target) const; + // apply this effectqueue on all actors matching ids targeting + // from pos, in range (no cone size yet) + void AffectAllInRange(Map *map, const Point &pos, int idstype, int idsvalue, unsigned int range, Actor *except); + /** Lists contents of the queue on a terminal for debugging */ + void dump() const; + //resolve effect + static int ResolveEffect(EffectRef &effect_reference); + static bool match_ids(Actor *target, int table, ieDword value); + /** returns true if the process should abort applying a stack of effects */ + int ApplyEffect(Actor* target, Effect* fx, ieDword first_apply, ieDword resistance=1) const; +private: + /** counts effects of specific opcode, parameters and resource */ + ieDword CountEffects(ieDword opcode, ieDword param1, ieDword param2, const char *ResRef) const; + void ModifyEffectPoint(ieDword opcode, ieDword x, ieDword y) const; + //use the effect reference style calls from outside + static Effect *CreateEffect(ieDword opcode, ieDword param1, ieDword param2, ieWord timing); + static Effect *CreateEffectCopy(Effect *oldfx, ieDword opcode, ieDword param1, ieDword param2); + void RemoveAllDetrimentalEffects(ieDword opcode, ieDword current) const; + void RemoveAllEffectsWithParam(ieDword opcode, ieDword param2) const; + Effect *HasOpcode(ieDword opcode) const; + Effect *HasOpcodeWithParam(ieDword opcode, ieDword param2) const; + Effect *HasOpcodeWithParamPair(ieDword opcode, ieDword param1, ieDword param2) const; + Effect *HasOpcodeWithResource(ieDword opcode, const ieResRef resource) const; + Effect *HasOpcodeWithSource(ieDword opcode, const ieResRef source) const; + void DecreaseParam1OfEffect(ieDword opcode, ieDword amount) const; + int SpecificDamageBonus(ieDword opcode, ieDword param2) const; + int BonusAgainstCreature(ieDword opcode, Actor *actor) const; + bool WeaponImmunity(ieDword opcode, int enchantment, ieDword weapontype) const; +}; + +#endif // ! EFFECTQUEUE_H diff --git a/project/jni/application/gemrb/src/core/Factory.cpp b/project/jni/application/gemrb/src/core/Factory.cpp new file mode 100644 index 000000000..797c4a09d --- /dev/null +++ b/project/jni/application/gemrb/src/core/Factory.cpp @@ -0,0 +1,65 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "Factory.h" + +#include "win32def.h" + +#include + +Factory::Factory(void) +{ +} + +Factory::~Factory(void) +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + delete( fobjects[i] ); + } +} + +void Factory::AddFactoryObject(FactoryObject* fobject) +{ + fobjects.push_back( fobject ); +} + +int Factory::IsLoaded(const char* ResRef, SClass_ID type) const +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + if (fobjects[i]->SuperClassID == type) { + if (strnicmp( fobjects[i]->ResRef, ResRef, 8 ) == 0) { + return i; + } + } + } + return -1; +} + +FactoryObject* Factory::GetFactoryObject(int pos) const +{ + return fobjects[pos]; +} + +void Factory::FreeObjects(void) +{ + for (unsigned int i = 0; i < fobjects.size(); i++) { + delete( fobjects[i] ); + } +} diff --git a/project/jni/application/gemrb/src/core/Factory.h b/project/jni/application/gemrb/src/core/Factory.h new file mode 100644 index 000000000..d6c1c09e3 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Factory.h @@ -0,0 +1,42 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 FACTORY_H +#define FACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "AnimationFactory.h" +#include "FactoryObject.h" + +class GEM_EXPORT Factory { +private: + std::vector< FactoryObject*> fobjects; +public: + Factory(void); + ~Factory(void); + void AddFactoryObject(FactoryObject* fobject); + int IsLoaded(const char* ResRef, SClass_ID type) const; + FactoryObject* GetFactoryObject(int pos) const; + void FreeObjects(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/FactoryObject.cpp b/project/jni/application/gemrb/src/core/FactoryObject.cpp new file mode 100644 index 000000000..8c0da9bae --- /dev/null +++ b/project/jni/application/gemrb/src/core/FactoryObject.cpp @@ -0,0 +1,33 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "FactoryObject.h" + +#include "win32def.h" + +FactoryObject::FactoryObject(const char* name, SClass_ID SuperClassID) +{ + strnlwrcpy( ResRef, name, 8 ); + this->SuperClassID = SuperClassID; +} + +FactoryObject::~FactoryObject(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/FactoryObject.h b/project/jni/application/gemrb/src/core/FactoryObject.h new file mode 100644 index 000000000..a1189c6eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/FactoryObject.h @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 FACTORYOBJECT_H +#define FACTORYOBJECT_H + +#include "exports.h" +#include "globals.h" + +class GEM_EXPORT FactoryObject { +public: + SClass_ID SuperClassID; + ieResRef ResRef; + FactoryObject(const char* ResRef, SClass_ID SuperClassID); + virtual ~FactoryObject(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Font.cpp b/project/jni/application/gemrb/src/core/Font.cpp new file mode 100644 index 000000000..d62afecc4 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Font.cpp @@ -0,0 +1,560 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 class represents game fonts. Fonts are special .bam files. +//Each cycle stands for a letter. + +#include "Font.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Video.h" + +#include + +unsigned int lastX = 0; + +#define PARAGRAPH_START_X 5; + +static const Color black = {0, 0, 0, 0}; + +inline size_t mystrlen(const char* string) +{ + if (!string) { + return ( size_t ) 0; + } + const char* tmp = string; + size_t count = 0; + while (*tmp != 0) { + if (( ( unsigned char ) * tmp ) >= 0xf0) { + tmp += 3; + count += 3; + } + count++; + tmp++; + } + return count; +} + +Font::Font(int w, int h, Palette* pal) +{ + lastX = 0; + count = 0; + FirstChar = 0; + sprBuffer = 0; + + width = w; + height = h; + tmpPixels = (unsigned char*)malloc(width*height); + + memset( xPos, 0, sizeof( xPos) ); + memset( yPos, 0, sizeof( yPos) ); + + pal->IncRef(); + palette = pal; + maxHeight = h; +} + +Font::~Font(void) +{ + Video *video = core->GetVideoDriver(); + gamedata->FreePalette( palette ); + video->FreeSprite( sprBuffer ); +} + +void Font::FinalizeSprite(bool cK, int index) +{ + sprBuffer = core->GetVideoDriver()->CreateSprite8( width, height, 8, tmpPixels, palette ? palette->col : 0, cK, index ); + tmpPixels = 0; +} + +void Font::AddChar(unsigned char* spr, int w, int h, short xPos, short yPos) +{ + if (!spr) { + size[count].x = 0; + size[count].y = 0; + size[count].w = 0; + size[count].h = 0; + this->xPos[count] = 0; + this->yPos[count] = 0; + count++; + return; + } + unsigned char * currPtr = tmpPixels + lastX; + unsigned char * srcPtr = ( unsigned char * ) spr; + for (int y = 0; y < h; y++) { + memcpy( currPtr, srcPtr, w ); + srcPtr += w; + currPtr += width; + } + size[count].x = lastX; + size[count].y = 0; + size[count].w = w; + size[count].h = h; + this->xPos[count] = xPos; + this->yPos[count] = yPos; + count++; + lastX += w; +} + +void Font::PrintFromLine(int startrow, Region rgn, const unsigned char* string, + Palette* hicolor, unsigned char Alignment, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + bool enablecap=false; + int capital = 0; + if (initials) + { + capital=1; + enablecap=true; + } + int initials_rows = 0; + int initials_x = 0; + + unsigned int psx = PARAGRAPH_START_X; + Palette *pal = hicolor; + if (!pal) { + pal = palette; + } + if (startrow) enablecap=false; + + if (initials==this) { + enablecap=false; + } + + sprBuffer->SetPalette( pal ); + size_t len = strlen( ( char* ) string ); + char* tmp = ( char* ) malloc( len + 1 ); + memcpy( tmp, ( char * ) string, len + 1 ); + SetupString( tmp, rgn.w, NoColor ); + int ystep = 0; + if (Alignment & IE_FONT_SINGLE_LINE) { + for (size_t i = 0; i < len; i++) { + int height = yPos[( unsigned char ) tmp[i] - 1]; + if (ystep < height) + ystep = height; + } + } else { + ystep = size[1].h; + } + if (!ystep) ystep = maxHeight; + int x = psx, y = ystep; + int w = CalcStringWidth( tmp, NoColor ); + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + if (Alignment & IE_FONT_ALIGN_MIDDLE) { + int h = 0; + for (size_t i = 0; i <= len; i++) { + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) + h++; + } + h = h * ystep; + y += ( rgn.h - h ) / 2; + } else if (Alignment & IE_FONT_ALIGN_BOTTOM) { + int h = 1; + for (size_t i = 0; i <= len; i++) { + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) + h++; + } + h = h * ystep; + y += ( rgn.h - h ); + } else if (Alignment & IE_FONT_ALIGN_TOP) { + y += 5; + } + + Video* video = core->GetVideoDriver(); + int row = 0; + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) { + i++; + char tag[256]; + tag[0]=0; + + for (int k = 0; k < 256 && i=startrow) ) { + enablecap=true; + } + continue; + } + + + if (strnicmp( tag, "color=", 6 ) == 0) { + unsigned int r,g,b; + if (sscanf( tag, "color=%02X%02X%02X", &r, &g, &b ) != 3) + continue; + const Color c = {(unsigned char) r,(unsigned char)g, (unsigned char)b, 0}; + Palette* newPal = core->CreatePalette( c, black ); + sprBuffer->SetPalette( newPal ); + gamedata->FreePalette( newPal ); + continue; + } + if (stricmp( tag, "/color" ) == 0) { + sprBuffer->SetPalette( pal ); + continue; + } + + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + } + continue; + } + + if (row < startrow) { + if (tmp[i] == 0) { + row++; + } + continue; + } + if (( tmp[i] == 0 ) || ( tmp[i] == '\n' )) { + y += ystep; + x = psx; + int w = CalcStringWidth( &tmp[i + 1], NoColor ); + if (initials_rows > 0) { + initials_rows--; + x += initials_x; + w += initials_x; + } + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + continue; + } + unsigned char currChar = ( unsigned char ) tmp[i] - 1; + if (initials && capital && enablecap) { + x = initials->PrintInitial( x, y, rgn, currChar ); + initials_x = x; + + //how many more lines to be indented (one was already indented) + initials_rows = (initials->maxHeight-1)/maxHeight; + enablecap = false; + continue; + } + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn ); + if (cursor && ( i == curpos )) { + video->BlitSprite( cursor, x + rgn.x, + y + rgn.y, true, &rgn ); + } + x += size[currChar].w; + } + if (cursor && ( curpos == len )) { + video->BlitSprite( cursor, x + rgn.x, + y + rgn.y, true, &rgn ); + } + free( tmp ); +} + +void Font::Print(Region rgn, const unsigned char* string, Palette* hicolor, + unsigned char Alignment, bool anchor, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + Print(rgn, rgn, string, hicolor, Alignment, anchor, initials, cursor, curpos, NoColor); +} + +void Font::Print(Region cliprgn, Region rgn, const unsigned char* string, + Palette* hicolor, unsigned char Alignment, bool anchor, Font* initials, + Sprite2D* cursor, unsigned int curpos, bool NoColor) const +{ + bool enablecap=false; + int capital = 0; + if (initials) + { + capital=1; + enablecap=true; + } + + unsigned int psx = PARAGRAPH_START_X; + Palette* pal = hicolor; + if (!pal) { + pal = palette; + } + if (initials==this) { + initials = NULL; + } + + sprBuffer->SetPalette( pal ); + size_t len = strlen( ( char* ) string ); + char* tmp = ( char* ) malloc( len + 1 ); + memcpy( tmp, ( char * ) string, len + 1 ); + while (len > 0 && (tmp[len - 1] == '\n' || tmp[len - 1] == '\r')) { + // ignore trailing newlines + tmp[len - 1] = 0; + len--; + } + + SetupString( tmp, rgn.w, NoColor ); + int ystep = 0; + if (Alignment & IE_FONT_SINGLE_LINE) { + + for (size_t i = 0; i < len; i++) { + if (tmp[i] == 0) continue; + int height = yPos[( unsigned char ) tmp[i] - 1]; + if (ystep < height) + ystep = height; + } + } else { + ystep = size[1].h; + } + if (!ystep) ystep = maxHeight; + int x = psx, y = ystep; + Video* video = core->GetVideoDriver(); + + if (Alignment & IE_FONT_ALIGN_CENTER) { + int w = CalcStringWidth( tmp, NoColor ); + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + int w = CalcStringWidth( tmp, NoColor ); + x = ( rgn.w - w ); + } + + if (Alignment & IE_FONT_ALIGN_MIDDLE) { + int h = 0; + for (size_t i = 0; i <= len; i++) { + if (tmp[i] == 0) + h++; + } + h = h * ystep; + y += ( rgn.h - h ) / 2; + } else if (Alignment & IE_FONT_ALIGN_BOTTOM) { + int h = 1; + for (size_t i = 0; i <= len; i++) { + if (tmp[i] == 0) + h++; + } + h = h * ystep; + y += ( rgn.h - h ); + } else if (Alignment & IE_FONT_ALIGN_TOP) { + y += 5; + } + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) tmp[i] ) == '[' && !NoColor) { + i++; + char tag[256]; + tag[0]=0; + for (int k = 0; k < 256 && iCreatePalette( c, black ); + sprBuffer->SetPalette( newPal ); + gamedata->FreePalette( newPal ); + continue; + } + if (stricmp( tag, "/color" ) == 0) { + sprBuffer->SetPalette( pal ); + continue; + } + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + continue; + } + continue; + } + + if (tmp[i] == 0) { + y += ystep; + x = psx; + int w = CalcStringWidth( &tmp[i + 1], NoColor ); + if (Alignment & IE_FONT_ALIGN_CENTER) { + x = ( rgn.w - w ) / 2; + } else if (Alignment & IE_FONT_ALIGN_RIGHT) { + x = ( rgn.w - w ); + } + continue; + } + unsigned char currChar = ( unsigned char ) tmp[i] - 1; + if (initials && capital) { + x = initials->PrintInitial( x, y, rgn, currChar ); + enablecap=false; + continue; + } + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], + anchor, &cliprgn ); + if (cursor && ( curpos == i )) + video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn ); + x += size[currChar].w; + } + if (cursor && ( curpos == len )) { + video->BlitSprite( cursor, x + rgn.x, y + rgn.y, anchor, &cliprgn ); + } + free( tmp ); +} + +int Font::PrintInitial(int x, int y, const Region &rgn, unsigned char currChar) const +{ + Video *video = core->GetVideoDriver(); + video->BlitSpriteRegion( sprBuffer, size[currChar], + x + rgn.x, y + rgn.y - yPos[currChar], true, &rgn ); + x += size[currChar].w; + return x; +} + +int Font::CalcStringWidth(const char* string, bool NoColor) const +{ + size_t ret = 0, len = strlen( string ); + for (size_t i = 0; i < len; i++) { + if (( ( unsigned char ) string[i] ) == '[' && !NoColor) { + i++; + if (i>=len) + break; + char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (string[i] == ']') { + tag[k] = 0; + break; + } + tag[k] = string[i++]; + } + continue; + } + ret += size[( unsigned char ) string[i] - 1].w; + } + return ( int ) ret; +} + +void Font::SetupString(char* string, unsigned int width, bool NoColor) const +{ + size_t len = strlen( string ); + unsigned int psx = PARAGRAPH_START_X; + int lastpos = 0; + unsigned int x = psx, wx = 0; + bool endword = false; + for (size_t pos = 0; pos < len; pos++) { + if (x + wx > width) { + if (!endword && ( x == psx )) + lastpos = ( int ) pos; + else + string[lastpos] = 0; + x = psx; + } + if (string[pos] == 0) { + continue; + } + endword = false; + if (string[pos] == '\r') + string[pos] = ' '; + if (string[pos] == '\n') { + string[pos] = 0; + x = psx; + wx = 0; + lastpos = ( int ) pos; + endword = true; + continue; + } + if (( ( unsigned char ) string[pos] ) == '[' && !NoColor) { + pos++; + if (pos>=len) + break; + char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (string[pos] == ']') { + tag[k] = 0; + break; + } + tag[k] = string[pos++]; + } + if (stricmp( "p", tag ) == 0) { + psx = x; + continue; + } + if (stricmp( "/p", tag ) == 0) { + psx = PARAGRAPH_START_X; + continue; + } + continue; + } + + if (string[pos] && string[pos] != ' ') { + string[pos] = ( unsigned char ) (string[pos] - FirstChar); + } + + wx += size[( unsigned char ) string[pos] - 1].w; + if (( string[pos] == ' ' ) || ( string[pos] == '-' )) { + x += wx; + wx = 0; + lastpos = ( int ) pos; + endword = true; + } + } +} + +Palette* Font::GetPalette() const +{ + assert(palette); + palette->IncRef(); + return palette; +} + +void Font::SetPalette(Palette* pal) +{ + if (palette) palette->Release(); + pal->IncRef(); + palette = pal; +} + +void Font::SetFirstChar( unsigned char first) +{ + FirstChar = first; +} diff --git a/project/jni/application/gemrb/src/core/Font.h b/project/jni/application/gemrb/src/core/Font.h new file mode 100644 index 000000000..240bdf436 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Font.h @@ -0,0 +1,111 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Font.h + * Declares Font, class for manipulating images serving as fonts + * @author The GemRB Project + */ + +#ifndef FONT_H +#define FONT_H + +#include "globals.h" +#include "exports.h" + +#include + +class Palette; + +struct StringList { + Sprite2D*** strings; + unsigned int* heights; + unsigned int* lengths; + int StringCount; + int starty; + int curx; + int cury; +}; + +#define IE_FONT_ALIGN_LEFT 0x00 +#define IE_FONT_ALIGN_CENTER 0x01 +#define IE_FONT_ALIGN_RIGHT 0x02 +#define IE_FONT_ALIGN_BOTTOM 0x04 +#define IE_FONT_ALIGN_TOP 0x10 //Single-Line and Multi-Line Text +#define IE_FONT_ALIGN_MIDDLE 0x20 //Only for single line Text +#define IE_FONT_SINGLE_LINE 0x40 + +/** + * @class Font + * Class for using and manipulating images serving as fonts + */ + +class GEM_EXPORT Font { +private: + int count; + Palette* palette; + Sprite2D* sprBuffer; + unsigned char FirstChar; + + short xPos[256]; + short yPos[256]; + + // For the temporary bitmap + unsigned char* tmpPixels; + unsigned int width, height; +public: + /** ResRef of the Font image */ + ieResRef ResRef; + int maxHeight; + Region size[256]; +public: + Font(int w, int h, Palette* palette); + ~Font(void); + void AddChar(unsigned char* spr, int w, int h, short xPos, short yPos); + /** Call this after adding all characters */ + void FinalizeSprite(bool cK, int index); + + void Print(Region cliprgn, Region rgn, const unsigned char* string, + Palette* color, unsigned char Alignment, bool anchor = false, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + void Print(Region rgn, const unsigned char* string, Palette* color, + unsigned char Alignment, bool anchor = false, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + void PrintFromLine(int startrow, Region rgn, const unsigned char* string, + Palette* color, unsigned char Alignment, + Font* initials = NULL, Sprite2D* cursor = NULL, + unsigned int curpos = 0, bool NoColor = false) const; + + Palette* GetPalette() const; + void SetPalette(Palette* pal); + /** Returns width of the string rendered in this font in pixels */ + int CalcStringWidth(const char* string, bool NoColor = false) const; + void SetupString(char* string, unsigned int width, bool NoColor = false) const; + /** Sets ASCII code of the first character in the font. + * (it allows remapping numeric fonts from \000 to '0') */ + void SetFirstChar(unsigned char first); + +private: + int PrintInitial(int x, int y, const Region &rgn, unsigned char currChar) const; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Button.cpp b/project/jni/application/gemrb/src/core/GUI/Button.cpp new file mode 100644 index 000000000..83930d2be --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Button.cpp @@ -0,0 +1,726 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Button.h" + +#include "GUI/GameControl.h" + +#include "defsounds.h" +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" + +Button::Button() +{ + Unpressed = Pressed = Selected = Disabled = NULL; + State = IE_GUI_BUTTON_UNPRESSED; + ResetEventHandler( ButtonOnPress ); + ResetEventHandler( ButtonOnDoublePress ); + ResetEventHandler( ButtonOnShiftPress ); + ResetEventHandler( ButtonOnRightPress ); + ResetEventHandler( ButtonOnDragDrop ); + ResetEventHandler( ButtonOnDrag ); + ResetEventHandler( MouseEnterButton ); + ResetEventHandler( MouseLeaveButton ); + ResetEventHandler( MouseOverButton ); + //Text = ( char * ) calloc( 64, sizeof(char) ); + Text = NULL; + hasText = false; + font = core->GetButtonFont(); + normal_palette = NULL; + disabled_palette = font->GetPalette()->Copy(); + for (int i = 0; i < 256; i++) { + disabled_palette->col[i].r = ( disabled_palette->col[i].r * 2 ) / 3; + disabled_palette->col[i].g = ( disabled_palette->col[i].g * 2 ) / 3; + disabled_palette->col[i].b = ( disabled_palette->col[i].b * 2 ) / 3; + } + Flags = IE_GUI_BUTTON_NORMAL; + ToggleState = false; + Picture = NULL; + Clipping = 1.0; + memset(&SourceRGB,0,sizeof(SourceRGB)); + memset(&DestRGB,0,sizeof(DestRGB)); + memset( borders, 0, sizeof( borders )); + starttime = 0; + Anchor.null(); +} +Button::~Button() +{ + Video* video = core->GetVideoDriver(); + video->FreeSprite( Disabled ); + video->FreeSprite( Selected ); + video->FreeSprite( Pressed ); + video->FreeSprite( Unpressed ); + video->FreeSprite( Picture ); + ClearPictureList(); + if (Text) { + free( Text ); + } + gamedata->FreePalette( normal_palette); + gamedata->FreePalette( disabled_palette); +} +/** Sets the 'type' Image of the Button to 'img'. +'type' may assume the following values: +- IE_GUI_BUTTON_UNPRESSED +- IE_GUI_BUTTON_PRESSED +- IE_GUI_BUTTON_SELECTED +- IE_GUI_BUTTON_DISABLED */ +void Button::SetImage(unsigned char type, Sprite2D* img) +{ + switch (type) { + case IE_GUI_BUTTON_UNPRESSED: + case IE_GUI_BUTTON_LOCKED: + case IE_GUI_BUTTON_LOCKED_PRESSED: + core->GetVideoDriver()->FreeSprite( Unpressed ); + Unpressed = img; + break; + + case IE_GUI_BUTTON_SECOND: + case IE_GUI_BUTTON_PRESSED: + core->GetVideoDriver()->FreeSprite( Pressed ); + Pressed = img; + break; + + case IE_GUI_BUTTON_SELECTED: + core->GetVideoDriver()->FreeSprite( Selected ); + Selected = img; + break; + + case IE_GUI_BUTTON_DISABLED: + case IE_GUI_BUTTON_THIRD: + core->GetVideoDriver()->FreeSprite( Disabled ); + Disabled = img; + break; + } + Changed = true; +} + +/** make SourceRGB go closer to DestRGB */ +void Button::CloseUpColor() +{ + if (!starttime) return; + //using the realtime timer, because i don't want to + //handle Game at this point + unsigned long newtime; + + Changed = true; + GetTime( newtime ); + if (newtimeFlags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535 || Width == 0) { + return; + } + + Video * video = core->GetVideoDriver(); + + // Button image + if (!( Flags & IE_GUI_BUTTON_NO_IMAGE )) { + Sprite2D* Image = NULL; + + switch (State) { + case IE_GUI_BUTTON_UNPRESSED: + case IE_GUI_BUTTON_LOCKED: + case IE_GUI_BUTTON_LOCKED_PRESSED: + Image = Unpressed; + break; + + case IE_GUI_BUTTON_SECOND: + case IE_GUI_BUTTON_PRESSED: + Image = Pressed; + if (! Image) + Image = Unpressed; + break; + + case IE_GUI_BUTTON_SELECTED: + Image = Selected; + if (! Image) + Image = Unpressed; + break; + + case IE_GUI_BUTTON_DISABLED: + case IE_GUI_BUTTON_THIRD: + Image = Disabled; + if (! Image) + Image = Unpressed; + break; + } + if (Image) { + // FIXME: maybe it's useless... + int xOffs = ( Width / 2 ) - ( Image->Width / 2 ); + int yOffs = ( Height / 2 ) - ( Image->Height / 2 ); + + video->BlitSprite( Image, x + XPos + xOffs, y + YPos + yOffs, true ); + } + } + + if (State == IE_GUI_BUTTON_PRESSED) { + //shift the writing/border a bit + x+= 2; + y+= 2; + } + + // Button picture + if (Picture && (Flags & IE_GUI_BUTTON_PICTURE) ) { + // Picture is drawn centered + int xOffs = ( Width / 2 ) - ( Picture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( Picture->Height / 2 ); + if (Flags & IE_GUI_BUTTON_HORIZONTAL) { + xOffs += x + XPos + Picture->XPos; + yOffs += y + YPos + Picture->YPos; + video->BlitSprite( Picture, xOffs, yOffs, true ); + Region r = Region( xOffs, yOffs + (int) (Picture->Height * Clipping), Picture->Width, (int) (Picture->Height*(1.0 - Clipping)) ); + video->DrawRect( r, SourceRGB, true ); + // do NOT uncomment this, you can't change Changed or invalidate things from + // the middle of Window::DrawWindow() -- it needs moving to somewhere else + //CloseUpColor(); + } + else { + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(Picture->Width * Clipping), Picture->Height ); + video->BlitSprite( Picture, x + XPos + xOffs + Picture->XPos, y + YPos + yOffs + Picture->YPos, true, &r ); + } + } + + // Composite pictures (paperdolls/description icons) + if (!PictureList.empty() && (Flags & IE_GUI_BUTTON_PICTURE) ) { + std::list::iterator iter = PictureList.begin(); + int xOffs = 0, yOffs = 0; + if (Flags & IE_GUI_BUTTON_CENTER_PICTURES) { + // Center the hotspots of all pictures + xOffs = Width/2; + yOffs = Height/2; + } else if (Flags & IE_GUI_BUTTON_BG1_PAPERDOLL) { + // Display as-is + xOffs = 0; + yOffs = 0; + } else { + // Center the first picture, and align the rest to that + xOffs = Width/2 - (*iter)->Width/2 + (*iter)->XPos; + yOffs = Height/2 - (*iter)->Height/2 + (*iter)->YPos; + } + + for (; iter != PictureList.end(); ++iter) { + video->BlitSprite( *iter, x + XPos + xOffs, y + YPos + yOffs, true ); + } + } + + // Button picture + if (AnimPicture) { + int xOffs = ( Width / 2 ) - ( AnimPicture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( AnimPicture->Height / 2 ); + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(AnimPicture->Width * Clipping), AnimPicture->Height ); + + if (Flags & IE_GUI_BUTTON_CENTER_PICTURES) { + video->BlitSprite( AnimPicture, x + XPos + xOffs + AnimPicture->XPos, y + YPos + yOffs + AnimPicture->YPos, true, &r ); + } else { + video->BlitSprite( AnimPicture, x + XPos + xOffs, y + YPos + yOffs, true, &r ); + } + } + + // Button label + if (hasText && ! ( Flags & IE_GUI_BUTTON_NO_TEXT )) { + Palette* ppoi = normal_palette; + int align = 0; + + if (State == IE_GUI_BUTTON_DISABLED) + ppoi = disabled_palette; + // FIXME: hopefully there's no button which sinks when selected + // AND has text label + //else if (State == IE_GUI_BUTTON_PRESSED || State == IE_GUI_BUTTON_SELECTED) { + + if (Flags & IE_GUI_BUTTON_ALIGN_LEFT) + align |= IE_FONT_ALIGN_LEFT; + else if (Flags & IE_GUI_BUTTON_ALIGN_RIGHT) + align |= IE_FONT_ALIGN_RIGHT; + else + align |= IE_FONT_ALIGN_CENTER; + + if (Flags & IE_GUI_BUTTON_ALIGN_TOP) + align |= IE_FONT_ALIGN_TOP; + else if (Flags & IE_GUI_BUTTON_ALIGN_BOTTOM) + align |= IE_FONT_ALIGN_BOTTOM; + else + align |= IE_FONT_ALIGN_MIDDLE; + + if (! (Flags & IE_GUI_BUTTON_MULTILINE)) { + align |= IE_FONT_SINGLE_LINE; + } + font->Print( Region( x + XPos, y + YPos, Width - 2, Height - 2), + ( unsigned char * ) Text, ppoi, + (ieByte) align, true ); + } + + if (! (Flags&IE_GUI_BUTTON_NO_IMAGE)) { + for (int i = 0; i < MAX_NUM_BORDERS; i++) { + ButtonBorder *fr = &borders[i]; + if (! fr->enabled) continue; + + Region r = Region( x + XPos + fr->dx1, y + YPos + fr->dy1, Width - (fr->dx1 + fr->dx2 + 1), Height - (fr->dy1 + fr->dy2 + 1) ); + video->DrawRect( r, fr->color, fr->filled ); + } + } +} +/** Sets the Button State */ +void Button::SetState(unsigned char state) +{ + if (state > IE_GUI_BUTTON_LOCKED_PRESSED) {// If wrong value inserted + return; + } + if (State != state) { + Changed = true; + State = state; + } +} +void Button::SetBorder(int index, int dx1, int dy1, int dx2, int dy2, const Color &color, bool enabled, bool filled) +{ + if (index >= MAX_NUM_BORDERS) + return; + + ButtonBorder *fr = &borders[index]; + fr->dx1 = dx1; + fr->dy1 = dy1; + fr->dx2 = dx2; + fr->dy2 = dy2; + fr->color = color; + fr->enabled = enabled; + fr->filled = filled; + Changed = true; +} + +void Button::EnableBorder(int index, bool enabled) +{ + if (index >= MAX_NUM_BORDERS) + return; + + if (borders[index].enabled != enabled) { + borders[index].enabled = enabled; + Changed = true; + } +} + +void Button::SetFont(Font* newfont) +{ + font = newfont; +} +/** Handling The default button (enter) */ +void Button::OnSpecialKeyPress(unsigned char Key) +{ + if (State != IE_GUI_BUTTON_DISABLED && State != IE_GUI_BUTTON_LOCKED) { + if (Key == GEM_RETURN) { + if (Flags & IE_GUI_BUTTON_DEFAULT ) { + RunEventHandler( ButtonOnPress ); + return; + } + } + else if (Key == GEM_ESCAPE) { + if (Flags & IE_GUI_BUTTON_CANCEL ) { + RunEventHandler( ButtonOnPress ); + return; + } + } + } + Control::OnSpecialKeyPress(Key); +} + +/** Mouse Button Down */ +void Button::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + Control::OnMouseDown(x,y,Button,Mod); + return; + } + + if (core->GetDraggedItem () && !ButtonOnDragDrop) { + Control::OnMouseDown(x,y,Button,Mod); + return; + } + + ScrollBar* scrlbr = (ScrollBar*) sb; + if (!scrlbr) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl->ControlType == IE_GUI_SCROLLBAR)) { + scrlbr = (ScrollBar *) ctrl; + } + } + + //Button == 1 means Left Mouse Button + switch(Button&GEM_MB_NORMAL) { + case GEM_MB_ACTION: + // We use absolute screen position here, so drag_start + // remains valid even after window/control is moved + drag_start.x = Owner->XPos + XPos + x; + drag_start.y = Owner->YPos + YPos + y; + + if (State == IE_GUI_BUTTON_LOCKED) { + SetState( IE_GUI_BUTTON_LOCKED_PRESSED ); + return; + } + SetState( IE_GUI_BUTTON_PRESSED ); + if (Flags & IE_GUI_BUTTON_SOUND) { + core->PlaySound( DS_BUTTON_PRESSED ); + } + if ((Button & GEM_MB_DOUBLECLICK) && ButtonOnDoublePress) { + RunEventHandler( ButtonOnDoublePress ); + printMessage("Button","Doubleclick detected\n",GREEN); + } + break; + case GEM_MB_SCRLUP: + if (scrlbr) { + scrlbr->ScrollUp(); + core->RedrawAll(); + } + break; + case GEM_MB_SCRLDOWN: + if (scrlbr) { + scrlbr->ScrollDown(); + core->RedrawAll(); + } + break; + } +} +/** Mouse Button Up */ +void Button::OnMouseUp(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + //what was just dropped? + int dragtype = 0; + if (core->GetDraggedItem ()) dragtype=1; + if (core->GetDraggedPortrait ()) dragtype=2; + + //if something was dropped, but it isn't handled here: it didn't happen + if (dragtype && !ButtonOnDragDrop) + return; + + switch (State) { + case IE_GUI_BUTTON_PRESSED: + if (ToggleState) { + SetState( IE_GUI_BUTTON_SELECTED ); + } else { + SetState( IE_GUI_BUTTON_UNPRESSED ); + } + break; + case IE_GUI_BUTTON_LOCKED_PRESSED: + SetState( IE_GUI_BUTTON_LOCKED ); + break; + } + + //in case of dragged/dropped portraits, allow the event to happen even + //when we are out of bound + if (dragtype!=2) { + if (( x >= Width ) || ( y >= Height )) { + return; + } + } + if (Flags & IE_GUI_BUTTON_CHECKBOX) { + //checkbox + ToggleState = !ToggleState; + if (ToggleState) + SetState( IE_GUI_BUTTON_SELECTED ); + else + SetState( IE_GUI_BUTTON_UNPRESSED ); + if (VarName[0] != 0) { + ieDword tmp = 0; + core->GetDictionary()->Lookup( VarName, tmp ); + tmp ^= Value; + core->GetDictionary()->SetAt( VarName, tmp ); + Owner->RedrawControls( VarName, tmp ); + } + } else { + if (Flags & IE_GUI_BUTTON_RADIOBUTTON) { + //radio button + ToggleState = true; + SetState( IE_GUI_BUTTON_SELECTED ); + } + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + Owner->RedrawControls( VarName, Value ); + } + } + + switch (dragtype) { + case 1: + RunEventHandler( ButtonOnDragDrop ); + return; + case 2: + RunEventHandler( ButtonOnDragDropPortrait ); + return; + } + + if ((Button&GEM_MB_NORMAL) == GEM_MB_ACTION) { + if ((Mod & GEM_MOD_SHIFT) && ButtonOnShiftPress) + RunEventHandler( ButtonOnShiftPress ); + else + RunEventHandler( ButtonOnPress ); + } else { + if (Button == GEM_MB_MENU && ButtonOnRightPress) + RunEventHandler( ButtonOnRightPress ); + } +} + +void Button::OnMouseOver(unsigned short x, unsigned short y) +{ + Owner->Cursor = IE_CURSOR_NORMAL; + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if ( RunEventHandler( MouseOverButton )) { + //event handler destructed this object + return; + } + + //well, no more flags for buttons, and the portraits we can perform action on + //are in fact 'draggable multiline pictures' (with image) + if ((Flags & IE_GUI_BUTTON_DISABLED_P) == IE_GUI_BUTTON_PORTRAIT) { + GameControl *gc = core->GetGameControl(); + if (gc) { + Owner->Cursor = gc->GetDefaultCursor(); + } + } + + if (State == IE_GUI_BUTTON_LOCKED) { + return; + } + + //portrait buttons are draggable and locked + if ((Flags & IE_GUI_BUTTON_DRAGGABLE) && + (State == IE_GUI_BUTTON_PRESSED || State ==IE_GUI_BUTTON_LOCKED_PRESSED)) { + // We use absolute screen position here, so drag_start + // remains valid even after window/control is moved + int dx = Owner->XPos + XPos + x - drag_start.x; + int dy = Owner->YPos + YPos + y - drag_start.y; + core->GetDictionary()->SetAt( "DragX", dx ); + core->GetDictionary()->SetAt( "DragY", dy ); + drag_start.x = (ieWord) (drag_start.x + dx); + drag_start.y = (ieWord) (drag_start.y + dy); + RunEventHandler( ButtonOnDrag ); + } +} + +void Button::OnMouseEnter(unsigned short /*x*/, unsigned short /*y*/) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if (MouseEnterButton !=0 && VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + + RunEventHandler( MouseEnterButton ); +} + +void Button::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + + if (MouseLeaveButton !=0 && VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + + RunEventHandler( MouseLeaveButton ); +} + + +/** Sets the Text of the current control */ +int Button::SetText(const char* string, int /*pos*/) +{ + free(Text); + Text = NULL; + if (string == NULL) { + hasText = false; + } else if (string[0] == 0) { + hasText = false; + } else { + Text = strndup( string, 255 ); + if (Flags&IE_GUI_BUTTON_LOWERCASE) + strlwr( Text ); + else if (Flags&IE_GUI_BUTTON_CAPS) + strupr( Text ); + hasText = true; + } + Changed = true; + return 0; +} + +/** Set Event Handler */ +bool Button::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_BUTTON_ON_PRESS: + ButtonOnPress = handler; + break; + case IE_GUI_MOUSE_OVER_BUTTON: + MouseOverButton = handler; + break; + case IE_GUI_MOUSE_ENTER_BUTTON: + MouseEnterButton = handler; + break; + case IE_GUI_MOUSE_LEAVE_BUTTON: + MouseLeaveButton = handler; + break; + case IE_GUI_BUTTON_ON_SHIFT_PRESS: + ButtonOnShiftPress = handler; + break; + case IE_GUI_BUTTON_ON_RIGHT_PRESS: + ButtonOnRightPress = handler; + break; + case IE_GUI_BUTTON_ON_DRAG_DROP: + ButtonOnDragDrop = handler; + break; + case IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT: + ButtonOnDragDropPortrait = handler; + break; + case IE_GUI_BUTTON_ON_DRAG: + ButtonOnDrag = handler; + break; + case IE_GUI_BUTTON_ON_DOUBLE_PRESS: + ButtonOnDoublePress = handler; + break; + default: + return false; + } + + return true; +} + +/** Redraws a button from a given radio button group */ +void Button::RedrawButton(const char* VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + if (State == IE_GUI_BUTTON_DISABLED) { + return; + } + if (Flags & IE_GUI_BUTTON_RADIOBUTTON) { + ToggleState = ( Sum == Value ); + } //radio button, exact value + else if (Flags & IE_GUI_BUTTON_CHECKBOX) { + ToggleState = !!( Sum & Value ); + } //checkbox, bitvalue + else { + return; + } //other buttons, nothing to redraw + if (ToggleState) { + SetState(IE_GUI_BUTTON_SELECTED); + } else { + SetState(IE_GUI_BUTTON_UNPRESSED); + } +} +/** Sets the Picture */ +void Button::SetPicture(Sprite2D* newpic) +{ + core->GetVideoDriver()->FreeSprite( Picture ); + ClearPictureList(); + Picture = newpic; + Changed = true; + Flags |= IE_GUI_BUTTON_PICTURE; + Owner->Invalidate(); +} + +/** Clears the list of Pictures */ +void Button::ClearPictureList() +{ + Video* video = core->GetVideoDriver(); + for (std::list::iterator iter = PictureList.begin(); + iter != PictureList.end(); ++iter) + video->FreeSprite( *iter ); + PictureList.clear(); + Changed = true; + Owner->Invalidate(); +} + +/** Add picture to the end of the list of Pictures */ +void Button::StackPicture(Sprite2D* Picture) +{ + PictureList.push_back(Picture); + Changed = true; + Flags |= IE_GUI_BUTTON_PICTURE; + Owner->Invalidate(); +} + +bool Button::IsPixelTransparent(unsigned short x, unsigned short y) +{ + // some buttons have hollow Image frame filled w/ Picture + // some buttons in BG2 are text only (if BAM == 'GUICTRL') + if (Picture || PictureList.size() || ! Unpressed) return false; + return Unpressed->IsPixelTransparent(x, y); +} + +// Set palette used for drawing button label in normal state +void Button::SetTextColor(const Color &fore, const Color &back) +{ + gamedata->FreePalette( normal_palette ); + normal_palette = core->CreatePalette( fore, back ); + Changed = true; +} + +void Button::SetHorizontalOverlay(double clip, const Color &src, const Color &dest) +{ + if ((Clipping>clip) || !(Flags&IE_GUI_BUTTON_HORIZONTAL) ) { + Flags |= IE_GUI_BUTTON_HORIZONTAL; + SourceRGB=src; + DestRGB=dest; + GetTime( starttime ); + starttime += 40; + } + Clipping = clip; + Changed = true; +} + +void Button::SetAnchor(ieWord x, ieWord y) +{ + Anchor = Point(x,y); +} diff --git a/project/jni/application/gemrb/src/core/GUI/Button.h b/project/jni/application/gemrb/src/core/GUI/Button.h new file mode 100644 index 000000000..1ae3d9126 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Button.h @@ -0,0 +1,219 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Button.h + * Declares Button widget, for displaying buttons in the GUI + * @author GemRB Development Team + */ + + +#ifndef BUTTON_H +#define BUTTON_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Font.h" +#include "Sprite2D.h" + +#include + +class Palette; + +// NOTE: keep these synchronized with GUIDefines.py!!! +#define IE_GUI_BUTTON_UNPRESSED 0 +#define IE_GUI_BUTTON_PRESSED 1 +#define IE_GUI_BUTTON_SELECTED 2 +#define IE_GUI_BUTTON_DISABLED 3 +// Like DISABLED, but processes MouseOver events and draws UNPRESSED bitmap +#define IE_GUI_BUTTON_LOCKED 4 +// Draws the disabled bitmap, but otherwise works like unpressed +#define IE_GUI_BUTTON_THIRD 5 +#define IE_GUI_BUTTON_SECOND 6 +#define IE_GUI_BUTTON_LOCKED_PRESSED 7 //all the same as LOCKED + +#define IE_GUI_BUTTON_NO_IMAGE 0x00000001 // don't draw image (BAM) +#define IE_GUI_BUTTON_PICTURE 0x00000002 // draw picture (BMP, MOS, ...) +#define IE_GUI_BUTTON_SOUND 0x00000004 +#define IE_GUI_BUTTON_ALT_SOUND 0x00000008 +#define IE_GUI_BUTTON_CHECKBOX 0x00000010 // or radio button +#define IE_GUI_BUTTON_RADIOBUTTON 0x00000020 // sticks in a state +#define IE_GUI_BUTTON_DEFAULT 0x00000040 // enter key triggers it +#define IE_GUI_BUTTON_ANIMATED 0x00000080 + +//these bits are hardcoded in the .chu structure +#define IE_GUI_BUTTON_ALIGN_LEFT 0x00000100 +#define IE_GUI_BUTTON_ALIGN_RIGHT 0x00000200 +#define IE_GUI_BUTTON_ALIGN_TOP 0x00000400 +#define IE_GUI_BUTTON_ALIGN_BOTTOM 0x00000800 +#define IE_GUI_BUTTON_ANCHOR 0x00001000 //not implemented yet +#define IE_GUI_BUTTON_LOWERCASE 0x00002000 +#define IE_GUI_BUTTON_MULTILINE 0x00004000 // don't set the single line flag +//end of hardcoded part +#define IE_GUI_BUTTON_DRAGGABLE 0x00008000 +#define IE_GUI_BUTTON_NO_TEXT 0x00010000 // don't draw button label +#define IE_GUI_BUTTON_PLAYRANDOM 0x00020000 +#define IE_GUI_BUTTON_PLAYONCE 0x00040000 + +#define IE_GUI_BUTTON_CENTER_PICTURES 0x00080000 // center button's PictureList +#define IE_GUI_BUTTON_BG1_PAPERDOLL 0x00100000 // BG1-style paperdoll PictureList +#define IE_GUI_BUTTON_HORIZONTAL 0x00200000 // horizontal clipping of overlay +#define IE_GUI_BUTTON_CANCEL 0x00400000 // cancel key triggers it +#define IE_GUI_BUTTON_CAPS 0x00800000 // convert text to uppercase + +//composite button flags +#define IE_GUI_BUTTON_NORMAL 0x00000004 // default button, doesn't stick +#define IE_GUI_BUTTON_PORTRAIT 0x0000c002 // portrait +#define IE_GUI_BUTTON_DISABLED_P 0x0000c003 // disabled portrait + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_BUTTON_ON_PRESS 0x00000000 +#define IE_GUI_MOUSE_OVER_BUTTON 0x00000001 +#define IE_GUI_MOUSE_ENTER_BUTTON 0x00000002 +#define IE_GUI_MOUSE_LEAVE_BUTTON 0x00000003 +#define IE_GUI_BUTTON_ON_SHIFT_PRESS 0x00000004 +#define IE_GUI_BUTTON_ON_RIGHT_PRESS 0x00000005 +#define IE_GUI_BUTTON_ON_DRAG_DROP 0x00000006 +#define IE_GUI_BUTTON_ON_DRAG_DROP_PORTRAIT 0x00000007 +#define IE_GUI_BUTTON_ON_DRAG 0x00000008 +#define IE_GUI_BUTTON_ON_DOUBLE_PRESS 0x00000009 + +/** Border/frame settings for a button */ +struct ButtonBorder { + int dx1; + int dy1; + int dx2; + int dy2; + Color color; + bool filled; + bool enabled; +}; + +#define MAX_NUM_BORDERS 3 + + +/** + * @class Button + * Button widget, used mainly for buttons, but also for PixMaps (static images) + * or for Toggle Buttons. + */ + +class GEM_EXPORT Button : public Control { +public: + Button(); + ~Button(); + /** Sets the 'type' Image of the Button to 'img'. + 'type' may assume the following values: + - IE_GUI_BUTTON_UNPRESSED + - IE_GUI_BUTTON_PRESSED + - IE_GUI_BUTTON_SELECTED + - IE_GUI_BUTTON_DISABLED */ + void SetImage(unsigned char type, Sprite2D* img); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Button State */ + void SetState(unsigned char state); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets the Picture */ + void SetPicture(Sprite2D* Picture); + /** Clears the list of Pictures */ + void ClearPictureList(); + /** Add picture to the end of the list of Pictures */ + void StackPicture(Sprite2D* Picture); + /** Sets border/frame parameters */ + void SetBorder(int index, int dx1, int dy1, int dx2, int dy2, const Color &color, bool enabled = false, bool filled = false); + /** Sets horizontal overlay, used in portrait hp overlay */ + void SetHorizontalOverlay(double clip, const Color &src, const Color &dest); + /** Sets font used for drawing button label */ + void SetFont(Font* newfont); + /** Enables or disables specified border/frame */ + void EnableBorder(int index, bool enabled); +public: // Public Events + /** Mouse Enter */ + void OnMouseEnter(unsigned short x, unsigned short y); + /** Mouse Leave */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Over */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** A special key has been pressed */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** Button Pressed Event Script Function Name */ + EventHandler ButtonOnPress; + EventHandler ButtonOnShiftPress; + EventHandler ButtonOnRightPress; + EventHandler ButtonOnDoublePress; + EventHandler ButtonOnDragDrop; + EventHandler ButtonOnDragDropPortrait; + EventHandler ButtonOnDrag; + EventHandler MouseEnterButton; + EventHandler MouseLeaveButton; + EventHandler MouseOverButton; + /** Refreshes the button from a radio group */ + void RedrawButton(const char* VariableName, unsigned int Sum); + /** Set palette used for drawing button label in normal state. */ + void SetTextColor(const Color &fore, const Color &back); + /** Sets percent (0-1.0) of width for clipping picture */ + void SetPictureClipping(double clip) { Clipping = clip; } + void SetAnchor(ieWord x, ieWord y); +private: // Private attributes + char* Text; + bool hasText; + Font* font; + bool ToggleState; + Palette* normal_palette; + Palette* disabled_palette; + /** Button Unpressed Image */ + Sprite2D* Unpressed; + /** Button Pressed Image */ + Sprite2D* Pressed; + /** Button Selected Image */ + Sprite2D* Selected; + /** Button Disabled Image */ + Sprite2D* Disabled; + /** Pictures to Apply when the hasPicture flag is set */ + Sprite2D* Picture; + /** If non-empty, list of Pictures to draw when hasPicture is set */ + std::list PictureList; + /** The current state of the Button */ + unsigned char State; + double Clipping; + Point drag_start; + /** HP Bar over portraits */ + unsigned long starttime; + Color SourceRGB, DestRGB; + Point Anchor; + /** frame settings */ + ButtonBorder borders[MAX_NUM_BORDERS]; + bool IsPixelTransparent (unsigned short x, unsigned short y); + void CloseUpColor(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Console.cpp b/project/jni/application/gemrb/src/core/GUI/Console.cpp new file mode 100644 index 000000000..f9e60fa93 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Console.cpp @@ -0,0 +1,221 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Console.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "ScriptEngine.h" +#include "Video.h" + +Console::Console(void) +{ + Cursor = NULL; + Back = NULL; + max = 128; + Buffer = ( unsigned char * ) malloc( max ); + Buffer[0] = 0; + for(size_t i=0;iGetVideoDriver(); + + gamedata->FreePalette( palette ); + video->FreeSprite( Cursor ); +} + +/** Draws the Console on the Output Display */ +void Console::Draw(unsigned short x, unsigned short y) +{ + if (Back) { + core->GetVideoDriver()->BlitSprite( Back, 0, y, true ); + } + Color black = { + 0x00, 0x00, 0x00, 0xff + }; + Region r( x + XPos, y + YPos, Width, Height ); + core->GetVideoDriver()->DrawRect( r, black ); + font->Print( r, Buffer, palette, + IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_MIDDLE, true, NULL, + Cursor, CurPos, true ); +} +/** Set Font */ +void Console::SetFont(Font* f) +{ + if (f != NULL) { + font = f; + } +} +/** Set Cursor */ +void Console::SetCursor(Sprite2D* cur) +{ + if (cur != NULL) { + Cursor = cur; + } +} +/** Set BackGround */ +void Console::SetBackGround(Sprite2D* back) +{ + //if 'back' is NULL then no BackGround will be drawn + Back = back; +} +/** Sets the Text of the current control */ +int Console::SetText(const char* string, int /*pos*/) +{ + strncpy( ( char * ) Buffer, string, max ); + return 0; +} +/** Key Press Event */ +void Console::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Key >= 0x20) { + size_t len = strlen( ( char* ) Buffer ); + if (len + 1 < max) { + for (size_t i = len; i > CurPos; i--) { + Buffer[i] = Buffer[i - 1]; + } + Buffer[CurPos++] = Key; + Buffer[len + 1] = 0; + } + } +} +/** Special Key Press */ +void Console::OnSpecialKeyPress(unsigned char Key) +{ + size_t len; + + switch (Key) { + case GEM_BACKSP: + if (CurPos != 0) { + size_t len = strlen( ( const char * ) Buffer ); + for (size_t i = CurPos; i < len; i++) { + Buffer[i - 1] = Buffer[i]; + } + Buffer[len - 1] = 0; + CurPos--; + } + break; + case GEM_HOME: + CurPos = 0; + break; + case GEM_END: + CurPos = (unsigned short) strlen( (const char * ) Buffer); + break; + case GEM_UP: + HistoryBack(); + break; + case GEM_DOWN: + HistoryForward(); + break; + case GEM_LEFT: + if (CurPos > 0) + CurPos--; + break; + case GEM_RIGHT: + len = strlen( ( const char * ) Buffer ); + if (CurPos < len) { + CurPos++; + } + break; + case GEM_DELETE: + len = strlen( ( const char * ) Buffer ); + if (CurPos < len) { + for (size_t i = CurPos; i < len; i++) { + Buffer[i] = Buffer[i + 1]; + } + } + break; + case GEM_RETURN: + core->GetGUIScriptEngine()->ExecString( ( char* ) Buffer ); + HistoryAdd(false); + Buffer[0] = 0; + CurPos = 0; + HistPos = 0; + Changed = true; + break; + } +} + +//ctrl-up +void Console::HistoryBack() +{ + HistoryAdd(false); + if (HistPos < HistMax-1 && Buffer[0]) { + HistPos++; + } + memcpy(Buffer, History[HistPos], max); + CurPos = (unsigned short) strlen ((const char *) Buffer); +} + +//ctrl-down +void Console::HistoryForward() +{ + HistoryAdd(false); + if (HistPos == 0) { + Buffer[0]=0; + CurPos=0; + return; + } + HistPos--; + memcpy(Buffer, History[HistPos], max); + CurPos = (unsigned short) strlen ((const char *) Buffer); +} + +void Console::HistoryAdd(bool force) +{ + int i; + + if (!force && !Buffer[0]) + return; + for (i=0;i0; i--) { + memcpy(History[i], History[i-1], max); + } + } + memcpy(History[0], Buffer, max); + if (HistMax +#include + +Control::Control() +{ + hasFocus = false; + Changed = true; + InHandler = false; + VarName[0] = 0; + Value = 0; + Flags = 0; + Tooltip = NULL; + Owner = NULL; + XPos = 0; + YPos = 0; + + sb = NULL; + animation = NULL; + AnimPicture = NULL; + ControlType = IE_GUI_INVALID; +} + +Control::~Control() +{ + if (InHandler) { + printMessage("Control","Destroying control inside event handler, crash may occur!\n", LIGHT_RED); + } + core->DisplayTooltip( 0, 0, NULL ); + free (Tooltip); + + delete animation; + + core->GetVideoDriver()->FreeSprite(AnimPicture); +} + +/** Sets the Tooltip text of the current control */ +int Control::SetTooltip(const char* string) +{ + free(Tooltip); + + if ((string == NULL) || (string[0] == 0)) { + Tooltip = NULL; + } else { + Tooltip = strdup (string); + } + Changed = true; + return 0; +} + +/** Sets the tooltip to be displayed on the screen now */ +void Control::DisplayTooltip() +{ + if (Tooltip) + core->DisplayTooltip( Owner->XPos + XPos + Width / 2, Owner->YPos + YPos + Height / 2, this ); + else + core->DisplayTooltip( 0, 0, NULL ); +} + +void Control::ResetEventHandler(EventHandler handler) +{ + handler = NULL; +} + +int Control::RunEventHandler(EventHandler handler) +{ + if (InHandler) { + printMessage("Control","Nested event handlers are not supported!\n", YELLOW); + return -1; + } + if (handler) { + Window *wnd = Owner; + if (!wnd) { + return -1; + } + unsigned short WID = wnd->WindowID; + unsigned short ID = (unsigned short) ControlID; + InHandler = true; + handler->call(); + if (!core->IsValidWindow(WID,wnd) ) { + printMessage ("Control","Owner window destructed!\n", LIGHT_RED); + return -1; + } + if (!wnd->IsValidControl(ID,this) ) { + printMessage ("Control","Control destructed!\n", LIGHT_RED); + return -1; + } + InHandler = false; + } + return 0; +} + +/** Key Press Event */ +void Control::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + //printf("OnKeyPress: CtrlID = 0x%08X, Key = %c (0x%02hX)\n", (unsigned int) ControlID, Key, Key); +#ifdef ANDROID // mapping volume control to volume control keys on device, these keys must be set up in AndroidAppSettings.cfg + switch(Key) + { + case 'o': // volume down + case 'p': // volume up + int Ambients, Movie, Music, SFX, Voices; + core->GetDictionary()->Lookup( "Volume Ambients", (ieDword&)Ambients ); + core->GetDictionary()->Lookup( "Volume Movie", (ieDword&)Movie ); + core->GetDictionary()->Lookup( "Volume Music", (ieDword&)Music ); + core->GetDictionary()->Lookup( "Volume SFX", (ieDword&)SFX ); + core->GetDictionary()->Lookup( "Volume Voices", (ieDword&)Voices ); + if(Key=='o') + { + if(Ambients>0) Ambients-=10; if(Ambients<0) Ambients=0; + if(Movie>0) Movie-=10; if(Movie<0) Movie=0; + if(Music>0) Music-=10; if(Music<0) Music=0; + if(SFX>0) SFX-=10; if(SFX<0) SFX=0; + if(Voices>0) Voices-=10; if(Voices<0) Voices=0; + } + else + { + if(Ambients<100) Ambients+=10; if(Ambients>100) Ambients=100; + if(Movie<100) Movie+=10; if(Movie>100) Movie=100; + if(Music<100) Music+=10; if(Music>100) Music=100; + if(SFX<100) SFX+=10; if(SFX>100) SFX=100; + if(Voices<100) Voices+=10; if(Voices>100) Voices=100; + } + core->GetDictionary()->SetAt( "Volume Ambients", (ieDword)Ambients ); + core->GetDictionary()->SetAt( "Volume Movie", Movie ); + core->GetDictionary()->SetAt( "Volume Music", Music ); + core->GetDictionary()->SetAt( "Volume SFX", SFX ); + core->GetDictionary()->SetAt( "Volume Voices", Voices ); + core->GetAudioDrv()->UpdateVolume(); + break; + } +#else +(void)Key; // unused, fool the compiler +#endif +} + +/** Key Release Event */ +void Control::OnKeyRelease(unsigned char /*Key*/, unsigned short /*Mod*/) +{ + //printf( "OnKeyRelease: CtrlID = 0x%08X, Key = %c (0x%02hX)\n", (unsigned int) ControlID, Key, Key ); +} + +/** Mouse Enter Event */ +void Control::OnMouseEnter(unsigned short /*x*/, unsigned short /*y*/) +{ +// printf("OnMouseEnter: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Leave Event */ +void Control::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ +// printf("OnMouseLeave: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Over Event */ +void Control::OnMouseOver(unsigned short /*x*/, unsigned short /*y*/) +{ + //printf("OnMouseOver: CtrlID = 0x%08X, x = %hd, y = %hd\n", (unsigned int) ControlID, x, y); +} + +/** Mouse Button Down */ +void Control::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod) +{ + if (Button == GEM_MB_SCRLUP || Button == GEM_MB_SCRLDOWN) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl!=this)) { + ctrl->OnMouseDown(x,y,Button,Mod); + } + } +} + +/** Mouse Button Up */ +void Control::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + //printf("OnMouseUp: CtrlID = 0x%08X, x = %hd, y = %hd, Button = %d, Mos = %hd\n", (unsigned int) ControlID, x, y, Button, Mod); +} + +/** Special Key Press */ +void Control::OnSpecialKeyPress(unsigned char Key) +{ + if (Key == GEM_UP || Key == GEM_DOWN) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl!=this)) { + ctrl->OnSpecialKeyPress(Key); + } + } +} + +/** Sets the Display Flags */ +int Control::SetFlags(int arg_flags, int opcode) +{ + if ((arg_flags >>24) != ControlType) + return -2; + switch (opcode) { + case BM_SET: + Flags = arg_flags; //set + break; + case BM_AND: + Flags &= arg_flags; + break; + case BM_OR: + Flags |= arg_flags; //turn on + break; + case BM_XOR: + Flags ^= arg_flags; + break; + case BM_NAND: + Flags &= ~arg_flags;//turn off + break; + default: + return -1; + } + Changed = true; + Owner->Invalidate(); + return 0; +} + +void Control::SetAnimPicture(Sprite2D* newpic) +{ + core->GetVideoDriver()->FreeSprite(AnimPicture); + AnimPicture = newpic; + //apparently this is needed too, so the artifacts are not visible + if (Owner->Visible==WINDOW_VISIBLE) { + Changed = true; + Owner->InvalidateForControl(this); + } +} + +/** Sets the Scroll Bar Pointer. If 'ptr' is NULL no Scroll Bar will be linked + to this Control. */ +int Control::SetScrollBar(Control* ptr) +{ + if (ptr && (ptr->ControlType!=IE_GUI_SCROLLBAR)) { + ptr = NULL; + printMessage("Control","Attached control is not a ScrollBar!\n",YELLOW); + return -1; + } + sb = ptr; + Changed = true; + if (ptr) return 1; + return 0; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Control.h b/project/jni/application/gemrb/src/core/GUI/Control.h new file mode 100644 index 000000000..50a5ad2d4 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Control.h @@ -0,0 +1,149 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Control.h + * Declares Control, root class for all widgets except of windows + */ + +#ifndef CONTROL_H +#define CONTROL_H + +#define IE_GUI_BUTTON 0 +#define IE_GUI_PROGRESSBAR 1 //gemrb extension +#define IE_GUI_SLIDER 2 +#define IE_GUI_EDIT 3 +#define IE_GUI_TEXTAREA 5 +#define IE_GUI_LABEL 6 +#define IE_GUI_SCROLLBAR 7 +#define IE_GUI_WORLDMAP 8 // gemrb extension +#define IE_GUI_MAP 9 // gemrb extension +#define IE_GUI_GAMECONTROL 128 +#define IE_GUI_INVALID 255 + +#define IE_GUI_CONTROL_FOCUSED 0x80 + +//this is in the control ID +#define IGNORE_CONTROL 0x10000000 + +#include "RGBAColor.h" +#include "exports.h" +#include "ie_types.h" +#include "win32def.h" + +#include "Callback.h" + +class ControlAnimation; +class Sprite2D; +class Window; + +/** + * @class Control + * Basic Control Object, also called widget or GUI element. Parent class for Labels, Buttons, etc. + * Every GUI element except of a Window is a descendant of this class. + */ + +class GEM_EXPORT Control { +public: + Control(); + virtual ~Control(); + /** Draws the Control on the Output Display */ + virtual void Draw(unsigned short x, unsigned short y) = 0; + /** Sets the Text of the current control */ + virtual int SetText(const char* string, int pos = 0) = 0; + /** Sets the Tooltip text of the current control */ + int SetTooltip(const char* string); + /** Displays the tooltip text, Worldmap handles this differently */ + virtual void DisplayTooltip(); + /** Variable length is 40-1 (zero terminator) */ + char VarName[MAX_VARIABLE_LENGTH]; + /** the value of the control to add to the variable */ + ieDword Value; + /** various flags based on the control type */ + ieDword Flags; + ControlAnimation* animation; + Sprite2D* AnimPicture; + +public: // Public attributes + /** Defines the Control ID Number used for GUI Scripting */ + ieDword ControlID; + /** X position of control relative to containing window */ + ieWord XPos; + /** Y position of control relative to containing window */ + ieWord YPos; + /** Width of control */ + ieWord Width; + /** Height of control */ + ieWord Height; + /** Type of control */ + ieByte ControlType; + /** Text to display as a tooltip when the mouse cursor hovers + * for some time over the control */ + char* Tooltip; + /** Focused Control */ + bool hasFocus; + /** If true, control is redrawn during next call to gc->DrawWindows. + * Then it's set back to false. */ + bool Changed; + /** True if we are currently in an event handler */ + bool InHandler; + /** Owner Window */ + Window* Owner; + /** Attached Scroll Bar Pointer*/ + Control* sb; +public: //Events + /** Reset/init event handler */ + void ResetEventHandler(EventHandler handler); + /** Returns the Owner */ + Window *GetOwner() const { return Owner; } + /** Set the Flags */ + int SetFlags(int arg_flags, int opcode); + /** Set handler for specified event. Override in child classes */ + virtual bool SetEvent(int eventType, EventHandler handler) = 0; + /** Run specified handler, it may return error code */ + int RunEventHandler(EventHandler handler); + /** Key Press Event */ + virtual void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Key Release Event */ + virtual void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Mouse Enter Event */ + virtual void OnMouseEnter(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + virtual void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Over Event */ + virtual void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Down */ + virtual void OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod); + /** Mouse Button Up */ + virtual void OnMouseUp(unsigned short x, unsigned short y, + unsigned short Button, unsigned short Mod); + /** Special Key Press */ + virtual void OnSpecialKeyPress(unsigned char Key); + virtual bool IsPixelTransparent(unsigned short /*x*/, unsigned short /*y*/) { + return false; + } + /** Sets the animation picture ref */ + void SetAnimPicture(Sprite2D* Picture); + /** Sets the Scroll Bar Pointer */ + int SetScrollBar(Control* ptr); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp b/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp new file mode 100644 index 000000000..41e22b2b0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/EventMgr.cpp @@ -0,0 +1,439 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/EventMgr.h" + +#include "GUI/GameControl.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +EventMgr::EventMgr(void) +{ + last_win_focused = NULL; + // Last window focused for mouse events (eg, with a click). Used to determine MouseUp events + last_win_mousefocused = NULL; + // Last window we were over. Used to determine MouseEnter and MouseLeave events + last_win_over = NULL; + MButtons = 0; + dc_x = 0; + dc_y = 0; + dc_time = 0; + dc_delay = 250; + rk_delay = 250; + rk_flags = GEM_RK_DISABLE; +} + +EventMgr::~EventMgr(void) +{ +} + +void EventMgr::SetOnTop(int Index) +{ + std::vector< int>::iterator t; + for (t = topwin.begin(); t != topwin.end(); ++t) { + if (( *t ) == Index) { + topwin.erase( t ); + break; + } + } + if (topwin.size() != 0) { + topwin.insert( topwin.begin(), Index ); + } else { + topwin.push_back( Index ); + } +} + +void EventMgr::SetDefaultFocus(Window *win) +{ + if (!last_win_focused) { + last_win_focused = win; + last_win_focused->SetFocused(last_win_focused->GetControl(0)); + } + last_win_over = NULL; +} + +/** Adds a Window to the Event Manager */ +void EventMgr::AddWindow(Window* win) +{ + unsigned int i; + + if (win == NULL) { + return; + } + bool found = false; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == win) { + goto ok; + } + if(windows[i]==NULL) { + windows[i] = win; +ok: + SetOnTop( i ); + found = true; + break; + } + } + if (!found) { + windows.push_back( win ); + if (windows.size() == 1) + topwin.push_back( 0 ); + else + SetOnTop( ( int ) windows.size() - 1 ); + } + SetDefaultFocus(win); +} +/** Frees and Removes all the Windows in the Array */ +void EventMgr::Clear() +{ + topwin.clear(); + windows.clear(); + last_win_focused = NULL; + last_win_mousefocused = NULL; + last_win_over = NULL; +} + +/** Remove a Window from the array */ +void EventMgr::DelWindow(Window *win) +//unsigned short WindowID, const char *WindowPack) +{ + if (last_win_focused == win) { + last_win_focused = NULL; + } + if (last_win_mousefocused == win) { + last_win_mousefocused = NULL; + } + if (last_win_over == win) { + last_win_over = NULL; + } + + if (windows.size() == 0) { + return; + } + int pos = -1; + std::vector< Window*>::iterator m; + for (m = windows.begin(); m != windows.end(); ++m) { + pos++; + if ( (*m) == win) { + (*m) = NULL; + std::vector< int>::iterator t; + for (t = topwin.begin(); t != topwin.end(); ++t) { + if ( (*t) == pos) { + topwin.erase( t ); + return; + } + } + printMessage("EventManager","Couldn't find window",YELLOW); + } + } +} + +/** BroadCast Mouse Move Event */ +void EventMgr::MouseMove(unsigned short x, unsigned short y) +{ + if (windows.size() == 0) { + return; + } + if (!last_win_focused) { + return; + } + GameControl *gc = core->GetGameControl(); + if (gc) { + // for scrolling + gc->OnGlobalMouseMove(x, y); + } + std::vector< int>::iterator t; + std::vector< Window*>::iterator m; + for (t = topwin.begin(); t != topwin.end(); ++t) { + m = windows.begin(); + m += ( *t ); + Window *win = *m; + if (win == NULL) + continue; + if (!win->Visible) + continue; + if (( win->XPos <= x ) && ( win->YPos <= y )) { + //Maybe we are on the window, let's check + if (( win->XPos + win->Width >= x ) && + ( win->YPos + win->Height >= y )) { + //Yes, we are on the Window + //Let's check if we have a Control under the Mouse Pointer + Control* ctrl = win->GetControl( x, y, true ); + //look for the low priority flagged controls (mostly static labels) + if (ctrl == NULL) { + ctrl = win->GetControl( x, y, false ); + } + if (win != last_win_over || ctrl != win->GetOver()) { + // Remove tooltip if mouse moved to different control + core->DisplayTooltip( 0, 0, NULL ); + if (last_win_over) { + last_win_over->OnMouseLeave( x, y ); + } + last_win_over = win; + win->OnMouseEnter( x, y, ctrl ); + } + if (ctrl != NULL) { + win->OnMouseOver( x, y ); + } + RefreshCursor(win->Cursor); + return; + } + } + //stop going further + if (( *m )->Visible == WINDOW_FRONT) + break; + } + core->DisplayTooltip( 0, 0, NULL ); +} + +void EventMgr::RefreshCursor(int idx) +{ + Video *video = core->GetVideoDriver(); + if (idx&IE_CURSOR_GRAY) { + video->SetMouseGrayed(true); + } else { + video->SetMouseGrayed(false); + } + idx &= IE_CURSOR_MASK; + video->SetCursor( core->Cursors[idx], core->Cursors[idx ^ 1] ); +} + +bool EventMgr::ClickMatch(unsigned short x, unsigned short y, unsigned long thisTime) +{ + if (dc_x+10x+10) return false; + if (dc_y+10y+10) return false; + if (dc_time::iterator t; + std::vector< Window*>::iterator m; + Control *ctrl; + unsigned long thisTime; + + GetTime( thisTime ); + if (ClickMatch(x, y, thisTime)) { + Button |= GEM_MB_DOUBLECLICK; + dc_x = 0; + dc_y = 0; + dc_time = 0; + } else { + dc_x = x; + dc_y = y; + dc_time = thisTime+dc_delay; + } + MButtons |= Button; + for (t = topwin.begin(); t != topwin.end(); ++t) { + m = windows.begin(); + m += ( *t ); + if (( *m ) == NULL) + continue; + if (!( *m )->Visible) + continue; + if (( ( *m )->XPos <= x ) && ( ( *m )->YPos <= y )) { + //Maybe we are on the window, let's check + if (( ( *m )->XPos + ( *m )->Width >= x ) && + ( ( *m )->YPos + ( *m )->Height >= y )) { + //Yes, we are on the Window + //Let's check if we have a Control under the Mouse Pointer + ctrl = ( *m )->GetControl( x, y, true ); + if (!ctrl) { + ctrl = ( *m )->GetControl( x, y, false); + } + last_win_mousefocused = *m; + if (ctrl != NULL) { + last_win_mousefocused->SetMouseFocused( ctrl ); + ctrl->OnMouseDown( x - last_win_mousefocused->XPos - ctrl->XPos, y - last_win_mousefocused->YPos - ctrl->YPos, Button, Mod ); + return; + } + } + } + if (( *m )->Visible == WINDOW_FRONT) //stop looking further + break; + } + + if ((Button == GEM_MB_SCRLUP || Button == GEM_MB_SCRLDOWN) && last_win_mousefocused) { + ctrl = last_win_mousefocused->GetScrollControl(); + if (ctrl) { + ctrl->OnMouseDown( x - last_win_mousefocused->XPos - ctrl->XPos, y - last_win_mousefocused->YPos - ctrl->YPos, Button, Mod ); + } + } + + if (last_win_mousefocused) { + last_win_mousefocused->SetMouseFocused(NULL); + } +} +/** BroadCast Mouse Up Event */ +void EventMgr::MouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod) +{ + MButtons &= ~Button; + if (last_win_mousefocused == NULL) return; + Control *last_ctrl_mousefocused = last_win_mousefocused->GetMouseFocus(); + if (last_ctrl_mousefocused == NULL) return; + last_ctrl_mousefocused->OnMouseUp( x - last_win_mousefocused->XPos - last_ctrl_mousefocused->XPos, + y - last_win_mousefocused->YPos - last_ctrl_mousefocused->YPos, Button, Mod ); +} + +/** BroadCast Mouse Idle Event */ +void EventMgr::MouseIdle(unsigned long /*time*/) +{ + if (last_win_over == NULL) return; + Control *ctrl = last_win_over->GetOver(); + if (ctrl == NULL) return; + ctrl->DisplayTooltip(); +} + +/** BroadCast Key Press Event */ +void EventMgr::KeyPress(unsigned char Key, unsigned short Mod) +{ + if (last_win_focused == NULL) return; + Control *ctrl = last_win_focused->GetFocus(); + if (ctrl == NULL) return; + ctrl->OnKeyPress( Key, Mod ); +} +/** BroadCast Key Release Event */ +void EventMgr::KeyRelease(unsigned char Key, unsigned short Mod) +{ + if (last_win_focused == NULL) return; + Control *ctrl = last_win_focused->GetFocus(); + if (Key == GEM_GRAB) { + core->GetVideoDriver()->ToggleGrabInput(); + return; + } + if (ctrl == NULL) return; + ctrl->OnKeyRelease( Key, Mod ); +} + +/** Special Key Press Event */ +void EventMgr::OnSpecialKeyPress(unsigned char Key) +{ + if (!last_win_focused) { + return; + } + Control *ctrl = NULL; + + // tab shows tooltips + if (Key == GEM_TAB) { + if (last_win_over != NULL) { + Control *ctrl = last_win_over->GetOver(); + if (ctrl != NULL) { + ctrl->DisplayTooltip(); + } + } + } + //the default control will get only GEM_RETURN + else if (Key == GEM_RETURN) { + ctrl = last_win_focused->GetDefaultControl(0); + } + //the default cancel control will get only GEM_ESCAPE + else if (Key == GEM_ESCAPE) { + ctrl = last_win_focused->GetDefaultControl(1); + } + + //if there was no default button set, then the current focus will get it + if (!ctrl) { + ctrl = last_win_focused->GetFocus(); + } + //if one is under focus, use the default scroll focus + if (!ctrl) { + if (Key == GEM_UP || Key == GEM_DOWN) { + ctrl = last_win_focused->GetScrollControl(); + } + } + if (ctrl) { + switch (ctrl->ControlType) { + //scrollbars will receive only mousewheel events + case IE_GUI_SCROLLBAR: + if (Key != GEM_UP && Key != GEM_DOWN) { + return; + } + break; + //buttons will receive only GEM_RETURN + case IE_GUI_BUTTON: + if (Key != GEM_RETURN && Key!=GEM_ESCAPE) { + return; + } + break; + case IE_GUI_GAMECONTROL: + //gamecontrols will receive all special keys + break; + case IE_GUI_EDIT: + case IE_GUI_TEXTAREA: + //editboxes and textareas will receive all special keys + break; + default: + //other controls don't receive any + return; + } + ctrl->OnSpecialKeyPress( Key ); + } +} + +void EventMgr::SetFocused(Window *win, Control *ctrl) +{ + last_win_focused = win; + last_win_focused->SetFocused(ctrl); + //this is to refresh changing mouse cursors should the focus change) + int x,y; + core->GetVideoDriver()->GetMousePos(x,y); + MouseMove((unsigned short) x, (unsigned short) y); +} + +void EventMgr::SetDCDelay(unsigned long t) +{ + dc_delay = t; +} + +void EventMgr::SetRKDelay(unsigned long t) +{ + rk_delay = t; +} + +unsigned long EventMgr::GetRKDelay() +{ + if (rk_flags&GEM_RK_DISABLE) return (unsigned long) ~0; + if (rk_flags&GEM_RK_DOUBLESPEED) return rk_delay/2; + if (rk_flags&GEM_RK_QUADRUPLESPEED) return rk_delay/4; + return rk_delay; +} + +unsigned long EventMgr::SetRKFlags(unsigned long arg, unsigned int op) +{ + unsigned long tmp = rk_flags; + switch (op) { + case BM_SET: tmp = arg; break; + case BM_OR: tmp |= arg; break; + case BM_NAND: tmp &= ~arg; break; + case BM_XOR: tmp ^= arg; break; + case BM_AND: tmp &= arg; break; + default: tmp = 0; break; + } + rk_flags=tmp; + return rk_flags; +} diff --git a/project/jni/application/gemrb/src/core/GUI/EventMgr.h b/project/jni/application/gemrb/src/core/GUI/EventMgr.h new file mode 100644 index 000000000..bda4ae8b8 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/EventMgr.h @@ -0,0 +1,142 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 EventMgr.h + * Declares EventMgr, class distributing events from input devices to GUI windows + * @author The GemRB Project + */ + + +#ifndef EVENTMGR_H +#define EVENTMGR_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "WindowMgr.h" + +#include + +#define GEM_LEFT 0x81 +#define GEM_RIGHT 0x82 +#define GEM_UP 0x83 +#define GEM_DOWN 0x84 +#define GEM_DELETE 0x85 +#define GEM_RETURN 0x86 +#define GEM_BACKSP 0x87 +#define GEM_TAB 0x88 +#define GEM_ALT 0x89 +#define GEM_HOME 0x8a +#define GEM_END 0x8b +#define GEM_ESCAPE 0x8c +#define GEM_PGUP 0x8d +#define GEM_PGDOWN 0x8e +#define GEM_GRAB 0x8f + + +#define GEM_MOD_SHIFT 1 +#define GEM_MOD_CTRL 2 +#define GEM_MOD_ALT 4 + +#define GEM_MOUSEOUT 128 + +// Mouse buttons +#define GEM_MB_ACTION 1 +#define GEM_MB_MENU 4 +#define GEM_MB_SCRLUP 8 +#define GEM_MB_SCRLDOWN 16 + +#define GEM_MB_NORMAL 255 +#define GEM_MB_DOUBLECLICK 256 + +#define GEM_RK_DOUBLESPEED 1 +#define GEM_RK_DISABLE 2 +#define GEM_RK_QUADRUPLESPEED 4 + +/** + * @class EventMgr + * Class distributing events from input devices to GUI windows. + * The events are pumped into instance of this class from a Video driver plugin + */ + +class GEM_EXPORT EventMgr { +private: + std::vector< Window*> windows; + std::vector< int> topwin; + + unsigned short dc_x, dc_y; + unsigned long dc_time, dc_delay; + unsigned long rk_delay, rk_flags; +public: + EventMgr(void); + ~EventMgr(void); + /** Adds a Window to the Event Manager */ + void AddWindow(Window* win); + /** Removes a Window from the Event chain */ + //void DelWindow(unsigned short WindowID, const char *WindowPack); + void DelWindow(Window* win); + /** Frees and Removes all the Windows in the Array */ + void Clear(); + /** Call this to change the cursor (moving over windows will change it back) */ + void RefreshCursor(int idx); + /** BroadCast Mouse Move Event */ + void MouseMove(unsigned short x, unsigned short y); + /** BroadCast Mouse Move Event */ + void MouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** BroadCast Mouse Move Event */ + void MouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** BroadCast Mouse Idle Event */ + void MouseIdle(unsigned long time); + /** BroadCast Key Press Event */ + void KeyPress(unsigned char Key, unsigned short Mod); + /** BroadCast Key Release Event */ + void KeyRelease(unsigned char Key, unsigned short Mod); + /** Special Ket Press Event */ + void OnSpecialKeyPress(unsigned char Key); + /** Sets focus to the control of the window */ + void SetFocused(Window *win, Control *ctrl); + /** Sets mouse event focus to the control of the window */ + void SetMouseFocused(Window *win, Control *ctrl); + /** Sets the maximum accepted doubleclick delay */ + void SetDCDelay(unsigned long t); + void SetRKDelay(unsigned long t); + unsigned long GetRKDelay(); + unsigned long SetRKFlags(unsigned long arg, unsigned int op); + + /** Mask of which Mouse Buttons are pressed */ + unsigned char MButtons; +private: + /** Last Window focused */ + Window* last_win_focused; + /** Last Window mouse event focused */ + Window* last_win_mousefocused; + /** Last Window under Mouse Pointer*/ + Window* last_win_over; + /** Sets a Window on the Top of the Window Queue */ + void SetDefaultFocus(Window *win); + void SetOnTop(int Index); + bool ClickMatch(unsigned short x, unsigned short y, unsigned long thisTime); +}; + +#endif // ! EVENTMGR_H diff --git a/project/jni/application/gemrb/src/core/GUI/GameControl.cpp b/project/jni/application/gemrb/src/core/GUI/GameControl.cpp new file mode 100644 index 000000000..a907b6f49 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/GameControl.cpp @@ -0,0 +1,2718 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/GameControl.h" + +#include "strrefs.h" +#include "win32def.h" + +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Effect.h" +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Item.h" +#include "SaveGameIterator.h" +#include "ScriptEngine.h" +#include "TileMap.h" +#include "Video.h" +#include "damages.h" +#include "GameScript/GSUtils.h" + +#include + +#define DEBUG_SHOW_INFOPOINTS 0x01 +#define DEBUG_SHOW_CONTAINERS 0x02 +#define DEBUG_SHOW_DOORS DEBUG_SHOW_CONTAINERS +#define DEBUG_SHOW_LIGHTMAP 0x08 + +static const Color cyan = { + 0x00, 0xff, 0xff, 0xff +}; +static const Color red = { + 0xff, 0x00, 0x00, 0xff +}; +static const Color magenta = { + 0xff, 0x00, 0xff, 0xff +}; +static const Color green = { + 0x00, 0xff, 0x00, 0xff +}; +/* +static Color white = { + 0xff, 0xff, 0xff, 0xff +}; +*/ +static const Color black = { + 0x00, 0x00, 0x00, 0xff +}; +static const Color blue = { + 0x00, 0x00, 0xff, 0x80 +}; + +//Animation* effect; + +#define FORMATIONSIZE 10 +typedef Point formation_type[FORMATIONSIZE]; +ieDword formationcount; +static formation_type *formations=NULL; +static bool mqs = false; +static ieResRef TestSpell="SPWI207"; + +//If one of the actors has tracking on, the gamecontrol needs to display +//arrow markers on the edges to point at detected monsters +//tracterID is the tracker actor's global ID +//distance is the detection distance +void GameControl::SetTracker(Actor *actor, ieDword dist) +{ + trackerID = actor->GetGlobalID(); + distance = dist; +} + +//Multiple Quick saves is an experimental GemRB feature. +//multiple quick saves are kept, their age is determined by the slot +//number. There is an algorithm which keeps about log2(n) slots alive. +//The algorithm is implemented in SaveGameIterator +void GameControl::MultipleQuickSaves(int arg) +{ + mqs=arg==1; +} + +GameControl::GameControl(void) +{ + if (!formations) { + ReadFormations(); + } + //this is the default action, individual actors should have one too + //at this moment we use only this + //maybe we don't even need it + Changed = true; + spellCount = 0; + user = NULL; + lastActorID = 0; + trackerID = 0; + distance = 0; + MouseIsDown = false; + DrawSelectionRect = false; + overDoor = NULL; + overContainer = NULL; + overInfoPoint = NULL; + drawPath = NULL; + pfs.null(); + lastCursor = IE_CURSOR_NORMAL; + moveX = moveY = 0; + scrolling = false; + numScrollCursor = 0; + DebugFlags = 0; + AIUpdateCounter = 1; + EnableRunning = true; //make this a game flag if you wish + ieDword tmp=0; + + ResetTargetMode(); + + core->GetDictionary()->Lookup("Center",tmp); + if (tmp) { + ScreenFlags=SF_ALWAYSCENTER|SF_CENTERONACTOR; + } + else { + ScreenFlags = SF_CENTERONACTOR; + } + LeftCount = 0; + BottomCount = 0; + RightCount = 0; + TopCount = 0; + DialogueFlags = 0; + dialoghandler = new DialogHandler(); + DisplayText = NULL; +} + +//TODO: +//There could be a custom formation which is saved in the save game +//alternatively, all formations could be saved in some compatible way +//so it doesn't cause problems with the original engine +void GameControl::ReadFormations() +{ + unsigned int i,j; + AutoTable tab("formatio"); + if (!tab) { + // fallback + formationcount = 1; + formations = (formation_type *) calloc(1,sizeof(formation_type) ); + return; + } + formationcount = tab->GetRowCount(); + formations = (formation_type *) calloc(formationcount, sizeof(formation_type)); + for(i=0; iQueryField(i,j*2)); + formations[i][j].x=k; + k=(short) atoi(tab->QueryField(i,j*2+1)); + formations[i][j].y=k; + } + } +} + +//returns a single point offset for a formation +//formation: the formation type +//pos: the actor's slot ID +Point GameControl::GetFormationOffset(ieDword formation, ieDword pos) +{ + if (formation>=formationcount) formation = 0; + if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1; + return formations[formation][pos]; +} + +//Moves an actor to a new position, keeping the current formation +//WARNING: don't pass p as a reference because it gets modified +void GameControl::MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p) +{ + Map* map = actor->GetCurrentArea() ; + + int formation=core->GetGame()->GetFormation(); + if (pos>=FORMATIONSIZE) pos=FORMATIONSIZE-1; + + // calculate angle + double angle; + double xdiff = src.x - p.x; + double ydiff = src.y - p.y; + if (ydiff == 0) { + if (xdiff > 0) { + angle = M_PI_2; + } else { + angle = -M_PI_2; + } + } else { + angle = atan(xdiff/ydiff); + if (ydiff < 0) angle += M_PI; + } + + // calculate new coordinates by rotating formation around (0,0) + double newx = -formations[formation][pos].x * cos(angle) + formations[formation][pos].y * sin(angle); + double newy = formations[formation][pos].x * sin(angle) + formations[formation][pos].y * cos(angle); + p.x += (int)newx; + p.y += (int)newy; + + if (p.x < 0) p.x = 8; + if (p.y < 0) p.y = 8; + if (p.x > map->GetWidth()*16) p.x = map->GetWidth()*16 - 8; + if (p.y > map->GetHeight()*12) p.y = map->GetHeight()*12 - 8; + + if(map->GetCursor(p) == IE_CURSOR_BLOCKED) { + //we can't get there --> adjust position + p.x/=16; + p.y/=12; + map->AdjustPosition(p); + p.x*=16; + p.y*=12; + } + CreateMovement(actor, p); +} + +void GameControl::Center(unsigned short x, unsigned short y) +{ + Video *video = core->GetVideoDriver(); + Region Viewport = video->GetViewport(); + Viewport.x += x - Viewport.w / 2; + Viewport.y += y - Viewport.h / 2; + core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false ); + video->MoveViewportTo( Viewport.x, Viewport.y ); +} + +// generate an action to do the actual movement +// only PST supports RunToPoint +void GameControl::CreateMovement(Actor *actor, const Point &p) +{ + char Tmp[256]; + + Action *action = NULL; + if (DoubleClick && EnableRunning) { + sprintf( Tmp, "RunToPoint([%d.%d])", p.x, p.y ); + action = GenerateAction( Tmp ); + //if it didn't work don't insist + if (!action) + EnableRunning = false; + } + if (!action) { + sprintf( Tmp, "MoveToPoint([%d.%d])", p.x, p.y ); + action = GenerateAction( Tmp ); + } + + actor->AddAction( action ); + // force action so that we get target recticles immediately + actor->ProcessActions(true); +} + +GameControl::~GameControl(void) +{ + //releasing the viewport of GameControl + core->GetVideoDriver()->SetViewport( 0,0,0,0 ); + if (formations) { + free( formations ); + formations = NULL; + } + delete dialoghandler; + if (DisplayText) { + core->FreeString(DisplayText); + } +} + +//Autosave was triggered by the GUI +void GameControl::AutoSave() +{ + core->GetSaveGameIterator()->CreateSaveGame(0, false); +} + +//QuickSave was triggered by the GUI +//mqs is the 'multiple quick saves' flag +void GameControl::QuickSave() +{ + core->GetSaveGameIterator()->CreateSaveGame(1, mqs == 1); +} + +// ArrowSprite cycles +// 321 +// 4 0 +// 567 + +#define D_LEFT 1 +#define D_UP 2 +#define D_RIGHT 4 +#define D_BOTTOM 8 +// Direction Bits +// 326 +// 1 4 +// 98c + +static const int arrow_orientations[16]={ +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + -1, 4, 2, 3, 0,-1, 1,-1, 6, 5,-1,-1, 7,-1,-1,-1 +}; + +//Draws arrow markers along the edge of the game window +//WARNING:don't use reference for point, because it is altered +void GameControl::DrawArrowMarker(const Region &screen, Point p, const Region &viewport) +{ + Video* video = core->GetVideoDriver(); + + //p.x-=viewport.x; + //p.y-=viewport.y; + ieDword draw = 0; + if (p.xGetScrollCursorSprite(0,0); + + tmp = spr->Width; + //tmp = core->ArrowSprites[0]->Width; + + if (p.x>viewport.x+viewport.w-tmp) { + p.x=viewport.x+viewport.w;//-tmp; + draw |= D_RIGHT; + } + + tmp = spr->Height; + //tmp = core->ArrowSprites[0]->Height; + + if (p.y>viewport.y+viewport.h-tmp) { + p.y=viewport.y+viewport.h;//-tmp; + draw |= D_BOTTOM; + } + if (arrow_orientations[draw]>=0) { + video->BlitGameSprite( core->GetScrollCursorSprite(arrow_orientations[draw], 0), p.x+screen.x, p.y+screen.y, 0, black, NULL); + } +} + +/** Draws the Control on the Output Display */ +void GameControl::Draw(unsigned short x, unsigned short y) +{ + bool update_scripts = !(DialogueFlags & DF_FREEZE_SCRIPTS); + + Game* game = core->GetGame(); + if (!game) + return; + + if (((short) Width) <=0 || ((short) Height) <= 0) { + return; + } + + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + Region screen( x + XPos, y + YPos, Width, Height ); + Map *area = core->GetGame()->GetCurrentArea(); + Video* video = core->GetVideoDriver(); + if (!area) { + video->DrawRect( screen, blue, true ); + return; + } + + Region viewport = video->GetViewport(); + if (moveX || moveY) { + viewport.x += moveX; + viewport.y += moveY; + Point mapsize = area->TMap->GetMapSize(); + if ( viewport.x < 0 )//if we are at top of the map + viewport.x = 0; + else if ( (viewport.x + viewport.w) >= mapsize.x) //if we are at the bottom + viewport.x = mapsize.x - viewport.w; + + if ( viewport.y < 0 ) //if we are at the left of the map + viewport.y = 0; + else if ( (viewport.y + viewport.h ) >= mapsize.y ) //if we are at the right + viewport.y = mapsize.y - viewport.h; + + // override any existing viewport moves which may be in progress + core->timer->SetMoveViewPort( viewport.x, viewport.y, 0, false ); + // move it directly ourselves, since we might be paused + video->MoveViewportTo( viewport.x, viewport.y ); + } + video->DrawRect( screen, black, true ); + + // setup outlines + InfoPoint *i; + unsigned int idx; + for (idx = 0; (i = area->TMap->GetInfoPoint( idx )); idx++) { + i->Highlight = false; + if (overInfoPoint == i && target_mode) { + if (i->VisibleTrap(0)) { + i->outlineColor = green; + i->Highlight = true; + continue; + } + } + if (i->VisibleTrap(DebugFlags & DEBUG_SHOW_INFOPOINTS)) { + i->outlineColor = red; // traps + } else if (DebugFlags & DEBUG_SHOW_INFOPOINTS) { + i->outlineColor = blue; // debug infopoints + } else { + continue; + } + i->Highlight = true; + } + + Door *d; + for (idx = 0; (d = area->TMap->GetDoor( idx )); idx++) { + d->Highlight = false; + if (overDoor == d) { + if (target_mode) { + if (d->Visible() && (d->VisibleTrap(0) || (d->Flags & DOOR_LOCKED))) { + // only highlight targettable doors + d->outlineColor = green; + d->Highlight = true; + continue; + } + } else if (!(d->Flags & DOOR_SECRET)) { + // mouse over, not in target mode, no secret door + d->outlineColor = cyan; + d->Highlight = true; + continue; + } + } + if (d->VisibleTrap(0)) { + d->outlineColor = red; // traps + } else if (d->Flags & DOOR_SECRET) { + if (DebugFlags & DEBUG_SHOW_DOORS || d->Flags & DOOR_FOUND) { + d->outlineColor = magenta; // found hidden door + } else { + // secret door is invisible + continue; + } + } else if (DebugFlags & DEBUG_SHOW_DOORS) { + d->outlineColor = cyan; // debug doors + } else { + continue; + } + d->Highlight = true; + } + + Container *c; + for (idx = 0; (c = area->TMap->GetContainer( idx )); idx++) { + c->Highlight = false; + if (overContainer == c && target_mode) { + if (c->VisibleTrap(0) || (c->Flags & CONT_LOCKED)) { + // only highlight targettable containers + c->outlineColor = green; + c->Highlight = true; + continue; + } + } else if (overContainer == c) { + // mouse over, not in target mode + c->outlineColor = cyan; + c->Highlight = true; + continue; + } + if (c->VisibleTrap(0)) { + c->outlineColor = red; // traps + } else if (DebugFlags & DEBUG_SHOW_CONTAINERS) { + c->outlineColor = cyan; // debug containers + } else { + continue; + } + c->Highlight = true; + } + + //drawmap should be here so it updates fog of war + area->DrawMap( screen ); + game->DrawWeather(screen, update_scripts); + + if (trackerID) { + Actor *actor = area->GetActorByGlobalID(trackerID); + + if (actor) { + Actor **monsters = area->GetAllActorsInRadius(actor->Pos, GA_NO_DEAD, distance); + + int i = 0; + while(monsters[i]) { + Actor *target = monsters[i++]; + if (target->InParty) continue; + if (target->GetStat(IE_NOTRACKING)) continue; + DrawArrowMarker(screen, target->Pos, viewport); + } + delete monsters; + } else { + trackerID = 0; + } + } + + if (ScreenFlags & SF_DISABLEMOUSE) + return; + Point p(lastMouseX, lastMouseY); + video->ConvertToGame( p.x, p.y ); + + // Draw selection rect + if (DrawSelectionRect) { + CalculateSelection( p ); + video->DrawRect( SelectionRect, green, false, true ); + } + + // Show wallpolygons + if (DebugFlags & DEBUG_SHOW_INFOPOINTS) { + + unsigned int count = area->GetWallCount(); + for (unsigned int i = 0; i < count; ++i) { + Wall_Polygon* poly = area->GetWallGroup(i); + if (!poly) continue; + // yellow + Color c; + c.r = 0x7F; + c.g = 0x7F; + c.b = 0; + c.a = 0; + //if polygon is disabled, make it grey + if (poly->wall_flag&WF_DISABLED) { + c.b = 0x7F; + } + + video->DrawPolyline( poly, c, true ); + } + } + + // Draw path + if (drawPath) { + PathNode* node = drawPath; + while (true) { + Point p( ( node-> x*16) + 8, ( node->y*12 ) + 6 ); + if (!node->Parent) { + video->DrawCircle( p.x, p.y, 2, red ); + } else { + short oldX = ( node->Parent-> x*16) + 8, oldY = ( node->Parent->y*12 ) + 6; + video->DrawLine( oldX, oldY, p.x, p.y, green ); + } + if (!node->Next) { + video->DrawCircle( p.x, p.y, 2, green ); + break; + } + node = node->Next; + } + } + + // Draw lightmap + if (DebugFlags & DEBUG_SHOW_LIGHTMAP) { + Sprite2D* spr = area->LightMap->GetSprite2D(); + video->BlitSprite( spr, 0, 0, true ); + video->FreeSprite( spr ); + Region point( p.x / 16, p.y / 12, 2, 2 ); + video->DrawRect( point, red ); + } + + if (core->HasFeature(GF_ONSCREEN_TEXT) && DisplayText) { + core->GetFont(1)->Print(screen, (unsigned char *)DisplayText, core->InfoTextPalette, IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true); + if (update_scripts) { + // just replicating original engine behaviour + if (DisplayTextTime == 0) { + SetDisplayText((char *)NULL, 0); + } else { + DisplayTextTime--; + } + } + } +} + +/** inherited from Control, GameControl doesn't need it */ +int GameControl::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Key Press Event */ +void GameControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (DialogueFlags&DF_IN_DIALOG) { + return; + } + unsigned int i, pc; + Game* game = core->GetGame(); + if (!game) return; + + switch (Key) { + case '0': + game->SelectActor( NULL, false, SELECT_NORMAL ); + i = game->GetPartySize(false)/2+1; + while(i--) { + SelectActor(i, true); + } + break; + case '-': + game->SelectActor( NULL, true, SELECT_NORMAL ); + i = game->GetPartySize(false)/2+1; + while(i--) { + SelectActor(i, false); + } + break; + case '=': + SelectActor(-1); + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + SelectActor(Key-'0'); + break; + case '7': // 1 & 2 + case '8': // 3 & 4 + case '9': // 5 & 6 + game->SelectActor( NULL, false, SELECT_NORMAL ); + i = game->GetPartySize(false); + pc = 2*(Key - '6')-1; + if (pc >= i) { + SelectActor(i, true); + break; + } + SelectActor(pc, true); + SelectActor(pc+1, true); + break; +#ifdef ANDROID + case 'o': + case 'p': + Control::OnKeyPress(Key, 0); + break; + case 'c': // show containers in ANDROID, GEM_ALT is not possible to use + DebugFlags |= DEBUG_SHOW_CONTAINERS; + return; +#endif + default: + core->GetGame()->SetHotKey(toupper(Key)); + break; + } +} + +//Select (or deselect) a new actor (or actors) +void GameControl::SelectActor(int whom, int type) +{ + Game* game = core->GetGame(); + if (whom==-1) { + game->SelectActor( NULL, true, SELECT_NORMAL ); + return; + } + + /* doesn't fall through here */ + Actor* actor = game->FindPC( whom ); + if (!actor) + return; + + if (type==0) { + game->SelectActor( actor, false, SELECT_NORMAL ); + return; + } + if (type==1) { + game->SelectActor( actor, true, SELECT_NORMAL ); + return; + } + + bool was_selected = actor->IsSelected(); + if (game->SelectActor( actor, true, SELECT_REPLACE )) + if (was_selected || (ScreenFlags & SF_ALWAYSCENTER)) { + ScreenFlags |= SF_CENTERONACTOR; + } +} + +//Effect for the ctrl-r cheatkey (resurrect) +static EffectRef heal_ref={"CurrentHPModifier", NULL, -1}; +static EffectRef damage_ref={"Damage", NULL, -1}; + +/** Key Release Event */ +void GameControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + unsigned int i; + Game* game = core->GetGame(); + + if (!game) + return; + + if (DialogueFlags&DF_IN_DIALOG) { + if (Mod) return; + switch(Key) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + TextArea *ta = core->GetMessageTextArea(); + if (ta) { + ta->OnKeyPress(Key,Mod); + } + } + break; + } + return; + } + //cheatkeys with ctrl- + if (Mod & GEM_MOD_CTRL) { + if (!core->CheatEnabled()) { + return; + } + Map* area = game->GetCurrentArea( ); + if (!area) + return; + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + Point p(lastMouseX, lastMouseY); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + switch (Key) { + case 'f': //toggle full screen mode + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + case 'd': //disarm a trap + if (overInfoPoint) { + overInfoPoint->DetectTrap(256); + } + if (overContainer) { + if (overContainer->Trapped && + !( overContainer->TrapDetected )) { + overContainer->TrapDetected = 1; + } + } + if (overDoor) { + if (overDoor->Trapped && + !( overDoor->TrapDetected )) { + overDoor->TrapDetected = 1; + } + } + break; + case 'l': //play an animation (vvc/bam) over an actor + //the original engine was able to swap through all animations + if (lastActor) { + lastActor->AddAnimation("S056ICBL", 0, 0, 0); + } + break; + + case 'c': //force cast a hardcoded spell + //caster is the last selected actor + //target is the door/actor currently under the pointer + if (game->selected.size() > 0) { + Actor *src = game->selected[0]; + Scriptable *target = lastActor; + if (overDoor) { + target = overDoor; + } + if (target) { + src->CastSpell( TestSpell, target, false ); + if (src->LastTarget) { + src->CastSpellEnd( TestSpell ); + } else { + src->CastSpellPointEnd( TestSpell ); + } + } + } + break; + + case 'b': //draw a path to the target (pathfinder debug) + //You need to select an origin with ctrl-o first + if (drawPath) { + PathNode* nextNode = drawPath->Next; + PathNode* thisNode = drawPath; + while (true) { + delete( thisNode ); + thisNode = nextNode; + if (!thisNode) + break; + nextNode = thisNode->Next; + } + } + drawPath = core->GetGame()->GetCurrentArea()->FindPath( pfs, p, lastActor?lastActor->size:1 ); + + break; + + case 'o': //set up the origin for the pathfinder + // origin + pfs.x = lastMouseX; + pfs.y = lastMouseY; + core->GetVideoDriver()->ConvertToGame( pfs.x, pfs.y ); + break; + case 'a': //switches through the avatar animations + if (lastActor) { + lastActor->GetNextAnimation(); + } + break; + case 's': //switches through the stance animations + if (lastActor) { + lastActor->GetNextStance(); + } + break; + case 'j': //teleports the selected actors + for (i = 0; i < game->selected.size(); i++) { + Actor* actor = game->selected[i]; + MoveBetweenAreasCore(actor, core->GetGame()->CurrentArea, p, -1, true); + } + break; + + case 'm': //prints a debug dump (ctrl-m in the original game too) + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (!lastActor) { + // ValidTarget never returns immobile targets, making debugging a nightmare + // so if we don't have an actor, we make really really sure by checking manually + unsigned int count = area->GetActorCount(true); + while (count--) { + Actor *actor = area->GetActor(count, true); + if (actor->IsOver(p)) { + actor->DebugDump(); + } + } + } + if (lastActor) { + lastActor->DebugDump(); + break; + } + if (overDoor) { + overDoor->DebugDump(); + break; + } + if (overContainer) { + overContainer->DebugDump(); + break; + } + if (overInfoPoint) { + overInfoPoint->DebugDump(); + break; + } + core->GetGame()->GetCurrentArea()->DebugDump(Mod & GEM_MOD_SHIFT); + break; + case 'v': //marks some of the map visited (random vision distance) + area->ExploreMapChunk( p, rand()%30, 1 ); + break; + case 'w': // consolidates found ground piles under the pointed pc + area->MoveVisibleGroundPiles(p); + break; + case 'x': // shows coordinates on the map + printf( "%s [%d.%d]\n", area->GetScriptName(), p.x, p.y ); + break; + case 'g'://shows loaded areas and other game information + game->DebugDump(); + break; + case 'i'://interact trigger (from the original game) + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (lastActor && !(lastActor->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE)) { + Actor *target; + int i = game->GetPartySize(true); + if(i<2) break; + i=rand()%i; + do + { + target = game->GetPC(i, true); + if(target==lastActor) continue; + if(target->GetStat(IE_MC_FLAGS)&MC_EXPORTABLE) continue; + + char Tmp[40]; + snprintf(Tmp,sizeof(Tmp),"Interact(\"%s\")",target->GetScriptName() ); + lastActor->AddAction(GenerateAction(Tmp)); + break; + } + while(i--); + } + break; + case 'r'://resurrects actor + if (!lastActor) { + lastActor = area->GetActor( p, GA_DEFAULT); + } + if (lastActor) { + Effect *fx = EffectQueue::CreateEffect(heal_ref, lastActor->GetStat(IE_MAXHITPOINTS), 0x30001, FX_DURATION_INSTANT_PERMANENT); + if (fx) { + core->ApplyEffect(fx, lastActor, lastActor); + } + } + break; + case 't'://advances time + // 7200 (one day) /24 (hours) == 300 + game->AdvanceTime(300*AI_UPDATE_TIME); + //refresh gui here once we got it + break; + + case 'q': //joins actor to the party + if (lastActor && !lastActor->InParty) { + lastActor->ClearActions(); + lastActor->ClearPath(); + char Tmp[40]; + strncpy(Tmp,"JoinParty()",sizeof(Tmp) ); + lastActor->AddAction( GenerateAction(Tmp) ); + } + break; + case 'p': //center on actor + ScreenFlags|=SF_CENTERONACTOR; + ScreenFlags^=SF_ALWAYSCENTER; + break; + case 'k': //kicks out actor + if (lastActor && lastActor->InParty) { + lastActor->ClearActions(); + lastActor->ClearPath(); + char Tmp[40]; + strncpy(Tmp,"LeaveParty()",sizeof(Tmp) ); + lastActor->AddAction( GenerateAction(Tmp) ); + } + break; + case 'y': //kills actor or all enemies + if (Mod & GEM_MOD_SHIFT) { + // mwahaha! + Effect *newfx; + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT); + Actor *victim; + for (int i = area->GetActorCount(0)-1; i >= 0; i--) { + victim = area->GetActor(i, 0); + if (victim->Modified[IE_EA] == EA_ENEMY) { + core->ApplyEffect(newfx, victim, victim); + } + } + delete newfx; + } else { + if (lastActor) { + //using action so the actor is killed + //correctly (synchronisation) + lastActor->ClearActions(); + lastActor->ClearPath(); + + Effect *newfx; + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_MAGIC<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + if (! (lastActor->GetInternalFlag() & IF_REALLYDIED)) { + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_ACID<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + newfx = EffectQueue::CreateEffect(damage_ref, 300, DAMAGE_CRUSHING<<16, FX_DURATION_INSTANT_PERMANENT); + core->ApplyEffect(newfx, lastActor, lastActor); + } + delete newfx; + } else if (overContainer) { + overContainer->SetContainerLocked(0); + } else if (overDoor) { + overDoor->SetDoorLocked(0,0); + } + } + break; + case 'z': //shift through the avatar animations backward + if (lastActor) { + lastActor->GetPrevAnimation(); + } + break; + case '1': //change paperdoll armour level + if (! lastActor) + break; + lastActor->NewStat(IE_ARMOR_TYPE,1,MOD_ADDITIVE); + break; + case '4': //show all traps and infopoints + DebugFlags ^= DEBUG_SHOW_INFOPOINTS; + printf("Show traps and infopoints %s\n", DebugFlags & DEBUG_SHOW_INFOPOINTS ? "ON" : "OFF"); + break; + case '6': //show the lightmap + DebugFlags ^= DEBUG_SHOW_LIGHTMAP; + printf("Show lightmap %s\n", DebugFlags & DEBUG_SHOW_LIGHTMAP ? "ON" : "OFF"); + break; + case '7': //toggles fog of war + core->FogOfWar ^= 1; + printf("Show Fog-Of-War: %s\n", core->FogOfWar & 1 ? "ON" : "OFF"); + break; + case '8': //show searchmap over area + core->FogOfWar ^= 2; + printf("Show searchmap %s\n", core->FogOfWar & 2 ? "ON" : "OFF"); + break; + default: + printf( "KeyRelease:%d - %d\n", Key, Mod ); + break; + } + return; //return from cheatkeys + } + switch (Key) { + case 'h': //hard pause + if (DialogueFlags & DF_FREEZE_SCRIPTS) break; + //fallthrough + case ' ': //soft pause + DialogueFlags ^= DF_FREEZE_SCRIPTS; + if (DialogueFlags&DF_FREEZE_SCRIPTS) { + displaymsg->DisplayConstantString(STR_PAUSED,0xff0000); + SetDisplayText(STR_PAUSED, 0); // time 0 = removed instantly on unpause + } else { + displaymsg->DisplayConstantString(STR_UNPAUSED,0xff0000); + } + break; + case 'm': + core->GetGUIScriptEngine()->RunFunction("GUIMA","OpenMapWindow"); + break; + case 'j': + core->GetGUIScriptEngine()->RunFunction("GUIJRNL","OpenJournalWindow"); + break; + case 'i': + core->GetGUIScriptEngine()->RunFunction("GUIINV","OpenInventoryWindow"); + break; + case 'r': + core->GetGUIScriptEngine()->RunFunction("GUIREC","OpenRecordsWindow"); + break; + case 'q': //quicksave + QuickSave(); + break; + case GEM_ALT: //alt key (shows containers) +#ifdef ANDROID + case 'c': // show containers in ANDROID, GEM_ALT is not possible to use +#endif + DebugFlags &= ~DEBUG_SHOW_CONTAINERS; + break; + default: + break; + } +} + +void GameControl::DisplayTooltip() { + Game* game = core->GetGame(); + if (game) { + Map* area = game->GetCurrentArea( ); + if (area) { + Actor *actor = area->GetActorByGlobalID(lastActorID); + if (actor && (actor->GetStat(IE_STATE_ID)&STATE_DEAD || actor->GetInternalFlag()&IF_JUSTDIED)) { + // checking IF_JUSTDIED is kind of horrid, but seems necessary + // no tooltips for dead actors! + actor->SetOver( false ); + lastActorID = 0; + actor = NULL; + } + + if (actor) { + char *name = actor->GetName(-1); + int hp = actor->GetStat(IE_HITPOINTS); + int maxhp = actor->GetStat(IE_MAXHITPOINTS); + + char buffer[100]; + if (!core->TooltipBack) { + // single-line tooltips without background (PS:T) + if (actor->InParty) { + snprintf(buffer, 100, "%s: %d/%d", name, hp, maxhp); + } else { + snprintf(buffer, 100, "%s", name); + } + } else { + // a guess at a neutral check + bool neutral = actor->GetStat(IE_EA) == EA_NEUTRAL; + // test for an injured string being present for this game + int strindex = displaymsg->GetStringReference(STR_UNINJURED); + // normal tooltips + if (actor->InParty) { + // in party: display hp + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else if (neutral) { + // neutral: display name only + snprintf(buffer, 100, "%s", name); + } else if (strindex == -1) { + // non-neutral, not in party, no injured strings: display hp + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else { + // non-neutral, not in party: display injured string + int strindex; + char *injuredstring = NULL; + // these boundaries are just a guess + if (hp == maxhp) { + strindex = STR_UNINJURED; + } else if (hp > (maxhp*3)/4) { + strindex = STR_INJURED1; + } else if (hp > maxhp/2) { + strindex = STR_INJURED2; + } else if (hp > maxhp/3) { + strindex = STR_INJURED3; + } else { + strindex = STR_INJURED4; + } + strindex = displaymsg->GetStringReference(strindex); + if (strindex != -1) { + injuredstring = core->GetString(strindex, 0); + } + + if (!injuredstring) { + // eek, where did the string go? + snprintf(buffer, 100, "%s\n%d/%d", name, hp, maxhp); + } else { + snprintf(buffer, 100, "%s\n%s", name, injuredstring); + free(injuredstring); + } + } + } + + Point p = actor->Pos; + core->GetVideoDriver()->ConvertToScreen( p.x, p.y ); + p.x += Owner->XPos + XPos; + p.y += Owner->YPos + YPos; + + // hack to position text above PS:T actors + if (!core->TooltipBack) p.y -= actor->size*50; + + // we should probably cope better with moving actors + SetTooltip(buffer); + core->DisplayTooltip(p.x, p.y, this); + return; + } + } + } + + SetTooltip(NULL); + core->DisplayTooltip(0, 0, NULL); + return; +} + +//returns the appropriate cursor over an active region (trap, infopoint, travel region) +int GameControl::GetCursorOverInfoPoint(InfoPoint *overInfoPoint) const +{ + if (target_mode == TARGET_MODE_PICK) { + if (overInfoPoint->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + // traps always display a walk cursor? + if (overInfoPoint->Type == ST_PROXIMITY) { + return IE_CURSOR_WALK; + } + return overInfoPoint->Cursor; +} + +//returns the appropriate cursor over a door +int GameControl::GetCursorOverDoor(Door *overDoor) const +{ + if (!overDoor->Visible()) { + if (target_mode == TARGET_MODE_NONE) { + return IE_CURSOR_BLOCKED; + } else { + return lastCursor|IE_CURSOR_GRAY; + } + } + if (target_mode == TARGET_MODE_PICK) { + if (overDoor->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + if (overDoor->Flags & DOOR_LOCKED) { + return IE_CURSOR_LOCK; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + return overDoor->Cursor; +} + +//returns the appropriate cursor over a container (or pile) +int GameControl::GetCursorOverContainer(Container *overContainer) const +{ + if (target_mode == TARGET_MODE_PICK) { + if (overContainer->VisibleTrap(0)) { + return IE_CURSOR_TRAP; + } + if (overContainer->Flags & CONT_LOCKED) { + return IE_CURSOR_LOCK2; + } + + return IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + return IE_CURSOR_TAKE; +} + +int GameControl::GetDefaultCursor() const +{ + switch(target_mode) { + case TARGET_MODE_TALK: + return IE_CURSOR_TALK; + case TARGET_MODE_ATTACK: + return IE_CURSOR_ATTACK; + case TARGET_MODE_CAST: + return IE_CURSOR_CAST; + case TARGET_MODE_DEFEND: + return IE_CURSOR_DEFEND; + case TARGET_MODE_PICK: + return IE_CURSOR_PICK; + } + return IE_CURSOR_NORMAL; +} + +/** Mouse Over Event */ +void GameControl::OnMouseOver(unsigned short x, unsigned short y) +{ + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + + lastMouseX = x; + lastMouseY = y; + Point p( x,y ); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + if (MouseIsDown && ( !DrawSelectionRect )) { + if (( abs( p.x - StartX ) > 5 ) || ( abs( p.y - StartY ) > 5 )) { + DrawSelectionRect = true; + } + } + Game* game = core->GetGame(); + if (!game) return; + Map* area = game->GetCurrentArea( ); + if (!area) return; + int nextCursor = area->GetCursor( p ); + //make the invisible area really invisible + if (nextCursor == IE_CURSOR_INVALID) { + Owner->Cursor = IE_CURSOR_BLOCKED; + lastCursor = IE_CURSOR_BLOCKED; + return; + } + + overInfoPoint = area->TMap->GetInfoPoint( p, true ); + if (overInfoPoint) { + //nextCursor = overInfoPoint->Cursor; + nextCursor = GetCursorOverInfoPoint(overInfoPoint); + } + + if (overDoor) { + overDoor->Highlight = false; + } + if (overContainer) { + overContainer->Highlight = false; + } + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + if (lastActor) { + lastActor->SetOver( false ); + } + + overDoor = area->TMap->GetDoor( p ); + overContainer = area->TMap->GetContainer( p ); + + if (!DrawSelectionRect) { + if (overDoor) { + nextCursor = GetCursorOverDoor(overDoor); + } + + if (overContainer) { + nextCursor = GetCursorOverContainer(overContainer); + } + + Actor *prevActor = lastActor; + lastActor = area->GetActor( p, target_types); + if (lastActor != prevActor) { + // we store prevActor so we can remove the tooltip on actor change + // (maybe we should be checking this and actor movements every frame?) + SetTooltip(NULL); + core->DisplayTooltip(0, 0, this); + } + + if ((target_types & GA_NO_SELF) && lastActor ) { + if (lastActor == core->GetFirstSelectedActor()) { + lastActor=NULL; + } + } + + if (lastActor) { + lastActorID = lastActor->GetGlobalID(); + lastActor->SetOver( true ); + ieDword type = lastActor->GetStat(IE_EA); + if (type >= EA_EVILCUTOFF || type == EA_GOODBUTRED) { + nextCursor = IE_CURSOR_ATTACK; + } else if ( type > EA_CHARMED ) { + nextCursor = IE_CURSOR_TALK; + //don't let the pc to talk to frozen/stoned creatures + ieDword state = lastActor->GetStat(IE_STATE_ID); + if (state & STATE_CANTMOVE) { + nextCursor |= IE_CURSOR_GRAY; + } + } else { + nextCursor = IE_CURSOR_NORMAL; + } + } else { + lastActorID = 0; + } + + if (target_mode == TARGET_MODE_TALK) { + nextCursor = IE_CURSOR_TALK; + if (!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } else { + //don't let the pc to talk to frozen/stoned creatures + ieDword state = lastActor->GetStat(IE_STATE_ID); + if (state & STATE_CANTMOVE) { + nextCursor |= IE_CURSOR_GRAY; + } + } + } else if (target_mode == TARGET_MODE_ATTACK) { + nextCursor = IE_CURSOR_ATTACK; + if (overDoor) { + if (!overDoor->Visible()) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (!lastActor && !overContainer) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (target_mode == TARGET_MODE_CAST) { + nextCursor = IE_CURSOR_CAST; + //point is always valid + if (!(target_types & GA_POINT)) { + if(!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } + } + } else if (target_mode == TARGET_MODE_DEFEND) { + nextCursor = IE_CURSOR_DEFEND; + if(!lastActor) { + nextCursor |= IE_CURSOR_GRAY; + } + } else if (target_mode == TARGET_MODE_PICK) { + if (lastActor) { + nextCursor = IE_CURSOR_PICK; + } else { + if (!overContainer && !overDoor && !overInfoPoint) { + nextCursor = IE_CURSOR_STEALTH|IE_CURSOR_GRAY; + } + } + goto end_function; + } + + if (lastActor) { + switch (lastActor->GetStat(IE_EA)) { + case EA_EVILCUTOFF: + case EA_GOODCUTOFF: + break; + + case EA_PC: + case EA_FAMILIAR: + case EA_ALLY: + case EA_CONTROLLED: + case EA_CHARMED: + case EA_EVILBUTGREEN: + if (target_types & GA_NO_ENEMY) + nextCursor^=1; + break; + + case EA_ENEMY: + case EA_GOODBUTRED: + if (target_types & GA_NO_ALLY) + nextCursor^=1; + break; + default: + if (!(target_types & GA_NO_NEUTRAL)) + nextCursor^=1; + break; + } + } + } +end_function: + if (lastCursor != nextCursor) { + Owner->Cursor = nextCursor; + lastCursor = (unsigned char) nextCursor; + } +} + +#define SCROLL_BORDER 5 + +/** Global Mouse Move Event */ +void GameControl::OnGlobalMouseMove(unsigned short x, unsigned short y) +{ + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + int mousescrollspd = core->GetMouseScrollSpeed(); + + if (x <= SCROLL_BORDER) + moveX = -mousescrollspd; + else { + if (x >= ( core->Width - SCROLL_BORDER )) + moveX = mousescrollspd; + else + moveX = 0; + } + if (y <= SCROLL_BORDER) + moveY = -mousescrollspd; + else { + if (y >= ( core->Height - SCROLL_BORDER )) + moveY = mousescrollspd; + else + moveY = 0; + } + + if (moveX != 0 || moveY != 0) { + scrolling = true; + } else if (scrolling) { + scrolling = false; + + Video* video = core->GetVideoDriver(); + video->SetDragCursor(NULL); + } +} + +void GameControl::UpdateScrolling() { + if (!scrolling) return; + + int mousescrollspd = core->GetMouseScrollSpeed(); // TODO: why check against this value and not +/-? + Video* video = core->GetVideoDriver(); + + if (moveX == mousescrollspd && moveY == 0) { // right + video->SetDragCursor(core->GetScrollCursorSprite(0,numScrollCursor)); + } else if (moveX == mousescrollspd && moveY == -mousescrollspd) { // upper right + video->SetDragCursor(core->GetScrollCursorSprite(1,numScrollCursor)); + } else if (moveX == 0 && moveY == -mousescrollspd) { // up + video->SetDragCursor(core->GetScrollCursorSprite(2,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == -mousescrollspd) { // upper left + video->SetDragCursor(core->GetScrollCursorSprite(3,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == 0) { // left + video->SetDragCursor(core->GetScrollCursorSprite(4,numScrollCursor)); + } else if (moveX == -mousescrollspd && moveY == mousescrollspd) { // bottom left + video->SetDragCursor(core->GetScrollCursorSprite(5,numScrollCursor)); + } else if (moveX == 0 && moveY == mousescrollspd) { // bottom + video->SetDragCursor(core->GetScrollCursorSprite(6,numScrollCursor)); + } else if (moveX == mousescrollspd && moveY == mousescrollspd) { // bottom right + video->SetDragCursor(core->GetScrollCursorSprite(7,numScrollCursor)); + } + + numScrollCursor = (numScrollCursor+1) % 15; +} + +//generate action code for source actor to try to attack a target +void GameControl::TryToAttack(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to defend a target +void GameControl::TryToDefend(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"NIDSpecial4()",sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to pick pockets of a target +//The -1 flag is a placeholder for dynamic target IDs +void GameControl::TryToPick(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"PickPockets([-1])", sizeof(Tmp) ); + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for source actor to try to pick a lock/disable trap on a door +void GameControl::TryToPick(Actor *source, Door *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + if (tgt->Trapped && tgt->TrapDetected) { + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + } else { + snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() ); + } + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to pick a lock/disable trap on a container +void GameControl::TryToPick(Actor *source, Container *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + if (tgt->Trapped && tgt->TrapDetected) { + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + } else { + snprintf(Tmp, sizeof(Tmp), "PickLock(\"%s\")", tgt->GetScriptName() ); + } + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to disable trap (only trap type active regions) +void GameControl::TryToDisarm(Actor *source, InfoPoint *tgt) +{ + if (tgt->Type!=ST_PROXIMITY) return; + + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + snprintf(Tmp, sizeof(Tmp), "RemoveTraps(\"%s\")", tgt->GetScriptName() ); + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to try to force open lock on a door/container +void GameControl::TryToBash(Actor *source, Scriptable *tgt) +{ + char Tmp[40]; + + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + snprintf(Tmp, sizeof(Tmp), "Attack(\"%s\")", tgt->GetScriptName() ); + source->AddAction( GenerateAction( Tmp ) ); +} + +//generate action code for source actor to use item/cast spell on a point +void GameControl::TryToCast(Actor *source, const Point &tgt) +{ + char Tmp[40]; + + if (!spellCount) { + ResetTargetMode(); + return; //not casting or using an own item + } + source->ClearPath(); + source->ClearActions(); + + spellCount--; + if (spellOrItem>=0) { + sprintf(Tmp, "NIDSpecial8()"); + } else { + //using item on target + sprintf(Tmp, "NIDSpecial7()"); + } + Action* action = GenerateAction( Tmp ); + action->pointParameter=tgt; + if (spellOrItem>=0) + { + CREMemorizedSpell *si; + //spell casting at target + si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex); + if (!si) + { + ResetTargetMode(); + return; + } + sprintf(action->string0Parameter,"%.8s",si->SpellResRef); + } + else + { + action->int0Parameter=spellSlot; + action->int1Parameter=spellIndex; + } + source->AddAction( action ); + if (!spellCount) { + ResetTargetMode(); + } +} + +//generate action code for source actor to use item/cast spell on another actor +void GameControl::TryToCast(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + if (!spellCount) { + ResetTargetMode(); + return; //not casting or using an own item + } + source->ClearPath(); + source->ClearActions(); + + spellCount--; + if (spellOrItem>=0) { + sprintf(Tmp, "NIDSpecial6()"); + } else { + //using item on target + sprintf(Tmp, "NIDSpecial5()"); + } + Action* action = GenerateActionDirect( Tmp, tgt); + if (spellOrItem>=0) + { + CREMemorizedSpell *si; + //spell casting at target + si = source->spellbook.GetMemorizedSpell(spellOrItem, spellSlot, spellIndex); + if (!si) + { + ResetTargetMode(); + return; + } + sprintf(action->string0Parameter,"%.8s",si->SpellResRef); + } + else + { + action->int0Parameter=spellSlot; + action->int1Parameter=spellIndex; + } + source->AddAction( action ); + if (!spellCount) { + ResetTargetMode(); + } +} + +//generate action code for source actor to use talk to target actor +void GameControl::TryToTalk(Actor *source, Actor *tgt) +{ + char Tmp[40]; + + //Nidspecial1 is just an unused action existing in all games + //(non interactive demo) + //i found no fitting action which would emulate this kind of + //dialog initation + source->ClearPath(); + source->ClearActions(); + source->SetModal(MS_NONE); + strncpy(Tmp,"NIDSpecial1()",sizeof(Tmp) ); + dialoghandler->targetID = tgt->GetGlobalID(); //this is a hack, but not so deadly + source->AddAction( GenerateActionDirect( Tmp, tgt) ); +} + +//generate action code for actor appropriate for the target mode when the target is a container +void GameControl::HandleContainer(Container *container, Actor *actor) +{ + char Tmp[256]; + + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the container back from the coordinates + TryToCast(actor, container->Pos); + //Do not reset target_mode, TryToCast does it for us!! + return; + } + + if (target_mode == TARGET_MODE_ATTACK) { + actor->ClearPath(); + actor->ClearActions(); + snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", container->GetScriptName()); + actor->AddAction(GenerateAction(Tmp)); + ResetTargetMode(); + return; + } + + if ((target_mode == TARGET_MODE_PICK)) { + TryToPick(actor, container); + ResetTargetMode(); + return; + } + + actor->ClearPath(); + actor->ClearActions(); + strncpy(Tmp,"UseContainer()",sizeof(Tmp) ); + core->SetCurrentContainer( actor, container); + actor->AddAction( GenerateAction( Tmp) ); +} + +//generate action code for actor appropriate for the target mode when the target is a door +void GameControl::HandleDoor(Door *door, Actor *actor) +{ + char Tmp[256]; + + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the door back from the coordinates + Point *p = door->toOpen; + Point *otherp = door->toOpen+1; + if (Distance(*p,actor)>Distance(*otherp,actor)) { + p=otherp; + } + TryToCast(actor, *p); + return; + } + + if (target_mode == TARGET_MODE_ATTACK) { + actor->ClearPath(); + actor->ClearActions(); + snprintf(Tmp, sizeof(Tmp), "BashDoor(\"%s\")", door->GetScriptName()); + actor->AddAction(GenerateAction(Tmp)); + ResetTargetMode(); + return; + } + + if (target_mode == TARGET_MODE_PICK) { + TryToPick(actor, door); + ResetTargetMode(); + return; + } + + actor->ClearPath(); + actor->ClearActions(); + actor->TargetDoor = door->GetGlobalID(); + // internal gemrb toggle door action hack - should we use UseDoor instead? + sprintf( Tmp, "NIDSpecial9()" ); + actor->AddAction( GenerateAction( Tmp) ); +} + +//generate action code for actor appropriate for the target mode when the target is an active region (infopoint, trap or travel) +bool GameControl::HandleActiveRegion(InfoPoint *trap, Actor * actor, Point &p) +{ + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + //we'll get the active region from the coordinates (if needed) + TryToCast(actor, p); + //don't bother with this region further + return true; + } + if ((target_mode == TARGET_MODE_PICK)) { + TryToDisarm(actor, trap); + ResetTargetMode(); + return true; + } + + switch(trap->Type) { + case ST_TRAVEL: + actor->UseExit(true); + return false; + case ST_TRIGGER: + //the importer shouldn't load the script + //if it is unallowed anyway (though + //deactivated scripts could be reactivated) + //only the 'trapped' flag should be honoured + //there. Here we have to check on the + //reset trap and deactivated flags + if (trap->Scripts[0]) { + if (!(trap->Flags&TRAP_DEACTIVATED) ) { + trap->LastTriggerObject = trap->LastTrigger = actor->GetGlobalID(); + trap->ImmediateEvent(); + //directly feeding the event, even if there are actions in the queue + trap->Scripts[0]->Update(); + trap->ProcessActions(true); + //if reset trap flag not set, deactivate it + //hmm, better not, info triggers don't deactivate themselves on click + //if (!(trap->Flags&TRAP_RESET)) { + // trap->Flags|=TRAP_DEACTIVATED; + //} + } + } else { + if (trap->overHeadText) { + if (trap->textDisplaying != 1) { + trap->textDisplaying = 1; + trap->timeStartDisplaying = core->GetGame()->Ticks; + DisplayString( trap ); + } + } + } + if (trap->Flags&TRAP_USEPOINT) { + //overriding the target point + p = trap->UsePoint; + return false; + } + return true; + default:; + } + return false; +} +/** Mouse Button Down */ +void GameControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + if (ScreenFlags&SF_DISABLEMOUSE) + return; + + short px=x; + short py=y; + DoubleClick = false; + switch(Button) + { + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + break; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + break; + case GEM_MB_ACTION|GEM_MB_DOUBLECLICK: + DoubleClick = true; + case GEM_MB_ACTION: + core->GetVideoDriver()->ConvertToGame( px, py ); + MouseIsDown = true; + SelectionRect.x = px; + SelectionRect.y = py; + StartX = px; + StartY = py; + SelectionRect.w = 0; + SelectionRect.h = 0; + } +} + +/** Mouse Button Up */ +void GameControl::OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + unsigned int i; + char Tmp[256]; + + if (ScreenFlags & SF_DISABLEMOUSE) { + return; + } + //heh, i found no better place + core->CloseCurrentContainer(); + + MouseIsDown = false; + Point p(x,y); + core->GetVideoDriver()->ConvertToGame( p.x, p.y ); + Game* game = core->GetGame(); + if (!game) return; + Map* area = game->GetCurrentArea( ); + if (!area) return; + + if (DrawSelectionRect) { + Actor** ab; + unsigned int count = area->GetActorInRect( ab, SelectionRect,true ); + if (count != 0) { + for (i = 0; i < highlighted.size(); i++) + highlighted[i]->SetOver( false ); + highlighted.clear(); + game->SelectActor( NULL, false, SELECT_NORMAL ); + for (i = 0; i < count; i++) { + // FIXME: should call handler only once + game->SelectActor( ab[i], true, SELECT_NORMAL ); + } + } + free( ab ); + DrawSelectionRect = false; + return; + } + + //hidden actors are not selectable by clicking on them + Actor* actor = area->GetActor( p, GA_DEFAULT | GA_NO_DEAD | GA_NO_HIDDEN); + if (Button == GEM_MB_MENU) { + if (actor) { + //from GSUtils + DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE); + return; + } + core->GetDictionary()->SetAt( "MenuX", x ); + core->GetDictionary()->SetAt( "MenuY", y ); + core->GetGUIScriptEngine()->RunFunction( "GUICommon", "OpenFloatMenuWindow" ); + return; + } + + if (Button != GEM_MB_ACTION) { + return; + } + + if (!game->selected.size()) { + //TODO: this is a hack, we need some restructuring here + //handling the special case when no one was selected, and + //the player clicks on a partymember + if (actor && (actor->GetStat(IE_EA)GetFirstSelectedPC(false); + if (!pc) { + //this could be a non-PC + pc = game->selected[0]; + } + if (!actor) { + //add a check if you don't want some random monster handle doors and such + if (overDoor) { + HandleDoor(overDoor, pc); + return; + } + if (overContainer) { + HandleContainer(overContainer, pc); + return; + } + if (overInfoPoint) { + if (HandleActiveRegion(overInfoPoint, pc, p)) { + return; + } + } + + //just a single actor, no formation + if (game->selected.size()==1) { + //the player is using an item or spell on the ground + if ((target_mode == TARGET_MODE_CAST) && spellCount) { + if (target_types & GA_POINT) { + TryToCast(pc, p); + } + return; + } + + pc->ClearPath(); + pc->ClearActions(); + CreateMovement(pc, p); + if (DoubleClick) Center(x,y); + //p is a searchmap travel region + if ( pc->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) { + sprintf( Tmp, "NIDSpecial2()" ); + pc->AddAction( GenerateAction( Tmp) ); + } + return; + } + + // construct a sorted party + // TODO: this is still ugly, help? + std::vector party; + // first, from the actual party + int max = game->GetPartySize(false); + for(int idx = 1; idx<=max; idx++) { + Actor *act = game->FindPC(idx); + if(act->IsSelected()) { + party.push_back(act); + } + } + for (i = 0; i < game->selected.size(); i++) { + Actor *act = game->selected[i]; + if (!act->InParty) { + party.push_back(act); + } + } + + //party formation movement + Point src = party[0]->Pos; + for(i = 0; i < party.size(); i++) { + actor = party[i]; + actor->ClearPath(); + actor->ClearActions(); + MoveToPointFormation(actor, i, src, p); + } + if (DoubleClick) Center(x,y); + + //p is a searchmap travel region + if ( party[0]->GetCurrentArea()->GetCursor(p) == IE_CURSOR_TRAVEL) { + sprintf( Tmp, "NIDSpecial2()" ); + party[0]->AddAction( GenerateAction( Tmp) ); + } + return; + } + if (!actor) return; + + //we got an actor past this point + if (target_mode == TARGET_MODE_NONE) { + DisplayStringCore(actor, VB_SELECT+core->Roll(1,3,-1), DS_CONST|DS_CONSOLE); + } + + PerformActionOn(actor); +} + +void GameControl::PerformActionOn(Actor *actor) +{ + Game* game = core->GetGame(); + unsigned int i; + + //determining the type of the clicked actor + ieDword type; + + type = actor->GetStat(IE_EA); + if ( type >= EA_EVILCUTOFF || type == EA_GOODBUTRED ) { + type = ACT_ATTACK; //hostile + } else if ( type > EA_CHARMED ) { + type = ACT_TALK; //neutral + } else { + type = ACT_NONE; //party + } + + if (target_mode == TARGET_MODE_ATTACK) { + type = ACT_ATTACK; + } else if (target_mode == TARGET_MODE_TALK) { + type = ACT_TALK; + } else if (target_mode == TARGET_MODE_CAST) { + type = ACT_CAST; + } else if (target_mode == TARGET_MODE_DEFEND) { + type = ACT_DEFEND; + } else if (target_mode == TARGET_MODE_PICK) { + type = ACT_THIEVING; + } + + if (type != ACT_NONE) { + if(!actor->ValidTarget(target_types)) { + return; + } + } + + //we shouldn't zero this for two reasons in case of spell or item + //1. there could be multiple targets + //2. the target mode is important + if (!(target_mode == TARGET_MODE_CAST) || !spellCount) { + ResetTargetMode(); + } + + switch (type) { + case ACT_NONE: //none + if (!actor->ValidTarget(GA_SELECT)) { + return; + } + + if (actor->InParty) + SelectActor( actor->InParty ); + else if (actor->GetStat(IE_EA) <= EA_CHARMED) { + /*let's select charmed/summoned creatures + EA_CHARMED is the maximum value known atm*/ + core->GetGame()->SelectActor(actor, true, SELECT_REPLACE); + } + break; + case ACT_TALK: + if (!actor->ValidTarget(GA_TALK)) { + return; + } + + //talk (first selected talks) + if (game->selected.size()) { + //if we are in PST modify this to NO! + Actor *source; + if (core->HasFeature(GF_PROTAGONIST_TALKS) ) { + source = game->GetPC(0, false); //protagonist + } else { + source = core->GetFirstSelectedPC(false); + } + // only party members can start conversations + if (source) { + TryToTalk(source, actor); + } + } + break; + case ACT_ATTACK: + //all of them attacks the red circled actor + for(i=0;iselected.size();i++) { + TryToAttack(game->selected[i], actor); + } + break; + case ACT_CAST: //cast on target or use item on target + if (game->selected.size()==1) { + Actor *source; + source = core->GetFirstSelectedActor(); + if(source) { + TryToCast(source, actor); + } + } + break; + case ACT_DEFEND: + for(i=0;iselected.size();i++) { + TryToDefend(game->selected[i], actor); + } + break; + case ACT_THIEVING: + if (game->selected.size()==1) { + Actor *source; + source = core->GetFirstSelectedActor(); + if(source) { + TryToPick(source, actor); + } + } + break; + } +} + +//sets target mode, and resets the cursor +void GameControl::SetTargetMode(int mode) { + int x,y; + + target_mode = mode; + //This hack is to refresh the mouse cursor + core->GetVideoDriver()->GetMousePos(x,y); + //calling into the videodriver to set the mouseposition won't work + core->GetEventMgr()->MouseMove(x,y); +} + +void GameControl::ResetTargetMode() { + SetTargetMode(TARGET_MODE_NONE); + target_types = GA_NO_DEAD|GA_NO_HIDDEN; +} + +/** Special Key Press */ +void GameControl::OnSpecialKeyPress(unsigned char Key) +{ + if (DialogueFlags&DF_IN_DIALOG) { + switch(Key) { + case GEM_RETURN: + //simulating the continue/end button pressed + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "CloseContinueWindow"); + break; + } + return; //don't accept keys in dialog + } + Region Viewport = core->GetVideoDriver()->GetViewport(); + Game *game = core->GetGame(); + if (!game) return; + Map *map = game->GetCurrentArea(); + if (!map) return; + + Point mapsize = map->TMap->GetMapSize(); + int partysize = game->GetPartySize(false); + int pm; + char tmpstr[10]; + + switch (Key) { + case GEM_LEFT: + if (Viewport.x > 63) + Viewport.x -= 64; + else + Viewport.x = 0; + break; + case GEM_UP: + if (Viewport.y > 63) + Viewport.y -= 64; + else + Viewport.y = 0; + break; + case GEM_DOWN: + if (Viewport.y + Viewport.h + 64 < mapsize.y) + Viewport.y += 64; + else { + Viewport.y = mapsize.y - Viewport.h; + if (Viewport.y<0) Viewport.y=0; + } + break; + case GEM_RIGHT: + if (Viewport.x + Viewport.w + 64 < mapsize.x) + Viewport.x += 64; + else { + Viewport.x = mapsize.x - Viewport.w; + if (Viewport.x<0) Viewport.x=0; + } + break; + case GEM_ALT: + DebugFlags |= DEBUG_SHOW_CONTAINERS; + return; + case GEM_TAB: + // show partymember hp/maxhp as overhead text + for (pm=0; pm < partysize; pm++) { + Actor *pc = game->GetPC(pm, true); + if (!pc) continue; + //sucks but this is set in different places + if (pc->GetStat(IE_MC_FLAGS) & MC_HIDE_HP) continue; + if (pc->GetStat(IE_EXTSTATE_ID) & EXTSTATE_NO_HP) continue; + memset(tmpstr, 0, 10); + snprintf(tmpstr, 10, "%d/%d", pc->Modified[IE_HITPOINTS], pc->Modified[IE_MAXHITPOINTS]); + pc->DisplayHeadText(strdup(tmpstr)); + } + return; + case GEM_MOUSEOUT: + moveX = 0; + moveY = 0; + return; + case GEM_ESCAPE: + core->GetGUIScriptEngine()->RunFunction("GUICommonWindows", "EmptyControls"); + core->SetEventFlag(EF_ACTION); + return; + case GEM_PGUP: + core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnIncreaseSize"); + return; + case GEM_PGDOWN: + core->GetGUIScriptEngine()->RunFunction("CommonWindow","OnDecreaseSize"); + return; + default: + return; + } + if (ScreenFlags & SF_LOCKSCROLL) { + moveX = 0; + moveY = 0; + } + else { + // override any existing viewport moves which may be in progress + core->timer->SetMoveViewPort( Viewport.x, Viewport.y, 0, false ); + // move it directly ourselves, since we might be paused + core->GetVideoDriver()->MoveViewportTo( Viewport.x, Viewport.y ); + } +} + +void GameControl::CalculateSelection(const Point &p) +{ + unsigned int i; + + Game* game = core->GetGame(); + Map* area = game->GetCurrentArea( ); + if (DrawSelectionRect) { + if (p.x < StartX) { + SelectionRect.w = StartX - p.x; + SelectionRect.x = p.x; + } else { + SelectionRect.x = StartX; + SelectionRect.w = p.x - StartX; + } + if (p.y < StartY) { + SelectionRect.h = StartY - p.y; + SelectionRect.y = p.y; + } else { + SelectionRect.y = StartY; + SelectionRect.h = p.y - StartY; + } + Actor** ab; + unsigned int count = area->GetActorInRect( ab, SelectionRect,true ); + for (i = 0; i < highlighted.size(); i++) + highlighted[i]->SetOver( false ); + highlighted.clear(); + if (count != 0) { + for (i = 0; i < count; i++) { + ab[i]->SetOver( true ); + highlighted.push_back( ab[i] ); + } + } + free( ab ); + } else { + Actor* actor = area->GetActor( p, GA_DEFAULT | GA_SELECT | GA_NO_DEAD | GA_NO_ENEMY); + SetLastActor( actor, area->GetActorByGlobalID(lastActorID) ); +/* + Actor *lastActor = area->GetActorByGlobalID(lastActorID); + if (lastActor) + lastActor->SetOver( false ); + if (!actor) { + lastActorID = 0; + } else { + lastActorID = actor->globalID; + actor->SetOver( true ); + } +*/ + } +} + +void GameControl::SetLastActor(Actor *actor, Actor *prevActor) +{ + if (prevActor) + prevActor->SetOver( false ); + if (!actor) { + lastActorID = 0; + } else { + lastActorID = actor->GetGlobalID(); + actor->SetOver( true ); + } +} + +void GameControl::SetCutSceneMode(bool active) +{ + if (active) { + ScreenFlags |= (SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE); + moveX = 0; + moveY = 0; + } else { + ScreenFlags &= ~(SF_DISABLEMOUSE | SF_LOCKSCROLL | SF_CUTSCENE); + } +} + +//Change game window geometries when a new window gets deactivated +void GameControl::HandleWindowHide(const char *WindowName, const char *WindowPosition) +{ + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( WindowName, index )) { + if (index != (ieDword) -1) { + Window* w = core->GetWindow( (unsigned short) index ); + if (w) { + core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE ); + if (dict->Lookup( WindowPosition, index )) { + ResizeDel( w, index ); + } + return; + } + printMessage("GameControl", "Invalid Window Index: ", LIGHT_RED); + printf("%s:%u\n",WindowName, index); + } + } +} + +//Hide all other windows on the GUI (gamecontrol is not hidden by this) +int GameControl::HideGUI() +{ + //hidegui is in effect + if (!(ScreenFlags&SF_GUIENABLED) ) { + return 0; + } + //no gamecontrol visible + if (Owner->Visible == WINDOW_INVISIBLE ) { + return 0; + } + ScreenFlags &=~SF_GUIENABLED; + HandleWindowHide("PortraitWindow", "PortraitPosition"); + HandleWindowHide("OtherWindow", "OtherPosition"); + HandleWindowHide("TopWindow", "TopPosition"); + HandleWindowHide("OptionsWindow", "OptionsPosition"); + HandleWindowHide("MessageWindow", "MessagePosition"); + HandleWindowHide("ActionsWindow", "ActionsPosition"); + //FloatWindow doesn't affect gamecontrol, so it is special + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( "FloatWindow", index )) { + if (index != (ieDword) -1) { + core->SetVisible( (unsigned short) index, WINDOW_INVISIBLE ); + } + } + core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height ); + return 1; +} + +//Change game window geometries when a new window gets activated +void GameControl::HandleWindowReveal(const char *WindowName, const char *WindowPosition) +{ + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( WindowName, index )) { + if (index != (ieDword) -1) { + Window* w = core->GetWindow( (unsigned short) index ); + if (w) { + core->SetVisible( (unsigned short) index, WINDOW_VISIBLE ); + if (dict->Lookup( WindowPosition, index )) { + ResizeAdd( w, index ); + } + return; + } + printMessage("GameControl", "Invalid Window Index ", LIGHT_RED); + printf("%s:%u\n",WindowName, index); + } + } +} + +//Reveal all windows on the GUI (including this one) +int GameControl::UnhideGUI() +{ + if (ScreenFlags&SF_GUIENABLED) { + return 0; + } + + ScreenFlags |= SF_GUIENABLED; + // Unhide the gamecontrol window + core->SetVisible( 0, WINDOW_VISIBLE ); + + HandleWindowReveal("ActionsWindow", "ActionsPosition"); + HandleWindowReveal("MessageWindow", "MessagePosition"); + HandleWindowReveal("OptionsWindow", "OptionsPosition"); + HandleWindowReveal("TopWindow", "TopPosition"); + HandleWindowReveal("OtherWindow", "OtherPosition"); + HandleWindowReveal("PortraitWindow", "PortraitPosition"); + //the floatwindow is a special case + Variables* dict = core->GetDictionary(); + ieDword index; + + if (dict->Lookup( "FloatWindow", index )) { + if (index != (ieDword) -1) { + Window* fw = core->GetWindow( (unsigned short) index ); + if (fw) { + core->SetVisible( (unsigned short) index, WINDOW_VISIBLE ); + fw->Flags |=WF_FLOAT; + core->SetOnTop( index ); + } + } + } + core->GetVideoDriver()->SetViewport( Owner->XPos, Owner->YPos, Width, Height ); + return 1; +} + +//a window got removed, so the GameControl gets enlarged +void GameControl::ResizeDel(Window* win, int type) +{ + switch (type) { + case 0: //Left + if (LeftCount!=1) { + printMessage("GameControl","More than one left window!\n",LIGHT_RED); + } + LeftCount--; + if (!LeftCount) { + Owner->XPos -= win->Width; + Owner->Width += win->Width; + Width = Owner->Width; + } + break; + + case 1: //Bottom + if (BottomCount!=1) { + printMessage("GameControl","More than one bottom window!\n",LIGHT_RED); + } + BottomCount--; + if (!BottomCount) { + Owner->Height += win->Height; + Height = Owner->Height; + } + break; + + case 2: //Right + if (RightCount!=1) { + printMessage("GameControl","More than one right window!\n",LIGHT_RED); + } + RightCount--; + if (!RightCount) { + Owner->Width += win->Width; + Width = Owner->Width; + } + break; + + case 3: //Top + if (TopCount!=1) { + printMessage("GameControl","More than one top window!\n",LIGHT_RED); + } + TopCount--; + if (!TopCount) { + Owner->YPos -= win->Height; + Owner->Height += win->Height; + Height = Owner->Height; + } + break; + + case 4: //BottomAdded + BottomCount--; + Owner->Height += win->Height; + Height = Owner->Height; + break; + case 5: //Inactivating + BottomCount--; + Owner->Height += win->Height; + Height = Owner->Height; + break; + } +} + +//a window got added, so the GameControl gets shrunk +//Owner is the GameControl's window +//GameControl is the only control on that window +void GameControl::ResizeAdd(Window* win, int type) +{ + switch (type) { + case 0: //Left + LeftCount++; + if (LeftCount == 1) { + Owner->XPos += win->Width; + Owner->Width -= win->Width; + Width = Owner->Width; + } + break; + + case 1: //Bottom + BottomCount++; + if (BottomCount == 1) { + Owner->Height -= win->Height; + Height = Owner->Height; + } + break; + + case 2: //Right + RightCount++; + if (RightCount == 1) { + Owner->Width -= win->Width; + Width = Owner->Width; + } + break; + + case 3: //Top + TopCount++; + if (TopCount == 1) { + Owner->YPos += win->Height; + Owner->Height -= win->Height; + Height = Owner->Height; + } + break; + + case 4: //BottomAdded + BottomCount++; + Owner->Height -= win->Height; + Height = Owner->Height; + break; + + case 5: //Inactivating + BottomCount++; + Owner->Height -= win->Height; + Height = 0; + } +} + +//Create an overhead text over an arbitrary point +void GameControl::DisplayString(const Point &p, const char *Text) +{ + Scriptable* scr = new Scriptable( ST_TRIGGER ); + scr->overHeadText = (char *) Text; + scr->textDisplaying = 1; + scr->timeStartDisplaying = 0; + scr->Pos = p; +} + +//Create an overhead text over a scriptable target +//Multiple texts are possible, as this code copies the text to a new object +void GameControl::DisplayString(Scriptable* target) +{ + Scriptable* scr = new Scriptable( ST_TRIGGER ); + scr->overHeadText = strdup( target->overHeadText ); +/* strdup should work here, we use it elsewhere + size_t len = strlen( target->overHeadText ) + 1; + scr->overHeadText = ( char * ) malloc( len ); + strcpy( scr->overHeadText, target->overHeadText ); +*/ + scr->textDisplaying = 1; + scr->timeStartDisplaying = target->timeStartDisplaying; + scr->Pos = target->Pos; +} + +/** changes displayed map to the currently selected PC */ +void GameControl::ChangeMap(Actor *pc, bool forced) +{ + //swap in the area of the actor + Game* game = core->GetGame(); + if (forced || (stricmp( pc->Area, game->CurrentArea) != 0) ) { + dialoghandler->EndDialog(); + overInfoPoint = NULL; + overContainer = NULL; + overDoor = NULL; + /*this is loadmap, because we need the index, not the pointer*/ + char *areaname = game->CurrentArea; + if (pc) { + areaname = pc->Area; + } + game->GetMap( areaname, true ); + ScreenFlags|=SF_CENTERONACTOR; + } + //center on first selected actor + Video *video = core->GetVideoDriver(); + Region vp = video->GetViewport(); + if (ScreenFlags&SF_CENTERONACTOR) { + core->timer->SetMoveViewPort( pc->Pos.x, pc->Pos.y, 0, true ); + video->MoveViewportTo( pc->Pos.x-vp.w/2, pc->Pos.y-vp.h/2 ); + ScreenFlags&=~SF_CENTERONACTOR; + } +} + +void GameControl::SetScreenFlags(int value, int mode) +{ + switch(mode) { + case BM_OR: ScreenFlags|=value; break; + case BM_NAND: ScreenFlags&=~value; break; + case BM_SET: ScreenFlags=value; break; + case BM_AND: ScreenFlags&=value; break; + case BM_XOR: ScreenFlags^=value; break; + } +} + +void GameControl::SetDialogueFlags(int value, int mode) +{ + switch(mode) { + case BM_OR: DialogueFlags|=value; break; + case BM_NAND: DialogueFlags&=~value; break; + case BM_SET: DialogueFlags=value; break; + case BM_AND: DialogueFlags&=value; break; + case BM_XOR: DialogueFlags^=value; break; + } +} + +//copies a screenshot into a sprite +Sprite2D* GameControl::GetScreenshot(bool show_gui) +{ + Sprite2D* screenshot; + if (show_gui) { + screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0) ); + } else { + int hf = HideGUI (); + Draw (0, 0); + screenshot = core->GetVideoDriver()->GetScreenshot( Region( 0, 0, 0, 0 ) ); + if (hf) { + UnhideGUI (); + } + core->DrawWindows (); + } + + return screenshot; +} + +//copies a downscaled screenshot into a sprite for save game preview +Sprite2D* GameControl::GetPreview() +{ + // We get preview by first taking a screenshot of size 640x405, + // centered in the display. This is to get a decent picture for + // higher screen resolutions. + // FIXME: how do orig games solve that? + Video* video = core->GetVideoDriver(); + int w = video->GetWidth(); + int h = video->GetHeight(); + int x = (w - 640) / 2; + int y = (h - 405) / 2; + + if (x < 0) { + x = 0; + } else { + w = 640; + } + + if (y < 0) { + y = 0; + } else { + h = 405; + } + + if (!x) + y = 0; + + int hf = HideGUI (); + signed char v = Owner->Visible; + Owner->Visible = WINDOW_VISIBLE; + Draw (0, 0); + Owner->Visible = v; + Sprite2D *screenshot = video->GetScreenshot( Region(x, y, w, h) ); + if (hf) { + UnhideGUI (); + } + core->DrawWindows(); + + Sprite2D* preview = video->SpriteScaleDown ( screenshot, 5 ); + video->FreeSprite( screenshot ); + return preview; +} + + +/** + * Returns PC portrait for a currently running game + */ +Sprite2D* GameControl::GetPortraitPreview(int pcslot) +{ + /** Portrait shrink ratio */ + // FIXME: this is just a random PST specific trait + // you can make it less random with a new feature bit + int ratio = (core->HasFeature( GF_ONE_BYTE_ANIMID )) ? 1 : 2; + + Video *video = core->GetVideoDriver(); + + Actor *actor = core->GetGame()->GetPC( pcslot, false ); + if (! actor) { + return NULL; + } + ResourceHolder im(actor->GetPortrait(true)); + if (! im) { + return NULL; + } + + Sprite2D* img = im->GetSprite2D(); + + if (ratio == 1) + return img; + + Sprite2D* img_scaled = video->SpriteScaleDown( img, ratio ); + video->FreeSprite( img ); + + return img_scaled; +} + +Actor *GameControl::GetActorByGlobalID(ieDword globalID) +{ + if (!globalID) + return NULL; + Game* game = core->GetGame(); + if (!game) + return NULL; + + Map* area = game->GetCurrentArea( ); + if (!area) + return NULL; + return + area->GetActorByGlobalID(globalID); +} + +Actor *GameControl::GetLastActor() +{ + return GetActorByGlobalID(lastActorID); +} + +//Set up an item use which needs targeting +//Slot is an inventory slot +//header is the used item extended header +//u is the user +//target type is a bunch of GetActor flags that alter valid targets +//cnt is the number of different targets (usually 1) +void GameControl::SetupItemUse(int slot, int header, Actor *u, int targettype, int cnt) +{ + spellOrItem = -1; + spellUser = u; + spellSlot = slot; + spellIndex = header; + //item use also uses the casting icon, this might be changed in some custom game? + SetTargetMode(TARGET_MODE_CAST); + target_types = targettype; + spellCount = cnt; +} + +//Set up spell casting which needs targeting +//type is the spell's type +//level is the caster level +//idx is the spell's number +//u is the caster +//target type is a bunch of GetActor flags that alter valid targets +//cnt is the number of different targets (usually 1) +void GameControl::SetupCasting(int type, int level, int idx, Actor *u, int targettype, int cnt) +{ + spellOrItem = type; + spellUser = u; + spellSlot = level; + spellIndex = idx; + SetTargetMode(TARGET_MODE_CAST); + target_types = targettype; + spellCount = cnt; +} + +//another method inherited from Control which has no use here +bool GameControl::SetEvent(int /*eventType*/, EventHandler /*handler*/) +{ + return false; +} + +void GameControl::SetDisplayText(char *text, unsigned int time) +{ + if (DisplayText) { + core->FreeString(DisplayText); + } + DisplayTextTime = time; + DisplayText = text; +} + +void GameControl::SetDisplayText(ieStrRef text, unsigned int time) +{ + SetDisplayText(core->GetString(displaymsg->GetStringReference(text), 0), time); +} + diff --git a/project/jni/application/gemrb/src/core/GUI/GameControl.h b/project/jni/application/gemrb/src/core/GUI/GameControl.h new file mode 100644 index 000000000..8faa5e5ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/GameControl.h @@ -0,0 +1,252 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 GameControl.h + * Declares GameControl widget which is responsible for displaying areas, + * interacting with PCs, NPCs and the rest of the game world. + * @author The GemRB Project + */ + +#ifndef GAMECONTROL_H +#define GAMECONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Dialog.h" +#include "Interface.h" +#include "Map.h" + +class GameControl; +class Window; +class DialogHandler; + +//dialog flags +#define DF_IN_DIALOG 1 +#define DF_TALKCOUNT 2 +#define DF_UNBREAKABLE 4 +#define DF_FREEZE_SCRIPTS 8 +#define DF_INTERACT 16 +#define DF_IN_CONTAINER 32 +#define DF_OPENCONTINUEWINDOW 64 +#define DF_OPENENDWINDOW 128 + +//screen flags +// !!! Keep these synchronized with GUIDefines.py !!! +#define SF_DISABLEMOUSE 1 //no mouse cursor +#define SF_CENTERONACTOR 2 // +#define SF_ALWAYSCENTER 4 +#define SF_GUIENABLED 8 // +#define SF_LOCKSCROLL 16 //don't scroll +#define SF_CUTSCENE 32 //don't push new actions onto the action queue +#define SF_TRACKING 64 //draw blue arrows on the edge for creatures + +// target modes and types +// !!! Keep these synchronized with GUIDefines.py !!! +#define TARGET_MODE_NONE 0 +#define TARGET_MODE_TALK 1 +#define TARGET_MODE_ATTACK 2 +#define TARGET_MODE_CAST 3 +#define TARGET_MODE_DEFEND 4 +#define TARGET_MODE_PICK 5 + +/* +#define TARGET_SELECT 16 +#define TARGET_NO_DEAD 32 +#define TARGET_POINT 64 +#define TARGET_NO_HIDDEN 128 +#define TARGET_TYPE_NONE 0x000 +#define TARGET_NO_ALLY 0x100 //0x100 +#define TARGET_NO_ENEMY 0x200 //0x200 +#define TARGET_NO_NEUTRAL 0x400 +#define TARGET_NO_SELF 0x800 +#define TARGET_TYPE_ALL 0 //(TARGET_TYPE_ALLY | TARGET_TYPE_ENEMY | TARGET_TYPE_NEUTRAL) +*/ + +/** + * @class GameControl + * Widget displaying areas, where most of the game 'happens'. + * It allows for interacting with PCs, NPCs and the rest of the world. + * It's also a very core part of GemRB, as some processes are driven from it. + * It's always assigned Control index 0. + */ + +class GEM_EXPORT GameControl : public Control { +public: + GameControl(void); + ~GameControl(void); +public: + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets multiple quicksaves flag*/ + static void MultipleQuickSaves(int arg); + void SetTracker(Actor *actor, ieDword dist); +private: + ieDword lastActorID; + ieDword trackerID; + ieDword distance; //tracking distance + std::vector< Actor*> highlighted; + bool DrawSelectionRect; + bool MouseIsDown; + bool DoubleClick; + Region SelectionRect; + short StartX, StartY; + //int action; +public: + Door* overDoor; + Container* overContainer; + InfoPoint* overInfoPoint; + + // allow targetting allies, enemies and/or neutrals (bitmask) + int target_types; +private: + // currently selected targetting type, such as talk, attack, cast, ... + // private to enforce proper cursor changes + int target_mode; + unsigned char lastCursor; + short moveX, moveY; + int numScrollCursor; + bool scrolling; + unsigned short lastMouseX, lastMouseY; + int DebugFlags; + Point pfs; + PathNode* drawPath; + unsigned long AIUpdateCounter; + int ScreenFlags; + int DialogueFlags; + char *DisplayText; + unsigned int DisplayTextTime; + bool EnableRunning; +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Global Mouse Move Event */ + void OnGlobalMouseMove(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + void DisplayTooltip(); + void UpdateScrolling(); + void SetTargetMode(int mode); + int GetTargetMode() { return target_mode; } + void SetScreenFlags(int value, int mode); + void SetDialogueFlags(int value, int mode); + int GetScreenFlags() { return ScreenFlags; } + int GetDialogueFlags() { return DialogueFlags; } + /** this function is called from the area when autosave is needed */ + void AutoSave(); + void SetDisplayText(char *text, unsigned int time); + void SetDisplayText(ieStrRef text, unsigned int time); + /* centers viewport to the points specified */ + void Center(unsigned short x, unsigned short y); +private: + /** this function is called when the user presses 'q' (or equivalent) */ + void QuickSave(); + /** this function safely retrieves an Actor by ID */ + Actor *GetActorByGlobalID(ieDword ID); + void CalculateSelection(const Point &p); + void ResizeDel(Window* win, int type); + void ResizeAdd(Window* win, int type); + void HandleWindowHide(const char *WindowName, const char *WindowPosition); + void HandleWindowReveal(const char *WindowName, const char *WindowPosition); + void ReadFormations(); + /** Draws an arrow on the edge of the screen based on the point (points at offscreen actors) */ + void DrawArrowMarker(const Region &screen, Point p, const Region &viewport); + +private: + unsigned char LeftCount, BottomCount, RightCount, TopCount; + Actor *user; //the user of item or spell +public: + DialogHandler *dialoghandler; + //using spell or item + int spellOrItem; // -1 = item, otherwise the spell type + //the user of spell or item + Actor *spellUser; + int spellSlot, spellIndex; //or inventorySlot/itemHeader + int spellCount; //multiple targeting +public: + /** Selects one or all PC */ + void SelectActor(int whom, int type = -1); + void SetLastActor(Actor *actor, Actor *prevActor); + void SetCutSceneMode(bool active); + int HideGUI(); + int UnhideGUI(); + void TryToAttack(Actor *source, Actor *target); + void TryToBash(Actor *source, Scriptable *tgt); + void TryToCast(Actor *source, const Point &p); + void TryToCast(Actor *source, Actor *target); + void TryToDefend(Actor *source, Actor *target); + void TryToTalk(Actor *source, Actor *target); + void TryToPick(Actor *source, Actor *tgt); + void TryToPick(Actor *source, Door *tgt); + void TryToPick(Actor *source, Container *tgt); + void TryToDisarm(Actor *source, InfoPoint *tgt); + void PerformActionOn(Actor *actor); + void ResetTargetMode(); + + // returns the default cursor fitting the targeting mode + int GetDefaultCursor() const; + //containers + int GetCursorOverContainer(Container *overContainer) const; + void HandleContainer(Container *container, Actor *actor); + //doors + int GetCursorOverDoor(Door *overDoor) const; + void HandleDoor(Door *door, Actor *actor); + //infopoints + int GetCursorOverInfoPoint(InfoPoint *overInfoPoint) const; + bool HandleActiveRegion(InfoPoint *trap, Actor *actor, Point &p); + + Point GetFormationOffset(ieDword formation, ieDword pos); + void MoveToPointFormation(Actor *actor, unsigned int pos, Point src, Point p); + /** calls MoveToPoint or RunToPoint */ + void CreateMovement(Actor *actor, const Point &p); + /** Displays a string over an object */ + void DisplayString(Scriptable* target); + /** Displays a string on screen */ + void DisplayString(const Point &p, const char *Text); + Actor *GetLastActor(); + /** changes map to the current PC */ + void ChangeMap(Actor *pc, bool forced); + /** Returns game screenshot, with or without GUI controls */ + Sprite2D* GetScreenshot( bool show_gui = 0 ); + /** Returns current area preview for saving a game */ + Sprite2D* GetPreview(); + /** Returns PC portrait for a currently running game */ + Sprite2D* GetPortraitPreview(int pcslot); + /** Sets up targeting with spells or items */ + void SetupItemUse(int slot, int header, Actor *actor, int targettype, int cnt); + /** Page is the spell type + spell level info */ + void SetupCasting(int type, int level, int slot, Actor *actor, int targettype, int cnt); + bool SetEvent(int eventType, EventHandler handler); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Label.cpp b/project/jni/application/gemrb/src/core/GUI/Label.cpp new file mode 100644 index 000000000..4d67af4aa --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Label.cpp @@ -0,0 +1,152 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Label.h" + +#include "win32def.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" + +Label::Label(Font* font) +{ + this->font = font; + Buffer = NULL; + useRGB = false; + ResetEventHandler( LabelOnPress ); + + Alignment = IE_FONT_ALIGN_CENTER|IE_FONT_ALIGN_MIDDLE; + palette = NULL; +} +Label::~Label() +{ + gamedata->FreePalette( palette ); + if (Buffer) { + free( Buffer ); + } +} +/** Draws the Control on the Output Display */ +void Label::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT)) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + if (font && Buffer) { + font->Print( Region( this->XPos + x, this->YPos + y, + this->Width, this->Height ), ( unsigned char * ) Buffer, + useRGB?palette:NULL, + Alignment | IE_FONT_SINGLE_LINE, true ); + } + + if (AnimPicture) { + int xOffs = ( Width / 2 ) - ( AnimPicture->Width / 2 ); + int yOffs = ( Height / 2 ) - ( AnimPicture->Height / 2 ); + Region r( x + XPos + xOffs, y + YPos + yOffs, (int)(AnimPicture->Width), AnimPicture->Height ); + core->GetVideoDriver()->BlitSprite( AnimPicture, x + XPos + xOffs, y + YPos + yOffs, true, &r ); + } + +} +/** This function sets the actual Label Text */ +int Label::SetText(const char* string, int /*pos*/) +{ + if (Buffer ) + free( Buffer ); + if (Alignment == IE_FONT_ALIGN_CENTER) { + if (core->HasFeature( GF_LOWER_LABEL_TEXT )) { + int len = strlen(string); + Buffer = (char *) malloc( len+1 ); + strnlwrcpy( Buffer, string, len ); + } + else { + Buffer = strdup( string ); + } + } + else { + Buffer = strdup( string ); + } + if (!palette) { + Color white = {0xff, 0xff, 0xff, 0x00}, black = {0x00, 0x00, 0x00, 0x00}; + SetColor(white, black); + } + if (Owner) { + Owner->Invalidate(); + } + return 0; +} +/** Sets the Foreground Font Color */ +void Label::SetColor(Color col, Color bac) +{ + gamedata->FreePalette( palette ); + palette = core->CreatePalette( col, bac ); + Changed = true; +} + +void Label::SetAlignment(unsigned char Alignment) +{ + this->Alignment = Alignment; + if (Alignment == IE_FONT_ALIGN_CENTER) { + if (core->HasFeature( GF_LOWER_LABEL_TEXT )) { + strlwr( Buffer ); + } + } + Changed = true; +} + +void Label::OnMouseUp(unsigned short x, unsigned short y, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + //printf( "Label::OnMouseUp\n" ); + if (( x <= Width ) && ( y <= Height )) { + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + if (LabelOnPress) { + RunEventHandler( LabelOnPress ); + } + } +} + +bool Label::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_LABEL_ON_PRESS: + LabelOnPress = handler; + break; + default: + return false; + } + + return true; +} + +/** Simply returns the pointer to the text, don't modify it! */ +const char* Label::QueryText() const +{ + return ( const char * ) Buffer; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Label.h b/project/jni/application/gemrb/src/core/GUI/Label.h new file mode 100644 index 000000000..b0a841dfc --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Label.h @@ -0,0 +1,83 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Label.h + * Declares Label widget for displaying static texts + * @author GemRB Developement Team + */ + +#ifndef LABEL_H +#define LABEL_H + +#include "GUI/Control.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +class Palette; + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_LABEL_ON_PRESS 0x06000000 + +/** + * @class Label + * Label widget for displaying static texts in the GUI + */ + +class GEM_EXPORT Label : public Control { +public: + Label(Font* font); + ~Label(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** This function sets the actual Label Text */ + int SetText(const char* string, int pos = 0); + /** Sets the Foreground Font Color */ + void SetColor(Color col, Color bac); + /** Sets the Alignment of Text */ + void SetAlignment(unsigned char Alignment); + /** Simply returns the pointer to the text, don't modify it! */ + const char* QueryText() const; + + /** Mouse Button Down */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** Use the RGB Color for the Font */ + bool useRGB; + /** OnPress Scripted Event Function Name */ + EventHandler LabelOnPress; +private: // Private attributes + /** Text String Buffer */ + char* Buffer; + /** Font for Text Writing */ + Font* font; + /** Foreground & Background Colors */ + Palette* palette; + + /** Alignment Variable */ + unsigned char Alignment; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/MapControl.cpp b/project/jni/application/gemrb/src/core/GUI/MapControl.cpp new file mode 100644 index 000000000..4abd158ad --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/MapControl.cpp @@ -0,0 +1,540 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/MapControl.h" + +#include "win32def.h" + +#include "Game.h" +#include "Interface.h" +#include "Map.h" +#include "Video.h" + +#define MAP_NO_NOTES 0 +#define MAP_VIEW_NOTES 1 +#define MAP_SET_NOTE 2 +#define MAP_REVEAL 3 + +// Ratio between pixel sizes of an Area (Big map) and a Small map + +static int MAP_DIV = 3; +static int MAP_MULT = 32; + +typedef enum {black=0, gray, violet, green, orange, red, blue, darkblue, darkgreen} colorcode; + +Color colors[]={ + { 0x00, 0x00, 0x00, 0xff }, //black + { 0x60, 0x60, 0x60, 0xff }, //gray + { 0xa0, 0x00, 0xa0, 0xff }, //violet + { 0x00, 0xff, 0x00, 0xff }, //green + { 0xff, 0xff, 0x00, 0xff }, //orange + { 0xff, 0x00, 0x00, 0xff }, //red + { 0x00, 0x00, 0xff, 0xff }, //blue + { 0x00, 0x00, 0x80, 0xff }, //darkblue + { 0x00, 0x80, 0x00, 0xff } //darkgreen +}; + +#define MAP_TO_SCREENX(x) (XWin + XPos + XCenter - ScrollX + (x)) +#define MAP_TO_SCREENY(y) (YWin + YPos + YCenter - ScrollY + (y)) +// Omit [XY]Pos, since these macros are used in OnMouseDown(x, y), and x, y is +// already relative to control [XY]Pos there +#define SCREEN_TO_MAPX(x) ((x) - XCenter + ScrollX) +#define SCREEN_TO_MAPY(y) ((y) - YCenter + ScrollY) + +#define GAME_TO_SCREENX(x) MAP_TO_SCREENX((int)((x) * MAP_DIV / MAP_MULT)) +#define GAME_TO_SCREENY(y) MAP_TO_SCREENY((int)((y) * MAP_DIV / MAP_MULT)) + +#define SCREEN_TO_GAMEX(x) (SCREEN_TO_MAPX(x) * MAP_MULT / MAP_DIV) +#define SCREEN_TO_GAMEY(y) (SCREEN_TO_MAPY(y) * MAP_MULT / MAP_DIV) + +MapControl::MapControl(void) +{ + if (core->HasFeature(GF_IWD_MAP_DIMENSIONS) ) { + MAP_DIV=4; + MAP_MULT=32; + } else { + MAP_DIV=3; + MAP_MULT=32; + } + + LinkedLabel = NULL; + ScrollX = 0; + ScrollY = 0; + NotePosX = 0; + NotePosY = 0; + mouseIsDown = false; + mouseIsDragging = false; + Changed = true; + convertToGame = true; + memset(Flag,0,sizeof(Flag) ); + + // initialize var and event callback to no-ops + VarName[0] = 0; + ResetEventHandler( MapControlOnPress ); + ResetEventHandler( MapControlOnRightPress ); + ResetEventHandler( MapControlOnDoublePress ); + + MyMap = core->GetGame()->GetCurrentArea(); + if (MyMap->SmallMap) { + MapMOS = MyMap->SmallMap; + MapMOS->acquire(); + } else + MapMOS = NULL; +} + +MapControl::~MapControl(void) +{ + Video *video = core->GetVideoDriver(); + + if (MapMOS) { + video->FreeSprite(MapMOS); + } + for(int i=0;i<8;i++) { + if (Flag[i]) { + video->FreeSprite(Flag[i]); + } + } +} + +// Draw fog on the small bitmap +void MapControl::DrawFog(unsigned short XWin, unsigned short YWin) +{ + Video *video = core->GetVideoDriver(); + + Region old_clip; + video->GetClipRect(old_clip); + + Region r( XWin + XPos, YWin + YPos, Width, Height ); + video->SetClipRect(&r); + + // FIXME: this is ugly, the knowledge of Map and ExploredMask + // sizes should be in Map.cpp + int w = MyMap->GetWidth() / 2; + int h = MyMap->GetHeight() / 2; + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + Point p( (short) (MAP_MULT * x), (short) (MAP_MULT * y) ); + bool visible = MyMap->IsVisible( p, true ); + if (! visible) { + Region rgn = Region ( MAP_TO_SCREENX(MAP_DIV * x), MAP_TO_SCREENY(MAP_DIV * y), MAP_DIV, MAP_DIV ); + video->DrawRect( rgn, colors[black] ); + } + } + } + + video->SetClipRect(&old_clip); +} + +// To be called after changes in control's or screen geometry +void MapControl::Realize() +{ + // FIXME: ugly!! How to get area size in pixels? + //Map *map = core->GetGame()->GetCurrentMap(); + //MapWidth = map->GetWidth(); + //MapHeight = map->GetHeight(); + + if (MapMOS) { + MapWidth = (short) MapMOS->Width; + MapHeight = (short) MapMOS->Height; + } else { + MapWidth = 0; + MapHeight = 0; + } + + // FIXME: ugly hack! What is the actual viewport size? + ViewWidth = (short) (core->Width * MAP_DIV / MAP_MULT); + ViewHeight = (short) (core->Height * MAP_DIV / MAP_MULT); + + XCenter = (short) (Width - MapWidth ) / 2; + YCenter = (short) (Height - MapHeight ) / 2; + if (XCenter < 0) XCenter = 0; + if (YCenter < 0) YCenter = 0; +} + +void MapControl::RedrawMapControl(const char *VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + Value = Sum; + Changed = true; +} + +/** Draws the Control on the Output Display */ +void MapControl::Draw(unsigned short XWin, unsigned short YWin) +{ + if (!Width || !Height) { + return; + } + if (Owner->Visible!=WINDOW_VISIBLE) { + return; + } + + if (Changed) { + Realize(); + Changed = false; + } + + // we're going to paint over labels/etc, so they need to repaint! + bool seen_this = false; + unsigned int i; + for (i = 0; i < Owner->GetControlCount(); i++) { + Control *ctrl = Owner->GetControl(i); + if (!ctrl) continue; + + // we could try working out which controls overlap, + // but the later controls are cheap to paint.. + if (ctrl == this) { seen_this = true; continue; } + if (!seen_this) continue; + + ctrl->Changed = true; + } + + Video* video = core->GetVideoDriver(); + Region r( XWin + XPos, YWin + YPos, Width, Height ); + + if (MapMOS) { + video->BlitSprite( MapMOS, MAP_TO_SCREENX(0), MAP_TO_SCREENY(0), true, &r ); + } + + if (core->FogOfWar&FOG_DRAWFOG) + DrawFog(XWin, YWin); + + Region vp = video->GetViewport(); + + vp.x = GAME_TO_SCREENX(vp.x); + vp.y = GAME_TO_SCREENY(vp.y); + vp.w = ViewWidth; + vp.h = ViewHeight; + + video->DrawRect( vp, colors[green], false, false ); + + // Draw PCs' ellipses + Game *game = core->GetGame(); + i = game->GetPartySize(true); + while (i--) { + Actor* actor = game->GetPC( i, true ); + if (MyMap->HasActor(actor) ) { + video->DrawEllipse( (short) GAME_TO_SCREENX(actor->Pos.x), (short) GAME_TO_SCREENY(actor->Pos.y), 3, 2, actor->Selected ? colors[green] : colors[darkgreen], false ); + } + } + // Draw Map notes, could be turned off in bg2 + // we use the common control value to handle it, because then we + // don't need another interface + if (Value!=MAP_NO_NOTES) { + i = MyMap -> GetMapNoteCount(); + while (i--) { + MapNote * mn = MyMap -> GetMapNote(i); + Sprite2D *anim = Flag[mn->color&7]; + Point pos = mn->Pos; + if (convertToGame) { + vp.x = GAME_TO_SCREENX(mn->Pos.x); + vp.y = GAME_TO_SCREENY(mn->Pos.y); + } else { //pst style + vp.x = MAP_TO_SCREENX(mn->Pos.x); + vp.y = MAP_TO_SCREENY(mn->Pos.y); + pos.x = pos.x * MAP_MULT / MAP_DIV; + pos.y = pos.y * MAP_MULT / MAP_DIV; + } + + //Skip unexplored map notes + bool visible = MyMap->IsVisible( pos, true ); + if (!visible) + continue; + + if (anim) { + video->BlitSprite( anim, vp.x - anim->Width/2, vp.y - anim->Height/2, true, &r ); + } else { + video->DrawEllipse( (short) vp.x, (short) vp.y, 6, 5, colors[mn->color&7], false ); + } + } + } +} + +/** Key Press Event */ +void MapControl::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ +#ifdef ANDROID + switch(Key) { + case 'o': + case 'p': + Control::OnKeyPress(Key, 0); + break; + } +#else +(void)Key; // unused, fool the compiler +#endif +} + +/** Key Release Event */ +void MapControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + switch (Key) { + case '\t': + //not GEM_TAB + printf( "TAB released\n" ); + return; + case 'f': + if (Mod & GEM_MOD_CTRL) + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + default: + break; + } + if (!core->CheatEnabled()) { + return; + } +} +/** Mouse Over Event */ +void MapControl::OnMouseOver(unsigned short x, unsigned short y) +{ + if (mouseIsDown) { + ScrollX -= x - lastMouseX; + ScrollY -= y - lastMouseY; + + if (ScrollX > MapWidth - Width) + ScrollX = MapWidth - Width; + if (ScrollY > MapHeight - Height) + ScrollY = MapHeight - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; + } + + if (mouseIsDragging) { + ViewHandle(x,y); + } + + lastMouseX = x; + lastMouseY = y; + + switch (Value) { + case MAP_REVEAL: //for farsee effect + Owner->Cursor = IE_CURSOR_CAST; + break; + case MAP_SET_NOTE: + Owner->Cursor = IE_CURSOR_GRAB; + break; + default: + Owner->Cursor = IE_CURSOR_NORMAL; + break; + } + + if (Value == MAP_VIEW_NOTES || Value == MAP_SET_NOTE || Value == MAP_REVEAL) { + Point mp; + unsigned int dist; + + if (convertToGame) { + mp.x = (short) SCREEN_TO_GAMEX(x); + mp.y = (short) SCREEN_TO_GAMEY(y); + dist = 100; + } else { + mp.x = (short) SCREEN_TO_MAPX(x); + mp.y = (short) SCREEN_TO_MAPY(y); + dist = 16; + } + int i = MyMap -> GetMapNoteCount(); + while (i--) { + MapNote * mn = MyMap -> GetMapNote(i); + if (Distance(mp, mn->Pos)SetText( mn->text ); + } + NotePosX = mn->Pos.x; + NotePosY = mn->Pos.y; + return; + } + } + NotePosX = mp.x; + NotePosY = mp.y; + } + if (LinkedLabel) { + LinkedLabel->SetText( "" ); + } +} + +/** Mouse Leave Event */ +void MapControl::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + Owner->Cursor = IE_CURSOR_NORMAL; +} + +void MapControl::ClickHandle(unsigned short Button) +{ + core->GetDictionary()->SetAt( "MapControlX", NotePosX ); + core->GetDictionary()->SetAt( "MapControlY", NotePosY ); + switch(Button&GEM_MB_NORMAL) { + case GEM_MB_ACTION: + if (Button&GEM_MB_DOUBLECLICK) { + RunEventHandler( MapControlOnDoublePress ); + } else { + RunEventHandler( MapControlOnPress ); + } + break; + case GEM_MB_MENU: + RunEventHandler( MapControlOnRightPress ); + break; + default: + break; + } +} + +void MapControl::ViewHandle(unsigned short x, unsigned short y) +{ + short xp = (short) (SCREEN_TO_MAPX(x) - ViewWidth / 2); + short yp = (short) (SCREEN_TO_MAPY(y) - ViewHeight / 2); + + if (xp + ViewWidth > MapWidth) xp = MapWidth - ViewWidth; + if (yp + ViewHeight > MapHeight) yp = MapHeight - ViewHeight; + if (xp < 0) xp = 0; + if (yp < 0) yp = 0; + + // clear any previously scheduled moves and then do it asap, so it works while paused + unsigned int vpx = xp * MAP_MULT / MAP_DIV; + unsigned int vpy = yp * MAP_MULT / MAP_DIV; + core->timer->SetMoveViewPort( vpx, vpy, 0, false ); + core->GetVideoDriver()->MoveViewportTo( vpx, vpy ); +} + +/** Mouse Button Down */ +void MapControl::OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short /*Mod*/) +{ + switch((unsigned char) Button) { + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + return; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + return; + case GEM_MB_ACTION: + if (Button & GEM_MB_DOUBLECLICK) { + ClickHandle(Button); + } + break; + default: + break; + } + + mouseIsDown = true; + short xp = (short) (SCREEN_TO_GAMEX(x)); + short yp = (short) (SCREEN_TO_GAMEY(y)); + Region vp = core->GetVideoDriver()->GetViewport(); + vp.w = vp.x+ViewWidth*MAP_MULT/MAP_DIV; + vp.h = vp.y+ViewHeight*MAP_MULT/MAP_DIV; + if ((xp>vp.x) && (xpvp.y) && (yp MapWidth - Width) + ScrollX = MapWidth - Width; + if (ScrollY > MapHeight - Height) + ScrollY = MapHeight - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; +} + +bool MapControl::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_MAP_ON_PRESS: + MapControlOnPress = handler; + break; + case IE_GUI_MAP_ON_RIGHT_PRESS: + MapControlOnRightPress = handler; + break; + case IE_GUI_MAP_ON_DOUBLE_PRESS: + MapControlOnDoublePress = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/MapControl.h b/project/jni/application/gemrb/src/core/GUI/MapControl.h new file mode 100644 index 000000000..1154bf8c1 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/MapControl.h @@ -0,0 +1,110 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 MapControl.h + * Declares MapControl, widget for displaying current area map + */ + +class MapControl; + +#ifndef MAPCONTROL_H +#define MAPCONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" +#include "Interface.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_MAP_ON_PRESS 0x09000000 +#define IE_GUI_MAP_ON_RIGHT_PRESS 0x09000005 +#define IE_GUI_MAP_ON_DOUBLE_PRESS 0x09000008 + + +/** + * @class MapControl + * Widget displaying current area map, with a viewport rectangle + * and PCs' ground circles + */ + +class GEM_EXPORT MapControl : public Control { +public: + int ScrollX, ScrollY; + int NotePosX, NotePosY; + unsigned short lastMouseX, lastMouseY; + bool mouseIsDown; + bool mouseIsDragging; + bool convertToGame; + // Small map bitmap + Sprite2D* MapMOS; + // current map + Map *MyMap; + // map flags + Sprite2D *Flag[8]; + // The MapControl can set the text of this label directly + Control *LinkedLabel; + // Size of big map (area) in pixels + short MapWidth, MapHeight; + // Size of area viewport. FIXME: hack! + short ViewWidth, ViewHeight; + short XCenter, YCenter; + EventHandler MapControlOnPress; + EventHandler MapControlOnRightPress; + EventHandler MapControlOnDoublePress; + + MapControl(void); + ~MapControl(void); + /** redraws the control after its associated variable has changed */ + void RedrawMapControl(const char *VariableName, unsigned int Sum); + /** Draws the Control on the Output Display */ + void Draw(unsigned short XWin, unsigned short YWin); + void DrawFog(unsigned short XWin, unsigned short YWin); + /** Compute parameters after changes in control's or screen geometry */ + void Realize(); + /** Sets the Text of the current control */ + int SetText(const char* /*string*/, int /*pos*/) { return 0; } + + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); +private: + /** Call event handler on click */ + void ClickHandle(unsigned short Button); + /** Move viewport */ + void ViewHandle(unsigned short x, unsigned short y); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp b/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp new file mode 100644 index 000000000..d31f58958 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Progressbar.cpp @@ -0,0 +1,184 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Progressbar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +#include + +Progressbar::Progressbar( unsigned short KnobStepsCount, bool Clear) +{ + BackGround = NULL; + BackGround2 = NULL; + this->Clear = Clear; + this->KnobStepsCount = KnobStepsCount; + PBarAnim = NULL; + PBarCap = NULL; + KnobXPos = KnobYPos = 0; + CapXPos = CapYPos = 0; + ResetEventHandler( EndReached ); +} + +Progressbar::~Progressbar() +{ + if (!Clear) { + return; + } + core->GetVideoDriver()->FreeSprite( BackGround ); + core->GetVideoDriver()->FreeSprite( BackGround2 ); + delete PBarAnim; + core->GetVideoDriver()->FreeSprite( PBarCap ); +} + +/** Draws the Control on the Output Display */ +void Progressbar::Draw(unsigned short x, unsigned short y) +{ + //it is unlikely that a floating window is above us, but... + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + Sprite2D *bcksprite; + + if((Value >= 100) && KnobStepsCount && BackGround2) { + bcksprite=BackGround2; //animated progbar end stage + } + else { + bcksprite=BackGround; + } + if (bcksprite) { + Region r( x + XPos, y + YPos, Width, Height ); + core->GetVideoDriver()->BlitSprite( bcksprite, + x + XPos, y + YPos, true, &r ); + if( bcksprite==BackGround2) { + return; //done for animated progbar + } + } + + unsigned int Count; + + if(!KnobStepsCount) { + //linear progressbar (pst, iwd) + int w = BackGround2->Width; + int h = BackGround2->Height; + //this is the PST/IWD specific part + Count = Value*w/100; + Region r( x + XPos + KnobXPos, y + YPos + KnobYPos, Count, h ); + core->GetVideoDriver()->BlitSprite( BackGround2, + r.x, r.y, true, &r ); + + core->GetVideoDriver()->BlitSprite( PBarCap, + x+XPos+CapXPos+Count-PBarCap->Width, y+YPos+CapYPos, true ); + return; + } + + //animated progressbar (bg2) + Count=Value*KnobStepsCount/100; + for(unsigned int i=0; iGetFrame(i); + core->GetVideoDriver()->BlitSprite( Knob, x , y , true ); + } +} + +/** Returns the actual Progressbar Position */ +unsigned int Progressbar::GetPosition() +{ + return Value; +} + +/** Sets the actual Progressbar Position trimming to the Max and Min Values */ +void Progressbar::SetPosition(unsigned int pos) +{ + if(pos>100) pos=100; + if (Value == pos) + return; + Value = pos; + Changed = true; +} + +void Progressbar::RedrawProgressbar(const char* VariableName, int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + SetPosition((unsigned int) Sum); + if((Value==100) && Changed) + RunEventHandler( EndReached ); +} + +/** Sets the selected image */ +void Progressbar::SetImage(Sprite2D* img, Sprite2D* img2) +{ + if (BackGround && Clear) + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = img; + if (BackGround2 && Clear) + core->GetVideoDriver()->FreeSprite( BackGround2 ); + BackGround2 = img2; + Changed = true; +} + +void Progressbar::SetBarCap(Sprite2D* img3) +{ + core->GetVideoDriver()->FreeSprite( PBarCap ); + PBarCap = img3; +} + +void Progressbar::SetAnimation(Animation *arg) +{ + delete PBarAnim; + PBarAnim = arg; +} + +void Progressbar::SetSliderPos(int x, int y, int x2, int y2) +{ + KnobXPos=x; + KnobYPos=y; + CapXPos=x2; + CapYPos=y2; +} + +/* dummy virtual function */ +int Progressbar::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +bool Progressbar::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_PROGRESS_END_REACHED: + EndReached = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Progressbar.h b/project/jni/application/gemrb/src/core/GUI/Progressbar.h new file mode 100644 index 000000000..411ae5510 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Progressbar.h @@ -0,0 +1,89 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * 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 Progressbar.h + * Declares Progressbar widget for displaying progress of loading and saving games + */ + +#ifndef PROGRESSBAR_H +#define PROGRESSBAR_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Animation.h" +#include "Sprite2D.h" + +// !!! Keep in sync with GUIDefines.py !!! +#define IE_GUI_PROGRESS_END_REACHED 0x01000000 + + +/** + * @class Progressbar + * Widget for displaying progressbars, mainly on loading/saving screens + */ + +class GEM_EXPORT Progressbar : public Control { +public: + Progressbar(unsigned short KnobStepsCount, bool Clear = false); + ~Progressbar(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Returns the actual Progressbar Position */ + unsigned int GetPosition(); + /** Sets the actual Progressbar Position trimming to the Max and Min Values */ + void SetPosition(unsigned int pos); + /** Sets the background images */ + void SetImage(Sprite2D * img, Sprite2D * img2); + /** Sets a bam resource for progressbar */ + void SetAnimation(Animation *arg); + /** Sets a mos resource for progressbar cap */ + void SetBarCap(Sprite2D *img3); + /** Sets the mos coordinates for the progressbar filler mos/cap */ + void SetSliderPos(int x, int y, int x2, int y2); + /** Dummy function */ + int SetText(const char * string, int pos = 0); + /** Redraws a progressbar which is associated with VariableName */ + void RedrawProgressbar(const char *VariableName, int Sum); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + +private: // Private attributes + /** BackGround Images. If smaller than the Control Size, the image will be tiled. */ + Sprite2D * BackGround; + Sprite2D * BackGround2; //mos resource for the filling of the bar + /** Knob Steps Count */ + unsigned int KnobStepsCount; + int KnobXPos, KnobYPos; //relative coordinates for Background2 + int CapXPos, CapYPos; //relative coordinates for PBarCap + /** If true, on deletion the Progressbar will destroy the associated images */ + bool Clear; + /** The bam cycle whose frames work as a progressbar (animated progressbar) */ + Animation *PBarAnim; + /** The most for the progressbar cap (linear progressbar) */ + Sprite2D *PBarCap; +public: + /** EndReached Scripted Event Function Name */ + EventHandler EndReached; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp b/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp new file mode 100644 index 000000000..aea1b9833 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/ScrollBar.cpp @@ -0,0 +1,297 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/ScrollBar.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Variables.h" +#include "Video.h" + +ScrollBar::ScrollBar(void) +{ + Pos = 0; + Value = 10; + State = 0; + ResetEventHandler( ScrollBarOnChange ); + ta = NULL; + for(int i=0;iGetVideoDriver(); + for(int i=0;iFreeSprite(Frames[i]); + } + } +} + +/** Sets a new position, relays the change to an associated textarea and calls + any existing GUI OnChange callback */ +void ScrollBar::SetPos(int NewPos) +{ + if (Pos && ( Pos == NewPos )) { + return; + } + Changed = true; + Pos = (ieWord) NewPos; + if (ta) { + TextArea* t = ( TextArea* ) ta; + t->SetRow( Pos ); + } + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Pos ); + } + RunEventHandler( ScrollBarOnChange ); +} + +/** Refreshes the ScrollBar according to a guiscript variable */ +void ScrollBar::RedrawScrollBar(const char* Variable, int Sum) +{ + if (strnicmp( VarName, Variable, MAX_VARIABLE_LENGTH )) { + return; + } + SetPos( Sum ); +} + +/** Mousewheel support */ +void ScrollBar::ScrollUp() +{ + if (Pos > 0) { + SetPos( Pos - 1 ); + } +} + +/** Mousewheel support */ +void ScrollBar::ScrollDown() +{ + if ( (ieDword) Pos + 1 < Value ) { + SetPos( Pos + 1 ); + } +} + +/** Draws the ScrollBar control */ +void ScrollBar::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + int upMy = Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + int doMy = Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height; + unsigned int domy = (Height - doMy); + + unsigned short slmy = ( unsigned short ) + ( upMy + + ( Pos * ( ( domy - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height - upMy ) / + ( double ) ( Value < 2 ? 1 : Value - 1 ) ) ) ); + unsigned short slx = ( unsigned short ) ((Width - Frames[IE_GUI_SCROLLBAR_SLIDER]->Width) / 2 ); + + if (( State & UP_PRESS ) != 0) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_UP_PRESSED], + x + XPos, y + YPos, true ); + } else { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED], + x + XPos, y + YPos, true ); + } + int maxy = y + YPos + Height - + Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height; + int stepy = Frames[IE_GUI_SCROLLBAR_TROUGH]->Height; + Region rgn( x + XPos, y + YPos + upMy, Width, domy - upMy); + for (int dy = y + YPos + upMy; dy < maxy; dy += stepy) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_TROUGH], + x + XPos + ( ( Width / 2 ) - + Frames[IE_GUI_SCROLLBAR_TROUGH]->Width / 2 ), + dy, true, &rgn ); + } + if (( State & DOWN_PRESS ) != 0) { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_DOWN_PRESSED], + x + XPos, maxy, true ); + } else { + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED], + x + XPos, maxy, true ); + } + core->GetVideoDriver()->BlitSprite( Frames[IE_GUI_SCROLLBAR_SLIDER], + x + XPos + slx + Frames[IE_GUI_SCROLLBAR_SLIDER]->XPos, + y + YPos + slmy + Frames[IE_GUI_SCROLLBAR_SLIDER]->YPos, + true ); +} + +/** Sets a ScrollBar GUI resource */ +void ScrollBar::SetImage(unsigned char type, Sprite2D* img) +{ + if (type >= SB_RES_COUNT) { + return; + } + if (Frames[type]) { + core->GetVideoDriver()->FreeSprite(Frames[type]); + } + Frames[type] = img; + Changed = true; +} + +/** Mouse Button Down */ +void ScrollBar::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short /*Mod*/) +{ + //removing the double click flag, use a more sophisticated method + //if it is needed later + Button&=GEM_MB_NORMAL; + if (Button==GEM_MB_SCRLUP) { + ScrollUp(); + return; + } + if (Button==GEM_MB_SCRLDOWN) { + ScrollDown(); + return; + } + + core->RedrawAll(); + + unsigned short upMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Width; + unsigned short upMy = (unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + unsigned short domy = (unsigned short) (Height - Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height); + unsigned short slheight = domy - upMy; + unsigned short refheight = (unsigned short) (slheight - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + double step = refheight / (double) ( Value < 2 ? 1 : Value - 1 ); + unsigned short ymax = upMy + refheight; + unsigned short ymy = y - upMy; + unsigned short doMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Width; + unsigned short slMx = (unsigned short) Frames[IE_GUI_SCROLLBAR_SLIDER]->Width; + unsigned short slmy = (unsigned short) (upMy + Pos * step); + unsigned short slMy = (unsigned short) (slmy + Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + if (( x <= upMx ) && ( y <= upMy )) { + if (Pos > 0) + SetPos( Pos - 1 ); + State |= UP_PRESS; + return; + } + if (y >= domy) { + if (( x <= doMx ) && ( y <= Height )) { + if ( (ieDword) Pos + 1 < Value ) + SetPos( Pos + 1 ); + State |= DOWN_PRESS; + return; + } + } + if (y >= slmy) { + if (( x <= slMx ) && ( y <= slMy )) { + State |= SLIDER_GRAB; + return; + } + } + if (y <= upMy) { + SetPos( 0 ); + return; + } + if (y >= ymax) { + SetPos( Value - 1 ); + return; + } + unsigned short befst = ( unsigned short ) ( ymy / step ); + unsigned short aftst = befst + 1; + if (( ymy - ( befst * step ) ) < ( ( aftst * step ) - ymy )) { + SetPos( befst ); + } else { + SetPos( aftst ); + } +} + +/** Mouse Button Up */ +void ScrollBar::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short /*Button*/, unsigned short /*Mod*/) +{ + Changed = true; + State = 0; +} + +/** Mouse Over Event */ +void ScrollBar::OnMouseOver(unsigned short /*x*/, unsigned short y) +{ + if (( State & SLIDER_GRAB ) != 0) { + core->RedrawAll(); + unsigned short upMy =(unsigned short) Frames[IE_GUI_SCROLLBAR_UP_UNPRESSED]->Height; + unsigned short domy = (unsigned short) (Height - Frames[IE_GUI_SCROLLBAR_DOWN_UNPRESSED]->Height); + unsigned short slheight = domy - upMy; + unsigned short refheight = (unsigned short) (slheight - Frames[IE_GUI_SCROLLBAR_SLIDER]->Height); + double step = refheight / ( double ) ( Value < 2 ? 1 : Value - 1 ); + unsigned short yzero = (unsigned short) (upMy + Frames[IE_GUI_SCROLLBAR_SLIDER]->Height / 2 ); + unsigned short ymax = yzero + refheight; + unsigned short ymy = y - yzero; + if (y <= yzero) { + SetPos( 0 ); + return; + } + if (y >= ymax) { + SetPos( Value - 1 ); + return; + } + unsigned short befst = ( unsigned short ) ( ymy / step ); + unsigned short aftst = befst + 1; + if (( ymy - ( befst * step ) ) < ( ( aftst * step ) - ymy )) { + if (befst > Value ) + SetPos( befst ); + } else { + if (aftst < Value ) + SetPos( aftst ); + } + } +} + +/** Sets the Text of the current control */ +int ScrollBar::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Sets the Maximum Value of the ScrollBar */ +void ScrollBar::SetMax(unsigned short Max) +{ + Value = Max; + if (Max == 0) { + SetPos( 0 ); + } else if (Pos >= Max) { + SetPos( Max - 1 ); + } +} + +/** Sets the ScrollBarOnChange event (guiscript callback) */ +bool ScrollBar::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_SCROLLBAR_ON_CHANGE: + ScrollBarOnChange = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/ScrollBar.h b/project/jni/application/gemrb/src/core/GUI/ScrollBar.h new file mode 100644 index 000000000..4a2d172ac --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/ScrollBar.h @@ -0,0 +1,103 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 ScrollBar.h + * Declares ScrollBar widget for paging in long text windows. + * This does not include scales and sliders, which are of Slider class. + * @author The GemRB Project + */ + +#ifndef SCROLLBAR_H +#define SCROLLBAR_H + +#include "GUI/Control.h" +#include "GUI/TextArea.h" + +#include "exports.h" + +#include "Sprite2D.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_SCROLLBAR_ON_CHANGE 0x07000000 + +#define IE_GUI_SCROLLBAR_DEFAULT 0x00000040 // mousewheel triggers it + +#define IE_GUI_SCROLLBAR_UP_UNPRESSED 0 +#define IE_GUI_SCROLLBAR_UP_PRESSED 1 +#define IE_GUI_SCROLLBAR_DOWN_UNPRESSED 2 +#define IE_GUI_SCROLLBAR_DOWN_PRESSED 3 +#define IE_GUI_SCROLLBAR_TROUGH 4 +#define IE_GUI_SCROLLBAR_SLIDER 5 + +#define UP_PRESS 0x0001 +#define DOWN_PRESS 0x0010 +#define SLIDER_GRAB 0x0100 + +/** + * @class ScrollBar + * Widget displaying scrollbars for paging in long text windows + */ + +#define SB_RES_COUNT 6 + +class GEM_EXPORT ScrollBar : public Control { +public: + ScrollBar(void); + ~ScrollBar(void); + /**sets position, updates associated stuff */ + void SetPos(int NewPos); + void ScrollUp(); + void ScrollDown(); + /**redraws scrollbar if associated with VarName */ + void RedrawScrollBar(const char* VarName, int Sum); + /**/ + void Draw(unsigned short x, unsigned short y); +private: //Private attributes + /** Images for drawing the Scroll Bar */ + Sprite2D* Frames[SB_RES_COUNT]; + /** Cursor Position */ + unsigned short Pos; + /** Scroll Bar Status */ + unsigned short State; + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); +public: + void SetImage(unsigned char type, Sprite2D* img); + /** Sets the Maximum Value of the ScrollBar */ + void SetMax(unsigned short Max); + /** TextArea Associated Control */ + Control* ta; +public: // Public Events + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler ScrollBarOnChange; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Slider.cpp b/project/jni/application/gemrb/src/core/GUI/Slider.cpp new file mode 100644 index 000000000..09812ab3c --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Slider.cpp @@ -0,0 +1,296 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Slider.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Variables.h" +#include "Video.h" + +#include + +Slider::Slider(short KnobXPos, short KnobYPos, short KnobStep, + unsigned short KnobStepsCount, bool Clear) +{ + this->KnobXPos = KnobXPos; + this->KnobYPos = KnobYPos; + this->KnobStep = KnobStep; + this->KnobStepsCount = KnobStepsCount; + Knob = NULL; + GrabbedKnob = NULL; + BackGround = NULL; + this->Clear = Clear; + ResetEventHandler( SliderOnChange ); + State = IE_GUI_SLIDER_KNOB; + Pos = 0; + Value = 1; +} + +Slider::~Slider() +{ + if (!Clear) { + return; + } + if (Knob) { + core->GetVideoDriver()->FreeSprite( Knob ); + } + if (GrabbedKnob) { + core->GetVideoDriver()->FreeSprite( GrabbedKnob ); + } + if (BackGround) { + core->GetVideoDriver()->FreeSprite( BackGround ); + } +} + +/** Draws the Control on the Output Display */ +void Slider::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + if (XPos == 65535) { + return; + } + Region r( x + XPos, y + YPos, Width, Height ); + if (BackGround) { + if (( BackGround->Width < Width ) || ( BackGround->Height < Height )) { + core->GetVideoDriver()->BlitTiled( r, BackGround, true ); + } else { + core->GetVideoDriver()->BlitSprite( BackGround, x + XPos, y + YPos, true, &r ); + } + } + switch (State) { + case IE_GUI_SLIDER_KNOB: + core->GetVideoDriver()->BlitSprite( Knob, + x + XPos + KnobXPos + ( Pos * KnobStep ), + y + YPos + KnobYPos, true ); + break; + + case IE_GUI_SLIDER_GRABBEDKNOB: + core->GetVideoDriver()->BlitSprite( GrabbedKnob, + x + XPos + KnobXPos + ( Pos * KnobStep ), + y + YPos + KnobYPos, true ); + break; + } +} + +/** Returns the actual Slider Position */ +unsigned int Slider::GetPosition() +{ + return Pos; +} + +/** Sets the actual Slider Position trimming to the Max and Min Values */ +void Slider::SetPosition(unsigned int pos) +{ + if (pos <= KnobStepsCount) { + Pos = pos; + } + if (VarName[0] != 0) { + if (!Value) + Value = 1; + core->GetDictionary()->SetAt( VarName, pos * Value ); + } + Changed = true; +} + +/** Redraws a slider which is associated with VariableName */ +void Slider::RedrawSlider(const char* VariableName, int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + if (!Value) { + Value = 1; + } + Sum /= Value; + if (Sum <= KnobStepsCount) { + Pos = Sum; + } + Changed = true; +} + +/** Sets the selected image */ +void Slider::SetImage(unsigned char type, Sprite2D* img) +{ + switch (type) { + case IE_GUI_SLIDER_KNOB: + if (Knob && Clear) + core->GetVideoDriver()->FreeSprite( Knob ); + Knob = img; + break; + + case IE_GUI_SLIDER_GRABBEDKNOB: + if (GrabbedKnob && Clear) + core->GetVideoDriver()->FreeSprite( GrabbedKnob ); + GrabbedKnob = img; + break; + + case IE_GUI_SLIDER_BACKGROUND: + if (BackGround && Clear) + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = img; + break; + } + Changed = true; +} + +/** Mouse Button Down */ +void Slider::OnMouseDown(unsigned short x, unsigned short y, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + Changed = true; + unsigned int oldPos = Pos; + int mx = (KnobXPos + ( Pos * KnobStep ) - Knob->XPos); + int my = (KnobYPos - Knob->YPos); + int Mx = (mx + Knob->Width); + int My = (my + Knob->Height); + + if (( x >= mx ) && ( y >= my )) { + if (( x <= Mx ) && ( y <= My )) { + State = IE_GUI_SLIDER_GRABBEDKNOB; + } else { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < + ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } + } else { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } +} + +/** Mouse Button Up */ +void Slider::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + if (State != IE_GUI_SLIDER_KNOB) { + Changed = true; + } + State = IE_GUI_SLIDER_KNOB; +} + +/** Mouse Over Event */ +void Slider::OnMouseOver(unsigned short x, unsigned short /*y*/) +{ + Changed = true; + unsigned int oldPos = Pos; + if (State == IE_GUI_SLIDER_GRABBEDKNOB) { + int mx = KnobXPos; + int xmx = x - mx; + if (x < mx) { + SetPosition( 0 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + int befst = xmx / KnobStep; + if (befst >= KnobStepsCount) { + SetPosition( KnobStepsCount - 1 ); + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + return; + } + short aftst = befst + KnobStep; + if (( xmx - ( befst * KnobStep ) ) < ( ( aftst * KnobStep ) - xmx )) { + SetPosition( befst ); + } else { + SetPosition( aftst ); + } + if (oldPos != Pos) { + RunEventHandler( SliderOnChange ); + } + } +} + +/** Sets the Text of the current control */ +int Slider::SetText(const char* /*string*/, int /*pos*/) +{ + return 0; +} + +/** Sets the slider change event */ +bool Slider::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_SLIDER_ON_CHANGE: + SliderOnChange = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/Slider.h b/project/jni/application/gemrb/src/core/GUI/Slider.h new file mode 100644 index 000000000..66f26ae0a --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Slider.h @@ -0,0 +1,106 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Slider.h + * Declares Slider widget for displaying scales and sliders for setting + * numerical values + * @author The GemRB Project + */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Sprite2D.h" + +// !!! Keep these synchronized with GUIDefines.py !!! +#define IE_GUI_SLIDER_ON_CHANGE 0x02000000 + + +#define IE_GUI_SLIDER_KNOB 0 +#define IE_GUI_SLIDER_GRABBEDKNOB 1 +#define IE_GUI_SLIDER_BACKGROUND 2 + +/** + * @class Slider + * Widget displaying sliders or scales for inputting numerical values + * with a limited range + */ + +class GEM_EXPORT Slider : public Control { +public: + Slider(short KnobXPos, short KnobYPos, short KnobStep, unsigned short KnobStepsCount, bool Clear = false); + ~Slider(); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Returns the actual Slider Position */ + unsigned int GetPosition(); + /** Sets the actual Slider Position trimming to the Max and Min Values */ + void SetPosition(unsigned int pos); + /** Sets the selected image */ + void SetImage(unsigned char type, Sprite2D * img); + /** Sets the Text of the current control */ + int SetText(const char * string, int pos = 0); + /** Sets the State of the Slider */ + void SetState(int arg) { State=(unsigned char) arg; } + /** Redraws a slider which is associated with VariableName */ + void RedrawSlider(const char *VariableName, int Sum); + +private: // Private attributes + /** BackGround Image. If smaller than the Control Size, the image will be tiled. */ + Sprite2D * BackGround; + /** Knob Image */ + Sprite2D * Knob; + /** Grabbed Knob Image */ + Sprite2D * GrabbedKnob; + /** Knob Starting X Position */ + short KnobXPos; + /** Knob Starting Y Position */ + short KnobYPos; + /** Knob Step Size */ + short KnobStep; + /** Knob Steps Count */ + unsigned short KnobStepsCount; + /** If true, on deletion the Slider will destroy the associated images */ + bool Clear; + /** Actual Knob Status */ + unsigned char State; + /** Slider Position Value */ + unsigned int Pos; +public: // Public Events + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler SliderOnChange; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/TextArea.cpp b/project/jni/application/gemrb/src/core/GUI/TextArea.cpp new file mode 100644 index 000000000..6f3d05d43 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextArea.cpp @@ -0,0 +1,958 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/TextArea.h" + +#include "GUI/GameControl.h" + +#include "win32def.h" + +#include "Audio.h" +#include "DialogHandler.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Palette.h" +#include "Variables.h" +#include "Video.h" +#include "Scriptable/Actor.h" + +#include +#include + +TextArea::TextArea(Color hitextcolor, Color initcolor, Color lowtextcolor) +{ + keeplines = 100; + rows = 0; + startrow = 0; + minrow = 0; + Cursor = NULL; + CurPos = 0; + CurLine = 0; + seltext = -1; + Value = 0xffffffff; + ResetEventHandler( TextAreaOnChange ); + ResetEventHandler( TextAreaOutOfText ); + PortraitResRef[0]=0; + palette = core->CreatePalette( hitextcolor, lowtextcolor ); + initpalette = core->CreatePalette( initcolor, lowtextcolor ); + Color tmp = { + hitextcolor.b, hitextcolor.g, hitextcolor.r, 0 + }; + selected = core->CreatePalette( tmp, lowtextcolor ); + tmp.r = 255; + tmp.g = 152; + tmp.b = 102; + lineselpal = core->CreatePalette( tmp, lowtextcolor ); + InternalFlags = 1; + //Drop Capitals means initials on! + core->GetDictionary()->Lookup("Drop Capitals", InternalFlags); + if (InternalFlags) { + InternalFlags = TA_INITIALS; + } +} + +TextArea::~TextArea(void) +{ + gamedata->FreePalette( palette ); + gamedata->FreePalette( initpalette ); + gamedata->FreePalette( selected ); + gamedata->FreePalette( lineselpal ); + core->GetVideoDriver()->FreeSprite( Cursor ); + for (size_t i = 0; i < lines.size(); i++) { + free( lines[i] ); + } +} + +void TextArea::RefreshSprite(const char *portrait) +{ + if (AnimPicture) { + if (!strnicmp(PortraitResRef, portrait, 8) ) { + return; + } + SetAnimPicture(NULL); + } + strnlwrcpy(PortraitResRef, portrait, 8); + if (!strnicmp(PortraitResRef, "none", 8) ) { + return; + } + ResourceHolder im(PortraitResRef); + if (im == NULL) { + return; + } + + SetAnimPicture ( im->GetSprite2D() ); +} + +void TextArea::Draw(unsigned short x, unsigned short y) +{ + /** Don't come back recursively */ + if (InternalFlags&TA_BITEMYTAIL) { + return; + } + int tx=x+XPos; + int ty=y+YPos; + Region clip( tx, ty, Width, Height ); + Video *video = core->GetVideoDriver(); + + if (Flags&IE_GUI_TEXTAREA_SPEAKER) { + if (AnimPicture) { + video->BlitSprite(AnimPicture, tx,ty, true, &clip); + clip.x+=AnimPicture->Width; + clip.w-=AnimPicture->Width; + } + } + + //this might look better in GlobalTimer + //or you might want to change the animated button to work like this + if (Flags &IE_GUI_TEXTAREA_SMOOTHSCROLL) + { + unsigned long thisTime; + + GetTime( thisTime); + if (thisTime>starttime) { + starttime = thisTime+ticks; + smooth--; + while (smooth<=0) { + smooth+=ftext->maxHeight; + if (startrowInvalidate(); + InternalFlags |= TA_BITEMYTAIL; + Owner->DrawWindow(); + InternalFlags &= ~TA_BITEMYTAIL; + } + } + + if (!Changed && !(Owner->Flags&WF_FLOAT) ) { + return; + } + Changed = false; + + if (XPos == 65535) { + return; + } + size_t linesize = lines.size(); + if (linesize == 0) { + return; + } + + //smooth vertical scrolling up + if (Flags & IE_GUI_TEXTAREA_SMOOTHSCROLL) { + clip.y+=smooth; + clip.h-=smooth; + } + + //if textarea is 'selectable' it actually means, it is a listbox + //in this case the selected value equals the line number + //if it is 'not selectable' it can still have selectable lines + //but then it is like the dialog window in the main game screen: + //the selected value is encoded into the line + if (!(Flags & IE_GUI_TEXTAREA_SELECTABLE) ) { + char* Buffer = (char *) malloc( 1 ); + Buffer[0] = 0; + int len = 0; + int lastlen = 0; + for (size_t i = 0; i < linesize; i++) { + if (strnicmp( "[s=", lines[i], 3 ) == 0) { + int tlen; + unsigned long idx, acolor, bcolor; + char* rest; + idx = strtoul( lines[i] + 3, &rest, 0 ); + if (*rest != ',') + goto notmatched; + acolor = strtoul( rest + 1, &rest, 16 ); + if (*rest != ',') + goto notmatched; + bcolor = strtoul( rest + 1, &rest, 16 ); + if (*rest != ']') + goto notmatched; + tlen = (int)(strstr( rest + 1, "[/s]" ) - rest - 1); + if (tlen < 0) + goto notmatched; + len += tlen + 23; + Buffer = (char *) realloc( Buffer, len + 2 ); + if (seltext == (int) i) { + sprintf( Buffer + lastlen, "[color=%6.6lX]%.*s[/color]", + acolor, tlen, rest + 1 ); + } else { + sprintf( Buffer + lastlen, "[color=%6.6lX]%.*s[/color]", + bcolor, tlen, rest + 1 ); + } + } else { + notmatched: + len += ( int ) strlen( lines[i] ) + 1; + Buffer = (char *) realloc( Buffer, len + 2 ); + memcpy( &Buffer[lastlen], lines[i], len - lastlen ); + } + lastlen = len; + if (i != linesize - 1) { + Buffer[lastlen - 1] = '\n'; + Buffer[lastlen] = 0; + } + } + video->SetClipRect( &clip ); + + int pos; + + if (startrow==CurLine) { + pos = CurPos; + } else { + pos = -1; + } + ftext->PrintFromLine( startrow, clip, + ( unsigned char * ) Buffer, palette, + IE_FONT_ALIGN_LEFT, finit, Cursor, pos ); + free( Buffer ); + video->SetClipRect( NULL ); + //streaming text + if (linesize>50) { + //the buffer is filled enough + return; + } + if (core->GetAudioDrv()->IsSpeaking() ) { + //the narrator is still talking + return; + } + if (RunEventHandler( TextAreaOutOfText )) { + return; + } + if (linesize==lines.size()) { + ResetEventHandler( TextAreaOutOfText ); + return; + } + AppendText("\n",-1); + return; + } + // normal scrolling textarea + int rc = 0; + int sr = startrow; + unsigned int i; + int yl; + for (i = 0; i < linesize; i++) { + if (rc + lrows[i] <= sr) { + rc += lrows[i]; + continue; + } + sr -= rc; + Palette* pal = NULL; + if (seltext == (int) i) + pal = selected; + else if (Value == i) + pal = lineselpal; + else + pal = palette; + ftext->PrintFromLine( sr, clip, + ( unsigned char * ) lines[i], pal, + IE_FONT_ALIGN_LEFT, finit, NULL ); + yl = ftext->size[1].h*(lrows[i]-sr); + clip.y+=yl; + clip.h-=yl; + break; + } + for (i++; i < linesize; i++) { + Palette* pal = NULL; + if (seltext == (int) i) + pal = selected; + else if (Value == i) + pal = lineselpal; + else + pal = palette; + ftext->Print( clip, ( unsigned char * ) lines[i], pal, + IE_FONT_ALIGN_LEFT, true ); + yl = ftext->size[1].h*lrows[i]; + clip.y+=yl; + clip.h-=yl; + + } +} +/** Sets the Scroll Bar Pointer. If 'ptr' is NULL no Scroll Bar will be linked + to this Text Area Control. */ +int TextArea::SetScrollBar(Control* ptr) +{ + int ret = Control::SetScrollBar(ptr); + CalcRowCount(); + return ret; +} + +/** Sets the Actual Text */ +int TextArea::SetText(const char* text, int pos) +{ + if (pos==0) { + if (!text[0]) { + lines.clear(); + lrows.clear(); + } + + if (lines.size() == 0) { + pos = -1; + } + } + if (pos >= ( int ) lines.size()) { + return -1; + } + int newlen = ( int ) strlen( text ); + + if (pos == -1) { + char* str = (char *) malloc( newlen + 1 ); + memcpy( str, text, newlen + 1 ); + lines.push_back( str ); + lrows.push_back( 0 ); + } else { + lines[pos] = (char *) realloc( lines[pos], newlen + 1 ); + memcpy( lines[pos], text, newlen + 1 ); + } + CurPos = newlen; + CurLine = lines.size()-1; + UpdateControls(); + return 0; +} + +void TextArea::SetMinRow(bool enable) +{ + if (enable) { + minrow = (int) lines.size(); + } else { + minrow = 0; + } + Changed = true; +} + +//drop lines scrolled out at the top. +//keeplines is the number of lines that should still be +//preserved (for scrollback history) +void TextArea::DiscardLines() +{ + if (rows<=keeplines) { + return; + } + int drop = rows-keeplines; + PopLines(drop, true); +} + +static char *note_const = NULL; +static const char inserted_crap[]="[/color][color=ffffff]"; +#define CRAPLENGTH sizeof(inserted_crap)-1 + +void TextArea::SetNoteString(const char *s) +{ + free(note_const); + if (s) { + note_const = (char *) malloc(strlen(s)+5); + sprintf(note_const, "\r\n\r\n%s", s); + } +} + +/** Appends a String to the current Text */ +int TextArea::AppendText(const char* text, int pos) +{ + int ret = 0; + if (pos >= ( int ) lines.size()) { + return -1; + } + int newlen = ( int ) strlen( text ); + + if (pos == -1) { + const char *note = NULL; + if (note_const) { + note = strstr(text,note_const); + } + char *str; + if (NULL == note) { + str = (char *) malloc( newlen +1 ); + memcpy(str, text, newlen+1); + } + else { + unsigned int notepos = (unsigned int) (note - text); + str = (char *) malloc( newlen + CRAPLENGTH+1 ); + memcpy(str,text,notepos); + memcpy(str+notepos,inserted_crap,CRAPLENGTH); + memcpy(str+notepos+CRAPLENGTH, text+notepos, newlen-notepos+1); + } + lines.push_back( str ); + lrows.push_back( 0 ); + ret =(int) (lines.size() - 1); + } else { + int mylen = ( int ) strlen( lines[pos] ); + + lines[pos] = (char *) realloc( lines[pos], mylen + newlen + 1 ); + memcpy( lines[pos]+mylen, text, newlen + 1 ); + ret = pos; + } + + //if the textarea is not a listbox, then discard scrolled out + //lines + if (Flags&IE_GUI_TEXTAREA_HISTORY) { + DiscardLines(); + } + + UpdateControls(); + return ret; +} + +/** Deletes last or first `count' lines */ +/** Probably not too optimal for many lines, but it isn't used */ +/** for many lines */ +void TextArea::PopLines(unsigned int count, bool top) +{ + if (count > lines.size()) { + count = (unsigned int) lines.size(); + } + + while (count > 0 ) { + if (top) { + int tmp = lrows.front(); + if (minrow || (startrowmaxHeight ); + else + pos = 0; + if (pos < 0) + pos = 0; + bar->SetPos( pos ); + } else { + if (Flags & IE_GUI_TEXTAREA_AUTOSCROLL) { + pos = rows - ( ( Height - 5 ) / ftext->maxHeight ); + SetRow(pos); + } + } + core->RedrawAll(); +} + +/** Sets the Fonts */ +void TextArea::SetFonts(Font* init, Font* text) +{ + finit = init; + ftext = text; + Changed = true; +} + +/** Key Press Event */ +void TextArea::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Flags & IE_GUI_TEXTAREA_EDITABLE) { + if (Key >= 0x20) { + Owner->Invalidate(); + Changed = true; + int len = GetRowLength(CurLine); + //printf("len: %d Before: %s\n",len, lines[CurLine]); + lines[CurLine] = (char *) realloc( lines[CurLine], len + 2 ); + for (int i = len; i > CurPos; i--) { + lines[CurLine][i] = lines[CurLine][i - 1]; + } + lines[CurLine][CurPos] = Key; + lines[CurLine][len + 1] = 0; + CurPos++; + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + CalcRowCount(); + RunEventHandler( TextAreaOnChange ); + } + return; + } + + //Selectable=false for dialogs, rather unintuitive, but fact + if ((Flags & IE_GUI_TEXTAREA_SELECTABLE) || ( Key < '1' ) || ( Key > '9' )) + return; + GameControl *gc = core->GetGameControl(); + if (gc && (gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + Changed = true; + seltext=minrow-1; + if ((unsigned int) seltext>=lines.size()) { + return; + } + for(int i=0;i=lines.size()) { + return; + } + } + while (strnicmp( lines[seltext], "[s=", 3 ) != 0 ); + } + int idx=-1; + sscanf( lines[seltext], "[s=%d,", &idx); + if (idx==-1) { + //this kills this object, don't use any more data! + gc->dialoghandler->EndDialog(); + return; + } + gc->dialoghandler->DialogChoose( idx ); + } +} + +/** Special Key Press */ +void TextArea::OnSpecialKeyPress(unsigned char Key) +{ + int len; + int i; + + if (!(Flags&IE_GUI_TEXTAREA_EDITABLE)) { + return; + } + Owner->Invalidate(); + Changed = true; + switch (Key) { + case GEM_HOME: + CurPos = 0; + CurLine = 0; + break; + case GEM_UP: + if (CurLine) { + CurLine--; + } + break; + case GEM_DOWN: + if (CurLine 0) { + CurPos--; + } else { + if (CurLine) { + CurLine--; + CurPos = GetRowLength(CurLine); + } + } + break; + case GEM_RIGHT: + len = GetRowLength(CurLine); + if (CurPos < len) { + CurPos++; + } else { + if(CurLine=len) { + //TODO: merge next line + break; + } + lines[CurLine] = (char *) realloc( lines[CurLine], len ); + for (i = CurPos; i < len; i++) { + lines[CurLine][i] = lines[CurLine][i + 1]; + } + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + break; + case GEM_BACKSP: + len = GetRowLength(CurLine); + if (CurPos != 0) { + //printf("len: %d Before: %s\n",len, lines[CurLine]); + if (len<1) { + break; + } + lines[CurLine] = (char *) realloc( lines[CurLine], len ); + for (i = CurPos; i < len; i++) { + lines[CurLine][i - 1] = lines[CurLine][i]; + } + lines[CurLine][len - 1] = 0; + CurPos--; + //printf("pos: %d After: %s\n",CurPos, lines[CurLine]); + } else { + if (CurLine) { + //TODO: merge lines + int oldline = CurLine; + CurLine--; + int old = GetRowLength(CurLine); + //printf("len: %d Before: %s\n",old, lines[CurLine]); + //printf("len: %d Before: %s\n",len, lines[oldline]); + lines[CurLine] = (char *) realloc (lines[CurLine], len+old); + memcpy(lines[CurLine]+old, lines[oldline],len); + free(lines[oldline]); + lines[CurLine][old+len]=0; + lines.erase(lines.begin()+oldline); + lrows.erase(lrows.begin()+oldline); + CurPos = old; + //printf("pos: %d len: %d After: %s\n",CurPos, GetRowLength(CurLine), lines[CurLine]); + } + } + break; + case GEM_RETURN: + //add an empty line after CurLine + //printf("pos: %d Before: %s\n",CurPos, lines[CurLine]); + lrows.insert(lrows.begin()+CurLine, 0); + len = GetRowLength(CurLine); + //copy the text after the cursor into the new line + char *str = (char *) malloc(len-CurPos+2); + memcpy(str, lines[CurLine]+CurPos, len-CurPos+1); + str[len-CurPos+1] = 0; + lines.insert(lines.begin()+CurLine+1, str); + //truncate the current line + lines[CurLine] = (char *) realloc (lines[CurLine], CurPos+1); + lines[CurLine][CurPos]=0; + //move cursor to next line beginning + CurLine++; + CurPos=0; + //printf("len: %d After: %s\n",GetRowLength(CurLine-1), lines[CurLine-1]); + //printf("len: %d After: %s\n",GetRowLength(CurLine), lines[CurLine]); + break; + } + CalcRowCount(); + RunEventHandler( TextAreaOnChange ); +} + +/** Returns Row count */ +int TextArea::GetRowCount() +{ + return ( int ) lines.size(); +} + +int TextArea::GetRowLength(unsigned int row) +{ + if (lines.size()<=row) { + return 0; + } + //this is just roughly the line size, escape sequences need to be removed + return strlen( lines[row] ); +} + +int TextArea::GetVisibleRowCount() +{ + return (Height-5) / ftext->maxHeight; +} + +/** Returns top index */ +int TextArea::GetTopIndex() +{ + return startrow; +} + +/** Set Starting Row */ +void TextArea::SetRow(int row) +{ + if (row < rows) { + startrow = row; + } + Changed = true; +} + +void TextArea::CalcRowCount() +{ + int tr; + int w = Width; + + if (Flags&IE_GUI_TEXTAREA_SPEAKER) { + const char *portrait = NULL; + Actor *actor = NULL; + GameControl *gc = core->GetGameControl(); + if (gc) { + Scriptable *target = gc->dialoghandler->GetTarget(); + if (target && target->Type == ST_ACTOR) { + actor = (Actor *)target; + } + } + if (actor) { + portrait = actor->GetPortrait(1); + } + if (portrait) { + RefreshSprite(portrait); + } + if (AnimPicture) { + w-=AnimPicture->Width; + } + } + + rows = 0; + if (lines.size() != 0) { + for (size_t i = 0; i < lines.size(); i++) { +// rows++; + tr = 0; + int len = ( int ) strlen( lines[i] ); + char* tmp = (char *) malloc( len + 1 ); + memcpy( tmp, lines[i], len + 1 ); + ftext->SetupString( tmp, w ); + for (int p = 0; p <= len; p++) { + if (( ( unsigned char ) tmp[p] ) == '[') { + p++; + //char tag[256]; + int k = 0; + for (k = 0; k < 256; k++) { + if (tmp[p] == ']') { + //tag[k] = 0; + break; + } + p++; + //tag[k] = tmp[p++]; + } + + continue; + } + if (tmp[p] == 0) { +// if (p != len) +// rows++; + tr++; + } + } + lrows[i] = tr; + rows += tr; + free( tmp ); + } + } + + if (lines.size()) + { + if (CurLine>=lines.size()) { + CurLine=lines.size()-1; + } + w = strlen(lines[CurLine]); + if (CurPos>w) { + CurPos = w; + } + } else { + CurLine=0; + CurPos=0; + } + + if (!sb) { + return; + } + ScrollBar* bar = ( ScrollBar* ) sb; + tr = rows - Height/ftext->size[1].h + 1; + if (tr<0) { + tr = 0; + } + bar->SetMax( (ieWord) tr ); +} +/** Mouse Over Event */ +void TextArea::OnMouseOver(unsigned short /*x*/, unsigned short y) +{ + int height = ftext->maxHeight; //size[1].h; + int r = y / height; + int row = 0; + + for (size_t i = 0; i < lines.size(); i++) { + row += lrows[i]; + if (r < ( row - startrow )) { + if (seltext != (int) i) + core->RedrawAll(); + seltext = ( int ) i; + //printf("CtrlId = 0x%08lx, seltext = %d, rows = %d, row = %d, r = %d\n", ControlID, i, rows, row, r); + return; + } + } + if (seltext != -1) { + core->RedrawAll(); + } + seltext = -1; + //printf("CtrlId = 0x%08lx, seltext = %d, rows %d, row %d, r = %d\n", ControlID, seltext, rows, row, r); +} + +/** Mouse Button Up */ +void TextArea::OnMouseUp(unsigned short x, unsigned short y, unsigned short /*Button*/, + unsigned short /*Mod*/) +{ + if (( x <= Width ) && ( y <= ( Height - 5 ) ) && ( seltext != -1 )) { + Value = (unsigned int) seltext; + Changed = true; + if (strnicmp( lines[seltext], "[s=", 3 ) == 0) { + if (minrow > seltext) + return; + int idx; + sscanf( lines[seltext], "[s=%d,", &idx ); + GameControl* gc = core->GetGameControl(); + if (gc && (gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + if (idx==-1) { + //this kills this object, don't use any more data! + gc->dialoghandler->EndDialog(); + return; + } + gc->dialoghandler->DialogChoose( idx ); + return; + } + } + } + + if (VarName[0] != 0) { + core->GetDictionary()->SetAt( VarName, Value ); + } + RunEventHandler( TextAreaOnChange ); +} + +/** Copies the current TextArea content to another TextArea control */ +void TextArea::CopyTo(TextArea* ta) +{ + ta->Clear(); + for (size_t i = 0; i < lines.size(); i++) { + ta->SetText( lines[i], -1 ); + } +} + +void TextArea::RedrawTextArea(const char* VariableName, unsigned int Sum) +{ + if (strnicmp( VarName, VariableName, MAX_VARIABLE_LENGTH )) { + return; + } + Value = Sum; + Changed = true; +} + +const char* TextArea::QueryText() +{ + if ( Valuegap + //minrow -2 ->npc text + while (i>=minrow-2 && i>=0) { + row+=lrows[i]; + i--; + } + row = GetVisibleRowCount()-row; + while (row>0) { + AppendText("",-1); + row--; + } +} + +void TextArea::SetPreservedRow(int arg) +{ + keeplines=arg; + Flags |= IE_GUI_TEXTAREA_HISTORY; +} + +void TextArea::Clear() +{ + for (size_t i = 0; i < lines.size(); i++) { + free( lines[i] ); + } + lines.clear(); + lrows.clear(); + rows = 0; +} + +//setting up the textarea for smooth scrolling, the first +//TEXTAREA_OUTOFTEXT callback is called automatically +void TextArea::SetupScroll(unsigned long tck) +{ + SetPreservedRow(0); + smooth = ftext->maxHeight; + startrow = 0; + ticks = tck; + //clearing the textarea + Clear(); + unsigned int i = (unsigned int) (Height/smooth); + while (i--) { + char *str = (char *) malloc(1); + str[0]=0; + lines.push_back(str); + lrows.push_back(0); + } + i = (unsigned int) lines.size(); + Flags |= IE_GUI_TEXTAREA_SMOOTHSCROLL; + GetTime( starttime ); + if (RunEventHandler( TextAreaOutOfText )) { + //event handler destructed this object? + return; + } + if (i==lines.size()) { + ResetEventHandler( TextAreaOutOfText ); + return; + } + //recalculates rows + AppendText("\n",-1); +} + +void TextArea::OnMouseDown(unsigned short /*x*/, unsigned short /*y*/, unsigned short Button, + unsigned short /*Mod*/) +{ + + ScrollBar* scrlbr = (ScrollBar*) sb; + + if (!scrlbr) { + Control *ctrl = Owner->GetScrollControl(); + if (ctrl && (ctrl->ControlType == IE_GUI_SCROLLBAR)) { + scrlbr = (ScrollBar *) ctrl; + } + } + if (scrlbr) { + switch(Button) { + case GEM_MB_SCRLUP: + scrlbr->ScrollUp(); + core->RedrawAll(); + break; + case GEM_MB_SCRLDOWN: + scrlbr->ScrollDown(); + core->RedrawAll(); + break; + } + } +} diff --git a/project/jni/application/gemrb/src/core/GUI/TextArea.h b/project/jni/application/gemrb/src/core/GUI/TextArea.h new file mode 100644 index 000000000..c861c068f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextArea.h @@ -0,0 +1,175 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 TextArea.h + * Declares TextArea widget for displaying long paragraphs of text + * @author The GemRB Project + */ + +#ifndef TEXTAREA_H +#define TEXTAREA_H + +#include "GUI/Control.h" +#include "GUI/ScrollBar.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +// Keep these synchronized with GUIDefines.py +// 0x05 is the control type of TextArea +#define IE_GUI_TEXTAREA_ON_CHANGE 0x05000000 +#define IE_GUI_TEXTAREA_OUT_OF_TEXT 0x05000001 + +// TextArea flags, keep these in sync too +// the control type is intentionally left out +#define IE_GUI_TEXTAREA_SELECTABLE 1 +#define IE_GUI_TEXTAREA_AUTOSCROLL 2 +#define IE_GUI_TEXTAREA_SMOOTHSCROLL 4 +#define IE_GUI_TEXTAREA_HISTORY 8 +#define IE_GUI_TEXTAREA_SPEAKER 16 +#define IE_GUI_TEXTAREA_ALT_FONT 32 //this one disables drop capitals +#define IE_GUI_TEXTAREA_EDITABLE 64 + +// internal flags +#define TA_INITIALS 1 +#define TA_BITEMYTAIL 2 + +/** + * @class TextArea + * Widget capable of displaying long paragraphs of text. + * It is usually scrolled with a ScrollBar widget + */ + +class GEM_EXPORT TextArea : public Control { +public: + TextArea(Color hitextcolor, Color initcolor, Color lowtextcolor); + ~TextArea(void); + /** global configuration */ + static void SetNoteString(const char *s); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the Actual Text */ + int SetText(const char* text, int pos = 0); + /** Clears the textarea */ + void Clear(); + /** Discards scrolled out lines from the textarea */ + /** preserving 'keeplines' lines for scroll back history */ + void DiscardLines(); + /** Appends a String to the current Text */ + int AppendText(const char* text, int pos = 0); + /** Deletes `count' lines (either last or top lines)*/ + void PopLines(unsigned int count, bool top = false); + /** Deletes last lines up to current 'minrow' */ + void PopMinRow() + { + PopLines((unsigned int) (lines.size()-minrow)); + } + /** adds empty lines so minrow will be the uppermost visible row */ + void PadMinRow(); + /** Sets up scrolling, tck is the scrolling speed */ + void SetupScroll(unsigned long tck); + /** Sets the Fonts */ + void SetFonts(Font* init, Font* text); + /** Returns Number of Rows */ + int GetRowCount(); + /** Returns the length of a Row */ + int GetRowLength(unsigned int row); + /** Returns Number of Visible Rows */ + int GetVisibleRowCount(); + /** Returns Starting Row */ + int GetTopIndex(); + /** Set Starting Row */ + void SetRow(int row); + /** Sets preserved lines */ + void SetPreservedRow(int arg); + /** Set Selectable */ + void SetSelectable(bool val); + /** Set Minimum Selectable Row (to the current ceiling) */ + void SetMinRow(bool enable); + /** Copies the current TextArea content to another TextArea control */ + void CopyTo(TextArea* ta); + /** Returns the selected text */ + const char* QueryText(); + /** Marks textarea for redraw with a new value */ + void RedrawTextArea(const char* VariableName, unsigned int Sum); + int SetScrollBar(Control *ptr); +private: // Private attributes + std::vector< char*> lines; + std::vector< int> lrows; + int seltext; + /** minimum selectable row */ + int minrow; + /** lines to be kept even if scrolled out */ + int keeplines; + /** vertical offset for smooth scrolling */ + int smooth; + /** timer for scrolling */ + unsigned long starttime; + /** timer ticks for scrolling (speed) */ + unsigned long ticks; + /** Number of Text Rows */ + int rows; + /** Starting Row */ + int startrow; + /** Text Colors */ + Palette* palette; + Palette* initpalette; + Palette* selected; + Palette* lineselpal; + /** a hack for smooth windows, drop capitals */ + ieDword InternalFlags; + /** Fonts */ + Font* finit, * ftext; + ieResRef PortraitResRef; + + /** Text Editing Cursor Sprite */ + Sprite2D* Cursor; + unsigned short CurPos, CurLine; + +private: //internal functions + void CalcRowCount(); + void UpdateControls(); + void RefreshSprite(const char *portrait); + +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse button down*/ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler TextAreaOnChange; + /** OutOfText Scripted Event Function Name */ + EventHandler TextAreaOutOfText; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp b/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp new file mode 100644 index 000000000..2b6998106 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextEdit.cpp @@ -0,0 +1,231 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/TextEdit.h" + +#include "GameData.h" +#include "Interface.h" +#include "Palette.h" +#include "Video.h" + +TextEdit::TextEdit(unsigned short maxLength, unsigned short px, unsigned short py) +{ + max = maxLength; + FontPosX = px; + FontPosY = py; + Buffer = ( unsigned char * ) malloc( max + 1 ); + font = NULL; + Cursor = NULL; + Back = NULL; + CurPos = 0; + Buffer[0] = 0; + ResetEventHandler( EditOnChange ); + ResetEventHandler( EditOnDone ); + ResetEventHandler( EditOnCancel ); + Color white = {0xff, 0xff, 0xff, 0x00}, black = {0x00, 0x00, 0x00, 0x00}; + palette = core->CreatePalette( white, black ); +} + +TextEdit::~TextEdit(void) +{ + Video *video = core->GetVideoDriver(); + gamedata->FreePalette( palette ); + free( Buffer ); + video->FreeSprite( Back ); + video->FreeSprite( Cursor ); +} + +/** Draws the Control on the Output Display */ +void TextEdit::Draw(unsigned short x, unsigned short y) +{ + if (!Changed && !(Owner->Flags&WF_FLOAT)) { + return; + } + Changed = false; + if (Back) { + core->GetVideoDriver()->BlitSprite( Back, x + XPos, y + YPos, true ); + + } + if (!font) + return; + + //The aligning of textedit fields is done by absolute positioning (FontPosX, FontPosY) + if (hasFocus) { + font->Print( Region( x + XPos + FontPosX, y + YPos + FontPosY, Width, Height ), Buffer, + palette, IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_TOP, + true, NULL, Cursor, CurPos ); + } else { + font->Print( Region( x + XPos + FontPosX, y + YPos + FontPosY, Width, Height ), Buffer, + palette, IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_TOP, true ); + } +} + +/** Set Font */ +void TextEdit::SetFont(Font* f) +{ + if (f != NULL) { + font = f; + Changed = true; + return; + } + printMessage("TextEdit","Invalid font set!\n", LIGHT_RED); +} + +Font *TextEdit::GetFont() { return font; } + +/** Set Cursor */ +void TextEdit::SetCursor(Sprite2D* cur) +{ + core->GetVideoDriver()->FreeSprite( Cursor ); + if (cur != NULL) { + Cursor = cur; + } + Changed = true; +} + +/** Set BackGround */ +void TextEdit::SetBackGround(Sprite2D* back) +{ + //if 'back' is NULL then no BackGround will be drawn + if (Back) + core->GetVideoDriver()->FreeSprite(Back); + Back = back; + Changed = true; +} + +/** Key Press Event */ +void TextEdit::OnKeyPress(unsigned char Key, unsigned short /*Mod*/) +{ + if (Key >= 0x20) { + if (Value && ( (Key<'0') || (Key>'9') ) ) + return; + Owner->Invalidate(); + Changed = true; + int len = ( int ) strlen( ( char* ) Buffer ); + if (len + 1 < max) { + for (int i = len; i > CurPos; i--) { + Buffer[i] = Buffer[i - 1]; + } + Buffer[CurPos] = Key; + Buffer[len + 1] = 0; + CurPos++; + } + RunEventHandler( EditOnChange ); + } +} +/** Special Key Press */ +void TextEdit::OnSpecialKeyPress(unsigned char Key) +{ + int len; + + Owner->Invalidate(); + Changed = true; + switch (Key) { + case GEM_HOME: + CurPos = 0; + break; + case GEM_END: + CurPos = (ieWord) strlen( (char * ) Buffer); + break; + case GEM_LEFT: + if (CurPos > 0) + CurPos--; + break; + case GEM_RIGHT: + len = ( int ) strlen( ( char * ) Buffer ); + if (CurPos < len) { + CurPos++; + } + break; + case GEM_DELETE: + len = ( int ) strlen( ( char * ) Buffer ); + if (CurPos < len) { + for (int i = CurPos; i < len; i++) { + Buffer[i] = Buffer[i + 1]; + } + } + break; + case GEM_BACKSP: + if (CurPos != 0) { + int len = ( int ) strlen( ( char* ) Buffer ); + for (int i = CurPos; i < len; i++) { + Buffer[i - 1] = Buffer[i]; + } + Buffer[len - 1] = 0; + CurPos--; + } + break; + case GEM_RETURN: + RunEventHandler( EditOnDone ); + return; + + } + RunEventHandler( EditOnChange ); +} + +/** Sets the Text of the current control */ +int TextEdit::SetText(const char* string, int /*pos*/) +{ + strncpy( ( char * ) Buffer, string, max ); + Buffer[max]=0; + CurPos = (ieWord) strlen((char *) Buffer); + if (Owner) { + Owner->Invalidate(); + } + return 0; +} + +void TextEdit::SetBufferLength(ieWord buflen) +{ + if(buflen<1) return; + if(buflen!=max) { + Buffer = (unsigned char *) realloc(Buffer, buflen+1); + max=(ieWord) buflen; + Buffer[max]=0; + } +} + +/** Simply returns the pointer to the text, don't modify it! */ +const char* TextEdit::QueryText() +{ + return ( const char * ) Buffer; +} + +bool TextEdit::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_EDIT_ON_CHANGE: + EditOnChange = handler; + break; + case IE_GUI_EDIT_ON_DONE: + EditOnDone = handler; + break; + case IE_GUI_EDIT_ON_CANCEL: + EditOnCancel = handler; + break; + default: + return false; + } + + return true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/TextEdit.h b/project/jni/application/gemrb/src/core/GUI/TextEdit.h new file mode 100644 index 000000000..f92e91370 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/TextEdit.h @@ -0,0 +1,101 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 TextEdit.h + * Declares TextEdit widget for displaying single line text input field + * @author The GemRB Project + */ + +#ifndef TEXTEDIT_H +#define TEXTEDIT_H + +#include "GUI/Control.h" + +#include "RGBAColor.h" +#include "exports.h" + +#include "Font.h" + +class Palette; + +// !!! Keep these synchronized with GUIDefines.py +#define IE_GUI_EDIT_ON_CHANGE 0x03000000 +#define IE_GUI_EDIT_ON_DONE 0x03000001 +#define IE_GUI_EDIT_ON_CANCEL 0x03000002 + +//this is stored in 'Value' of Control class +#define IE_GUI_EDIT_NUMBER 1 + +/** + * @class TextEdit + * Widget displaying single line text input field + */ + +class GEM_EXPORT TextEdit : public Control { +public: + TextEdit(unsigned short maxLength, unsigned short x, unsigned short y); + ~TextEdit(void); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Set Font */ + void SetFont(Font* f); + Font *GetFont(); + /** Set Cursor */ + void SetCursor(Sprite2D* cur); + /** Set BackGround */ + void SetBackGround(Sprite2D* back); + /** Sets the Text of the current control */ + int SetText(const char* string, int pos = 0); + /** Sets the Text of the current control */ + const char* QueryText(); + /** Sets the buffer length */ + void SetBufferLength(ieWord buflen); +private: + /** Text Editing Cursor Sprite */ + Sprite2D* Cursor; + /** Text Font */ + Font* font; + /** Background */ + Sprite2D* Back; + /** Max Edit Text Length */ + unsigned short max; + /** Client area position */ + unsigned short FontPosX, FontPosY; + /** Text Buffer */ + unsigned char* Buffer; + /** Cursor Position */ + unsigned short CurPos; + /** Color Palette */ + Palette* palette; +public: //Events + /** Key Press Event */ + void OnKeyPress(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); + /** OnChange Scripted Event Function Name */ + EventHandler EditOnChange; + EventHandler EditOnDone; + EventHandler EditOnCancel; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/Window.cpp b/project/jni/application/gemrb/src/core/GUI/Window.cpp new file mode 100644 index 000000000..ed1395aaf --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Window.cpp @@ -0,0 +1,442 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/Window.h" + +#include "GUI/Button.h" +#include "GUI/Control.h" +#include "GUI/MapControl.h" +#include "GUI/Progressbar.h" +#include "GUI/Slider.h" + +#include "win32def.h" + +#include "Interface.h" +#include "Video.h" + +Window::Window(unsigned short WindowID, unsigned short XPos, + unsigned short YPos, unsigned short Width, unsigned short Height) +{ + this->WindowID = WindowID; + this->XPos = XPos; + this->YPos = YPos; + this->Width = Width; + this->Height = Height; + this->BackGround = NULL; + lastC = NULL; + lastFocus = NULL; + lastMouseFocus = NULL; + lastOver = NULL; + Visible = WINDOW_INVISIBLE; + Flags = WF_CHANGED; + Cursor = IE_CURSOR_NORMAL; + DefaultControl[0] = -1; + DefaultControl[1] = -1; + ScrollControl = -1; +} + +Window::~Window() +{ + std::vector< Control*>::iterator m = Controls.begin(); + while (Controls.size() != 0) { + Control* ctrl = ( *m ); + delete ctrl; + Controls.erase( m ); + m = Controls.begin(); + } + core->GetVideoDriver()->FreeSprite( BackGround ); + BackGround = NULL; +} +/** Add a Control in the Window */ +void Window::AddControl(Control* ctrl) +{ + if (ctrl == NULL) { + return; + } + ctrl->Owner = this; + for (size_t i = 0; i < Controls.size(); i++) { + if (Controls[i]->ControlID == ctrl->ControlID) { + delete( Controls[i] ); + Controls[i] = ctrl; + Invalidate(); + return; + } + } + Controls.push_back( ctrl ); + Invalidate(); +} +/** Set the Window's BackGround Image. If 'img' is NULL, no background will be set. If the 'clean' parameter is true (default is false) the old background image will be deleted. */ +void Window::SetBackGround(Sprite2D* img, bool clean) +{ + if (clean && BackGround) { + core->GetVideoDriver()->FreeSprite( this->BackGround ); + } + BackGround = img; + Invalidate(); +} +/** This function Draws the Window on the Output Screen */ +void Window::DrawWindow() +{ + Video* video = core->GetVideoDriver(); + Region clip( XPos, YPos, Width, Height ); + //Frame && Changed + if ( (Flags & (WF_FRAME|WF_CHANGED) )== (WF_FRAME|WF_CHANGED) ) { + Region screen( 0, 0, core->Width, core->Height ); + video->SetClipRect( NULL ); + //removed this? + Color black = { 0, 0, 0, 255 }; + video->DrawRect( screen, black ); + if (core->WindowFrames[0]) + video->BlitSprite( core->WindowFrames[0], 0, 0, true ); + if (core->WindowFrames[1]) + video->BlitSprite( core->WindowFrames[1], core->Width - core->WindowFrames[1]->Width, 0, true ); + if (core->WindowFrames[2]) + video->BlitSprite( core->WindowFrames[2], (core->Width - core->WindowFrames[2]->Width) / 2, 0, true ); + if (core->WindowFrames[3]) + video->BlitSprite( core->WindowFrames[3], (core->Width - core->WindowFrames[3]->Width) / 2, core->Height - core->WindowFrames[3]->Height, true ); + } else if (clip_regions.size()) { + // clip drawing (we only do Background right now) for InvalidateForControl + for (unsigned int i = 0; i < clip_regions.size(); i++) { + Region to_clip = clip_regions[i]; + to_clip.x += XPos; + to_clip.y += YPos; + video->SetClipRect(&to_clip); + if (BackGround) { + video->BlitSprite( BackGround, XPos, YPos, true ); + } + } + } + clip_regions.clear(); + video->SetClipRect( &clip ); + //Float || Changed + if (BackGround && (Flags & (WF_FLOAT|WF_CHANGED) ) ) { + video->BlitSprite( BackGround, XPos, YPos, true ); + } + std::vector< Control*>::iterator m; + for (m = Controls.begin(); m != Controls.end(); ++m) { + ( *m )->Draw( XPos, YPos ); + } + if ( (Flags&WF_CHANGED) && (Visible == WINDOW_GRAYED) ) { + Color black = { 0, 0, 0, 128 }; + video->DrawRect(clip, black); + } + video->SetClipRect( NULL ); + Flags &= ~WF_CHANGED; +} + +/** Set window frame used to fill screen on higher resolutions*/ +void Window::SetFrame() +{ + if ( (Width < core->Width) || (Height < core->Height) ) { + Flags|=WF_FRAME; + } + Invalidate(); +} + +/** Returns the Control at X,Y Coordinates */ +Control* Window::GetControl(unsigned short x, unsigned short y, bool ignore) +{ + Control* ctrl = NULL; + + //Check if we are still on the last control + if (( lastC != NULL )) { + if (( XPos + lastC->XPos <= x ) + && ( YPos + lastC->YPos <= y ) + && ( XPos + lastC->XPos + lastC->Width >= x ) + && ( YPos + lastC->YPos + lastC->Height >= y ) + && ! lastC->IsPixelTransparent (x - XPos - lastC->XPos, y - YPos - lastC->YPos)) { + //Yes, we are on the last returned Control + return lastC; + } + } + std::vector< Control*>::const_iterator m; + for (m = Controls.begin(); m != Controls.end(); m++) { + if (ignore && (*m)->ControlID&IGNORE_CONTROL) { + continue; + } + if (( XPos + ( *m )->XPos <= x ) + && ( YPos + ( *m )->YPos <= y ) + && ( XPos + ( *m )->XPos + ( *m )->Width >= x ) + && ( YPos + ( *m )->YPos + ( *m )->Height >= y ) + && ! ( *m )->IsPixelTransparent (x - XPos - ( *m )->XPos, y - YPos - ( *m )->YPos)) { + ctrl = *m; + break; + } + } + lastC = ctrl; + return ctrl; +} + +Control* Window::GetOver() const +{ + return lastOver; +} + +Control* Window::GetFocus() const +{ + return lastFocus; +} + +Control* Window::GetMouseFocus() const +{ + return lastMouseFocus; +} + +/** Sets 'ctrl' as Focused */ +void Window::SetFocused(Control* ctrl) +{ + if (lastFocus != NULL) { + lastFocus->hasFocus = false; + lastFocus->Changed = true; + } + lastFocus = ctrl; + if (ctrl != NULL) { + lastFocus->hasFocus = true; + lastFocus->Changed = true; + } +} + +/** Sets 'ctrl' as Mouse Focused */ +void Window::SetMouseFocused(Control* ctrl) +{ + if (lastMouseFocus != NULL) { + lastMouseFocus->Changed = true; + } + lastMouseFocus = ctrl; + if (ctrl != NULL) { + lastMouseFocus->Changed = true; + } +} + +unsigned int Window::GetControlCount() const +{ + return Controls.size(); +} + +Control* Window::GetControl(unsigned short i) const +{ + if (i < Controls.size()) { + return Controls[i]; + } + return NULL; +} + +bool Window::IsValidControl(unsigned short ID, Control *ctrl) const +{ + size_t i = Controls.size(); + while (i--) { + if (Controls[i]==ctrl) { + return ctrl->ControlID==ID; + } + } + return false; +} + +void Window::DelControl(unsigned short i) +{ + if (i < Controls.size() ) { + Control *ctrl = Controls[i]; + if (ctrl==lastC) { + lastC=NULL; + } + if (ctrl==lastOver) { + lastOver=NULL; + } + if (ctrl==lastFocus) { + lastFocus=NULL; + } + if (ctrl==lastMouseFocus) { + lastMouseFocus=NULL; + } + delete ctrl; + Controls.erase(Controls.begin()+i); + } + Invalidate(); +} + +Control* Window::GetDefaultControl(unsigned int ctrltype) const +{ + if (!Controls.size()) { + return NULL; + } + if (ctrltype>=2) { + return NULL; + } + return GetControl( (ieWord) DefaultControl[ctrltype] ); +} + +Control* Window::GetScrollControl() const +{ + if (!Controls.size()) { + return NULL; + } + return GetControl( (ieWord) ScrollControl ); +} + +void Window::release(void) +{ + Visible = WINDOW_INVALID; + lastC = NULL; + lastFocus = NULL; + lastMouseFocus = NULL; + lastOver = NULL; +} + +/** Redraw all the Window */ +void Window::Invalidate() +{ + DefaultControl[0] = -1; + DefaultControl[1] = -1; + ScrollControl = -1; + for (unsigned int i = 0; i < Controls.size(); i++) { + if (!Controls[i]) { + continue; + } + Controls[i]->Changed = true; + switch (Controls[i]->ControlType) { + case IE_GUI_SCROLLBAR: + if ((ScrollControl == -1) || (Controls[i]->Flags & IE_GUI_SCROLLBAR_DEFAULT)) + ScrollControl = i; + break; + case IE_GUI_BUTTON: + if (( Controls[i]->Flags & IE_GUI_BUTTON_DEFAULT )) { + DefaultControl[0] = i; + } + if (( Controls[i]->Flags & IE_GUI_BUTTON_CANCEL )) { + DefaultControl[1] = i; + } + break; + //falling through + case IE_GUI_GAMECONTROL: + DefaultControl[0] = i; + DefaultControl[1] = i; + break; + default: ; + } + } + Flags |= WF_CHANGED; +} + +/** Redraw enough to update the specified Control */ +void Window::InvalidateForControl(Control *ctrl) { + // TODO: for this to be general-purpose, we should mark anything inside this + // region with Changed, and also do mass Invalidate() if we overlap with + // another window, but for now this just clips the *background*, see DrawWindow() + clip_regions.push_back( Region(ctrl->XPos, ctrl->YPos, ctrl->Width, ctrl->Height) ); +} + +void Window::RedrawControls(const char* VarName, unsigned int Sum) +{ + for (unsigned int i = 0; i < Controls.size(); i++) { + switch (Controls[i]->ControlType) { + case IE_GUI_MAP: + { + MapControl *mc = ( MapControl* ) (Controls[i]); + mc->RedrawMapControl( VarName, Sum ); + break; + } + case IE_GUI_BUTTON: + { + Button* bt = ( Button* ) ( Controls[i] ); + bt->RedrawButton( VarName, Sum ); + break; + } + case IE_GUI_TEXTAREA: + { + TextArea* pb = ( TextArea* ) ( Controls[i] ); + pb->RedrawTextArea( VarName, Sum ); + break; + } + case IE_GUI_PROGRESSBAR: + { + Progressbar* pb = ( Progressbar* ) ( Controls[i] ); + pb->RedrawProgressbar( VarName, Sum ); + break; + } + case IE_GUI_SLIDER: + { + Slider* sl = ( Slider* ) ( Controls[i] ); + sl->RedrawSlider( VarName, Sum ); + break; + } + case IE_GUI_SCROLLBAR: + { + ScrollBar* sb = ( ScrollBar* ) ( Controls[i] ); + sb->RedrawScrollBar( VarName, Sum ); + break; + } + } + } +} + +/** Searches for a ScrollBar and a TextArea to link them */ +void Window::Link(unsigned short SBID, unsigned short TAID) +{ + ScrollBar* sb = NULL; + TextArea* ta = NULL; + std::vector< Control*>::iterator m; + for (m = Controls.begin(); m != Controls.end(); m++) { + if (( *m )->Owner != this) + continue; + if (( *m )->ControlType == IE_GUI_SCROLLBAR) { + if (( *m )->ControlID == SBID) { + sb = ( ScrollBar * ) ( *m ); + if (ta != NULL) + break; + } + } else if (( *m )->ControlType == IE_GUI_TEXTAREA) { + if (( *m )->ControlID == TAID) { + ta = ( TextArea * ) ( *m ); + if (sb != NULL) + break; + } + } + } + if (sb && ta) { + sb->ta = ta; + ta->SetScrollBar( sb ); + } +} + +void Window::OnMouseEnter(unsigned short x, unsigned short y, Control *ctrl) +{ + lastOver = ctrl; + if (!lastOver) { + return; + } + lastOver->OnMouseEnter( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); +} + +void Window::OnMouseLeave(unsigned short x, unsigned short y) +{ + if (!lastOver) { + return; + } + lastOver->OnMouseLeave( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); + lastOver = NULL; +} + +void Window::OnMouseOver(unsigned short x, unsigned short y) +{ + if (!lastOver) { + return; + } + lastOver->OnMouseOver( x - XPos - lastOver->XPos, y - YPos - lastOver->YPos ); +} diff --git a/project/jni/application/gemrb/src/core/GUI/Window.h b/project/jni/application/gemrb/src/core/GUI/Window.h new file mode 100644 index 000000000..a4798ddee --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/Window.h @@ -0,0 +1,190 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Window.h + * Declares Window, class serving as a container for Control/widget objects + * and displaying windows in GUI + * @author The GemRB Project + */ + +#ifndef WINDOW_H +#define WINDOW_H + +#include "GUI/Control.h" +#include "GUI/ScrollBar.h" +#include "GUI/TextArea.h" + +#include "exports.h" + +#include "Sprite2D.h" + +#include + +// Window Flags +#define WF_CHANGED 1 //window changed +#define WF_FRAME 2 //window has frame +#define WF_FLOAT 4 //floating window +#define WF_CHILD 8 //if invalidated, it invalidates all windows on top of it + +// Window position anchors (actually flags for WindowSetPos()) +// !!! Keep these synchronized with GUIDefines.py !!! +#define WINDOW_TOPLEFT 0x00 +#define WINDOW_CENTER 0x01 +#define WINDOW_ABSCENTER 0x02 +#define WINDOW_RELATIVE 0x04 +#define WINDOW_SCALE 0x08 +#define WINDOW_BOUNDED 0x10 + +// IE specific cursor types + +#define IE_CURSOR_INVALID -1 +#define IE_CURSOR_NORMAL 0 +#define IE_CURSOR_TAKE 2 //over pile type containers +#define IE_CURSOR_WALK 4 +#define IE_CURSOR_BLOCKED 6 +#define IE_CURSOR_USE 8 //never hardcoded +#define IE_CURSOR_WAIT 10 //hourglass +#define IE_CURSOR_ATTACK 12 +#define IE_CURSOR_SWAP 14 //dragging portraits +#define IE_CURSOR_DEFEND 16 +#define IE_CURSOR_TALK 18 +#define IE_CURSOR_CAST 20 //targeting with non weapon +#define IE_CURSOR_INFO 22 //never hardcoded +#define IE_CURSOR_LOCK 24 //locked door +#define IE_CURSOR_LOCK2 26 //locked container +#define IE_CURSOR_STAIR 28 //never hardcoded +#define IE_CURSOR_DOOR 30 //doors +#define IE_CURSOR_CHEST 32 +#define IE_CURSOR_TRAVEL 34 +#define IE_CURSOR_STEALTH 36 +#define IE_CURSOR_TRAP 38 +#define IE_CURSOR_PICK 40 //pickpocket +#define IE_CURSOR_PASS 42 //never hardcoded +#define IE_CURSOR_GRAB 44 +#define IE_CURSOR_WAY 46 //waypoint (not in PST) +#define IE_CURSOR_INFO2 46 //PST +#define IE_CURSOR_PORTAL 48 //PST +#define IE_CURSOR_STAIR2 50 //PST +#define IE_CURSOR_EXTRA 52 //PST + +#define IE_CURSOR_MASK 127 +#define IE_CURSOR_GRAY 128 +/** + * @class Window + * Class serving as a container for Control/widget objects + * and displaying windows in GUI. + */ + +class GEM_EXPORT Window { +public: + Window(unsigned short WindowID, unsigned short XPos, unsigned short YPos, + unsigned short Width, unsigned short Height); + ~Window(); + /** Set the Window's BackGround Image. + * If 'img' is NULL, no background will be set. If the 'clean' parameter is true (default is false) the old background image will be deleted. */ + void SetBackGround(Sprite2D* img, bool clean = false); + /** Add a Control in the Window */ + void AddControl(Control* ctrl); + /** This function Draws the Window on the Output Screen */ + void DrawWindow(); + /** Set window frame used to fill screen on higher resolutions*/ + void SetFrame(); + /** Returns the Control at X,Y Coordinates */ + Control* GetControl(unsigned short x, unsigned short y, bool ignore=0); + /** Returns the Control by Index */ + Control* GetControl(unsigned short i) const; + /** Returns the number of Controls */ + unsigned int GetControlCount() const; + /** Returns true if ctrl is valid and ctrl->ControlID is ID */ + bool IsValidControl(unsigned short ID, Control *ctrl) const; + /** Deletes the xth. Control */ + void DelControl(unsigned short i); + /** Returns the Default Control which may be a button/gamecontrol atm */ + Control* GetDefaultControl(unsigned int ctrltype) const; + /** Returns the Control which should get mouse scroll events */ + Control* GetScrollControl() const; + /** Sets 'ctrl' as currently under mouse */ + void SetOver(Control* ctrl); + /** Returns last control under mouse */ + Control* GetOver() const; + /** Sets 'ctrl' as Focused */ + void SetFocused(Control* ctrl); + /** Sets 'ctrl' as mouse event Focused */ + void SetMouseFocused(Control* ctrl); + /** Returns last focused control */ + Control* GetFocus() const; + /** Returns last mouse event focused control */ + Control* GetMouseFocus() const; + /** Redraw all the Window */ + void Invalidate(); + /** Redraw enough to update the specified Control */ + void InvalidateForControl(Control *ctrl); + /** Redraw controls of the same group */ + void RedrawControls(const char* VarName, unsigned int Sum); + /** Links a scrollbar to a text area */ + void Link(unsigned short SBID, unsigned short TAID); + /** Mouse entered a new control's rectangle */ + void OnMouseEnter(unsigned short x, unsigned short y, Control *ctrl); + /** Mouse left the current control */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse is over the current control */ + void OnMouseOver(unsigned short x, unsigned short y); +public: //Public attributes + /** WinPack */ + char WindowPack[10]; + /** Window ID */ + unsigned short WindowID; + /** X Position */ + unsigned short XPos; + /** Y Position */ + unsigned short YPos; + /** Width */ + unsigned short Width; + /** Height */ + unsigned short Height; + /** Visible value: deleted, invisible, visible, grayed */ + signed char Visible; //-1,0,1,2 + /** Window flags: Changed, Floating, Framed, Child */ + int Flags; + int Cursor; + int DefaultControl[2]; //default enter and cancel + int ScrollControl; +private: // Private attributes + /** BackGround Image. No BackGround if this variable is NULL. */ + Sprite2D* BackGround; + /** Controls Array */ + std::vector< Control*> Controls; + /** Last Control returned by GetControl */ + Control* lastC; + /** Last Focused Control */ + Control* lastFocus; + /** Last mouse event Focused Control */ + Control* lastMouseFocus; + /** Last Control under mouse */ + Control* lastOver; + /** Regions which need to be redrawn */ + std::vector< Region> clip_regions; + +public: + void release(void); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp new file mode 100644 index 000000000..a0591b043 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.cpp @@ -0,0 +1,399 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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/WorldMapControl.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Video.h" +#include "WorldMap.h" + +#define MAP_TO_SCREENX(x) XWin + XPos - ScrollX + (x) +#define MAP_TO_SCREENY(y) YWin + YPos - ScrollY + (y) + +WorldMapControl::WorldMapControl(const char *font, int direction) +{ + ScrollX = 0; + ScrollY = 0; + MouseIsDown = false; + Changed = true; + Area = NULL; + Value = direction; + Game* game = core->GetGame(); + WorldMap* worldmap = core->GetWorldMap(); + strncpy(currentArea, game->CurrentArea, 8); + int entry = core->GetAreaAlias(currentArea); + if (entry >= 0) { + WMPAreaEntry *m = worldmap->GetEntry(entry); + strncpy(currentArea, m->AreaResRef, 8); + } + + //if there is no trivial area, look harder + if (!worldmap->GetArea(currentArea, (unsigned int &) entry) && + core->HasFeature(GF_FLEXIBLE_WMAP) ) { + WMPAreaEntry *m = worldmap->FindNearestEntry(currentArea, (unsigned int &) entry); + if (m) { + strncpy(currentArea, m->AreaResRef, 8); + } + } + + //this also updates visible locations + worldmap->CalculateDistances(currentArea, Value); + + // alpha bit is unfortunately ignored + if (font[0]) { + ftext = core->GetFont(font); + } else { + ftext = NULL; + } + + // initialize label colors + Color normal = { 0xf0, 0xf0, 0xf0, 0xff }; + Color selected = { 0xf0, 0x80, 0x80, 0xff }; + Color notvisited = { 0x80, 0x80, 0xf0, 0xff }; + Color black = { 0x00, 0x00, 0x00, 0x00 }; + + pal_normal = core->CreatePalette ( normal, black ); + pal_selected = core->CreatePalette ( selected, black ); + pal_notvisited = core->CreatePalette ( notvisited, black ); + + + ResetEventHandler( WorldMapControlOnPress ); + ResetEventHandler( WorldMapControlOnEnter ); +} + +WorldMapControl::~WorldMapControl(void) +{ + //Video *video = core->GetVideoDriver(); + + gamedata->FreePalette( pal_normal ); + gamedata->FreePalette( pal_selected ); + gamedata->FreePalette( pal_notvisited ); +} + +/** Draws the Control on the Output Display */ +void WorldMapControl::Draw(unsigned short XWin, unsigned short YWin) +{ + WorldMap* worldmap = core->GetWorldMap(); + if (!Width || !Height) { + return; + } + if(!Changed) + return; + Changed = false; + Video* video = core->GetVideoDriver(); + Region r( XWin+XPos, YWin+YPos, Width, Height ); + Region clipbackup; + video->GetClipRect(clipbackup); + video->SetClipRect(&r); + video->BlitSprite( worldmap->GetMapMOS(), MAP_TO_SCREENX(0), MAP_TO_SCREENY(0), true, &r ); + + unsigned int i; + unsigned int ec = worldmap->GetEntryCount(); + for(i=0;iGetEntry(i); + if (! (m->GetAreaStatus() & WMP_ENTRY_VISIBLE)) continue; + + int xOffs = MAP_TO_SCREENX(m->X); + int yOffs = MAP_TO_SCREENY(m->Y); + Sprite2D* icon = m->GetMapIcon(worldmap->bam); + if( icon ) { + video->BlitSprite( icon, xOffs, yOffs, true, &r ); + video->FreeSprite( icon ); + } + + if (AnimPicture && !strnicmp(m->AreaResRef, currentArea, 8) ) { + core->GetVideoDriver()->BlitSprite( AnimPicture, xOffs, yOffs, true, &r ); + } + } + + // Draw WMP entry labels + if (ftext==NULL) { + video->SetClipRect(&clipbackup); + return; + } + for(i=0;iGetEntry(i); + if (! (m->GetAreaStatus() & WMP_ENTRY_VISIBLE)) continue; + Sprite2D *icon=m->GetMapIcon(worldmap->bam); + int h=0,w=0,xpos=0,ypos=0; + if (icon) { + h=icon->Height; + w=icon->Width; + xpos=icon->XPos; + ypos=icon->YPos; + video->FreeSprite( icon ); + } + + Region r2 = Region( MAP_TO_SCREENX(m->X-xpos), MAP_TO_SCREENY(m->Y-ypos), w, h ); + if (!m->GetCaption()) + continue; + + int tw = ftext->CalcStringWidth( m->GetCaption() ) + 5; + int th = ftext->maxHeight; + + Palette* text_pal = pal_normal; + + if (Area == m) { + text_pal = pal_selected; + } else { + if (! (m->GetAreaStatus() & WMP_ENTRY_VISITED)) { + text_pal = pal_notvisited; + } + } + + ftext->Print( Region( r2.x + (r2.w - tw)/2, r2.y + r2.h, tw, th ), + ( const unsigned char * ) m->GetCaption(), text_pal, 0, true ); + } + video->SetClipRect(&clipbackup); +} + +/** Key Release Event */ +void WorldMapControl::OnKeyRelease(unsigned char Key, unsigned short Mod) +{ + switch (Key) { + case 'f': + if (Mod & GEM_MOD_CTRL) + core->GetVideoDriver()->ToggleFullscreenMode(); + break; + default: + break; + } +} +void WorldMapControl::AdjustScrolling(short x, short y) +{ + WorldMap* worldmap = core->GetWorldMap(); + if (x || y) { + ScrollX += x; + ScrollY += y; + } else { + //center worldmap on current area + unsigned entry; + + WMPAreaEntry *m = worldmap->GetArea(currentArea,entry); + if (m) { + ScrollX = m->X - Width/2; + ScrollY = m->Y - Height/2; + } + } + Sprite2D *MapMOS = worldmap->GetMapMOS(); + if (ScrollX > MapMOS->Width - Width) + ScrollX = MapMOS->Width - Width; + if (ScrollY > MapMOS->Height - Height) + ScrollY = MapMOS->Height - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; + Changed = true; + Area = NULL; +} + +/** Mouse Over Event */ +void WorldMapControl::OnMouseOver(unsigned short x, unsigned short y) +{ + WorldMap* worldmap = core->GetWorldMap(); + lastCursor = IE_CURSOR_GRAB; + + if (MouseIsDown) { + AdjustScrolling(lastMouseX-x, lastMouseY-y); + } + + lastMouseX = x; + lastMouseY = y; + + if (Value!=(ieDword) -1) { + x =(ieWord) (x + ScrollX); + y =(ieWord) (y + ScrollY); + + WMPAreaEntry *oldArea = Area; + Area = NULL; + + unsigned int i; + unsigned int ec = worldmap->GetEntryCount(); + for (i=0;iGetEntry(i); + + if ( (ae->GetAreaStatus() & WMP_ENTRY_WALKABLE)!=WMP_ENTRY_WALKABLE) { + continue; //invisible or inaccessible + } + if (!strnicmp(ae->AreaResRef, currentArea, 8) ) { + continue; //current area + } + + Sprite2D *icon=ae->GetMapIcon(worldmap->bam); + int h=0,w=0; + if (icon) { + h=icon->Height; + w=icon->Width; + core->GetVideoDriver()->FreeSprite( icon ); + } + if (ftext && ae->GetCaption()) { + int tw = ftext->CalcStringWidth( ae->GetCaption() ) + 5; + int th = ftext->maxHeight; + if(hX > x) continue; + if (ae->X + w < x) continue; + if (ae->Y > y) continue; + if (ae->Y + h < y) continue; + lastCursor = IE_CURSOR_NORMAL; + Area=ae; + if(oldArea!=ae) { + RunEventHandler(WorldMapControlOnEnter); + } + break; + } + } + + Owner->Cursor = lastCursor; +} + +/** Sets the tooltip to be displayed on the screen now */ +void WorldMapControl::DisplayTooltip() +{ + if (Area) { + int x = Owner->XPos+XPos+lastMouseX; + int y = Owner->YPos+YPos+lastMouseY-50; + core->DisplayTooltip( x, y, this ); + } else { + core->DisplayTooltip( 0, 0, NULL ); + } +} + +/** Mouse Leave Event */ +void WorldMapControl::OnMouseLeave(unsigned short /*x*/, unsigned short /*y*/) +{ + Owner->Cursor = IE_CURSOR_NORMAL; + Area = NULL; +} + +/** Mouse Button Down */ +void WorldMapControl::OnMouseDown(unsigned short x, unsigned short y, + unsigned short Button, unsigned short /*Mod*/) +{ + switch(Button) { + case GEM_MB_ACTION: + MouseIsDown = true; + lastMouseX = x; + lastMouseY = y; + break; + case GEM_MB_SCRLUP: + OnSpecialKeyPress(GEM_UP); + break; + case GEM_MB_SCRLDOWN: + OnSpecialKeyPress(GEM_DOWN); + break; + } +} +/** Mouse Button Up */ +void WorldMapControl::OnMouseUp(unsigned short /*x*/, unsigned short /*y*/, + unsigned short Button, unsigned short /*Mod*/) +{ + if (Button != GEM_MB_ACTION) { + return; + } + if (lastCursor==IE_CURSOR_NORMAL) { + RunEventHandler( WorldMapControlOnPress ); + } + MouseIsDown = false; +} + +/** Special Key Press */ +void WorldMapControl::OnSpecialKeyPress(unsigned char Key) +{ + WorldMap* worldmap = core->GetWorldMap(); + switch (Key) { + case GEM_LEFT: + ScrollX -= 64; + break; + case GEM_UP: + ScrollY -= 64; + break; + case GEM_RIGHT: + ScrollX += 64; + break; + case GEM_DOWN: + ScrollY += 64; + break; + case GEM_ALT: + printf( "ALT pressed\n" ); + break; + case GEM_TAB: + printf( "TAB pressed\n" ); + break; + } + + Sprite2D *MapMOS = worldmap->GetMapMOS(); + if (ScrollX > MapMOS->Width - Width) + ScrollX = MapMOS->Width - Width; + if (ScrollY > MapMOS->Height - Height) + ScrollY = MapMOS->Height - Height; + if (ScrollX < 0) + ScrollX = 0; + if (ScrollY < 0) + ScrollY = 0; +} + +bool WorldMapControl::SetEvent(int eventType, EventHandler handler) +{ + Changed = true; + + switch (eventType) { + case IE_GUI_WORLDMAP_ON_PRESS: + WorldMapControlOnPress = handler; + break; + case IE_GUI_MOUSE_ENTER_WORLDMAP: + WorldMapControlOnEnter = handler; + break; + default: + return false; + } + + return true; +} + +void WorldMapControl::SetColor(int which, Color color) +{ + Color black = { 0x00, 0x00, 0x00, 0x00 }; + switch (which) { + case IE_GUI_WMAP_COLOR_NORMAL: + gamedata->FreePalette( pal_normal ); + pal_normal = core->CreatePalette( color, black ); + break; + case IE_GUI_WMAP_COLOR_SELECTED: + gamedata->FreePalette( pal_selected ); + pal_selected = core->CreatePalette( color, black ); + break; + case IE_GUI_WMAP_COLOR_NOTVISITED: + gamedata->FreePalette( pal_notvisited ); + pal_notvisited = core->CreatePalette( color, black ); + break; + default: + break; + } + + Changed = true; +} diff --git a/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h new file mode 100644 index 000000000..36ee06007 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GUI/WorldMapControl.h @@ -0,0 +1,115 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 WorldMapControl.h + * Declares WorldMapControl, widget for displaying world map + */ + + +#ifndef WORLDMAPCONTROL_H +#define WORLDMAPCONTROL_H + +#include "GUI/Control.h" + +#include "exports.h" + +#include "Dialog.h" +#include "Interface.h" + +class Palette; +class WMPAreaEntry; +class WorldMapControl; + +// !!! Keep these synchronized with GUIDefines.py !!! +/** Which label color is set with SetColor() */ +#define IE_GUI_WMAP_COLOR_NORMAL 0 +#define IE_GUI_WMAP_COLOR_SELECTED 1 +#define IE_GUI_WMAP_COLOR_NOTVISITED 2 + + +/** + * @class WorldMapControl + * Widget displaying "world" map, with particular locations and possibly + * allowing travelling between areas. + */ + +#define IE_GUI_WORLDMAP_ON_PRESS 0x08000000 +#define IE_GUI_MOUSE_ENTER_WORLDMAP 0x08000002 + +class GEM_EXPORT WorldMapControl : public Control { +public: + WorldMapControl(const char *fontname, int direction); + ~WorldMapControl(void); + + /** Allows modification of the scrolling factor from outside */ + void AdjustScrolling(short x, short y); + /** Draws the Control on the Output Display */ + void Draw(unsigned short x, unsigned short y); + /** Sets the exit direction (we need this to calculate distances) */ + void SetDirection(int direction); + /** Sets the Text of the current control */ + int SetText(const char* /*string*/, int /*pos*/) { return 0; } + /** Set color for one type of area labels */ + void SetColor(int which, Color color); + int ScrollX, ScrollY; + unsigned short lastMouseX, lastMouseY; + bool MouseIsDown; + /** pointer to last pointed area */ + WMPAreaEntry *Area; + /** Set handler for specified event */ + bool SetEvent(int eventType, EventHandler handler); +private: + //font for printing area names + Font* ftext; + //mouse cursor + unsigned char lastCursor; + //current area + ieResRef currentArea; + /** Label color of a visited area */ + Palette *pal_normal; + /** Label color of a currently selected area */ + Palette *pal_selected; + /** Label color of a not yet visited area */ + Palette *pal_notvisited; + /** guiscript Event when button pressed */ + EventHandler WorldMapControlOnPress; + /** guiscript Event when mouse is over a reachable area */ + EventHandler WorldMapControlOnEnter; + + /** Mouse Over Event */ + void OnMouseOver(unsigned short x, unsigned short y); + /** Mouse Leave Event */ + void OnMouseLeave(unsigned short x, unsigned short y); + /** Mouse Button Down */ + void OnMouseDown(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Mouse Button Up */ + void OnMouseUp(unsigned short x, unsigned short y, unsigned short Button, + unsigned short Mod); + /** Key Release Event */ + void OnKeyRelease(unsigned char Key, unsigned short Mod); + /** Special Key Press */ + void OnSpecialKeyPress(unsigned char Key); + /** DisplayTooltip */ + void DisplayTooltip(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Game.cpp b/project/jni/application/gemrb/src/core/Game.cpp new file mode 100644 index 000000000..59a218593 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Game.cpp @@ -0,0 +1,1816 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2004 The GemRB Project + * + * 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 class represents the .gam (savegame) file in the engine + +#include "Game.h" + +#include "defsounds.h" +#include "strrefs.h" +#include "win32def.h" + +#include "DisplayMessage.h" +#include "GameData.h" +#include "Interface.h" +#include "MapMgr.h" +#include "MusicMgr.h" +#include "Particles.h" +#include "ScriptEngine.h" +#include "GameScript/GameScript.h" +#include "GUI/GameControl.h" +#include "System/DataStream.h" + +#define MAX_MAPS_LOADED 1 + +Game::Game(void) : Scriptable( ST_GLOBAL ) +{ + protagonist = PM_YES; //set it to 2 for iwd/iwd2 and 0 for pst + partysize = 6; + Ticks = 0; + version = 0; + Expansion = 0; + LoadMos[0] = 0; + SelectedSingle = 1; //the PC we are looking at (inventory, shop) + PartyGold = 0; + SetScript( core->GlobalScript, 0 ); + MapIndex = -1; + Reputation = 0; + ControlStatus = 0; + CombatCounter = 0; //stored here until we know better + StateOverrideTime = 0; + StateOverrideFlag = 0; + BanterBlockTime = 0; + BanterBlockFlag = 0; + WeatherBits = 0; + crtable = NULL; + kaputz = NULL; + beasts = NULL; + mazedata = NULL; + timestop_owner = NULL; + timestop_end = 0; + event_timer = 0; + event_handler = NULL; + weather = new Particles(200); + weather->SetRegion(0, 0, core->Width, core->Height); + LastScriptUpdate = 0; + + //loading master areas + AutoTable table; + if (table.load("mastarea")) { + int i = table->GetRowCount(); + mastarea.reserve(i); + while(i--) { + char *tmp = (char *) malloc(9); + strnuprcpy (tmp,table->QueryField(i,0),8); + mastarea.push_back( tmp ); + } + } + + //loading rest/daylight switching movies (only bg2 has them) + memset(restmovies,'*',sizeof(restmovies)); + memset(daymovies,'*',sizeof(restmovies)); + memset(nightmovies,'*',sizeof(restmovies)); + if (table.load("restmov")) { + for(int i=0;i<8;i++) { + strnuprcpy(restmovies[i],table->QueryField(i,0),8); + strnuprcpy(daymovies[i],table->QueryField(i,1),8); + strnuprcpy(nightmovies[i],table->QueryField(i,2),8); + } + } + + interval = 1000/AI_UPDATE_TIME; + //FIXME:i'm not sure in this... + NoInterrupt(); +} + +Game::~Game(void) +{ + size_t i; + + delete weather; + for (i = 0; i < Maps.size(); i++) { + delete( Maps[i] ); + } + for (i = 0; i < PCs.size(); i++) { + delete ( PCs[i] ); + } + for (i = 0; i < NPCs.size(); i++) { + delete ( NPCs[i] ); + } + for (i = 0; i < mastarea.size(); i++) { + free ( mastarea[i] ); + } + + if (crtable) { + delete[] crtable; + } + + if (mazedata) { + free (mazedata); + } + if (kaputz) { + delete kaputz; + } + if (beasts) { + free (beasts); + } + i=Journals.size(); + while(i--) { + delete Journals[i]; + } + + i=savedpositions.size(); + while(i--) { + free (savedpositions[i]); + } + + i=planepositions.size(); + while(i--) { + free (planepositions[i]); + } +} + +bool IsAlive(Actor *pc) +{ + if (pc->GetStat(IE_STATE_ID)&STATE_DEAD) { + return false; + } + return true; +} + +int Game::FindPlayer(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) { + return slot; + } + } + return -1; +} + +Actor* Game::FindPC(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) return PCs[slot]; + } + return NULL; +} + +Actor* Game::FindPC(const char *scriptingname) +{ + for (unsigned int slot=0; slotGetScriptName(),scriptingname,32)==0 ) { + return PCs[slot]; + } + } + return NULL; +} + +Actor* Game::FindNPC(unsigned int partyID) +{ + for (unsigned int slot=0; slotInParty==partyID) return NPCs[slot]; + } + return NULL; +} + +Actor* Game::FindNPC(const char *scriptingname) +{ + for (unsigned int slot=0; slotGetScriptName(),scriptingname,32)==0 ) + { + return NPCs[slot]; + } + } + return NULL; +} + +Actor *Game::GetGlobalActorByGlobalID(ieDword globalID) +{ + unsigned int slot; + + for (slot=0; slotGetGlobalID()==globalID ) { + return PCs[slot]; + } + } + for (slot=0; slotGetGlobalID()==globalID ) { + return NPCs[slot]; + } + } + return NULL; +} + +Actor* Game::GetPC(unsigned int slot, bool onlyalive) +{ + if (slot >= PCs.size()) { + return NULL; + } + if (onlyalive) { + unsigned int i=0; + while(i= PCs.size()) { + return -1; + } + if (!PCs[slot]) { + return -1; + } + SelectActor(PCs[slot], false, SELECT_NORMAL); + if (autoFree) { + delete( PCs[slot] ); + } + std::vector< Actor*>::iterator m = PCs.begin() + slot; + PCs.erase( m ); + return 0; +} + +int Game::DelNPC(unsigned int slot, bool autoFree) +{ + if (slot >= NPCs.size()) { + return -1; + } + if (!NPCs[slot]) { + return -1; + } + if (autoFree) { + delete( NPCs[slot] ); + } + std::vector< Actor*>::iterator m = NPCs.begin() + slot; + NPCs.erase( m ); + return 0; +} + +//i'm sure this could be faster +void Game::ConsolidateParty() +{ + int max = (int) PCs.size(); + std::vector< Actor*>::const_iterator m; + for (int i=1;i<=max;) { + if (FindPlayer(i)==-1) { + + for ( m = PCs.begin(); m != PCs.end(); ++m) { + if ( (*m)->InParty>i) { + (*m)->InParty--; + } + } + } else i++; + } + for ( m = PCs.begin(); m != PCs.end(); ++m) { + (*m)->RefreshEffects(NULL); + } +} + +int Game::LeaveParty (Actor* actor) +{ + core->SetEventFlag(EF_PORTRAIT); + actor->CreateStats(); //create or update stats for leaving + actor->SetBase(IE_EXPLORE, 0); + + SelectActor(actor, false, SELECT_NORMAL); + int slot = InParty( actor ); + if (slot < 0) { + return slot; + } + std::vector< Actor*>::iterator m = PCs.begin() + slot; + PCs.erase( m ); + + ieDword id = actor->GetGlobalID(); + for ( m = PCs.begin(); m != PCs.end(); ++m) { + (*m)->PCStats->LastLeft = id; + if ( (*m)->InParty>actor->InParty) { + (*m)->InParty--; + } + } + //removing from party, but actor remains in 'game' + actor->SetPersistent(0); + NPCs.push_back( actor ); + + if (core->HasFeature( GF_HAS_DPLAYER )) { + actor->SetScript( "", SCR_DEFAULT ); + } + actor->SetBase( IE_EA, EA_NEUTRAL ); + return ( int ) NPCs.size() - 1; +} + +//determines if startpos.2da has rotation rows (it cannot have tutorial line) +bool Game::DetermineStartPosType(const TableMgr *strta) +{ + if ((strta->GetRowCount()>=6) && !stricmp(strta->GetRowName(4),"START_ROT" ) ) + { + return true; + } + return false; +} + +#define PMODE_COUNT 3 + +void Game::InitActorPos(Actor *actor) +{ + //start.2da row labels + const char *mode[PMODE_COUNT] = { "NORMAL", "TUTORIAL", "EXPANSION" }; + + unsigned int ip = (unsigned int) (actor->InParty-1); + AutoTable start("start"); + AutoTable strta("startpos"); + // 0 - single player, 1 - tutorial, 2 - expansion + ieDword playmode = 0; + core->GetDictionary()->Lookup( "PlayMode", playmode ); + + //Sometimes playmode is set to -1 (in pregenerate) + //normally execution shouldn't ever come here, but it actually does + //preventing problems by defaulting to the regular entry points + if (playmode>PMODE_COUNT) { + playmode = 0; + } + const char *xpos = start->QueryField(mode[playmode],"XPOS"); + const char *ypos = start->QueryField(mode[playmode],"YPOS"); + const char *area = start->QueryField(mode[playmode],"AREA"); + const char *rot = start->QueryField(mode[playmode],"ROT"); + + actor->Pos.x = actor->Destination.x = (short) atoi( strta->QueryField( strta->GetRowIndex(xpos), ip ) ); + actor->Pos.y = actor->Destination.y = (short) atoi( strta->QueryField( strta->GetRowIndex(ypos), ip ) ); + actor->SetOrientation( atoi( strta->QueryField( strta->GetRowIndex(rot), ip) ), false ); + + strta.load("startare"); + strnlwrcpy(actor->Area, strta->QueryField( strta->GetRowIndex(area), 0 ), 8 ); +} + +int Game::JoinParty(Actor* actor, int join) +{ + core->SetEventFlag(EF_PORTRAIT); + actor->CreateStats(); //create stats if they didn't exist yet + actor->InitButtons(actor->GetStat(IE_CLASS), false); //init actor's buttons + actor->SetBase(IE_EXPLORE, 1); + if (join&JP_INITPOS) { + InitActorPos(actor); + } + int slot = InParty( actor ); + if (slot != -1) { + return slot; + } + size_t size = PCs.size(); + //set the lastjoined trigger + + if (join&JP_JOIN) { + //update kit abilities of actor + actor->ApplyKit(false); + //update the quickslots + actor->ReinitQuickSlots(); + //set the joining date + actor->PCStats->JoinDate = GameTime; + if (size) { + ieDword id = actor->GetGlobalID(); + for (size_t i=0;iPCStats->LastJoined = id; + } + } else { + Reputation = actor->GetStat(IE_REPUTATION); + } + } + slot = InStore( actor ); + if (slot >= 0) { + std::vector< Actor*>::iterator m = NPCs.begin() + slot; + NPCs.erase( m ); + } + + + PCs.push_back( actor ); + if (!actor->InParty) { + actor->InParty = (ieByte) (size+1); + } + + if (join&(JP_INITPOS|JP_SELECT)) { + actor->Selected = 0; // don't confuse SelectActor! + SelectActor(actor,true, SELECT_NORMAL); + } + + return ( int ) size; +} + +int Game::GetPartySize(bool onlyalive) const +{ + if (onlyalive) { + int count = 0; + for (unsigned int i = 0; i < PCs.size(); i++) { + if (!IsAlive(PCs[i])) { + continue; + } + count++; + } + return count; + } + return (int) PCs.size(); +} + +/* sends the hotkey trigger to all selected actors */ +void Game::SetHotKey(unsigned long Key) +{ + std::vector< Actor*>::const_iterator m; + + for ( m = selected.begin(); m != selected.end(); ++m) { + Actor *actor = *m; + + if (actor->IsSelected()) { + actor->HotKey = (ieDword) Key; + } + } +} + +bool Game::SelectPCSingle(int index) +{ + Actor* actor = FindPC( index ); + if (!actor || ! actor->ValidTarget( GA_NO_HIDDEN )) + return false; + + SelectedSingle = index; + return true; +} + +int Game::GetSelectedPCSingle() const +{ + return SelectedSingle; +} + +/* + * SelectActor() - handle (de)selecting actors. + * If selection was changed, runs "SelectionChanged" handler + * + * actor - either specific actor, or NULL for all + * select - whether actor(s) should be selected or deselected + * flags: + * SELECT_REPLACE - if true, deselect all other actors when selecting one + * SELECT_QUIET - do not run handler if selection was changed. Used for + * nested calls to SelectActor() + */ + +bool Game::SelectActor(Actor* actor, bool select, unsigned flags) +{ + std::vector< Actor*>::iterator m; + + // actor was not specified, which means all selectables should be (de)selected + if (! actor) { + for ( m = selected.begin(); m != selected.end(); ++m) { + (*m)->Select( false ); + (*m)->SetOver( false ); + } + selected.clear(); + + if (select) { + area->SelectActors(); +/* + for ( m = PCs.begin(); m != PCs.end(); ++m) { + if (! *m) { + continue; + } + SelectActor( *m, true, SELECT_QUIET ); + } +*/ + } + + if (! (flags & SELECT_QUIET)) { + core->SetEventFlag(EF_SELECTION); + } + Infravision(); + return true; + } + + // actor was specified, so we will work with him + if (select) { + if (! actor->ValidTarget( GA_SELECT | GA_NO_DEAD )) + return false; + + // deselect all actors first when exclusive + if (flags & SELECT_REPLACE) { + if (selected.size() == 1 && actor->IsSelected()) { + assert(selected[0] == actor); + // already the only selected actor + return true; + } + SelectActor( NULL, false, SELECT_QUIET ); + } else if (actor->IsSelected()) { + // already selected + return true; + } + + actor->Select( true ); + assert(actor->IsSelected()); + selected.push_back( actor ); + } else { + if (!actor->IsSelected()) { + // already not selected + return true; + + /*for ( m = selected.begin(); m != selected.end(); ++m) { + assert((*m) != actor); + } + return true;*/ + } + for ( m = selected.begin(); m != selected.end(); ++m) { + if ((*m) == actor) { + selected.erase( m ); + break; + } + } + actor->Select( false ); + assert(!actor->IsSelected()); + } + + if (! (flags & SELECT_QUIET)) { + core->SetEventFlag(EF_SELECTION); + } + Infravision(); + return true; +} + +// Gets average party level, of onlyalive is true, then counts only living PCs +int Game::GetPartyLevel(bool onlyalive) const +{ + int count = 0; + for (unsigned int i = 0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + } + count += PCs[i]->GetXPLevel(0); + } + return count; +} + +// Returns map structure (ARE) if it is already loaded in memory +int Game::FindMap(const char *ResRef) +{ + int index = (int) Maps.size(); + while (index--) { + Map *map=Maps[index]; + if (strnicmp(ResRef, map->GetScriptName(), 8) == 0) { + return index; + } + } + return -1; +} + +Map* Game::GetMap(unsigned int index) const +{ + if (index >= Maps.size()) { + return NULL; + } + return Maps[index]; +} + +Map *Game::GetMap(const char *areaname, bool change) +{ + int index = LoadMap(areaname, change); + if (index >= 0) { + if (change) { + MapIndex = index; + area = GetMap(index); + memcpy (CurrentArea, areaname, 8); + area->SetupAmbients(); + //change the tileset if needed + area->ChangeMap(IsDay()); + ChangeSong(false, true); + Infravision(); + return area; + } + return GetMap(index); + } + return NULL; +} + +bool Game::MasterArea(const char *area) +{ + unsigned int i=(int) mastarea.size(); + while(i--) { + if (strnicmp(mastarea[i], area, 8) ) { + return true; + } + } + return false; +} + +void Game::SetMasterArea(const char *area) +{ + if (MasterArea(area) ) return; + char *tmp = (char *) malloc(9); + strnlwrcpy (tmp,area,8); + mastarea.push_back(tmp); +} + +int Game::AddMap(Map* map) +{ + if (MasterArea(map->GetScriptName()) ) { + Maps.insert(Maps.begin(), 1, map); + MapIndex++; + return 0; + } + unsigned int i = (unsigned int) Maps.size(); + Maps.push_back( map ); + return i; +} + +int Game::DelMap(unsigned int index, int forced) +{ +//this function should archive the area, and remove it only if the area +//contains no active actors (combat, partymembers, etc) + if (index >= Maps.size()) { + return -1; + } + Map *map = Maps[index]; + + if (MapIndex==(int) index) { //can't remove current map in any case + const char *name = map->GetScriptName(); + memcpy(AnotherArea, name, sizeof(AnotherArea) ); + return -1; + } + + + if (!map) { //this shouldn't happen, i guess + printMessage("Game","Erased NULL Map\n",YELLOW); + Maps.erase( Maps.begin()+index); + if (MapIndex>(int) index) { + MapIndex--; + } + return 1; + } + + if (forced || (Maps.size()>MAX_MAPS_LOADED) ) + { + //keep at least one master + const char *name = map->GetScriptName(); + if (MasterArea(name)) { + if(!AnotherArea[0]) { + memcpy(AnotherArea, name, sizeof(AnotherArea)); + if (!forced) { + return -1; + } + } + } + //this check must be the last, because + //after PurgeActors you cannot keep the + //area in memory + //Or the queues should be regenerated! + if (!map->CanFree()) + { + return 1; + } + //remove map from memory + core->SwapoutArea(Maps[index]); + delete( Maps[index] ); + Maps.erase( Maps.begin()+index); + //current map will be decreased + if (MapIndex>(int) index) { + MapIndex--; + } + return 1; + } + //didn't remove the map + return 0; +} + +/* Loads an area */ +int Game::LoadMap(const char* ResRef, bool loadscreen) +{ + unsigned int i; + Map *newMap; + PluginHolder mM(IE_ARE_CLASS_ID); + + //this shouldn't happen + if (!mM) { + return -1; + } + + int index = FindMap(ResRef); + if (index>=0) { + return index; + } + + bool hide = false; + if (loadscreen) { + hide = core->HideGCWindow(); + core->GetGUIScriptEngine()->RunFunction("LoadScreen", "StartLoadScreen"); + core->GetGUIScriptEngine()->RunFunction("LoadScreen", "SetLoadScreen"); + } + DataStream* ds = gamedata->GetResource( ResRef, IE_ARE_CLASS_ID ); + if (!ds) { + goto failedload; + } + if(!mM->Open( ds, true )) { + goto failedload; + } + newMap = mM->GetMap(ResRef, IsDay()); + if (!newMap) { + goto failedload; + } + core->LoadProgress(100); + + for (i = 0; i < PCs.size(); i++) { + if (stricmp( PCs[i]->Area, ResRef ) == 0) { + newMap->AddActor( PCs[i] ); + } + } + for (i = 0; i < NPCs.size(); i++) { + if (stricmp( NPCs[i]->Area, ResRef ) == 0) { + newMap->AddActor( NPCs[i] ); + } + } + if (hide) { + core->UnhideGCWindow(); + } + return AddMap( newMap ); +failedload: + if (hide) + core->UnhideGCWindow(); + core->LoadProgress(100); + return -1; +} + +int Game::AddNPC(Actor* npc) +{ + int slot = InStore( npc ); //already an npc + if (slot != -1) { + return slot; + } + slot = InParty( npc ); + if (slot != -1) { + return -1; + } //can't add as npc already in party + npc->SetPersistent(0); + NPCs.push_back( npc ); + + return (int) NPCs.size() - 1; +} + +Actor* Game::GetNPC(unsigned int Index) +{ + if (Index >= NPCs.size()) { + return NULL; + } + return NPCs[Index]; +} + +void Game::SwapPCs(unsigned int Index1, unsigned int Index2) +{ + if (Index1 >= PCs.size()) { + return; + } + + if (Index2 >= PCs.size()) { + return; + } + int tmp = PCs[Index1]->InParty; + PCs[Index1]->InParty = PCs[Index2]->InParty; + PCs[Index2]->InParty = tmp; + //signal a change of the portrait window + core->SetEventFlag(EF_PORTRAIT | EF_SELECTION); +} + +void Game::DeleteJournalEntry(ieStrRef strref) +{ + size_t i=Journals.size(); + while(i--) { + if ((Journals[i]->Text==strref) || (strref==(ieStrRef) -1) ) { + delete Journals[i]; + Journals.erase(Journals.begin()+i); + } + } +} + +void Game::DeleteJournalGroup(int Group) +{ + size_t i=Journals.size(); + while(i--) { + if (Journals[i]->Group==(ieByte) Group) { + delete Journals[i]; + Journals.erase(Journals.begin()+i); + } + } +} +/* returns true if it modified or added a journal entry */ +bool Game::AddJournalEntry(ieStrRef strref, int Section, int Group) +{ + GAMJournalEntry *je = FindJournalEntry(strref); + if (je) { + //don't set this entry again in the same section + if (je->Section==Section) { + return false; + } + if ((Section == IE_GAM_QUEST_DONE) && Group) { + //removing all of this group and adding a new entry + DeleteJournalGroup(Group); + } else { + //modifying existing entry + je->Section = (ieByte) Section; + je->Group = (ieByte) Group; + ieDword chapter = 0; + locals->Lookup("CHAPTER", chapter); + je->Chapter = (ieByte) chapter; + je->GameTime = GameTime; + return true; + } + } + je = new GAMJournalEntry; + je->GameTime = GameTime; + ieDword chapter = 0; + locals->Lookup("CHAPTER", chapter); + je->Chapter = (ieByte) chapter; + je->unknown09 = 0; + je->Section = (ieByte) Section; + je->Group = (ieByte) Group; + je->Text = strref; + + Journals.push_back( je ); + return true; +} + +void Game::AddJournalEntry(GAMJournalEntry* entry) +{ + Journals.push_back( entry ); +} + +unsigned int Game::GetJournalCount() const +{ + return (unsigned int) Journals.size(); +} + +GAMJournalEntry* Game::FindJournalEntry(ieStrRef strref) +{ + unsigned int Index = (unsigned int) Journals.size(); + while(Index--) { + GAMJournalEntry *ret = Journals[Index]; + + if (ret->Text==strref) { + return ret; + } + } + + return NULL; +} + +GAMJournalEntry* Game::GetJournalEntry(unsigned int Index) +{ + if (Index >= Journals.size()) { + return NULL; + } + return Journals[Index]; +} + +unsigned int Game::GetSavedLocationCount() const +{ + return (unsigned int) savedpositions.size(); +} + +void Game::ClearSavedLocations() +{ + size_t i=savedpositions.size(); + while(i--) { + delete savedpositions[i]; + } + savedpositions.clear(); +} + +GAMLocationEntry* Game::GetSavedLocationEntry(unsigned int i) +{ + size_t current = savedpositions.size(); + if (i>=current) { + if (i>PCs.size()) { + return NULL; + } + savedpositions.resize(i+1); + while(current<=i) { + savedpositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) ); + } + } + return savedpositions[i]; +} + +unsigned int Game::GetPlaneLocationCount() const +{ + return (unsigned int) planepositions.size(); +} + +void Game::ClearPlaneLocations() +{ + size_t i=planepositions.size(); + while(i--) { + delete planepositions[i]; + } + planepositions.clear(); +} + +GAMLocationEntry* Game::GetPlaneLocationEntry(unsigned int i) +{ + size_t current = planepositions.size(); + if (i>=current) { + if (i>PCs.size()) { + return NULL; + } + planepositions.resize(i+1); + while(current<=i) { + planepositions[current++]=(GAMLocationEntry *) calloc(1, sizeof(GAMLocationEntry) ); + } + } + return planepositions[i]; +} + +char *Game::GetFamiliar(unsigned int Index) +{ + return Familiars[Index]; +} + +//reading the challenge rating table for iwd2 (only when needed) +void Game::LoadCRTable() +{ + AutoTable table("moncrate"); + if (table.ok()) { + int maxrow = table->GetRowCount()-1; + crtable = new CRRow[MAX_LEVEL]; + for(int i=0;iGetColumnCount(row)-1; + for(int j=0;jQueryField(row,col) ); + } + } + } +} + +int Game::GetXPFromCR(int cr) +{ + if (!crtable) LoadCRTable(); + if (crtable) { + int level = GetPartyLevel(true); + if (cr>=MAX_CRLEVEL) { + cr=MAX_CRLEVEL-1; + } + printf("Challenge Rating: %d, party level: %d ", cr, level); + return crtable[level][cr]; + } + printMessage("Game","Cannot find moncrate.2da!\n", LIGHT_RED); + return 0; +} + +void Game::ShareXP(int xp, int flags) +{ + int individual; + + if (flags&SX_CR) { + xp = GetXPFromCR(xp); + } + + if (flags&SX_DIVIDE) { + int PartySize = GetPartySize(true); //party size, only alive + if (PartySize<1) { + return; + } + individual = xp / PartySize; + } else { + individual = xp; + } + + if (!individual) { + return; + } + + if (xp>0) { + displaymsg->DisplayConstantStringValue( STR_GOTXP, 0xbcefbc, (ieDword) xp); //you have gained ... xp + } else { + displaymsg->DisplayConstantStringValue( STR_LOSTXP, 0xbcefbc, (ieDword) -xp); //you have lost ... xp + } + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + PCs[i]->AddExperience(individual); + } +} + +bool Game::EveryoneStopped() const +{ + for (unsigned int i=0; iGetNextStep() ) return false; + } + return true; +} + +//canmove=true: if some PC can't move (or hostile), then this returns false +bool Game::EveryoneNearPoint(Map *area, const Point &p, int flags) const +{ + for (unsigned int i=0; iSelected) { + continue; + } + } + if (PCs[i]->GetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + if (flags&ENP_CANMOVE) { + //someone is uncontrollable, can't move + if (PCs[i]->GetStat(IE_EA)>EA_GOODCUTOFF) { + return false; + } + + if (PCs[i]->GetStat(IE_STATE_ID)&STATE_CANTMOVE) { + return false; + } + } + if (PCs[i]->GetCurrentArea()!=area) { + return false; + } + if (Distance(p,PCs[i])>MAX_TRAVELING_DISTANCE) { + return false; + } + } + return true; +} + +//called when someone died +void Game::PartyMemberDied(Actor *actor) +{ + //this could be null, in some extreme cases... + Map *area = actor->GetCurrentArea(); + + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_DEAD) { + continue; + } + if (PCs[i]->GetCurrentArea()!=area) { + continue; + } + PCs[i]->ReactToDeath(actor->GetScriptName()); + } +} + +//reports if someone died +int Game::PartyMemberDied() const +{ + for (unsigned int i=0; iGetInternalFlag()&IF_JUSTDIED) { + return i; + } + } + return -1; +} + +void Game::IncrementChapter() +{ + //chapter first set to 0 (prologue) + ieDword chapter = (ieDword) -1; + locals->Lookup("CHAPTER",chapter); + locals->SetAt("CHAPTER",chapter+1); + //clear statistics + for (unsigned int i=0; iPCStats->IncrementChapter(); + } +} + +void Game::SetReputation(ieDword r) +{ + if (r<10) r=10; + else if (r>200) r=200; + if (Reputation>r) { + displaymsg->DisplayConstantStringValue(STR_LOSTREP,0xc0c000,(Reputation-r)/10); + } else if (ReputationDisplayConstantStringValue(STR_GOTREP,0xc0c000,(r-Reputation)/10); + } + Reputation = r; + for (unsigned int i=0; iSetBase(IE_REPUTATION, Reputation); + } +} + +void Game::SetControlStatus(int value, int mode) +{ + switch(mode) { + case BM_OR: ControlStatus|=value; break; + case BM_NAND: ControlStatus&=~value; break; + case BM_SET: ControlStatus=value; break; + case BM_AND: ControlStatus&=value; break; + case BM_XOR: ControlStatus^=value; break; + } + core->SetEventFlag(EF_CONTROL); +} + +void Game::AddGold(ieDword add) +{ + ieDword old; + + if (!add) { + return; + } + old = PartyGold; + PartyGold += add; + if (oldDisplayConstantStringValue( STR_GOTGOLD, 0xc0c000, PartyGold-old); + } else { + displaymsg->DisplayConstantStringValue( STR_LOSTGOLD, 0xc0c000, old-PartyGold); + } +} + +//later this could be more complicated +void Game::AdvanceTime(ieDword add) +{ + ieDword h = GameTime/(300*AI_UPDATE_TIME); + GameTime+=add; + if (h!=GameTime/(300*AI_UPDATE_TIME)) { + //asking for a new weather when the hour changes + WeatherBits&=~WB_HASWEATHER; + } + Ticks+=add*interval; + //change the tileset if needed + Map *map = GetCurrentArea(); + if (map && map->ChangeMap(IsDay())) { + //play the daylight transition movie appropriate for the area + //it is needed to play only when the area truly changed its tileset + //this is signalled by ChangeMap + int areatype = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3; + ieResRef *res; + + printMessage("Game","Switching DayLight\n",GREEN); + if (IsDay()) { + res=&nightmovies[areatype]; + } else { + res=&daymovies[areatype]; + } + if (*res[0]!='*') { + core->PlayMovie(*res); + } + } +} + +//returns true if there are excess players in the team +bool Game::PartyOverflow() const +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + return false; + } + //don't start this screen when the gui is busy + if (gc->GetDialogueFlags() & (DF_IN_DIALOG|DF_IN_CONTAINER|DF_FREEZE_SCRIPTS) ) { + return false; + } + if (!partysize) { + return false; + } + return (PCs.size()>partysize); +} + +bool Game::PCInCombat(Actor* actor) const +{ + if (!CombatCounter) { + return false; + } + + if (actor->LastTarget) { + return true; + } + if (AttackersOf(actor->GetGlobalID(), actor->GetCurrentArea())) { + return true; + } + return false; +} + +bool Game::AnyPCInCombat() const +{ + if (!CombatCounter) { + return false; + } + + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_NOSAVE) { + if (area->INISpawn) { + area->INISpawn->RespawnNameless(); + } + } + return false; + } + // if protagonist died + if (protagonist==PM_YES) { + if (PCs[0]->GetStat(IE_STATE_ID)&STATE_NOSAVE) { + return true; + } + return false; + } + //protagonist == 2 + for (unsigned int i=0; iGetStat(IE_STATE_ID)&STATE_NOSAVE) ) { + return false; + } + } + return true; +} + +//runs all area scripts + +void Game::UpdateScripts() +{ + ExecuteScript( 1 ); + ProcessActions(false); + size_t idx; + + bool PartyAttack = false; + + for (idx=0;idxUpdateScripts(); + size_t acnt=Attackers.size(); + while(acnt--) { + Actor *actor = Maps[idx]->GetActorByGlobalID(Attackers[acnt]); + if (actor) { + if ( !Maps[idx]->GetActorByGlobalID(actor->LastTarget) ) { + //Actor's target left area + OutAttack(Attackers[acnt]); + continue; + } else { + //each attacker handles their own round initiation + actor->InitRound(GameTime); + if (actor->InParty) { + PartyAttack = true; + } + } + } else { + //Attacker is gone from area + OutAttack(Attackers[acnt]); + } + } + } + + if (PartyAttack) { + //ChangeSong will set the battlesong only if CombatCounter is nonzero + CombatCounter=150; + ChangeSong(false, true); + } else { + if (CombatCounter) { + CombatCounter--; + //Change song if combatcounter went down to 0 + if (!CombatCounter) { + ChangeSong(false, false); + } + } + } + + if (StateOverrideTime) + StateOverrideTime--; + if (BanterBlockTime) + BanterBlockTime--; + + if (Maps.size()>MAX_MAPS_LOADED) { + idx = Maps.size(); + + //starting from 0, so we see the most recent master area first + for(unsigned int i=0;iGetMusicMgr()->IsPlaying()) { + ChangeSong(false,false); + } + + //this is used only for the death delay so far + if (event_handler) { + if (!event_timer) { + event_handler->call(); + event_handler = NULL; + } + event_timer--; + } + + if (EveryoneDead()) { + //don't check it any more + protagonist = PM_NO; + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "DeathWindow"); + return; + } + + if (PartyOverflow()) { + partysize = 0; + core->GetGUIScriptEngine()->RunFunction("GUIWORLD", "OpenReformPartyWindow"); + return; + } +} + +void Game::SetTimedEvent(EventHandler func, int count) +{ + event_timer = count; + event_handler = func; +} + +void Game::SetProtagonistMode(int mode) +{ + protagonist = mode; +} + +void Game::SetPartySize(int size) +{ + // 0 size means no party size control + if (size<0) { + return; + } + partysize = (size_t) size; +} + +//Get the area dependent rest movie +ieResRef *Game::GetDream(Map *area) +{ + //select dream based on area + int daynight = IsDay(); + if (area->Dream[daynight][0]) { + return area->Dream+daynight; + } + int dream = (area->AreaType&(AT_FOREST|AT_CITY|AT_DUNGEON))>>3; + return restmovies+dream; +} + +//Start dream cutscenes for player1 +void Game::PlayerDream() +{ + Scriptable *Sender = GetPC(0,true); + if (!Sender) return; + + GameScript* gs = new GameScript( "player1d", Sender,0,0 ); + gs->Update(); + delete( gs ); +} + +//noareacheck = no random encounters +//dream = 0 - based on area non-0 - select from list +//-1 no dream +//hp is how much hp the rest will heal +void Game::RestParty(int checks, int dream, int hp) +{ + if (!(checks&REST_NOMOVE) ) { + if (!EveryoneStopped()) { + return; + } + } + Actor *leader = GetPC(0, true); + if (!leader) { + return; + } + + Map *area = leader->GetCurrentArea(); + //we let them rest if someone is paralyzed, but the others gather around + if (!(checks&REST_NOSCATTER) ) { + if (!EveryoneNearPoint( area, leader->Pos, 0 ) ) { + //party too scattered + displaymsg->DisplayConstantString( STR_SCATTERED, 0xff0000 ); + return; + } + } + + if (!(checks&REST_NOCRITTER) ) { + //don't allow resting while in combat + if (AnyPCInCombat()) { + displaymsg->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 ); + return; + } + //don't allow resting if hostiles are nearby + if (area->AnyEnemyNearPoint(leader->Pos)) { + displaymsg->DisplayConstantString( STR_CANTRESTMONS, 0xff0000 ); + return; + } + } + + //rest check, if PartyRested should be set, area should return true + //area should advance gametime too (so partial rest is possible) + int hours = 8; + if (!(checks&REST_NOAREA) ) { + //you cannot rest here + if (area->AreaFlags&1) { + displaymsg->DisplayConstantString( STR_MAYNOTREST, 0xff0000 ); + return; + } + //you may not rest here, find an inn + if (!(area->AreaType&(AT_OUTDOOR|AT_FOREST|AT_DUNGEON|AT_CAN_REST) )) + { + displaymsg->DisplayConstantString( STR_MAYNOTREST, 0xff0000 ); + return; + } + //area encounters + if(area->Rest( leader->Pos, 8, (GameTime/AI_UPDATE_TIME)%7200/3600) ) { + return; + } + } + AdvanceTime(2400*AI_UPDATE_TIME); + + int i = GetPartySize(true); // party size, only alive + + while (i--) { + Actor *tar = GetPC(i, true); + tar->ClearPath(); + tar->ClearActions(); + tar->SetModal(MS_NONE, 0); + //if hp = 0, then healing will be complete + tar->Heal(hp); + //removes fatigue, recharges spells + tar->Rest(0); + tar->PartyRested(); + } + + //movie and cutscene dreams + if (dream>=0) { + //cutscene dreams + if (gamedata->Exists("player1d",IE_BCS_CLASS_ID, true)) + PlayerDream(); + + //select dream based on area + ieResRef *movie; + if (dream==0 || dream>7) { + movie = GetDream(area); + } else { + movie = restmovies+dream; + } + if (*movie[0]!='*') { + core->PlayMovie(*movie); + } + } + + //set partyrested flags + PartyRested(); + area->PartyRested(); + core->SetEventFlag(EF_ACTION); + + //restindex will be -1 in the case of PST + //FIXME: I don't quite see why we can't sumply use the same strings.2da entry + //It seems we could reduce complexity here, and free up 2-3 string slots too + int restindex = displaymsg->GetStringReference(STR_REST); + int strindex; + char* tmpstr = NULL; + + core->GetTokenDictionary()->SetAtCopy("HOUR", hours); + if (restindex != -1) { + strindex = displaymsg->GetStringReference(STR_HOURS); + } else { + strindex = displaymsg->GetStringReference(STR_PST_HOURS); + restindex = displaymsg->GetStringReference(STR_PST_REST); + } + + //this would be bad + if (strindex == -1 || restindex == -1) return; + tmpstr = core->GetString(strindex, 0); + //as would this + if (!tmpstr) return; + + core->GetTokenDictionary()->SetAtCopy("DURATION", tmpstr); + core->FreeString(tmpstr); + displaymsg->DisplayString(restindex, 0xffffff, 0); +} + +//timestop effect +void Game::TimeStop(Actor* owner, ieDword end) +{ + timestop_owner=owner; + timestop_end=GameTime+end; +} + +//recalculate the party's infravision state +void Game::Infravision() +{ + hasInfra = false; + Map *map = GetCurrentArea(); + if (!map) return; + for(size_t i=0;iGetCurrentArea()!=map) continue; + //Group infravision overrides this??? + if (!actor->Selected) continue; + if (actor->GetStat(IE_STATE_ID) & STATE_INFRA) { + hasInfra = true; + return; + } + } +} + +//returns the colour which should be applied onto the whole game area viewport +//this is based on timestop, dream area, weather, daytime + +static const Color TimeStopTint={0xe0,0xe0,0xe0,0x20}; //greyscale +static const Color DreamTint={0xf0,0xe0,0xd0,0x10}; //light brown scale +static const Color NightTint={0x80,0x80,0xe0,0x40}; //dark, bluish +static const Color DuskTint={0xe0,0x80,0x80,0x40}; //dark, reddish +static const Color FogTint={0xff,0xff,0xff,0x40}; //whitish +static const Color DarkTint={0x80,0x80,0xe0,0x10}; //slightly dark bluish + +const Color *Game::GetGlobalTint() const +{ + if (timestop_end>GameTime) { + return &TimeStopTint; + } + Map *map = GetCurrentArea(); + if (!map) return NULL; + if (map->AreaFlags&AF_DREAM) { + return &DreamTint; + } + if ((map->AreaType&(AT_OUTDOOR|AT_DAYNIGHT|AT_EXTENDED_NIGHT)) == (AT_OUTDOOR|AT_DAYNIGHT) ) { + //get daytime colour + ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300); + if (daynight<2 || daynight>22) { + return &NightTint; + } + if (daynight>20 || daynight<4) { + return &DuskTint; + } + } + if ((map->AreaType&(AT_OUTDOOR|AT_WEATHER)) == (AT_OUTDOOR|AT_WEATHER)) { + //get weather tint + if (WeatherBits&WB_RAIN) { + return &DarkTint; + } + if (WeatherBits&WB_FOG) { + return &FogTint; + } + } + + return NULL; +} + +bool Game::IsDay() +{ + ieDword daynight = ((GameTime/AI_UPDATE_TIME)%7200/300); + if(daynight<4 || daynight>20) { + return false; + } + return true; +} + +void Game::InAttack(ieDword globalID) +{ + std::vector< ieDword>::const_iterator idx; + + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + if (*idx==globalID) return; + } + Attackers.push_back(globalID); +} + +void Game::OutAttack(ieDword globalID) +{ + std::vector< ieDword>::iterator idx; + + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + if (*idx==globalID) { + Attackers.erase(idx); + break; + } + } +} + +void Game::ChangeSong(bool always, bool force) +{ + int Song; + + if (CombatCounter) { + //battlesong + Song = SONG_BATTLE; + } else { + //will select SONG_DAY or SONG_NIGHT + Song = (GameTime/AI_UPDATE_TIME)%7200/3600; + } + //area may override the song played (stick in battlemusic) + //always transition gracefully with ChangeSong + //force just means, we schedule the song for later, if currently + //is playing + area->PlayAreaSong( Song, always, force ); +} + +int Game::AttackersOf(ieDword globalID, Map *area) const +{ + if (!area) { + return 0; + } + std::vector< ieDword>::const_iterator idx; + + int cnt = 0; + for(idx=Attackers.begin(); idx!=Attackers.end();idx++) { + Actor * actor = area->GetActorByGlobalID(*idx); + if (actor) { + if (actor->LastTarget==globalID) { + cnt++; + } + } + } + return cnt; +} + +/* this method redraws weather. If update is false, +then the weather particles won't change (game paused) +*/ +void Game::DrawWeather(const Region &screen, bool update) +{ + if (!weather) { + return; + } + if (!area->HasWeather()) { + return; + } + + weather->Draw( screen ); + if (!update) { + return; + } + + if (!(WeatherBits & (WB_RAIN|WB_SNOW)) ) { + if (weather->GetPhase() == P_GROW) { + weather->SetPhase(P_FADE); + } + } + //if (GameTime&1) { + int drawn = weather->Update(); + if (drawn) { + WeatherBits &= ~WB_START; + } + //} + + if (WeatherBits&WB_HASWEATHER) { + return; + } + StartRainOrSnow(true, area->GetWeather()); +} + +/* sets the weather type */ +void Game::StartRainOrSnow(bool conditional, int w) +{ + if (conditional && (w & (WB_RAIN|WB_SNOW)) ) { + if (WeatherBits & (WB_RAIN | WB_SNOW) ) + return; + } + // whatever was responsible for calling this, we now have some set weather + WeatherBits = w | WB_HASWEATHER; + if (w & WB_LIGHTNING) { + if (WeatherBits&WB_START) { + //already raining + if (GameTime&1) { + core->PlaySound(DS_LIGHTNING1); + } else { + core->PlaySound(DS_LIGHTNING2); + } + } else { + //start raining (far) + core->PlaySound(DS_LIGHTNING3); + } + } + if (w&WB_SNOW) { + core->PlaySound(DS_SNOW); + weather->SetType(SP_TYPE_POINT, SP_PATH_FLIT, SP_SPAWN_SOME); + weather->SetPhase(P_GROW); + weather->SetColor(SPARK_COLOR_WHITE); + return; + } + if (w&WB_RAIN) { + core->PlaySound(DS_RAIN); + weather->SetType(SP_TYPE_LINE, SP_PATH_RAIN, SP_SPAWN_SOME); + weather->SetPhase(P_GROW); + weather->SetColor(SPARK_COLOR_STONE); + return; + } + weather->SetPhase(P_FADE); +} + +void Game::SetExpansion(ieDword value) +{ + if (Expansion>=value) { + return; + } + Expansion = value; + + switch(Expansion) { + default: + core->SetEventFlag(EF_EXPANSION); + break; + //TODO: move this hardcoded hack to the scripts + case 5: + core->GetDictionary()->SetAt( "PlayMode", 2 ); + + int i = GetPartySize(false); + while(i--) { + Actor *actor = GetPC(i, false); + InitActorPos(actor); + } + } +} + +void Game::DebugDump() +{ + size_t idx; + + printf("Currently loaded areas:\n"); + for(idx=0;idxGetScriptName()); + } + printf("Current area: %s Previous area: %s\n", CurrentArea, PreviousArea); + printf("Global script: %s\n", Scripts[0]->GetName()); + printf("CombatCounter: %d\n", (int) CombatCounter); + printf("Attackers count: %d\n", (int) Attackers.size()); + for(idx=0;idxLastTarget); + printf("Name: %s Attacking : %s\n", actor->ShortName, whom?whom->ShortName:"???"); + } + + printf("Party size: %d\n", (int) PCs.size()); + for(idx=0;idxShortName, actor->InParty, actor->Selected?"x":"-"); + } +} + +Actor *Game::GetActorByGlobalID(ieDword globalID) +{ + size_t mc = GetLoadedMapCount(); + while(mc--) { + Map *map = GetMap(mc); + Actor *actor = map->GetActorByGlobalID(globalID); + if (actor) return actor; + } + return GetGlobalActorByGlobalID(globalID); +} + diff --git a/project/jni/application/gemrb/src/core/Game.h b/project/jni/application/gemrb/src/core/Game.h new file mode 100644 index 000000000..1e4f9b9ea --- /dev/null +++ b/project/jni/application/gemrb/src/core/Game.h @@ -0,0 +1,424 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Game.h + * Declares Game class, object representing current game state. + * @author The GemRB Project + */ + + +class Game; + +#ifndef GAME_H +#define GAME_H + +#include "exports.h" +#include "ie_types.h" + +#include "Callback.h" +#include "Map.h" +#include "Variables.h" +#include "Scriptable/Actor.h" + +#include + +class Particles; + +//the size of the bestiary register +#define BESTIARY_SIZE 260 + +//ShareXP flags +#define SX_DIVIDE 1 //divide XP among team members +#define SX_CR 2 //use challenge rating resolution + +//joinparty flags +#define JP_JOIN 1 //refresh join time +#define JP_INITPOS 2 //init startpos +#define JP_SELECT 4 //select the actor after joining + +//protagonist mode +#define PM_NO 0 //no death checks +#define PM_YES 1 //if protagonist dies, game over +#define PM_TEAM 2 //if team dies, game over + +// Flags bits for SelectActor() +// !!! Keep these synchronized with GUIDefines.py !!! +#define SELECT_NORMAL 0x00 +#define SELECT_REPLACE 0x01 // when selecting actor, deselect all others +#define SELECT_QUIET 0x02 // do not run handler when changing selection + +// Flags bits for EveryoneNearPoint() +#define ENP_CANMOVE 1 // also check if the PC can move +#define ENP_ONLYSELECT 2 // check only selected PC + +// GUI Control Status flags (saved in game) +#define CS_PARTY_AI 1 //enable party AI +#define CS_MEDIUM 2 //medium dialog +#define CS_LARGE 6 //large dialog, both bits set +#define CS_DIALOGSIZEMASK 6 +#define CS_DIALOG 8 //dialog is running +#define CS_HIDEGUI 16 //hide all gui +#define CS_ACTION 32 //hide action pane +#define CS_PORTRAIT 64 //hide portrait pane +#define CS_MAPNOTES 128 //hide mapnotes + +//Weather bits +#define WB_NORMAL 0 +#define WB_RAIN 1 +#define WB_SNOW 2 +#define WB_FOG 3 +#define WB_MASK 7 +#define WB_LIGHTNING 8 +#define WB_HASWEATHER 0x40 +#define WB_START 0x80 + +//Rest flags +#define REST_NOAREA 1 //no area check +#define REST_NOSCATTER 2 //no scatter check +#define REST_NOMOVE 4 //no movement check +#define REST_NOCRITTER 8 //no hostiles check + +//Song types (hardcoded) +#define SONG_DAY 0 +#define SONG_NIGHT 1 +#define SONG_BATTLE 3 + +/** + * @struct PCStruct + * Information about party member. + */ + +struct PCStruct { + ieWord Selected; + ieWord PartyOrder; + ieDword OffsetToCRE; + ieDword CRESize; + ieResRef CREResRef; + ieDword Orientation; + ieResRef Area; + ieWord XPos; + ieWord YPos; + ieWord ViewXPos; + ieWord ViewYPos; + ieWord ModalState; + ieWord Happiness; + ieDword Interact[MAX_INTERACT]; + ieWord QuickWeaponSlot[MAX_QUICKWEAPONSLOT]; + ieWord QuickWeaponHeader[MAX_QUICKWEAPONSLOT]; + ieResRef QuickSpellResRef[MAX_QSLOTS]; + ieWord QuickItemSlot[MAX_QUICKITEMSLOT]; + ieWord QuickItemHeader[MAX_QUICKITEMSLOT]; + char Name[32]; + ieDword TalkCount; + ieByte QSlots[GUIBT_COUNT]; + ieByte QuickSpellClass[MAX_QSLOTS]; +}; + +#define IE_GAM_JOURNAL 0 +#define IE_GAM_QUEST_UNSOLVED 1 +#define IE_GAM_QUEST_DONE 2 +#define IE_GAM_JOURNAL_USER 3 + +/** + * @struct GAMJournalEntry + * Single entry in a journal + */ + +struct GAMJournalEntry { + ieStrRef Text; + ieDword GameTime; // in game time seconds + ieByte Chapter; + ieByte unknown09; + ieByte Section; + ieByte Group; // this is a GemRB extension +}; + +// Saved location of party member. +struct GAMLocationEntry { + ieResRef AreaResRef; + Point Pos; +}; + +#define MAX_CRLEVEL 32 + +typedef int CRRow[MAX_CRLEVEL]; + +/** + * @class Game + * Object representing current game state, mostly party. + */ + +class GEM_EXPORT Game : public Scriptable { +public: + Game(void); + ~Game(void); +private: + std::vector< Actor*> PCs; + std::vector< Actor*> NPCs; + std::vector< Map*> Maps; + std::vector< GAMJournalEntry*> Journals; + std::vector< GAMLocationEntry*> savedpositions; + std::vector< GAMLocationEntry*> planepositions; + std::vector< char*> mastarea; + std::vector< ieDword> Attackers; + CRRow *crtable; + ieResRef restmovies[8]; + ieResRef daymovies[8]; + ieResRef nightmovies[8]; + int MapIndex; +public: + std::vector< Actor*> selected; + int version; + Variables* kaputz; + ieByte* beasts; + ieByte* mazedata; //only in PST + ieResRef Familiars[9]; + ieDword CombatCounter; + ieDword StateOverrideFlag, StateOverrideTime; + ieDword BanterBlockFlag, BanterBlockTime; + + /** Index of PC selected in non-walking environment (shops, inventory...) */ + int SelectedSingle; + /** 0 if the protagonist's death doesn't cause game over */ + /** 1 if the protagonist's death causes game over */ + /** 2 if no check is needed (pst) */ + int protagonist; + /** if party size exceeds this amount, a callback will be called */ + size_t partysize; + ieDword Ticks; + ieDword interval; // 1000/AI_UPDATE (a tenth of a round in ms) + ieDword GameTime; + ieDword LastScriptUpdate; // GameTime at which UpdateScripts last ran + ieDword RealTime; + ieWord WhichFormation; + ieWord Formations[5]; + ieDword PartyGold; + ieWord NpcInParty; + ieWord WeatherBits; + ieDword Unknown48; //still unknown + ieDword Reputation; + ieDword ControlStatus; // used in bg2, iwd (where you can switch panes off) + ieDword Expansion; // mostly used by BG2. IWD games set it to 3 on newgame + ieResRef AnotherArea; + ieResRef CurrentArea; + ieResRef PreviousArea; //move here if the worldmap exit is illegal? + ieResRef LoadMos; + Actor *timestop_owner; + ieDword timestop_end; + Particles *weather; + int event_timer; + EventHandler event_handler; //like in Control + bool hasInfra; +private: + /** reads the challenge rating table */ + void LoadCRTable(); +public: + /** Returns the PC's slot count for partyID */ + int FindPlayer(unsigned int partyID); + /** Returns actor by slot */ + Actor* GetPC(unsigned int slot, bool onlyalive); + /** Finds an actor in party by party ID, returns Actor, if not there, returns NULL*/ + Actor* FindPC(unsigned int partyID); + Actor* FindNPC(unsigned int partyID); + /** Finds a global actor by global ID */ + Actor* GetGlobalActorByGlobalID(ieDword globalID); + /** Finds an actor in party, returns slot, if not there, returns -1*/ + int InParty(Actor* pc) const; + /** Finds an actor in store, returns slot, if not there, returns -1*/ + int InStore(Actor* pc) const; + /** Finds an actor in party by scripting name*/ + Actor* FindPC(const char *deathvar); + /** Finds an actor in store by scripting name*/ + Actor* FindNPC(const char *deathvar); + /** Sets the area and position of the actor to the starting position */ + void InitActorPos(Actor *actor); + /** Joins party */ + int JoinParty(Actor* pc, int join=JP_JOIN); + /** Return current party size */ + int GetPartySize(bool onlyalive) const; + /** Returns the npcs count */ + int GetNPCCount() const { return (int)NPCs.size(); } + /** Sends the hotkey trigger to all selected pcs */ + void SetHotKey(unsigned long Key); + /** Select PC for non-walking environment (shops, inventory, ...) */ + bool SelectPCSingle(int index); + /** Get index of selected PC for non-walking env (shops, inventory, ...) */ + int GetSelectedPCSingle() const; + /** (De)selects actor. */ + bool SelectActor( Actor* actor, bool select, unsigned flags ); + + /** Return current party level count for xp calculations */ + int GetPartyLevel(bool onlyalive) const; + /** Reassigns inparty numbers, call it after party creation */ + void ConsolidateParty(); + /** Removes actor from party (if in there) */ + int LeaveParty(Actor* pc); + /** Returns slot*/ + int DelPC(unsigned int slot, bool autoFree = false); + int DelNPC(unsigned int slot, bool autoFree = false); + /** Returns map in index */ + Map* GetMap(unsigned int index) const; + /** Returns a map from area name, loads it if needed + * use it for the biggest safety, change = true will change the current map */ + Map* GetMap(const char *areaname, bool change); + /** Returns slot of the map if found */ + int FindMap(const char *ResRef); + int AddMap(Map* map); + /** Determine if area is master area*/ + bool MasterArea(const char *area); + /** Dynamically adding an area to master areas*/ + void SetMasterArea(const char *area); + /** Returns slot of the map, if it was already loaded, + * don't load it again, set changepf == true, + * if you want to change the pathfinder too. */ + int LoadMap(const char* ResRef, bool loadscreen); + int DelMap(unsigned int index, int forced = 0); + int AddNPC(Actor* npc); + Actor* GetNPC(unsigned int Index); + void SwapPCs(unsigned int Index1, unsigned int Index2); + bool IsDay(); + void InAttack(ieDword globalID); + void OutAttack(ieDword globalID); + int AttackersOf(ieDword globalID, Map *area) const; + + //journal entries + /** Deletes one or all journal entries if strref is -1 */ + void DeleteJournalEntry(ieStrRef strref); + /** Delete entries of the same group */ + void DeleteJournalGroup(int Group); + /** Adds a journal entry from dialog data. + * Time and chapter are calculated on the fly + * Returns false if the entry already exists */ + bool AddJournalEntry(ieStrRef strref, int section, int group); + /** Adds a journal entry while loading the .gam structure */ + void AddJournalEntry(GAMJournalEntry* entry); + unsigned int GetJournalCount() const; + GAMJournalEntry* FindJournalEntry(ieStrRef strref); + GAMJournalEntry* GetJournalEntry(unsigned int Index); + + //saved locations + unsigned int GetSavedLocationCount() const; + void ClearSavedLocations(); + GAMLocationEntry* GetSavedLocationEntry(unsigned int Index); + + //plane locations + unsigned int GetPlaneLocationCount() const; + void ClearPlaneLocations(); + GAMLocationEntry* GetPlaneLocationEntry(unsigned int Index); + + char *GetFamiliar(unsigned int Index); + + bool IsBeastKnown(unsigned int Index) const { + if (!beasts) { + return false; + } + if (Index>=BESTIARY_SIZE) { + return false; + } + return beasts[Index] != 0; + } + void SetBeastKnown(unsigned int Index) { + if (!beasts) { + return; + } + if (Index>=BESTIARY_SIZE) { + return; + } + beasts[Index] = 1; + } + ieWord GetFormation() const { + if (WhichFormation>4) { + return 0; + } + return Formations[WhichFormation]; + } + size_t GetAttackerCount() const { + return Attackers.size(); + } + + /** converts challenge rating to xp */ + int GetXPFromCR(int cr); + /** shares XP among all party members */ + void ShareXP(int XP, int flags); + /** returns true if we should start the party overflow window */ + bool PartyOverflow() const; + /** returns true if actor is an attacker or being attacked */ + bool PCInCombat(Actor *actor) const; + /** returns true if any pc is attacker or being attacked */ + bool AnyPCInCombat() const; + /** returns true if the party death condition is true */ + bool EveryoneDead() const; + /** returns true if no one moves */ + bool EveryoneStopped() const; + bool EveryoneNearPoint(Map *map, const Point &p, int flags) const; + /** returns true if a PC just died */ + int PartyMemberDied() const; + /** a party member just died now */ + void PartyMemberDied(Actor *); + /** Increments chapter variable and refreshes kill stats */ + void IncrementChapter(); + /** Sets party reputation */ + void SetReputation(ieDword r); + /** Sets the gamescreen control status (pane states, dialog textarea size) */ + void SetControlStatus(int value, int operation); + /** Sets party size (1-32000) */ + void SetPartySize(int value); + /** Sets a guiscript function to happen after x AI cycles have elapsed */ + void SetTimedEvent(EventHandler func, int count); + /** Sets protagonist mode to 0-none,1-protagonist,2-team */ + void SetProtagonistMode(int value); + void StartRainOrSnow(bool conditional, int weather); + size_t GetLoadedMapCount() const { return Maps.size(); } + /** Adds or removes gold */ + void AddGold(ieDword add); + /** Adds ticks to game time */ + void AdvanceTime(ieDword add); + /** Runs the script engine on the global script and the area scripts + areas run scripts on door, infopoint, container, actors too */ + void UpdateScripts(); + /** runs area functionality, sets partyrested trigger */ + void RestParty(int checks, int dream, int hp); + /** timestop effect initiated by actor */ + void TimeStop(Actor *actor, ieDword end); + /** gets the colour which should be applied over the game area, + may return NULL */ + const Color *GetGlobalTint() const; + /** returns true if party has infravision */ + bool PartyHasInfravision() const { return hasInfra; } + /** draw weather */ + void DrawWeather(const Region &screen, bool update); + /** updates current area music */ + void ChangeSong(bool always = true, bool force = true); + /** sets expansion mode */ + void SetExpansion(ieDword value); + /** Dumps information about the object */ + void DebugDump(); + /** Finds an actor by global ID */ + Actor *GetActorByGlobalID(ieDword objectID); + /** updates the infravision info */ + void Infravision(); +private: + bool DetermineStartPosType(const TableMgr *strta); + ieResRef *GetDream(Map *area); + void PlayerDream(); +}; + +#endif // ! GAME_H diff --git a/project/jni/application/gemrb/src/core/GameData.cpp b/project/jni/application/gemrb/src/core/GameData.cpp new file mode 100644 index 000000000..f8fb2d6e6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameData.cpp @@ -0,0 +1,490 @@ +/* GemRB - Infinity Engine Emulator +* Copyright (C) 2003-2005 The GemRB Project +* +* 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 "GameData.h" + +#include "ActorMgr.h" +#include "AnimationMgr.h" +#include "Cache.h" +#include "Effect.h" +#include "EffectMgr.h" +#include "Factory.h" +#include "Game.h" +#include "ImageFactory.h" +#include "ImageMgr.h" +#include "Interface.h" +#include "Item.h" +#include "ItemMgr.h" +#include "ResourceDesc.h" +#include "Spell.h" +#include "SpellMgr.h" +#include "Scriptable/Actor.h" +#include "System/FileStream.h" + +#include + +static void ReleaseItem(void *poi) +{ + delete ((Item *) poi); +} + +static void ReleaseSpell(void *poi) +{ + delete ((Spell *) poi); +} + +static void ReleaseEffect(void *poi) +{ + delete ((Effect *) poi); +} + +static void ReleasePalette(void *poi) +{ + //we allow nulls, but we shouldn't release them + if (!poi) return; + //as long as palette has its own refcount, this should be Release + ((Palette *) poi)->Release(); +} + +GEM_EXPORT GameData* gamedata; + +GameData::GameData() +{ + factory = new Factory(); +} + +GameData::~GameData() +{ + delete factory; +} + +void GameData::ClearCaches() +{ + ItemCache.RemoveAll(ReleaseItem); + SpellCache.RemoveAll(ReleaseSpell); + EffectCache.RemoveAll(ReleaseEffect); + PaletteCache.RemoveAll(ReleasePalette); +} + +Actor *GameData::GetCreature(const char* ResRef, unsigned int PartySlot) +{ + DataStream* ds = GetResource( ResRef, IE_CRE_CLASS_ID ); + if (!ds) + return 0; + + PluginHolder actormgr(IE_CRE_CLASS_ID); + if (!actormgr->Open( ds, true )) { + return 0; + } + Actor* actor = actormgr->GetActor(PartySlot); + return actor; +} + +int GameData::LoadCreature(const char* ResRef, unsigned int PartySlot, bool character, int VersionOverride) +{ + DataStream *stream; + + Actor* actor; + if (character) { + char nPath[_MAX_PATH], fName[16]; + snprintf( fName, sizeof(fName), "%s.chr", ResRef); + PathJoin( nPath, core->GamePath, "characters", fName, NULL ); + FileStream *fs = new FileStream(); + fs -> Open( nPath, true ); + stream = (DataStream *) fs; + PluginHolder actormgr(IE_CRE_CLASS_ID); + if (!actormgr->Open( stream, true )) { + return -1; + } + actor = actormgr->GetActor(PartySlot); + } else { + actor = GetCreature(ResRef, PartySlot); + } + + if ( !actor ) { + return -1; + } + + if (VersionOverride != -1) { + actor->version = VersionOverride; + } + + //both fields are of length 9, make this sure! + memcpy(actor->Area, core->GetGame()->CurrentArea, sizeof(actor->Area) ); + if (actor->BaseStats[IE_STATE_ID] & STATE_DEAD) { + actor->SetStance( IE_ANI_TWITCH ); + } else { + actor->SetStance( IE_ANI_AWAKE ); + } + actor->SetOrientation( 0, false ); + + if ( PartySlot != 0 ) { + return core->GetGame()->JoinParty( actor, JP_JOIN|JP_INITPOS ); + } + else { + return core->GetGame()->AddNPC( actor ); + } +} + +/** Loads a 2DA Table, returns -1 on error or the Table Index on success */ +int GameData::LoadTable(const ieResRef ResRef) +{ + int ind = GetTableIndex( ResRef ); + if (ind != -1) { + tables[ind].refcount++; + return ind; + } + //printf("(%s) Table not found... Loading from file\n", ResRef); + DataStream* str = GetResource( ResRef, IE_2DA_CLASS_ID ); + if (!str) { + return -1; + } + PluginHolder tm(IE_2DA_CLASS_ID); + if (!tm) { + delete str; + return -1; + } + if (!tm->Open( str, true )) { + return -1; + } + Table t; + t.refcount = 1; + strncpy( t.ResRef, ResRef, 8 ); + t.tm = tm; + ind = -1; + for (size_t i = 0; i < tables.size(); i++) { + if (tables[i].refcount == 0) { + ind = ( int ) i; + break; + } + } + if (ind != -1) { + tables[ind] = t; + return ind; + } + tables.push_back( t ); + return ( int ) tables.size() - 1; +} +/** Gets the index of a loaded table, returns -1 on error */ +int GameData::GetTableIndex(const char* ResRef) const +{ + for (size_t i = 0; i < tables.size(); i++) { + if (tables[i].refcount == 0) + continue; + if (strnicmp( tables[i].ResRef, ResRef, 8 ) == 0) + return ( int ) i; + } + return -1; +} +/** Gets a Loaded Table by its index, returns NULL on error */ +Holder GameData::GetTable(unsigned int index) const +{ + if (index >= tables.size()) { + return NULL; + } + if (tables[index].refcount == 0) { + return NULL; + } + return tables[index].tm; +} + +/** Frees a Loaded Table, returns false on error, true on success */ +bool GameData::DelTable(unsigned int index) +{ + if (index==0xffffffff) { + tables.clear(); + return true; + } + if (index >= tables.size()) { + return false; + } + if (tables[index].refcount == 0) { + return false; + } + tables[index].refcount--; + if (tables[index].refcount == 0) + if (tables[index].tm) + tables[index].tm.release(); + return true; +} + +Palette *GameData::GetPalette(const ieResRef resname) +{ + Palette *palette = (Palette *) PaletteCache.GetResource(resname); + if (palette) { + return palette; + } + //additional hack for allowing NULL's + if (PaletteCache.RefCount(resname)!=-1) { + return NULL; + } + ResourceHolder im(resname); + if (im == NULL) { + PaletteCache.SetAt(resname, NULL); + return NULL; + } + + palette = new Palette(); + im->GetPalette(256,palette->col); + palette->named=true; + PaletteCache.SetAt(resname, (void *) palette); + return palette; +} + +void GameData::FreePalette(Palette *&pal, const ieResRef name) +{ + int res; + + if (!pal) { + return; + } + if (!name || !name[0]) { + if(pal->named) { + printf("Palette is supposed to be named, but got no name!\n"); + abort(); + } else { + pal->Release(); + pal=NULL; + } + return; + } + if (!pal->named) { + printf("Unnamed palette, it should be %s!\n", name); + abort(); + } + res=PaletteCache.DecRef((void *) pal, name, true); + if (res<0) { + printMessage( "Core", "Corrupted Palette cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Palette name is: %.8s\n", name); + abort(); + } + if (!res) { + pal->Release(); + } + pal = NULL; +} + +Item* GameData::GetItem(const ieResRef resname) +{ + Item *item = (Item *) ItemCache.GetResource(resname); + if (item) { + return item; + } + DataStream* str = GetResource( resname, IE_ITM_CLASS_ID ); + PluginHolder sm(IE_ITM_CLASS_ID); + if (!sm) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + item = new Item(); + //this is required for storing the 'source' + strnlwrcpy(item->Name, resname, 8); + sm->GetItem( item ); + if (item == NULL) { + return NULL; + } + + ItemCache.SetAt(resname, (void *) item); + return item; +} + +//you can supply name for faster access +void GameData::FreeItem(Item const *itm, const ieResRef name, bool free) +{ + int res; + + res=ItemCache.DecRef((void *) itm, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Item cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Item name is: %.8s\n", name); + abort(); + } + if (res) return; + if (free) delete itm; +} + +Spell* GameData::GetSpell(const ieResRef resname, bool silent) +{ + Spell *spell = (Spell *) SpellCache.GetResource(resname); + if (spell) { + return spell; + } + DataStream* str = GetResource( resname, IE_SPL_CLASS_ID, silent ); + PluginHolder sm(IE_SPL_CLASS_ID); + if (!sm) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + spell = new Spell(); + //this is required for storing the 'source' + strnlwrcpy(spell->Name, resname, 8); + sm->GetSpell( spell, silent ); + if (spell == NULL) { + return NULL; + } + + SpellCache.SetAt(resname, (void *) spell); + return spell; +} + +void GameData::FreeSpell(Spell *spl, const ieResRef name, bool free) +{ + int res; + + res=SpellCache.DecRef((void *) spl, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Spell cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Spell name is: %.8s or %.8s\n", name, spl->Name); + abort(); + } + if (res) return; + if (free) delete spl; +} + +Effect* GameData::GetEffect(const ieResRef resname) +{ + Effect *effect = (Effect *) EffectCache.GetResource(resname); + if (effect) { + return effect; + } + DataStream* str = GetResource( resname, IE_EFF_CLASS_ID ); + PluginHolder em(IE_EFF_CLASS_ID); + if (!em) { + delete ( str ); + return NULL; + } + if (!em->Open( str, true )) { + return NULL; + } + + effect = em->GetEffect(new Effect() ); + if (effect == NULL) { + return NULL; + } + + EffectCache.SetAt(resname, (void *) effect); + return effect; +} + +void GameData::FreeEffect(Effect *eff, const ieResRef name, bool free) +{ + int res; + + res=EffectCache.DecRef((void *) eff, name, free); + if (res<0) { + printMessage( "Core", "Corrupted Effect cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Effect name is: %.8s\n", name); + abort(); + } + if (res) return; + if (free) delete eff; +} + +//if the default setup doesn't fit for an animation +//create a vvc for it! +ScriptedAnimation* GameData::GetScriptedAnimation( const char *effect, bool doublehint) +{ + ScriptedAnimation *ret = NULL; + + if (Exists( effect, IE_VVC_CLASS_ID ) ) { + DataStream *ds = GetResource( effect, IE_VVC_CLASS_ID ); + ret = new ScriptedAnimation(ds, true); + } else { + AnimationFactory *af = (AnimationFactory *) + GetFactoryResource( effect, IE_BAM_CLASS_ID, IE_NORMAL ); + if (af) { + ret = new ScriptedAnimation(); + ret->LoadAnimationFactory( af, doublehint?2:0); + } + } + if (ret) { + strnlwrcpy(ret->ResName, effect, 8); + } + return ret; +} + +// Return single BAM frame as a sprite. Use if you want one frame only, +// otherwise it's not efficient +Sprite2D* GameData::GetBAMSprite(const ieResRef ResRef, int cycle, int frame) +{ + Sprite2D *tspr; + AnimationFactory* af = ( AnimationFactory* ) + GetFactoryResource( ResRef, IE_BAM_CLASS_ID, IE_NORMAL ); + if (!af) return 0; + if (cycle == -1) + tspr = af->GetFrameWithoutCycle( (unsigned short) frame ); + else + tspr = af->GetFrame( (unsigned short) frame, (unsigned char) cycle ); + return tspr; +} + +void* GameData::GetFactoryResource(const char* resname, SClass_ID type, + unsigned char mode, bool silent) +{ + int fobjindex = factory->IsLoaded(resname,type); + // already cached + if ( fobjindex != -1) + return factory->GetFactoryObject( fobjindex ); + + // empty resref + if (!strcmp(resname, "")) + return NULL; + + switch (type) { + case IE_BAM_CLASS_ID: + { + DataStream* ret = GetResource( resname, type, silent ); + if (ret) { + PluginHolder ani(IE_BAM_CLASS_ID); + if (!ani) + return NULL; + ani->Open( ret, true ); + AnimationFactory* af = ani->GetAnimationFactory( resname, mode ); + factory->AddFactoryObject( af ); + return af; + } + return NULL; + } + case IE_BMP_CLASS_ID: + { + ResourceHolder img(resname); + if (img) { + ImageFactory* fact = img->GetImageFactory( resname ); + factory->AddFactoryObject( fact ); + return fact; + } + + return NULL; + } + default: + printf( "\n" ); + printMessage( "KEYImporter", " ", WHITE ); + printf( "%s files are not supported.\n", core->TypeExt( type ) ); + return NULL; + } +} diff --git a/project/jni/application/gemrb/src/core/GameData.h b/project/jni/application/gemrb/src/core/GameData.h new file mode 100644 index 000000000..e61a65eb0 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameData.h @@ -0,0 +1,121 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 GAMEDATA_H +#define GAMEDATA_H + +#include "SClassID.h" +#include "exports.h" +#include "ie_types.h" + +#include "Cache.h" +#include "Holder.h" +#include "ResourceManager.h" + +class Actor; +struct Effect; +class Factory; +class Item; +class Palette; +class ScriptedAnimation; +class Spell; +class Sprite2D; +class TableMgr; + +struct Table { + Holder tm; + char ResRef[8]; + unsigned int refcount; +}; + +class GEM_EXPORT GameData : public ResourceManager +{ +public: + GameData(); + ~GameData(); + + void ClearCaches(); + + /** Returns actor */ + Actor *GetCreature(const char *ResRef, unsigned int PartySlot=0); + /** Returns a PC index, by loading a creature */ + int LoadCreature(const char *ResRef, unsigned int PartySlot, bool character=false, int VersionOverride=-1); + + + // 2DA table functions. + // (See also the AutoTable class) + + /** Loads a 2DA Table, returns -1 on error or the Table Index on success */ + int LoadTable(const char * ResRef); + /** Gets the index of a loaded table, returns -1 on error */ + int GetTableIndex(const char * ResRef) const; + /** Gets a Loaded Table by its index, returns NULL on error */ + Holder GetTable(unsigned int index) const; + /** Frees a Loaded Table, returns false on error, true on success */ + bool DelTable(unsigned int index); + + Palette* GetPalette(const ieResRef resname); + void FreePalette(Palette *&pal, const ieResRef name=NULL); + + Item* GetItem(const ieResRef resname); + void FreeItem(Item const *itm, const ieResRef name, bool free=false); + Spell* GetSpell(const ieResRef resname, bool silent=false); + void FreeSpell(Spell *spl, const ieResRef name, bool free=false); + Effect* GetEffect(const ieResRef resname); + void FreeEffect(Effect *eff, const ieResRef name, bool free=false); + + /** creates a vvc/bam animation object at point */ + ScriptedAnimation* GetScriptedAnimation( const char *ResRef, bool doublehint); + + /** returns a single sprite (not cached) from a BAM resource */ + Sprite2D* GetBAMSprite(const ieResRef ResRef, int cycle, int frame); + + /** returns factory resource, currently works only with animations */ + void* GetFactoryResource(const char* resname, SClass_ID type, + unsigned char mode = IE_NORMAL, bool silent=false); +private: + Cache ItemCache; + Cache SpellCache; + Cache EffectCache; + Cache PaletteCache; + Factory* factory; + std::vector tables; +}; + +extern GEM_EXPORT GameData * gamedata; + +template +class ResourceHolder : public Holder +{ +public: + ResourceHolder() + { + } + ResourceHolder(const char* resname) + : Holder(static_cast(gamedata->GetResource(resname,&T::ID))) + { + } + ResourceHolder(const char* resname, const ResourceManager& manager, bool silent = false) + : Holder(static_cast(manager.GetResource(resname,&T::ID,silent))) + { + } +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Actions.cpp b/project/jni/application/gemrb/src/core/GameScript/Actions.cpp new file mode 100644 index 000000000..2a9284883 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Actions.cpp @@ -0,0 +1,7109 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2007 The GemRB Project + * + * 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 "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "AmbientMgr.h" +#include "Audio.h" +#include "DataFileMgr.h" +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Item.h" +#include "Map.h" +#include "MusicMgr.h" +#include "SaveGameIterator.h" +#include "ScriptEngine.h" +#include "TileMap.h" +#include "Video.h" +#include "WorldMap.h" +#include "GUI/GameControl.h" + +//------------------------------------------------------------ +// Action Functions +//------------------------------------------------------------- + +void GameScript::SetExtendedNight(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + //sets the 'can rest other' bit + if (parameters->int0Parameter) { + map->AreaType|=AT_EXTENDED_NIGHT; + } else { + map->AreaType&=~AT_EXTENDED_NIGHT; + } +} + +void GameScript::SetAreaRestFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + //sets the 'can rest other' bit + if (parameters->int0Parameter) { + map->AreaType|=AT_CAN_REST; + } else { + map->AreaType&=~AT_CAN_REST; + } +} + +void GameScript::AddAreaFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaFlags|=parameters->int0Parameter; +} + +void GameScript::RemoveAreaFlag(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaFlags&=~parameters->int0Parameter; +} + +void GameScript::SetAreaFlags(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + ieDword value = map->AreaFlags; + HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter); + map->AreaFlags=value; +} + +void GameScript::AddAreaType(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaType|=parameters->int0Parameter; +} + +void GameScript::RemoveAreaType(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->AreaType&=~parameters->int0Parameter; +} + +void GameScript::NoActionAtAll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + //thats all :) +} + +// this action stops modal actions, so... +void GameScript::NoAction(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_NONE); +} + +void GameScript::SG(Scriptable* Sender, Action* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, "GLOBAL", parameters->int0Parameter ); +} + +void GameScript::SetGlobal(Scriptable* Sender, Action* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, parameters->int0Parameter ); +} + +void GameScript::SetGlobalRandom(Scriptable* Sender, Action* parameters) +{ + int max=parameters->int1Parameter-parameters->int0Parameter+1; + if (max>0) { + SetVariable( Sender, parameters->string0Parameter, RandomNumValue%max+parameters->int0Parameter ); + } else { + SetVariable( Sender, parameters->string0Parameter, 0); + } +} + +void GameScript::StartTimer(Scriptable* Sender, Action* parameters) +{ + Sender->StartTimer(parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::StartRandomTimer(Scriptable* Sender, Action* parameters) +{ + ieDword value = core->Roll(1, parameters->int2Parameter-parameters->int1Parameter, parameters->int2Parameter-1); + Sender->StartTimer(parameters->int0Parameter, value); +} + +void GameScript::SetGlobalTimer(Scriptable* Sender, Action* parameters) +{ + ieDword mytime; + + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter*AI_UPDATE_TIME + mytime); +} + +void GameScript::SetGlobalTimerRandom(Scriptable* Sender, Action* parameters) +{ + ieDword mytime; + int random; + + //This works both ways in the original engine + if (parameters->int1Parameter>parameters->int0Parameter) { + random = parameters->int1Parameter-parameters->int0Parameter+1; + //random cannot be 0, its minimal value is 1 + random = RandomNumValue % random + parameters->int0Parameter; + } else { + random = parameters->int0Parameter-parameters->int1Parameter+1; + random = RandomNumValue % random + parameters->int1Parameter; + } + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, random*AI_UPDATE_TIME + mytime); +} + +void GameScript::SetGlobalTimerOnce(Scriptable* Sender, Action* parameters) +{ + ieDword mytime = CheckVariable( Sender, parameters->string0Parameter ); + if (mytime != 0) { + return; + } + mytime=core->GetGame()->GameTime; //gametime (should increase it) + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter*AI_UPDATE_TIME + mytime); +} + +void GameScript::RealSetGlobalTimer(Scriptable* Sender, Action* parameters) +{ + ieDword mytime=core->GetGame()->RealTime; + + SetVariable( Sender, parameters->string0Parameter, + parameters->int0Parameter + mytime); +} + +void GameScript::ChangeAllegiance(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_EA, parameters->int0Parameter ); +} + +void GameScript::ChangeGeneral(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_GENERAL, parameters->int0Parameter ); +} + +void GameScript::ChangeRace(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_RACE, parameters->int0Parameter ); +} + +void GameScript::ChangeClass(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_CLASS, parameters->int0Parameter ); +} + +void GameScript::SetNamelessClass(Scriptable* /*Sender*/, Action* parameters) +{ + //same as Protagonist + Actor* actor = core->GetGame()->GetPC(0, false); + actor->SetBase( IE_CLASS, parameters->int0Parameter ); +} + +void GameScript::SetNamelessDisguise(Scriptable* Sender, Action* parameters) +{ + SetVariable(Sender, "APPEARANCE", "GLOBAL", parameters->int0Parameter); + core->SetEventFlag(EF_UPDATEANIM); +} + +void GameScript::ChangeSpecifics(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_SPECIFIC, parameters->int0Parameter ); +} + +void GameScript::PermanentStatChange(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + ieDword value; + switch (parameters->int1Parameter) { + case 1: + value = actor->GetBase(parameters->int0Parameter); + value-= parameters->int2Parameter; + break; + case 2: + value = actor->GetBase(parameters->int0Parameter); + value+= parameters->int2Parameter; + break; + case 3: + default: //no idea what happens + value = parameters->int2Parameter; + break; + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeStat(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + ieDword value = parameters->int1Parameter; + if (parameters->int2Parameter==1) { + value+=actor->GetBase(parameters->int0Parameter); + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeStatGlobal(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + ieDword value = (ieDword) CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + Actor* actor = ( Actor* ) scr; + if (parameters->int1Parameter==1) { + value+=actor->GetBase(parameters->int0Parameter); + } + actor->SetBase( parameters->int0Parameter, value); +} + +void GameScript::ChangeGender(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_SEX, parameters->int0Parameter ); +} + +void GameScript::ChangeAlignment(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_ALIGNMENT, parameters->int0Parameter ); +} + +void GameScript::SetFaction(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_FACTION, parameters->int0Parameter ); +} + +void GameScript::SetHP(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_HITPOINTS, parameters->int0Parameter ); +} + +void GameScript::SetHPPercent(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->NewBase( IE_HITPOINTS, parameters->int0Parameter, MOD_PERCENT); +} + +void GameScript::AddHP(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->NewBase(IE_HITPOINTS, parameters->int0Parameter, MOD_ADDITIVE); +} + +//this works on an object (pst) +//but can also work on actor itself (gemrb) +void GameScript::SetTeam(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + actor->SetBase( IE_TEAM, parameters->int0Parameter ); +} + +//this works on an object (gemrb) +//or on Myself if object isn't given (iwd2) +void GameScript::SetTeamBit(Scriptable* Sender, Action* parameters) +{ + Scriptable *scr = Sender; + if (parameters->objects[1]) { + scr=GetActorFromObject( Sender, parameters->objects[1] ); + } + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) scr; + if (parameters->int1Parameter) { + actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) | parameters->int0Parameter ); + } else { + actor->SetBase( IE_TEAM, actor->GetStat(IE_TEAM) & ~parameters->int0Parameter ); + } +} + +void GameScript::TriggerActivation(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) { + printf("Script error: No Trigger Named \"%s\"\n", parameters->objects[1]->objectName); + return; + } + InfoPoint *trigger = (InfoPoint *) ip; + if ( parameters->int0Parameter != 0 ) { + trigger->Flags &= ~TRAP_DEACTIVATED; + } else { + trigger->Flags |= TRAP_DEACTIVATED; + } +} + +void GameScript::FadeToColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeToColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FadeFromColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeFromColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FadeToAndFromColor(Scriptable* Sender, Action* parameters) +{ + core->timer->SetFadeToColor( parameters->pointParameter.x ); + core->timer->SetFadeFromColor( parameters->pointParameter.x ); +// Sender->SetWait( parameters->pointParameter.x<<1 ); //multiply by 2 + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::JumpToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* ab = ( Actor* ) Sender; + ab->SetPosition( parameters->pointParameter, true ); +} + +void GameScript::JumpToPointInstant(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* ab = ( Actor* ) tar; + ab->SetPosition( parameters->pointParameter, true ); +} + +/** instant jump to location saved in stats */ +/** default subject is the current actor */ +void GameScript::JumpToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + Point p((short) actor->GetStat(IE_SAVEDXPOS), (short) actor->GetStat(IE_SAVEDYPOS) ); + actor->SetPosition(p, true ); + actor->SetOrientation( actor->GetStat(IE_SAVEDFACE), false ); +} + +void GameScript::JumpToObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + + if (!tar) { + return; + } + const Map *map = tar->GetCurrentArea(); + + if (map) { + if (parameters->string0Parameter[0]) { + CreateVisualEffectCore(Sender, Sender->Pos, parameters->string0Parameter, 0); + } + MoveBetweenAreasCore( (Actor *) Sender, map->GetScriptName(), tar->Pos, -1, true); + } +} + +void GameScript::TeleportParty(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } +} + +//5 is the ToB value, but it might be useful to have multiple expansions +void GameScript::MoveToExpansion(Scriptable* Sender, Action* parameters) +{ + Game *game = core->GetGame(); + + if (!parameters->int0Parameter) { + parameters->int0Parameter = 5; + } + game->SetExpansion(parameters->int0Parameter); + Sender->ReleaseCurrentAction(); +} + +//add some animation effects too? +void GameScript::ExitPocketPlane(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + if (act) { + if (game->GetPlaneLocationCount() <= (unsigned int)i) { + // what are we meant to do here? + printf("argh, couldn't restore party member %d!", i + 1); + continue; + } + GAMLocationEntry *gle = game->GetPlaneLocationEntry(i); + MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true); + } + } + + // don't clear locations! +} + +//moves pcs and npcs from an area to another area +void GameScript::MoveGlobalsTo(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + //if the actor isn't in the area, we don't care + if (strnicmp(tar->Area, parameters->string0Parameter,8) ) { + continue; + } + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } + i = game->GetNPCCount(); + while (i--) { + Actor *tar = game->GetNPC(i); + //if the actor isn't in the area, we don't care + if (strnicmp(tar->Area, parameters->string0Parameter,8) ) { + continue; + } + MoveBetweenAreasCore( tar, parameters->string1Parameter, + parameters->pointParameter, -1, true); + } +} + +void GameScript::MoveGlobal(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + + MoveBetweenAreasCore( (Actor *) tar, parameters->string0Parameter, + parameters->pointParameter, -1, true); +} + +//we also allow moving to door, container +void GameScript::MoveGlobalObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] ); + if (!to) { + return; + } + const Map *map = to->GetCurrentArea(); + + if (map) { + MoveBetweenAreasCore( (Actor *) tar, map->GetScriptName(), + to->Pos, -1, true); + } +} + +void GameScript::MoveGlobalObjectOffScreen(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Scriptable* to = GetActorFromObject( Sender, parameters->objects[2] ); + if (!to) { + return; + } + MoveBetweenAreasCore( (Actor *) tar, parameters->string0Parameter, + to->Pos, -1, false); +} + +//don't use offset from Sender +void GameScript::CreateCreature(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP|CC_SCRIPTNAME ); +} + +//another highly redundant action +void GameScript::CreateCreatureDoor(Scriptable* Sender, Action* parameters) +{ + //we hack this to death + strcpy(parameters->string1Parameter, "SPDIMNDR"); + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM ); +} + +//another highly redundant action +void GameScript::CreateCreatureObjectDoor(Scriptable* Sender, Action* parameters) +{ + //we hack this to death + strcpy(parameters->string1Parameter, "SPDIMNDR"); + CreateCreatureCore( Sender, parameters, CC_OFFSET | CC_CHECK_IMPASSABLE|CC_CHECK_OVERLAP | CC_PLAY_ANIM ); +} + +//don't use offset from Sender +void GameScript::CreateCreatureImpassable(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_OVERLAP ); +} + +void GameScript::CreateCreatureImpassableAllowOverlap(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, 0 ); +} + +//use offset from Sender +void GameScript::CreateCreatureAtFeet(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSET | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP); +} + +void GameScript::CreateCreatureOffScreen(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP ); +} + +//creates copy at actor, plays animation +void GameScript::CreateCreatureObjectCopy(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM ); +} + +//creates copy at absolute point +void GameScript::CreateCreatureCopyPoint(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_COPY | CC_PLAY_ANIM ); +} + +//this is the same, object + offset +//using this for simple createcreatureobject, (0 offsets) +//createcreatureobjecteffect may have animation +void GameScript::CreateCreatureObjectOffset(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP | CC_PLAY_ANIM); +} + +void GameScript::CreateCreatureObjectOffScreen(Scriptable* Sender, Action* parameters) +{ + CreateCreatureCore( Sender, parameters, CC_OFFSCREEN | CC_OBJECT | CC_CHECK_IMPASSABLE | CC_CHECK_OVERLAP ); +} + +//I think this simply removes the cursor and hides the gui without disabling scripts +//See Interface::SetCutSceneMode +void GameScript::SetCursorState(Scriptable* /*Sender*/, Action* parameters) +{ + int active = parameters->int0Parameter; + + Game *game = core->GetGame(); + if (active) { + game->ControlStatus |= CS_HIDEGUI; + } else { + game->ControlStatus &= ~CS_HIDEGUI; + } + core->SetEventFlag(EF_CONTROL); + core->GetVideoDriver()->SetMouseEnabled(!active); +} + +void GameScript::StartCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->SetCutSceneMode( true ); +} + +void GameScript::EndCutSceneMode(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->SetCutSceneMode( false ); +} + +void GameScript::StartCutScene(Scriptable* Sender, Action* parameters) +{ + GameScript* gs = new GameScript( parameters->string0Parameter, Sender ); + gs->EvaluateAllBlocks(); + delete( gs ); +} + +void GameScript::CutSceneID(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + // shouldn't get called + printMessage("GameScript","CutSceneID was called!\n",YELLOW); +} + +void GameScript::Enemy(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase( IE_EA, EA_ENEMY ); +} + +void GameScript::Ally(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase( IE_EA, EA_ALLY ); +} + +/** GemRB extension: you can replace baldur.bcs */ +void GameScript::ChangeAIScript(Scriptable* Sender, Action* parameters) +{ + if (parameters->int0Parameter>7) { + return; + } + if (Sender->Type!=ST_ACTOR && parameters->int0Parameter) { + return; + } + Sender->SetScript( parameters->string0Parameter, parameters->int0Parameter, false ); +} + +void GameScript::ForceAIScript(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + //changeaiscript clears the queue, i believe + // actor->ClearActions(); + actor->SetScript( parameters->string0Parameter, parameters->int0Parameter, false ); +} + +void GameScript::SetPlayerSound(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->StrRefs[parameters->int0Parameter]=parameters->int1Parameter; +} + +//this one works only on real actors, they got constants +void GameScript::VerbalConstantHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + DisplayStringCore( tar, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_CONST); +} + +void GameScript::VerbalConstant(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + DisplayStringCore( tar, parameters->int0Parameter, DS_CONSOLE|DS_CONST); +} + +//bg2 - variable +void GameScript::SaveLocation(Scriptable* Sender, Action* parameters) +{ + ieDword value = parameters->pointParameter.asDword(); + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + SetVariable(Sender, parameters->string0Parameter, value); +} + +//PST:has parameters, IWD2: no params +void GameScript::SetSavedLocation(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + //iwd2 + if (parameters->pointParameter.isnull()) { + actor->SetBase(IE_SAVEDXPOS, actor->Pos.x); + actor->SetBase(IE_SAVEDYPOS, actor->Pos.y); + actor->SetBase(IE_SAVEDFACE, actor->GetOrientation()); + return; + } + //pst + actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x); + actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y); + actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter); +} +//IWD2, sets the homepoint int0,int1,int2 +void GameScript::SetSavedLocationPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase(IE_SAVEDXPOS, parameters->int0Parameter); + actor->SetBase(IE_SAVEDYPOS, parameters->int1Parameter); + actor->SetBase(IE_SAVEDFACE, parameters->int2Parameter); +} +//IWD2, sets the homepoint P +void GameScript::SetStartPos(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase(IE_SAVEDXPOS, parameters->pointParameter.x); + actor->SetBase(IE_SAVEDYPOS, parameters->pointParameter.y); + actor->SetBase(IE_SAVEDFACE, parameters->int0Parameter); +} + +void GameScript::SaveObjectLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + ieDword value = tar->Pos.asDword(); + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + SetVariable(Sender, parameters->string0Parameter, value); +} + +/** you may omit the string0Parameter, in this case this will be a */ +/** CreateCreatureAtSavedLocation */ +void GameScript::CreateCreatureAtLocation(Scriptable* Sender, Action* parameters) +{ + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + ieDword value = CheckVariable(Sender, parameters->string0Parameter); + parameters->pointParameter.y = (ieWord) (value & 0xffff); + parameters->pointParameter.x = (ieWord) (value >> 16); + CreateCreatureCore(Sender, parameters, CC_CHECK_IMPASSABLE|CC_STRING1); +} + +void GameScript::WaitRandom(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + int width = parameters->int1Parameter-parameters->int0Parameter; + if (width<2) { + width = parameters->int0Parameter; + } else { + width = rand() % width + parameters->int0Parameter; + } + Sender->CurrentActionState = width * AI_UPDATE_TIME; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::Wait(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter * AI_UPDATE_TIME; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::SmallWait(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::SmallWaitRandom(Scriptable* Sender, Action* parameters) +{ + if (!Sender->CurrentActionState) { + int random = parameters->int1Parameter - parameters->int0Parameter; + if (random<1) { + random = 1; + } + Sender->CurrentActionState = rand() % random + parameters->int0Parameter; + } else { + Sender->CurrentActionState--; + } + + if (!Sender->CurrentActionState) { + Sender->ReleaseCurrentAction(); + } + + assert(Sender->CurrentActionState >= 0); +} + +void GameScript::MoveViewPoint(Scriptable* Sender, Action* parameters) +{ + core->timer->SetMoveViewPort( parameters->pointParameter.x, parameters->pointParameter.y, parameters->int0Parameter<<1, true ); + Sender->SetWait(1); // todo, blocking? + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::MoveViewObject(Scriptable* Sender, Action* parameters) +{ + Scriptable * scr = GetActorFromObject( Sender, parameters->objects[1]); + if (!scr) { + Sender->ReleaseCurrentAction(); + return; + } + core->timer->SetMoveViewPort( scr->Pos.x, scr->Pos.y, parameters->int0Parameter<<1, true ); + Sender->SetWait(1); // todo, blocking? + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::AddWayPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->AddWayPoint( parameters->pointParameter ); + // this is marked as AF_BLOCKING (and indeed AddWayPoint causes moves), + // but this probably needs more thought + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveToPointNoRecticle(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NORECTICLE, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToPointNoInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NOINT, 0 ); + } + // should we always force IF_NOINT here? + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + actor->Interrupt(); + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunToPointNoRecticle(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_NORECTICLE|IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//movetopoint until timer is down or target reached +void GameScript::TimedMoveToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (parameters->int0Parameter<=0) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, parameters->int1Parameter,0 ); + } + + //hopefully this hack will prevent lockups + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + return; + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveToPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + //WalkTo could release the current action, so we need this + ieDword tmp = (ieDword) parameters->int0Parameter; + //InMove can clear destination, so we need to save it + Point dest = actor->Destination; + + // try the actual move, if we are not already moving there + if (!actor->InMove() || actor->Destination != parameters->pointParameter) { + actor->WalkTo( parameters->pointParameter, 0, tmp ); + dest = actor->Destination; + } + + // give up if we can't move there (no path was found) + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } + + if (tmp) { + if (!actor->InMove()) { + //can't reach target, movement failed + //we have to use tmp-1 because the distance required might be 0, + //so in GoNearAndRetry we add 1 to distance + if (Distance(dest,actor)>tmp-1) { + //to prevent deadlocks, we free the action + //which caused MoveToPoint in the first place + Sender->PopNextAction(); + } + } + } +} + +//bg2, jumps to saved location in variable +void GameScript::MoveToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p; + Actor* actor = ( Actor* ) tar; + ieDword value = (ieDword) CheckVariable( Sender, parameters->string0Parameter ); + p.fromDword(value); + actor->SetPosition(p, true ); + Sender->ReleaseCurrentAction(); +} +/** iwd2 returntosavedlocation (with stats) */ +/** pst returntosavedplace */ +/** use Sender as default subject */ +void GameScript::ReturnToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//PST +void GameScript::RunToSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, IF_RUNNING, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +//iwd2 +void GameScript::ReturnToSavedLocationDelete(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor* actor = ( Actor* ) tar; + Point p((short) actor->GetBase(IE_SAVEDXPOS),(short) actor->GetBase(IE_SAVEDYPOS) ); + actor->SetBase(IE_SAVEDXPOS,0); + actor->SetBase(IE_SAVEDYPOS,0); + if (p.isnull()) { + Sender->ReleaseCurrentAction(); + return; + } + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + //what else? + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToObjectNoInterrupt(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, IF_NOINT, false); +} + +void GameScript::RunToObject(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, IF_RUNNING, false); +} + +void GameScript::MoveToObject(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, 0, false); +} + +void GameScript::MoveToObjectUntilSee(Scriptable* Sender, Action* parameters) +{ + MoveToObjectCore(Sender, parameters, 0, true); +} + +void GameScript::MoveToObjectFollow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + //follow leader from a distance of 5 + //could also follow the leader with a point offset + if (target->Type==ST_ACTOR) { + actor->SetLeader( (Actor *) target, 5); + } + MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE); +} + +void GameScript::StorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + GAMLocationEntry *gle = game->GetSavedLocationEntry(i); + if (act && gle) { + gle->Pos = act->Pos; + memcpy(gle->AreaResRef, act->Area, 9); + } + } +} + +void GameScript::RestorePartyLocation(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor* act = game->GetPC( i, false ); + if (act) { + if (game->GetSavedLocationCount() <= (unsigned int)i) { + // what are we meant to do here? + printf("argh, couldn't restore party member %d!", i + 1); + continue; + } + GAMLocationEntry *gle = game->GetSavedLocationEntry(i); + MoveBetweenAreasCore(act, gle->AreaResRef, gle->Pos, -1, true); + } + } + + // presumably this is correct + game->ClearSavedLocations(); +} + +void GameScript::MoveToCenterOfScreen(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Region vp = core->GetVideoDriver()->GetViewport(); + Actor* actor = ( Actor* ) Sender; + Point p((short) (vp.x+vp.w/2), (short) (vp.y+vp.h/2) ); + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, IF_NOINT, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::MoveToOffset(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Point p(Sender->Pos.x+parameters->pointParameter.x, Sender->Pos.y+parameters->pointParameter.y); + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo( p, 0, 0 ); + } + if (!actor->InMove()) { + // we should probably instead keep retrying until we reach dest + Sender->ReleaseCurrentAction(); + } +} + +void GameScript::RunAwayFrom(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + //TODO: actor could use travel areas + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromNoLeaveArea(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromNoInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //i believe being dead still interrupts this action + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + //actor->InternalFlags|=IF_NOINT; + actor->NoInterrupt(); + // we should be using int0Parameter for the timing here, not distance + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( tar->Pos, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } else { + actor->Interrupt(); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::RunAwayFromPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + // we should be using int0Parameter for the timing here, not distance? + if (!actor->InMove()) { + // we should make sure our existing walk is a 'run away', or fix moving/path code + actor->RunAwayFrom( parameters->pointParameter, parameters->int0Parameter, false); + } + + //repeat movement... + if (parameters->int0Parameter>0) { + Action *newaction = ParamCopyNoOverride(parameters); + newaction->int0Parameter--; + actor->AddActionInFront(newaction); + Sender->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::DisplayStringNoName(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + if (Sender->Type==ST_ACTOR) { + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_NONAME); + } else { + DisplayStringCore( target, parameters->int0Parameter, DS_AREA|DS_NONAME); + } +} + +void GameScript::DisplayStringNoNameHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + } + + DisplayStringCore( target, parameters->int0Parameter, DS_HEAD|DS_CONSOLE|DS_NONAME); +} + +//display message over current script owner +void GameScript::DisplayMessage(Scriptable* Sender, Action* parameters) +{ + DisplayStringCore(Sender, parameters->int0Parameter, DS_CONSOLE ); +} + +//float message over target +void GameScript::DisplayStringHead(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD|DS_SPEECH ); +} + +void GameScript::KillFloatMessage(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + } + target->DisplayHeadText(NULL); +} + +void GameScript::DisplayStringHeadOwner(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->inventory.HasItem(parameters->string0Parameter,parameters->int0Parameter) ) { + DisplayStringCore(actor, parameters->int0Parameter, DS_CONSOLE|DS_HEAD ); + } + } +} + +void GameScript::FloatMessageFixed(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + DisplayStringCore(target, parameters->int0Parameter, DS_CONSOLE|DS_HEAD); +} + +void GameScript::FloatMessageFixedRnd(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + SrcVector *rndstr=LoadSrc(parameters->string0Parameter); + if (!rndstr) { + printMessage("GameScript","Cannot display resource!",LIGHT_RED); + return; + } + DisplayStringCore(target, rndstr->at(rand()%rndstr->size()), DS_CONSOLE|DS_HEAD); + FreeSrc(rndstr, parameters->string0Parameter); +} + +void GameScript::FloatMessageRnd(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + target=Sender; + printf("DisplayStringHead/FloatMessage got no target, assuming Sender!\n"); + } + + SrcVector *rndstr=LoadSrc(parameters->string0Parameter); + if (!rndstr) { + printMessage("GameScript","Cannot display resource!",LIGHT_RED); + return; + } + DisplayStringCore(target, rndstr->at(rand()%rndstr->size()), DS_CONSOLE|DS_HEAD); + FreeSrc(rndstr, parameters->string0Parameter); +} + +//apparently this should not display over head (for actors) +void GameScript::DisplayString(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + if (Sender->Type==ST_ACTOR) { + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE); + } else { + DisplayStringCore( target, parameters->int0Parameter, DS_AREA); + } +} + +//DisplayStringHead, but wait until done +void GameScript::DisplayStringWait(Scriptable* Sender, Action* parameters) +{ + if (Sender->CurrentActionState) { + // TODO: should probably store the actual time and wait for that, + // rather than this hack + if (!core->GetAudioDrv()->IsSpeaking()) { + Sender->ReleaseCurrentAction(); + } + return; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1]); + if (!target) { + target=Sender; + } + DisplayStringCore( target, parameters->int0Parameter, DS_CONSOLE|DS_WAIT|DS_SPEECH|DS_HEAD); + Sender->CurrentActionState = 1; +} + +void GameScript::ForceFacing(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) tar; + actor->SetOrientation(parameters->int0Parameter, false); +} + +/* A -1 means random facing? */ +void GameScript::Face(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter==-1) { + actor->SetOrientation(core->Roll(1,MAX_ORIENT,-1), false); + } else { + actor->SetOrientation(parameters->int0Parameter, false); + } + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FaceObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetOrientation( GetOrient( target->Pos, actor->Pos ), false); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::FaceSavedLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objects[1] ); + if (!target || target->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) target; + ieDword value; + if (!parameters->string0Parameter[0]) { + strcpy(parameters->string0Parameter,"LOCALSsavedlocation"); + } + value = (ieDword) CheckVariable( target, parameters->string0Parameter ); + Point p; + p.fromDword(value); + + actor->SetOrientation ( GetOrient( p, actor->Pos ), false); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +//pst and bg2 can play a song designated by index +//actually pst has some extra params not currently implemented +//switchplaylist implements fade by simply scheduling the next +//music after the currently running one +//FIXME: This code is similar to PlayAreaSong, consider refactoring +void GameScript::StartSong(Scriptable* /*Sender*/, Action* parameters) +{ + //the force play logic should be handled by SwitchPlayList + bool force; + char* poi = core->GetMusicPlaylist( parameters->int0Parameter ); + if (!poi) return; + + //if parameter is force, force the music, otherwise just schedule it for next + if (parameters->int1Parameter==1) { + force = true; + } else { + force = false; + } + int ret = core->GetMusicMgr()->SwitchPlayList( poi, force ); + if (ret) { + *poi = '*'; + } + if (parameters->int0Parameter == SONG_BATTLE) { + core->GetGame()->CombatCounter = 150; + } +} + +//starts the current area music (songtype is in int0Parameter) +//PlayAreaSong will set the CombatCounter to 150 if +//it is battlemusic (the Counter will tick back to 0) +void GameScript::StartMusic(Scriptable* Sender, Action* parameters) +{ + //don't break on bad values + if (parameters->int0Parameter>10) return; + Map *map = Sender->GetCurrentArea(); + if (!map) return; + bool force, restart; + + switch (parameters->int1Parameter) { + case 1: //force switch + force = true; + restart = true; + break; + case 3: //force switch, but wait for previous music to end gracefully + force = false; + restart = true; + default: + force = false; + restart = false; + break; + } + map->PlayAreaSong(parameters->int0Parameter, restart, force); +} + +void GameScript::StartCombatCounter(Scriptable* Sender, Action* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->PlayAreaSong(3, 1, 1); +} + +/*iwd2 can set an areasong slot*/ +void GameScript::SetMusic(Scriptable* Sender, Action* parameters) +{ + //iwd2 allows setting all 10 slots, though, there is no evidence they are used + if (parameters->int0Parameter>10) return; + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->SongHeader.SongList[parameters->int0Parameter]=parameters->int1Parameter; +} + +//optional integer parameter (isSpeech) +void GameScript::PlaySound(Scriptable* Sender, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, Sender->Pos.x, + Sender->Pos.y, parameters->int0Parameter ? GEM_SND_SPEECH : 0 ); +} + +void GameScript::PlaySoundPoint(Scriptable* /*Sender*/, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, parameters->pointParameter.x, parameters->pointParameter.y ); +} + +void GameScript::PlaySoundNotRanged(Scriptable* /*Sender*/, Action* parameters) +{ + printf( "PlaySound(%s)\n", parameters->string0Parameter ); + core->GetAudioDrv()->Play( parameters->string0Parameter, 0, 0); +} + +void GameScript::Continue(Scriptable* /*Sender*/, Action* /*parameters*/) +{ +} + +// creates area vvc at position of object +void GameScript::CreateVisualEffectObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter); +} + +// creates sticky vvc on actor or normal animation on object +void GameScript::CreateVisualEffectObjectSticky(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type==ST_ACTOR) { + CreateVisualEffectCore((Actor *) tar, parameters->string0Parameter, parameters->int0Parameter); + } else { + CreateVisualEffectCore(tar, tar->Pos, parameters->string0Parameter, parameters->int0Parameter); + } +} + +// creates area effect at point +void GameScript::CreateVisualEffect(Scriptable* Sender, Action* parameters) +{ + CreateVisualEffectCore(Sender, parameters->pointParameter, parameters->string0Parameter, parameters->int0Parameter); +} + +void GameScript::DestroySelf(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Sender->ClearActions(); + Actor* actor = ( Actor* ) Sender; + actor->DestroySelf(); + //actor->InternalFlags |= IF_CLEANUP; +} + +void GameScript::ScreenShake(Scriptable* Sender, Action* parameters) +{ + if (parameters->int1Parameter) { //IWD2 has a different profile + core->timer->SetScreenShake( parameters->int1Parameter, + parameters->int2Parameter, parameters->int0Parameter ); + } else { + core->timer->SetScreenShake( parameters->pointParameter.x, + parameters->pointParameter.y, parameters->int0Parameter ); + } + Sender->SetWait( parameters->int0Parameter ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::UnhideGUI(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game* game = core->GetGame(); + game->SetControlStatus(CS_HIDEGUI, BM_NAND); +} + +void GameScript::HideGUI(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game* game = core->GetGame(); + game->SetControlStatus(CS_HIDEGUI, BM_OR); +} + +void GameScript::LockScroll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + GameControl* gc = core->GetGameControl(); + if (gc) { + gc->SetScreenFlags(SF_LOCKSCROLL, BM_OR); + } +} + +void GameScript::UnlockScroll(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + GameControl* gc = core->GetGameControl(); + if (gc) { + gc->SetScreenFlags(SF_LOCKSCROLL, BM_NAND); + } +} + +//no string, increase talkcount, no interrupt +void GameScript::Dialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_CHECKDIST ); +} + +void GameScript::DialogueForceInterrupt(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_SOURCE | BD_TALKCOUNT | BD_INTERRUPT ); +} + +// not in IESDP but this one should affect ambients +void GameScript::SoundActivate(Scriptable* /*Sender*/, Action* parameters) +{ + AmbientMgr * ambientmgr = core->GetAudioDrv()->GetAmbientMgr(); + if (parameters->int0Parameter) { + ambientmgr->activate(parameters->objects[1]->objectName); + } else { + ambientmgr->deactivate(parameters->objects[1]->objectName); + } +} + +// according to IESDP this action is about animations +void GameScript::AmbientActivate(Scriptable* Sender, Action* parameters) +{ + AreaAnimation* anim = Sender->GetCurrentArea( )->GetAnimation( parameters->string0Parameter); + if (!anim) { + anim = Sender->GetCurrentArea( )->GetAnimation( parameters->objects[1]->objectName ); + } + if (!anim) { + printf( "Script error: No Animation Named \"%s\" or \"%s\"\n", + parameters->string0Parameter,parameters->objects[1]->objectName ); + return; + } + if (parameters->int0Parameter) { + anim->Flags |= A_ANI_ACTIVE; + } else { + anim->Flags &= ~A_ANI_ACTIVE; + } +} + +void GameScript::ChangeTileState(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + int state = parameters->int0Parameter; + if(door) { + door->ToggleTiles(state); /* default is false for playsound */ + } +} + +void GameScript::StaticStart(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->Flags &=~A_ANI_PLAYONCE; +} + +void GameScript::StaticStop(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->Flags |= A_ANI_PLAYONCE; +} + +void GameScript::StaticPalette(Scriptable* Sender, Action* parameters) +{ + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objects[1]->objectName); + if (!anim) { + printf( "Script error: No Animation Named \"%s\"\n", + parameters->objects[1]->objectName ); + return; + } + anim->SetPalette( parameters->string0Parameter ); +} + +//this is a special case of PlaySequence (with wait time, not for area anims) +void GameScript::PlaySequenceTimed(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + } else { + tar=Sender; + } + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->SetStance( parameters->int0Parameter ); + int delay = parameters->int1Parameter || 1; + actor->SetWait( delay ); +} + +//waitanimation: waiting while animation of target is of a certain type +void GameScript::WaitAnimation(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar=Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStance()!=parameters->int0Parameter) { + Sender->ReleaseCurrentAction(); + return; + } +} + +// PlaySequence without object parameter defaults to Sender +void GameScript::PlaySequence(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + //could be an animation + AreaAnimation* anim = Sender->GetCurrentArea( )->GetAnimation( parameters->objects[1]->objectName); + if (anim) { + //set animation's cycle to parameters->int0Parameter; + anim->sequence=parameters->int0Parameter; + anim->frame=0; + //what else to be done??? + anim->InitAnimation(); + } + return; + } + + } else { + tar = Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->SetStance( parameters->int0Parameter ); +} + +void GameScript::SetDialogue(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) Sender; + target->SetDialog( parameters->string0Parameter ); +} + +void GameScript::ChangeDialogue(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetDialog( parameters->string0Parameter ); +} + +//string0, no interrupt, talkcount increased +void GameScript::StartDialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_STRING0 | BD_TALKCOUNT | BD_SETDIALOG ); +} + +//string0, no interrupt, talkcount increased, don't set default +//optionally item name is used +void GameScript::StartDialogueOverride(Scriptable* Sender, Action* parameters) +{ + int flags = BD_STRING0 | BD_TALKCOUNT; + + if (parameters->int2Parameter) { + flags|=BD_ITEM; + } + BeginDialog( Sender, parameters, flags ); +} + +//string0, no interrupt, talkcount increased, don't set default +//optionally item name is used +void GameScript::StartDialogueOverrideInterrupt(Scriptable* Sender, + Action* parameters) +{ + int flags = BD_STRING0 | BD_TALKCOUNT | BD_INTERRUPT; + + if (parameters->int2Parameter) { + flags|=BD_ITEM; + } + BeginDialog( Sender, parameters, flags ); +} + +//start talking to oneself +void GameScript::PlayerDialogue(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_RESERVED | BD_OWN ); +} + +//we hijack this action for the player initiated dialogue +void GameScript::NIDSpecial1(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_INTERRUPT | BD_TARGET /*| BD_NUMERIC*/ | BD_TALKCOUNT | BD_CHECKDIST ); +} + +void GameScript::NIDSpecial2(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game=core->GetGame(); + if (!game->EveryoneStopped() ) { + //wait for a while + Sender->SetWait( 1 * AI_UPDATE_TIME ); + return; + } + Actor *actor = (Actor *) Sender; + if (!game->EveryoneNearPoint(actor->GetCurrentArea(), actor->Pos, true) ) { + //we abort the command, everyone should be here + Sender->ReleaseCurrentAction(); + return; + } + //travel direction passed to guiscript + int direction = Sender->GetCurrentArea()->WhichEdge(actor->Pos); + printf("Travel direction returned: %d\n", direction); + if (direction==-1) { + Sender->ReleaseCurrentAction(); + return; + } + core->GetDictionary()->SetAt("Travel", (ieDword) direction); + core->GetGUIScriptEngine()->RunFunction( "GUIMA", "OpenWorldMapWindow" ); + //sorry, i have absolutely no idea when i should do this :) + Sender->ReleaseCurrentAction(); +} + +void GameScript::StartDialogueInterrupt(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, + BD_STRING0 | BD_INTERRUPT | BD_TALKCOUNT | BD_SETDIALOG ); +} + +//No string, flags:0 +void GameScript::StartDialogueNoSet(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE ); +} + +void GameScript::StartDialogueNoSetInterrupt(Scriptable* Sender, + Action* parameters) +{ + BeginDialog( Sender, parameters, BD_TALKCOUNT | BD_SOURCE | BD_INTERRUPT ); +} + +//no talkcount, using banter dialogs +//probably banter dialogs are random, like rumours! +//no, they aren't, but they increase interactcount +void GameScript::Interact(Scriptable* Sender, Action* parameters) +{ + BeginDialog( Sender, parameters, BD_INTERACT | BD_NOEMPTY ); +} + +static unsigned int FindNearPoint(Scriptable* Sender, Point *&p1, Point *&p2) +{ + unsigned int distance1 = Distance(*p1, Sender); + unsigned int distance2 = Distance(*p2, Sender); + if (distance1 <= distance2) { + return distance1; + } else { + Point *tmp = p1; + p1 = p2; + p2 = tmp; + return distance2; + } +} + +//this is an immediate action without checking Sender +void GameScript::DetectSecretDoor(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + if (door->Flags & DOOR_SECRET) { + door->Flags |= DOOR_FOUND; + } +} + +//this is an immediate action without checking Sender +void GameScript::Lock(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + switch (tar->Type) { + case ST_DOOR: + ((Door *)tar)->SetDoorLocked(true, true); + break; + case ST_CONTAINER: + ((Container *)tar)->SetContainerLocked(true); + break; + default: + return; + } +} + +void GameScript::Unlock(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + switch (tar->Type) { + case ST_DOOR: + ((Door *)tar)->SetDoorLocked(false, true); + break; + case ST_CONTAINER: + ((Container *)tar)->SetContainerLocked(false); + break; + default: + return; + } +} + +void GameScript::SetDoorLocked(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + door->SetDoorLocked( parameters->int0Parameter!=0, false); +} + +void GameScript::SetDoorFlag(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + ieDword flag = parameters->int0Parameter; + + //these are special flags + if (flag&DOOR_LOCKED) { + flag&=~DOOR_LOCKED; + door->SetDoorLocked(parameters->int1Parameter!=0, false); + } + if (flag&DOOR_OPEN) { + flag&=~DOOR_OPEN; + door->SetDoorOpen(parameters->int1Parameter!=0, false, 0); + } + + if (parameters->int1Parameter) { + door->Flags|=flag; + } else { + door->Flags&=~flag; + } +} + +void GameScript::RemoveTraps(Scriptable* Sender, Action* parameters) +{ + //only actors may try to pick a lock + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p, *otherp; + Door *door = NULL; + Container *container = NULL; + InfoPoint *trigger = NULL; + ScriptableType type = tar->Type; + ieDword flags; + + switch (type) { + case ST_DOOR: + door = ( Door* ) tar; + if (door->IsOpen()) { + //door is already open + Sender->ReleaseCurrentAction(); + return; + } + p = door->toOpen; + otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + flags = door->Trapped && door->TrapDetected; + break; + case ST_CONTAINER: + container = (Container *) tar; + p = &container->Pos; + otherp = p; + distance = Distance(*p, Sender); + flags = container->Trapped && container->TrapDetected; + break; + case ST_PROXIMITY: + trigger = (InfoPoint *) tar; + // this point is incorrect! will cause actor to enter trap + // need to find a point using trigger->outline + p = &trigger->Pos; + otherp = p; + distance = Distance(tar, Sender); + flags = trigger->Trapped && trigger->TrapDetected && trigger->CanDetectTrap(); + break; + default: + Sender->ReleaseCurrentAction(); + return; + } + Actor * actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (distance <= MAX_OPERATING_DISTANCE) { + if (flags) { + switch(type) { + case ST_DOOR: + door->TryDisarm(actor); + break; + case ST_CONTAINER: + container->TryDisarm(actor); + break; + case ST_PROXIMITY: + trigger->TryDisarm(actor); + break; + default: + //not gonna happen! + assert(false); + } + } else { + //no trap here + //displaymsg->DisplayString(STR_NOT_TRAPPED); + } + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); +} + +void GameScript::PickLock(Scriptable* Sender, Action* parameters) +{ + //only actors may try to pick a lock + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p, *otherp; + Door *door = NULL; + Container *container = NULL; + ScriptableType type = tar->Type; + ieDword flags; + + switch (type) { + case ST_DOOR: + door = ( Door* ) tar; + if (door->IsOpen()) { + //door is already open + Sender->ReleaseCurrentAction(); + return; + } + p = door->toOpen; + otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + flags = door->Flags&DOOR_LOCKED; + break; + case ST_CONTAINER: + container = (Container *) tar; + p = &container->Pos; + otherp = p; + distance = Distance(*p, Sender); + flags = container->Flags&CONT_LOCKED; + break; + default: + Sender->ReleaseCurrentAction(); + return; + } + Actor * actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (distance <= MAX_OPERATING_DISTANCE) { + if (flags) { + if (type==ST_DOOR) { + door->TryPickLock(actor); + } else { + container->TryPickLock(actor); + } + } else { + //notlocked + //displaymsg->DisplayString(STR_NOT_LOCKED); + } + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); +} + +void GameScript::OpenDoor(Scriptable* Sender, Action* parameters) { + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + // no idea if this is right, or whether OpenDoor/CloseDoor should allow opening + // of all doors, or some doors, or whether it should still check for non-actors + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *)Sender; + actor->SetModal(MS_NONE); + if (!door->TryUnlock(actor)) { + return; + } + } + //if not an actor opens, it don't play sound + door->SetDoorOpen( true, (Sender->Type == ST_ACTOR), 0 ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::CloseDoor(Scriptable* Sender, Action* parameters) { + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + // see comments in OpenDoor above + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *)Sender; + if (!door->TryUnlock(actor)) { + return; + } + } + //if not an actor closes, it don't play sound + door->SetDoorOpen( false, (Sender->Type == ST_ACTOR), 0 ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::ToggleDoor(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal(MS_NONE); + + Door* door = actor->GetCurrentArea()->GetDoorByGlobalID(actor->TargetDoor); + if (!door) { + Sender->ReleaseCurrentAction(); + return; + } + unsigned int distance; + Point *p = door->toOpen; + Point *otherp = door->toOpen+1; + distance = FindNearPoint( Sender, p, otherp); + if (distance <= MAX_OPERATING_DISTANCE) { + actor->SetOrientation( GetOrient( *otherp, actor->Pos ), false); + if (!door->TryUnlock(actor)) { + displaymsg->DisplayConstantString(STR_DOORLOCKED,0xd7d7be,door); + //playsound unsuccessful opening of door + if(door->IsOpen()) + core->PlaySound(DS_CLOSE_FAIL); + else + core->PlaySound(DS_OPEN_FAIL); + Sender->ReleaseCurrentAction(); + actor->TargetDoor = 0; + return; //don't open door + } + + // should we be triggering the trap on close? + door->TriggerTrap(0, actor->GetGlobalID()); + door->SetDoorOpen( !door->IsOpen(), true, actor->GetGlobalID() ); + } else { + MoveNearerTo(Sender, *p, MAX_OPERATING_DISTANCE,0); + return; + } + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + actor->TargetDoor = 0; +} + +void GameScript::ContainerEnable(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_CONTAINER) { + return; + } + Container *cnt = (Container *) tar; + if (parameters->int0Parameter) { + cnt->Flags&=~CONT_DISABLED; + } else { + cnt->Flags|=CONT_DISABLED; + } +} + +void GameScript::MoveBetweenAreas(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + if (parameters->string1Parameter[0]) { + CreateVisualEffectCore(Sender, Sender->Pos, parameters->string1Parameter, 0); + } + MoveBetweenAreasCore((Actor *) Sender, parameters->string0Parameter, + parameters->pointParameter, parameters->int0Parameter, true); +} + +//spell is depleted, casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::Spell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + int seeflag; + unsigned int dist = GetSpellDistance(spellres, Sender); + if (dist == 0xffffffff) { + seeflag = 0; + } else { + seeflag = GA_NO_DEAD; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + if(Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + + //move near to target + if (dist != 0xfffffff) { + if (PersonalDistance(tar, Sender) > dist) { + MoveNearerTo(Sender,tar,dist); + return; + } + } + + //face target + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell( spellres, tar, true ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is depleted, casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + if(Sender->Type==ST_ACTOR) { + unsigned int dist = GetSpellDistance(spellres, Sender); + + Actor *act = (Actor *) Sender; + //move near to target + if (PersonalDistance(parameters->pointParameter, Sender) > dist) { + MoveNearerTo(Sender,parameters->pointParameter,dist, 0); + return; + } + + //face target + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint( spellres, parameters->pointParameter, true ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellNoDec(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell( spellres, tar, false ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, interruptible +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::SpellPointNoDec(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint( spellres, parameters->pointParameter, false ); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ForceSpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + //resolve spellname + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (Sender->LastTarget) { + Sender->CastSpellEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //the target was converted to a point + if(!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //parse target + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + if (tar != Sender) { + act->SetOrientation( GetOrient( tar->Pos, act->Pos ), false ); + } + + //stop doing anything else + act->SetModal(MS_NONE); + } + int duration = Sender->CastSpell (spellres, tar, false); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (!Sender->LastTarget && Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//spell is not depleted (doesn't need to be memorised or known) +//casting time is calculated, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ForceSpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //if target was set, fire spell + if (!Sender->LastTargetPos.isempty()) { + Sender->CastSpellPointEnd( spellres ); + Sender->ReleaseCurrentAction(); + return; + } + + //face target + if (Sender->Type==ST_ACTOR) { + Actor *act = (Actor *) Sender; + act->SetOrientation( GetOrient( parameters->pointParameter, act->Pos ), false ); + + //stop doing anything else + act->SetModal(MS_NONE); + } + + int duration = Sender->CastSpellPoint (spellres, parameters->pointParameter, false); + if (duration != -1) Sender->SetWait(duration); + + //if target was set, feed action back + if (Sender->LastTargetPos.isempty()) { + Sender->ReleaseCurrentAction(); + } +} + +//ForceSpell with zero casting time +//zero casting time, no depletion, not interruptable +//FIXME The caster must meet the level requirements as set in the spell file +//FIXME The spell level is taken as parameter2 in some cases +void GameScript::ReallyForceSpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *) Sender; + if (tar != Sender) { + actor->SetOrientation( GetOrient( tar->Pos, actor->Pos ), false ); + } + actor->SetStance (IE_ANI_CONJURE); + } + Sender->CastSpell (spellres, tar, false, true); + if (tar->Type==ST_ACTOR) { + Sender->CastSpellEnd(spellres); + } else { + Sender->CastSpellPointEnd(spellres); + } + Sender->ReleaseCurrentAction(); +} + +//ForceSpellPoint with zero casting time +//zero casting time, no depletion (finish casting at point), not interruptable +//no CFB +//FIXME The caster must meet the level requirements as set in the spell file +void GameScript::ReallyForceSpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //Sender->LastTargetPos=parameters->pointParameter; + if (Sender->Type == ST_ACTOR) { + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation( GetOrient( parameters->pointParameter, actor->Pos ), false ); + actor->SetStance (IE_ANI_CONJURE); + } + Sender->CastSpellPoint (spellres, parameters->pointParameter, false, true); + Sender->CastSpellPointEnd(spellres); + Sender->ReleaseCurrentAction(); +} + +// this differs from ReallyForceSpell that this one allows dead Sender casting +// zero casting time, no depletion +void GameScript::ReallyForceSpellDead(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Sender->LastTargetPos=parameters->pointParameter; + /* + if (Sender->Type == ST_ACTOR) { + Actor *actor = (Actor *) Sender; + //the dead don't wiggle their fingers + //actor->SetStance (IE_ANI_CONJURE); + } + */ + Sender->CastSpell (spellres, tar, false, true); + if (tar->Type==ST_ACTOR) { + Sender->CastSpellEnd(spellres); + } else { + Sender->CastSpellPointEnd(spellres); + } + Sender->ReleaseCurrentAction(); +} + +void GameScript::Deactivate(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + tar->Hide(); +} + +void GameScript::MakeGlobal(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + core->GetGame()->AddNPC( act ); +} + +void GameScript::UnMakeGlobal(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + int slot; + slot = core->GetGame()->InStore( act ); + if (slot >= 0) { + core->GetGame()->DelNPC( slot ); + } +} + +//this apparently doesn't check the gold, thus could be used from non actors +void GameScript::GivePartyGoldGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword gold = (ieDword) CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + ieDword mygold = act->GetStat(IE_GOLD); + if (mygold < gold) { + gold = mygold; + } + //will get saved, not adjusted + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold); + } + core->GetGame()->AddGold(gold); +} + +void GameScript::CreatePartyGold(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AddGold(parameters->int0Parameter); +} + +void GameScript::GivePartyGold(Scriptable* Sender, Action* parameters) +{ + ieDword gold = (ieDword) parameters->int0Parameter; + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + ieDword mygold = act->GetStat(IE_GOLD); + if (mygold < gold) { + gold = mygold; + } + //will get saved, not adjusted + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-gold); + } + core->GetGame()->AddGold(gold); +} + +void GameScript::DestroyPartyGold(Scriptable* /*Sender*/, Action* parameters) +{ + int gold = core->GetGame()->PartyGold; + if (gold>parameters->int0Parameter) { + gold=parameters->int0Parameter; + } + core->GetGame()->AddGold(-gold); +} + +void GameScript::TakePartyGold(Scriptable* Sender, Action* parameters) +{ + ieDword gold = core->GetGame()->PartyGold; + if (gold>(ieDword) parameters->int0Parameter) { + gold=(ieDword) parameters->int0Parameter; + } + core->GetGame()->AddGold((ieDword) -(int) gold); + if (Sender->Type == ST_ACTOR) { + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)+gold); + } +} + +void GameScript::AddXPObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + int xp = parameters->int0Parameter; + if (displaymsg->HasStringReference(STR_GOTQUESTXP)) { + core->GetTokenDictionary()->SetAtCopy("EXPERIENCEAMOUNT", xp); + displaymsg->DisplayConstantStringName(STR_GOTQUESTXP, 0xbcefbc, actor); + } else { + displaymsg->DisplayConstantStringValue(STR_GOTXP, 0xbcefbc, (ieDword)xp); + } + actor->AddExperience(xp); +} + +void GameScript::AddXP2DA(Scriptable* /*Sender*/, Action* parameters) +{ + AutoTable xptable; + + if (core->HasFeature(GF_HAS_EXPTABLE) ) { + xptable.load("exptable"); + } else { + xptable.load("xplist"); + } + + if (parameters->int0Parameter>0) { + displaymsg->DisplayString(parameters->int0Parameter, 0x40f0f000,IE_STR_SOUND); + } + if (!xptable) { + printMessage("GameScript","Can't perform ADDXP2DA",LIGHT_RED); + return; + } + const char * xpvalue = xptable->QueryField( parameters->string0Parameter, "0" ); //level is unused + + if ( xpvalue[0]=='P' && xpvalue[1]=='_') { + //divide party xp + core->GetGame()->ShareXP(atoi(xpvalue+2), SX_DIVIDE ); + } else { + //give xp everyone + core->GetGame()->ShareXP(atoi(xpvalue), 0 ); + } +} + +void GameScript::AddExperienceParty(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE); +} + +//this needs moncrate.2da, but otherwise independent from GF_CHALLENGERATING +void GameScript::AddExperiencePartyCR(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->ShareXP(parameters->int0Parameter, SX_DIVIDE|SX_CR); +} + +void GameScript::AddExperiencePartyGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword xp = CheckVariable( Sender, parameters->string0Parameter, parameters->string1Parameter ); + core->GetGame()->ShareXP(xp, SX_DIVIDE); +} + +void GameScript::SetMoraleAI(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_MORALE, parameters->int0Parameter); +} + +void GameScript::IncMoraleAI(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + act->SetBase(IE_MORALE, parameters->int0Parameter+act->GetBase(IE_MORALE) ); +} + +void GameScript::MoraleSet(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, parameters->int0Parameter); +} + +void GameScript::MoraleInc(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, act->GetBase(IE_MORALEBREAK)+parameters->int0Parameter); +} + +void GameScript::MoraleDec(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) tar; + act->SetBase(IE_MORALEBREAK, act->GetBase(IE_MORALEBREAK)-parameters->int0Parameter); +} + +void GameScript::JoinParty(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + /* calling this, so it is simpler to change */ + /* i'm not sure this is required here at all */ + SetBeenInPartyFlags(Sender, parameters); + Actor* act = ( Actor* ) Sender; + act->SetBase( IE_EA, EA_PC ); + if (core->HasFeature( GF_HAS_DPLAYER )) { + /* we must reset various existing scripts */ + act->SetScript( "DEFAULT", AI_SCRIPT_LEVEL, true ); + act->SetScript( "", SCR_RACE, true ); + act->SetScript( "", SCR_GENERAL, true ); + act->SetScript( "DPLAYER2", SCR_DEFAULT, false ); + } + AutoTable pdtable("pdialog"); + if (pdtable) { + const char* scriptname = act->GetScriptName(); + ieResRef resref; + //set dialog only if we got a row + if (pdtable->GetRowIndex( scriptname ) != -1) { + strnlwrcpy(resref, pdtable->QueryField( scriptname, "JOIN_DIALOG_FILE"),8); + act->SetDialog( resref ); + } + } + core->GetGame()->JoinParty( act, JP_JOIN ); + //core->GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); +} + +void GameScript::LeaveParty(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* act = ( Actor* ) Sender; + core->GetGame()->LeaveParty( act ); + //core->GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); +} + +//HideCreature hides only the visuals of a creature +//(feet circle and avatar) +//the scripts of the creature are still running +//iwd2 stores this flag in the MC field +void GameScript::HideCreature(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->BaseStats[IE_AVATARREMOVAL]=parameters->int0Parameter; +} + +//i have absolutely no idea why this is needed when we have HideCreature +void GameScript::ForceHide(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + tar=Sender; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + actor->BaseStats[IE_AVATARREMOVAL]=1; +} + +void GameScript::Activate(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + // Deactivate hides, so this should unhide.. + //tar->Activate(); + tar->Unhide(); +} + +void GameScript::ForceLeaveAreaLUA(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + //the LoadMos ResRef may be empty + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +void GameScript::LeaveAreaLUA(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + //the LoadMos ResRef may be empty + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +//this is a blocking action, because we have to move to the Entry +void GameScript::LeaveAreaLUAEntry(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game = core->GetGame(); + strncpy(game->LoadMos, parameters->string1Parameter,8); + Point p = GetEntryPoint(parameters->string0Parameter, parameters->string1Parameter); + if (p.isempty()) { + Sender->ReleaseCurrentAction(); + return; + } + parameters->pointParameter=p; + strcpy(parameters->string1Parameter, ""); + LeaveAreaLUA(Sender, parameters); + Sender->ReleaseCurrentAction(); +} + +void GameScript::LeaveAreaLUAPanic(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + strncpy(core->GetGame()->LoadMos, parameters->string1Parameter,8); + MoveBetweenAreasCore( actor, parameters->string0Parameter, parameters->pointParameter, parameters->int0Parameter, true); +} + +//this is a blocking action, because we have to move to the Entry +void GameScript::LeaveAreaLUAPanicEntry(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Game *game = core->GetGame(); + strncpy(game->LoadMos, parameters->string1Parameter,8); + Point p = GetEntryPoint(parameters->string0Parameter, parameters->string1Parameter); + if (p.isempty()) { + Sender->ReleaseCurrentAction(); + return; + } + parameters->pointParameter=p; + strcpy(parameters->string1Parameter, ""); + LeaveAreaLUAPanic(Sender, parameters); + Sender->ReleaseCurrentAction(); +} + +void GameScript::SetToken(Scriptable* /*Sender*/, Action* parameters) +{ + //SetAt takes a newly created reference (no need of free/copy) + char * str = core->GetString( parameters->int0Parameter); + core->GetTokenDictionary()->SetAt( parameters->string1Parameter, str); +} + +//Assigns a numeric variable to the token +void GameScript::SetTokenGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + //using SetAtCopy because we need a copy of the value + core->GetTokenDictionary()->SetAtCopy( parameters->string1Parameter, value ); +} + +//Assigns the target object's name (not scriptname) to the token +void GameScript::SetTokenObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + core->GetTokenDictionary()->SetAtCopy( parameters->string0Parameter, actor->GetName(0) ); +} + +void GameScript::PlayDead(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->CurrentActionInterruptable = false; + if (Sender->CurrentActionState == 0) { + // TODO: what if parameter is 0? see orphan2 + Sender->CurrentActionState = parameters->int0Parameter; + actor->SetStance( IE_ANI_DIE ); + } else { + actor->CurrentActionState--; + if (Sender->CurrentActionState == 0) { + actor->SetStance( IE_ANI_GET_UP ); + Sender->ReleaseCurrentAction(); + } + } +} + +void GameScript::PlayDeadInterruptable(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (Sender->CurrentActionState == 0) { + // TODO: what if parameter is 0? see orphan2 + Sender->CurrentActionState = parameters->int0Parameter; + actor->SetStance( IE_ANI_DIE ); + } else { + actor->CurrentActionState--; + if (Sender->CurrentActionState == 0) { + actor->SetStance( IE_ANI_GET_UP ); + Sender->ReleaseCurrentAction(); + } + } +} + +/* this may not be correct, just a placeholder you can fix */ +void GameScript::Swing(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_ATTACK ); + actor->SetWait( 1 ); +} + +/* this may not be correct, just a placeholder you can fix */ +void GameScript::SwingOnce(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_ATTACK ); + actor->SetWait( 1 ); +} + +void GameScript::Recoil(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetStance( IE_ANI_DAMAGE ); + actor->SetWait( 1 ); +} + +void GameScript::AnkhegEmerge(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetStance()!=IE_ANI_EMERGE) { + actor->SetStance( IE_ANI_EMERGE ); + actor->SetWait( 1 ); + } +} + +void GameScript::AnkhegHide(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetStance()!=IE_ANI_HIDE) { + actor->SetStance( IE_ANI_HIDE ); + actor->SetWait( 1 ); + } +} + +void GameScript::GlobalSetGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + SetVariable( Sender, parameters->string1Parameter, value ); +} + +/* adding the second variable to the first, they must be GLOBAL */ +void GameScript::AddGlobals(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter, "GLOBAL"); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter, "GLOBAL"); + SetVariable( Sender, parameters->string0Parameter, "GLOBAL", value1 + value2 ); +} + +/* adding the second variable to the first, they could be area or locals */ +void GameScript::GlobalAddGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 + value2 ); +} + +/* adding the number to the global, they could be area or locals */ +void GameScript::IncrementGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value + parameters->int0Parameter ); +} + +/* adding the number to the global ONLY if the first global is zero */ +void GameScript::IncrementGlobalOnce(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable( Sender, parameters->string0Parameter ); + if (value != 0) { + return; + } + value = CheckVariable( Sender, parameters->string1Parameter ); + SetVariable( Sender, parameters->string1Parameter, + value + parameters->int0Parameter ); +} + +void GameScript::GlobalSubGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 - value2 ); +} + +void GameScript::GlobalAndGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 && value2 ); +} + +void GameScript::GlobalOrGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 || value2 ); +} + +void GameScript::GlobalBOrGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 | value2 ); +} + +void GameScript::GlobalBAndGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 & value2 ); +} + +void GameScript::GlobalXorGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, + parameters->string1Parameter ); + SetVariable( Sender, parameters->string0Parameter, value1 ^ value2 ); +} + +void GameScript::GlobalBOr(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 | parameters->int0Parameter ); +} + +void GameScript::GlobalBAnd(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 & parameters->int0Parameter ); +} + +void GameScript::GlobalXor(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 ^ parameters->int0Parameter ); +} + +void GameScript::GlobalMax(Scriptable* Sender, Action* parameters) +{ + long value1 = CheckVariable( Sender, parameters->string0Parameter ); + if (value1 > parameters->int0Parameter) { + SetVariable( Sender, parameters->string0Parameter, value1 ); + } +} + +void GameScript::GlobalMin(Scriptable* Sender, Action* parameters) +{ + long value1 = CheckVariable( Sender, parameters->string0Parameter ); + if (value1 < parameters->int0Parameter) { + SetVariable( Sender, parameters->string0Parameter, value1 ); + } +} + +void GameScript::BitClear(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + SetVariable( Sender, parameters->string0Parameter, + value1 & ~parameters->int0Parameter ); +} + +void GameScript::GlobalShL(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = parameters->int0Parameter; + if (value2 > 31) { + value1 = 0; + } else { + value1 <<= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::GlobalShR(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, + parameters->string0Parameter ); + ieDword value2 = parameters->int0Parameter; + if (value2 > 31) { + value1 = 0; + } else { + value1 >>= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::GlobalMaxGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value1 < value2) { + SetVariable( Sender, parameters->string0Parameter, value2 ); + } +} + +void GameScript::GlobalMinGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value1 > value2) { + SetVariable( Sender, parameters->string0Parameter, value2 ); + } +} + +void GameScript::GlobalShLGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value2 > 31) { + value1 = 0; + } else { + value1 <<= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} +void GameScript::GlobalShRGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter ); + if (value2 > 31) { + value1 = 0; + } else { + value1 >>= value2; + } + SetVariable( Sender, parameters->string0Parameter, value1 ); +} + +void GameScript::ClearAllActions(Scriptable* Sender, Action* /*parameters*/) +{ + Actor *except = NULL; + if (Sender->Type==ST_ACTOR) { + except = (Actor *) Sender; + } + Map *map = Sender->GetCurrentArea(); + ieDword gametime = core->GetGame()->GameTime; + int i = map->GetActorCount(true); + while(i--) { + Actor* act = map->GetActor(i,true); + if (act && act!=except) { + if (!act->ValidTarget(GA_NO_DEAD) ) { + continue; + } + //Do we need this??? + if (!act->Schedule(gametime, false) ) { + continue; + } + act->ClearActions(); + act->ClearPath(); + act->SetModal(MS_NONE); + } + } +} + +void GameScript::ClearActions(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = Sender; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + printMessage("GameScript","Couldn't find target for ClearActions!",YELLOW); + parameters->objects[1]->Dump(); + return; + } + } + tar->ClearActions(); + if (tar->Type==ST_ACTOR) { + Actor* act = (Actor *) tar; + act->ClearPath(); + //not sure about this + //act->SetModal(MS_NONE); + } +} + +void GameScript::SetNumTimesTalkedTo(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->TalkCount = parameters->int0Parameter; +} + +void GameScript::StartMovie(Scriptable* Sender, Action* parameters) +{ + core->PlayMovie( parameters->string0Parameter ); + Sender->ReleaseCurrentAction(); // should this be blocking? +} + +void GameScript::SetLeavePartyDialogFile(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + AutoTable pdtable("pdialog"); + Actor* act = ( Actor* ) Sender; + const char* scriptingname = act->GetScriptName(); + act->SetDialog( pdtable->QueryField( scriptingname, "POST_DIALOG_FILE" ) ); +} + +void GameScript::TextScreen(Scriptable* Sender, Action* parameters) +{ + strnlwrcpy(core->GetGame()->LoadMos, parameters->string0Parameter,8); + core->GetGUIScriptEngine()->RunFunction( "TextScreen", "StartTextScreen" ); + core->GetVideoDriver()->SetMouseEnabled(true); + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // should this be blocking? +} + +void GameScript::IncrementChapter(Scriptable* Sender, Action* parameters) +{ + TextScreen(Sender, parameters); // textscreen will release blocking for us + core->GetGame()->IncrementChapter(); +} + +void GameScript::SetCriticalPathObject(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) tar; + if (parameters->int0Parameter) { + actor->SetMCFlag(MC_PLOT_CRITICAL, BM_OR); + } else { + actor->SetMCFlag(MC_PLOT_CRITICAL, BM_NAND); + } +} + +void GameScript::SetBeenInPartyFlags(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + //it is bit 15 of the multi-class flags (confirmed) + actor->SetMCFlag(MC_BEENINPARTY, BM_OR); +} + +/*iwd2 sets the high MC bits this way*/ +void GameScript::SetCreatureAreaFlag(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetMCFlag(parameters->int0Parameter, parameters->int1Parameter); +} + +//this will be a global change, fixme if it should be local +void GameScript::SetTextColor(Scriptable* /*Sender*/, Action* parameters) +{ + Color c; + memcpy(&c,¶meters->int0Parameter,4); + core->SetInfoTextColor(c); +} + +void GameScript::BitGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value = CheckVariable(Sender, parameters->string0Parameter ); + HandleBitMod( value, parameters->int0Parameter, parameters->int1Parameter); + SetVariable(Sender, parameters->string0Parameter, value); +} + +void GameScript::GlobalBitGlobal(Scriptable* Sender, Action* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter ); + HandleBitMod( value1, value2, parameters->int1Parameter); + SetVariable(Sender, parameters->string0Parameter, value1); +} + +void GameScript::SetVisualRange(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->SetBase(IE_VISUALRANGE,parameters->int0Parameter); +} + +void GameScript::MakeUnselectable(Scriptable* Sender, Action* parameters) +{ + Sender->UnselectableTimer=parameters->int0Parameter; + + //update color + if (Sender->Type != ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + // flags may be wrong + core->GetGame()->SelectActor(actor, false, SELECT_QUIET); + } + + actor->SetCircleSize(); +} + +void GameScript::Debug(Scriptable* /*Sender*/, Action* parameters) +{ + InDebug=parameters->int0Parameter; + printMessage("GameScript",parameters->string0Parameter,YELLOW); +} + +void GameScript::IncrementProficiency(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the proficiency stats + target->SetBase(IE_PROFICIENCYBASTARDSWORD+idx, + target->GetBase(IE_PROFICIENCYBASTARDSWORD+idx)+parameters->int1Parameter); +} + +void GameScript::IncrementExtraProficiency(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetBase(IE_FREESLOTS, target->GetBase(IE_FREESLOTS)+parameters->int0Parameter); +} + +//the third parameter is a GemRB extension +void GameScript::AddJournalEntry(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AddJournalEntry(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::SetQuestDone(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + game->DeleteJournalEntry(parameters->int0Parameter); + game->AddJournalEntry(parameters->int0Parameter, IE_GAM_QUEST_DONE, parameters->int2Parameter); + +} + +void GameScript::RemoveJournalEntry(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->DeleteJournalEntry(parameters->int0Parameter); +} + +void GameScript::SetInternal(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the internal stats + target->SetBase(IE_INTERNAL_0+idx, parameters->int1Parameter); +} + +void GameScript::IncInternal(Scriptable* Sender, Action* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + //start of the internal stats + target->SetBase(IE_INTERNAL_0+idx, + target->GetBase(IE_INTERNAL_0+idx)+parameters->int1Parameter); +} + +void GameScript::DestroyAllEquipment(Scriptable* Sender, Action* /*parameters*/) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem("",0,(ieDword) ~0); //destroy any and all + } +} + +void GameScript::DestroyItem(Scriptable* Sender, Action* parameters) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem(parameters->string0Parameter,0,1); //destroy one (even indestructible?) + } +} + +//negative destroygold creates gold +void GameScript::DestroyGold(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) + return; + Actor *act=(Actor *) Sender; + int max=(int) act->GetStat(IE_GOLD); + if (parameters->int0Parameter != 0) { + if (max>parameters->int0Parameter) { + max=parameters->int0Parameter; + } + } + act->SetBase(IE_GOLD, act->GetBase(IE_GOLD)-max); +} + +void GameScript::DestroyPartyItem(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + ieDword count; + if (parameters->int0Parameter) { + count=0; + } else { + count=1; + } + while (i--) { + Inventory *inv = &(game->GetPC( i,false )->inventory); + int res=inv->DestroyItem(parameters->string0Parameter,0,count); + if ( (count == 1) && res) { + break; + } + } +} + +/* this is a gemrb extension */ +void GameScript::DestroyPartyItemNum(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + ieDword count; + count = parameters->int0Parameter; + while (i--) { + Inventory *inv = &(game->GetPC( i,false )->inventory); + count -= inv->DestroyItem(parameters->string0Parameter,0,count); + if (!count ) { + break; + } + } +} + +void GameScript::DestroyAllDestructableEquipment(Scriptable* Sender, Action* /*parameters*/) +{ + Inventory *inv=NULL; + + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) Sender)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) Sender)->inventory); + break; + default:; + } + if (inv) { + inv->DestroyItem("", IE_INV_ITEM_DESTRUCTIBLE, (ieDword) ~0); + } +} + +void GameScript::SetApparentName(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetName(parameters->int0Parameter,1); +} + +void GameScript::SetRegularName(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + target->SetName(parameters->int0Parameter,2); +} + +/** this is a gemrb extension */ +void GameScript::UnloadArea(Scriptable* /*Sender*/, Action* parameters) +{ + int map=core->GetGame()->FindMap(parameters->string0Parameter); + if (map>=0) { + core->GetGame()->DelMap(map, parameters->int0Parameter); + } +} + +static EffectRef fx_death_ref={"Death", NULL, -1}; +void GameScript::Kill(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor* target = ( Actor* ) tar; + Effect *fx = EffectQueue::CreateEffect(fx_death_ref, 0, 0, FX_DURATION_INSTANT_PERMANENT); + target->fxqueue.AddEffect(fx, false); + delete fx; +} + +void GameScript::SetGabber(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + GameControl* gc = core->GetGameControl(); + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + gc->dialoghandler->speakerID = tar->GetGlobalID(); + } else { + printMessage("GameScript","Can't set gabber!",YELLOW); + } +} + +void GameScript::ReputationSet(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->SetReputation(parameters->int0Parameter*10); +} + +void GameScript::ReputationInc(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + game->SetReputation( (int) game->Reputation + parameters->int0Parameter*10); +} + +void GameScript::FullHeal(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + Actor *scr = (Actor *) tar; + //0 means full healing + //Heal() might contain curing of some conditions + //if FullHeal doesn't do that, replace this with a SetBase + //fullhealex might still be the curing action + scr->Heal(0); +} + +void GameScript::RemovePaladinHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->ApplyKit(true); + act->SetMCFlag(MC_FALLEN_PALADIN, BM_OR); + if (act->InParty) displaymsg->DisplayConstantStringName(STR_PALADIN_FALL, 0xbcefbc, act); +} + +void GameScript::RemoveRangerHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->ApplyKit(true); + act->SetMCFlag(MC_FALLEN_RANGER, BM_OR); + if (act->InParty) displaymsg->DisplayConstantStringName(STR_RANGER_FALL, 0xbcefbc, act); +} + +void GameScript::RegainPaladinHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetMCFlag(MC_FALLEN_PALADIN, BM_NAND); + act->ApplyKit(false); +} + +void GameScript::RegainRangerHood(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetMCFlag(MC_FALLEN_RANGER, BM_NAND); + act->ApplyKit(false); +} + +//transfering item from Sender to target, target must be an actor +//if target can't get it, it will be dropped at its feet +//a container or an actor can take an item from someone +void GameScript::GetItem(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + MoveItemCore(tar, Sender, parameters->string0Parameter,0,0); +} + +//getting one single item +void GameScript::TakePartyItem(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0,IE_INV_ITEM_UNSTEALABLE); + if (res!=MIC_NOITEM) return; + } +} + +//getting x single item +void GameScript::TakePartyItemNum(Scriptable* Sender, Action* parameters) +{ + int count = parameters->int0Parameter; + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + int res=MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE); + if (res == MIC_GOTITEM) { + i++; + count--; + } + if (!count) return; + } +} + +void GameScript::TakePartyItemRange(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + Actor *ac = game->GetPC(i,false); + if (Distance(Sender, ac)string0Parameter,0,IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { } + } + } +} + +void GameScript::TakePartyItemAll(Scriptable* Sender, Action* parameters) +{ + Game *game=core->GetGame(); + int i=game->GetPartySize(false); + while (i--) { + while (MoveItemCore(game->GetPC(i,false), Sender, parameters->string0Parameter,0, IE_INV_ITEM_UNSTEALABLE)==MIC_GOTITEM) { } + } +} + +//an actor can 'give' an item to a container or another actor +void GameScript::GiveItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + MoveItemCore(Sender, tar, parameters->string0Parameter,0,0); +} + +//this action creates an item in a container or a creature +//if there is an object it works as GiveItemCreate +//otherwise it creates the item on the Sender +void GameScript::CreateItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar; + if (parameters->objects[1]) { + tar = GetActorFromObject( Sender, parameters->objects[1] ); + } else { + tar = Sender; + } + if (!tar) + return; + Inventory *myinv; + + switch(tar->Type) { + case ST_ACTOR: + myinv = &((Actor *) tar)->inventory; + break; + case ST_CONTAINER: + myinv = &((Container *) tar)->inventory; + break; + default: + return; + } + + CREItem *item = new CREItem(); + CreateItemCore(item, parameters->string0Parameter, parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); + if (tar->Type==ST_CONTAINER) { + myinv->AddItem(item); + } else { + if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) { + Map *map=tar->GetCurrentArea(); + // drop it at my feet + map->AddItemToLocation(tar->Pos, item); + if (((Actor *)tar)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + } else { + if (((Actor *)tar)->InParty) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + } + } +} + +void GameScript::CreateItemNumGlobal(Scriptable *Sender, Action* parameters) +{ + Inventory *myinv; + + switch(Sender->Type) { + case ST_ACTOR: + myinv = &((Actor *) Sender)->inventory; + break; + case ST_CONTAINER: + myinv = &((Container *) Sender)->inventory; + break; + default: + return; + } + int value = CheckVariable( Sender, parameters->string0Parameter ); + CREItem *item = new CREItem(); + CreateItemCore(item, parameters->string1Parameter, value, 0, 0); + if (Sender->Type==ST_CONTAINER) { + myinv->AddItem(item); + } else { + if ( ASI_SUCCESS != myinv->AddSlotItem(item, SLOT_ONLYINVENTORY)) { + Map *map=Sender->GetCurrentArea(); + // drop it at my feet + map->AddItemToLocation(Sender->Pos, item); + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + } else { + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + } + } +} + +void GameScript::TakeItemReplace(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + return; + } + + Actor *scr = (Actor *) tar; + CREItem *item; + int slot = scr->inventory.RemoveItem(parameters->string1Parameter, IE_INV_ITEM_UNDROPPABLE, &item); + if (!item) { + item = new CREItem(); + } + CreateItemCore(item, parameters->string0Parameter, -1, 0, 0); + if (ASI_SUCCESS != scr->inventory.AddSlotItem(item,slot)) { + Map *map = scr->GetCurrentArea(); + map->AddItemToLocation(Sender->Pos, item); + } +} + +//same as equipitem, but with additional slots parameter, and object to perform action +void GameScript::XEquipItem(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + int slot = actor->inventory.FindItem(parameters->string0Parameter, 0); + if (slot<0) { + return; + } + actor->inventory.EquipItem(slot); + actor->ReinitQuickSlots(); +} + +//GemRB extension: if int1Parameter is nonzero, don't destroy existing items +void GameScript::FillSlot(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + + CREItem *tmp = NULL; + Actor *actor = (Actor *) Sender; + int slot = parameters->int0Parameter; + + //free up target slot + tmp = actor->inventory.RemoveItem(slot); + + actor->inventory.TryEquipAll(slot); + + if (tmp) { + if (actor->inventory.HasItemInSlot("",slot) ) { + slot = SLOT_ONLYINVENTORY; + } + + //reequip original item + if(actor->inventory.AddSlotItem(tmp, slot)!=ASI_SUCCESS) { + delete tmp; + } + } +} + +//iwd2 also has a flag for unequip (it might collide with original!) +void GameScript::EquipItem(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + int slot = actor->inventory.FindItem(parameters->string0Parameter, IE_INV_ITEM_UNDROPPABLE); + if (slot<0) { + return; + } + + int slot2; + + if (parameters->int0Parameter) { + //unequip item, and move it to the inventory + slot2 = SLOT_ONLYINVENTORY; + } else { + //equip item if possible + slot2 = SLOT_AUTOEQUIP; + } + CREItem *si = actor->inventory.RemoveItem(slot); + if (si) { + if (actor->inventory.AddSlotItem(si, slot2)==ASI_FAILED) { + Map *map = Sender->GetCurrentArea(); + if (map) { + //drop item at the feet of the character instead of destroying it + map->AddItemToLocation(Sender->Pos, si); + } else { + delete si; + } + } + } + actor->ReinitQuickSlots(); +} + +void GameScript::DropItem(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + if (Distance(parameters->pointParameter, Sender) > 10) { + MoveNearerTo(Sender, parameters->pointParameter, 10,0); + return; + } + Actor *scr = (Actor *) Sender; + Map *map = Sender->GetCurrentArea(); + + if (parameters->string0Parameter[0]) { + //dropping location isn't exactly our place, this is why i didn't use a simple DropItem + scr->inventory.DropItemAtLocation(parameters->string0Parameter, +0, map, parameters->pointParameter); + } else { + //this should be converted from scripting slot to physical slot + scr->inventory.DropItemAtLocation(parameters->int0Parameter, 0, map, parameters->pointParameter); + } + + Sender->ReleaseCurrentAction(); +} + +void GameScript::DropInventory(Scriptable *Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + scr->DropItem("",0); +} + +//this should work on containers! +//using the same code for DropInventoryEXExclude +void GameScript::DropInventoryEX(Scriptable *Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + Inventory *inv = NULL; + switch (Sender->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (inv) { + int x = inv->GetSlotCount(); + Map *area = tar->GetCurrentArea(); + while(x--) { + if (parameters->string0Parameter[0]) { + const char *resref = inv->GetSlotItem(x)->ItemResRef; + if (!strnicmp(parameters->string0Parameter, resref, 8)) { + continue; + } + } + inv->DropItemAtLocation(x, 0, area, tar->Pos); + } + } +} + +void GameScript::GivePartyAllEquipment(Scriptable *Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Game *game = core->GetGame(); + // pick the first actor first + for (int i = 0; i < game->GetPartySize(false); i++) { + Actor *tar = game->GetPC(i,false); + //don't try to give self, it would be an infinite loop + if (tar==(Actor *) Sender) + continue; + while(MoveItemCore(Sender, tar, "",0,0)!=MIC_NOITEM) { } + } +} + +//This is unsure, Plunder could be just handling ground piles and not dead actors +void GameScript::Plunder(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + + //you must be joking + if (tar==Sender) { + Sender->ReleaseCurrentAction(); + return; + } + + if (tar->Type == ST_ACTOR) { + Actor *scr = (Actor *) tar; + //can plunder only dead actors + if (! (scr->BaseStats[IE_STATE_ID]&STATE_DEAD) ) { + Sender->ReleaseCurrentAction(); + return; + } + } + if (PersonalDistance(Sender, tar)>MAX_OPERATING_DISTANCE ) { + MoveNearerTo(Sender, tar->Pos, MAX_OPERATING_DISTANCE,0); + return; + } + //move all movable item from the target to the Sender + //the rest will be dropped at the feet of Sender + while(MoveItemCore(tar, Sender, "",0,0)!=MIC_NOITEM) { } + Sender->ReleaseCurrentAction(); +} + +void GameScript::MoveInventory(Scriptable *Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src || src->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + //don't try to move to self, it would create infinite loop + if (src==tar) + return; + //move all movable item from the target to the Sender + //the rest will be dropped at the feet of Sender + while(MoveItemCore(src, tar, "",0,0)!=MIC_NOITEM) { } +} + +void GameScript::PickPockets(Scriptable *Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *snd = (Actor *) Sender; + Actor *scr = (Actor *) tar; + //for PP one must go REALLY close + Map *map=Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + if (PersonalDistance(Sender, tar)>10 ) { + MoveNearerTo(Sender, tar, 10); + return; + } + + if (scr->GetStat(IE_EA)>EA_EVILCUTOFF) { + displaymsg->DisplayConstantString(STR_PICKPOCKET_EVIL,0xffffff); + Sender->ReleaseCurrentAction(); + return; + } + + int skill = snd->GetStat(IE_PICKPOCKET); + int tgt = scr->GetStat(IE_PICKPOCKET); + //the original engine has no random here + if (tgt != 255) { + skill -= tgt; + //if you want original behaviour: remove this + skill += core->Roll(1,100, snd->GetStat(IE_LUCK) ); + } else { + skill = 0; + } + //and change this 50 to 0. + if (skill<50) { + //noticed attempt + displaymsg->DisplayConstantString(STR_PICKPOCKET_FAIL,0xffffff); + if (core->HasFeature(GF_STEAL_IS_ATTACK) ) { + tar->LastAttacker = snd->GetGlobalID(); + } else { + //pickpocket failed trigger + tar->LastOpenFailed = snd->GetGlobalID(); + } + Sender->ReleaseCurrentAction(); + return; + } + + int ret = MIC_NOITEM; + if ((RandomNumValue&3) || (scr->GetStat(IE_GOLD)<=0) ) { + int slot = scr->inventory.FindStealableItem(); + if (slot) { + CREItem *item = scr->inventory.RemoveItem(slot); + ret = snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY); + if (ret!=ASI_SUCCESS) { + map->AddItemToLocation(snd->Pos, item); + ret = MIC_FULL; + } + } + } + + if (ret==MIC_NOITEM) { + int money=0; + //go for money too + if (scr->GetStat(IE_GOLD)>0) { + money=RandomNumValue%(scr->GetStat(IE_GOLD)+1); + } + if (!money) { + //no stuff to steal + displaymsg->DisplayConstantString(STR_PICKPOCKET_NONE,0xffffff); + Sender->ReleaseCurrentAction(); + return; + } + CREItem *item = new CREItem(); + CreateItemCore(item, core->GoldResRef, money, 0, 0); + if ( ASI_SUCCESS == snd->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY)) { + scr->SetBase(IE_GOLD,scr->GetBase(IE_GOLD)-money); + } else { + // drop it at my feet + map->AddItemToLocation(Sender->Pos, item); + if (((Actor *)Sender)->InParty) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + Sender->ReleaseCurrentAction(); + return; + } + } + + displaymsg->DisplayConstantString(STR_PICKPOCKET_DONE,0xffffff); + DisplayStringCore(snd, VB_PP_SUCC, DS_CONSOLE|DS_CONST ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::TakeItemList(Scriptable * Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + + int rows = tab->GetRowCount(); + for (int i=0;iQueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + } +} + +void GameScript::TakeItemListParty(Scriptable * Sender, Action* parameters) +{ + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + Game *game = core->GetGame(); + int rows = tab->GetRowCount(); + for (int i=0;iGetPartySize(false); + while (j--) { + Actor *tar = game->GetPC(j, false); + MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + } + } +} + +void GameScript::TakeItemListPartyNum(Scriptable * Sender, Action* parameters) +{ + AutoTable tab(parameters->string0Parameter); + if (!tab) { + return; + } + Game *game = core->GetGame(); + int rows = tab->GetRowCount(); + int count = parameters->int0Parameter; + for (int i=0;iGetPartySize(false); + while (j--) { + Actor *tar = game->GetPC(j, false); + int res=MoveItemCore(tar, Sender, tab->QueryField(i,0), 0, IE_INV_ITEM_UNSTEALABLE); + if (res==MIC_GOTITEM) { + j++; + count--; + } + if (!count) break; + } + } + if (count == 1) { + // grant the default table item to the Sender in regular games + Action *params = new Action(true); + sprintf(params->string0Parameter, "%s", tab->QueryField(9999,9999)); + CreateItem(Sender, params); + delete params; + } +} + +//bg2 +void GameScript::SetRestEncounterProbabilityDay(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.DayChance = (ieWord) parameters->int0Parameter; +} + +void GameScript::SetRestEncounterProbabilityNight(Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.NightChance = (ieWord) parameters->int0Parameter; +} + +//iwd +void GameScript::SetRestEncounterChance(Scriptable * Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RestHeader.DayChance = (ieWord) parameters->int0Parameter; + map->RestHeader.NightChance = (ieWord) parameters->int1Parameter; +} + +//easily hardcoded end sequence +void GameScript::EndCredits(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->PlayMovie("credits"); +} + +//easily hardcoded end sequence +void GameScript::ExpansionEndCredits(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->PlayMovie("ecredit"); +} + +//always quits game, but based on game it can play end animation, or display +//death text, etc +//this covers: +//QuitGame (play two of 3 movies in PST, display death screen with strref) +//EndGame (display death screen with strref) +void GameScript::QuitGame(Scriptable* Sender, Action* parameters) +{ + ClearAllActions(Sender, parameters); + core->GetDictionary()->SetAt("QuitGame1", (ieDword) parameters->int0Parameter); + core->GetDictionary()->SetAt("QuitGame2", (ieDword) parameters->int1Parameter); + core->GetDictionary()->SetAt("QuitGame3", (ieDword) parameters->int2Parameter); + core->SetNextScript("QuitGame"); +} + +void GameScript::StopMoving(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->ClearPath(); +} + +void GameScript::ApplyDamage(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + damagee->Damage(parameters->int0Parameter, parameters->int1Parameter, damager); +} + +void GameScript::ApplyDamagePercent(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + damagee->Damage(damagee->GetBase(IE_HITPOINTS)*parameters->int0Parameter/100, parameters->int1Parameter, damager); +} + +void GameScript::Damage(Scriptable* Sender, Action* parameters) +{ + Actor *damagee; + Actor *damager; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + damagee = (Actor *) tar; + if (Sender->Type==ST_ACTOR) { + damager=(Actor *) Sender; + } else { + damager=damagee; + } + int damage = damagee->LuckyRoll( (parameters->int1Parameter>>12)&15, (parameters->int1Parameter>>4)&255, parameters->int1Parameter&15, 0, 1, damager); + int type=MOD_ADDITIVE; + switch(parameters->int0Parameter) { + case 2: //raise + damage=-damage; + break; + case 3: //set + type=MOD_ABSOLUTE; + break; + case 4: // + type=MOD_PERCENT; + break; + } + damagee->Damage( damage, type, damager ); +} +/* +void GameScript::SetHomeLocation(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Movable *movable = (Movable *) tar; //not actor, though it is the only moveable + movable->Destination = parameters->pointParameter; + //no movement should be started here, i think +} +*/ + +void GameScript::SetMasterArea(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->SetMasterArea(parameters->string0Parameter); +} + +void GameScript::Berserk(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBaseBit(IE_STATE_ID, STATE_BERSERK, true); + act->Panic(NULL, PANIC_BERSERK); +} + +void GameScript::Panic(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->Panic(NULL, PANIC_RANDOMWALK); +} + +/* as of now: removes panic and berserk */ +void GameScript::Calm(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBaseBit(IE_STATE_ID, STATE_BERSERK|STATE_PANIC, false); +} + +void GameScript::RevealAreaOnMap(Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *worldmap = core->GetWorldMap(); + if (!worldmap) { + printf("Can't find worldmap!\n"); + abort(); + } + // WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates + worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, BM_OR); + displaymsg->DisplayConstantString(STR_WORLDMAPCHANGE, 0xc8ffc8); +} + +void GameScript::HideAreaOnMap( Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *worldmap = core->GetWorldMap(); + if (!worldmap) { + printf("Can't find worldmap!\n"); + abort(); + } + // WMP_ENTRY_ADJACENT because otherwise revealed bg2 areas are unreachable from city gates + worldmap->SetAreaStatus(parameters->string0Parameter, WMP_ENTRY_VISIBLE|WMP_ENTRY_ADJACENT, BM_NAND); +} + +void GameScript::SendTrigger(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar) { + return; + } + tar->TriggerID=parameters->int0Parameter; +} + +void GameScript::Shout( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //according to IESDP silenced creatures cannot use shout + Actor *actor = (Actor *) Sender; + if (actor->GetStat( IE_STATE_ID) & STATE_SILENCED) { + return; + } + Map *map=Sender->GetCurrentArea(); + //max. shouting distance, please adjust it if you know better + map->Shout(actor, parameters->int0Parameter, MAX_TRAVELING_DISTANCE); +} + +void GameScript::GlobalShout( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //according to IESDP silenced creatures cannot use shout + Actor *actor = (Actor *) Sender; + if (actor->GetStat( IE_STATE_ID) & STATE_SILENCED) { + return; + } + Map *map=Sender->GetCurrentArea(); + // 0 means unlimited shout distance + map->Shout(actor, parameters->int0Parameter, 0); +} + +void GameScript::Help( Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Map *map=Sender->GetCurrentArea(); + map->Shout((Actor *) Sender, 0, 40); +} + +void GameScript::GiveOrder(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (tar) { + tar->LastOrderer = Sender->GetGlobalID(); + tar->LastOrder = parameters->int0Parameter; + } +} + +void GameScript::AddMapnote( Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + char *str = core->GetString( parameters->int0Parameter, 0); + map->AddMapNote(parameters->pointParameter, parameters->int1Parameter, str, parameters->int0Parameter); +} + +void GameScript::RemoveMapnote( Scriptable* Sender, Action* parameters) +{ + Map *map=Sender->GetCurrentArea(); + map->RemoveMapNote(parameters->pointParameter); +} + +void GameScript::AttackOneRound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = core->Time.round_size; + } + + AttackCore(Sender, tar, 0); + + if (Sender->CurrentActionState == 1) { + //this is the LastDisarmFailed field, but this is an actor + //Sender->LastTarget = 0; + Sender->ReleaseCurrentAction(); + } else { + Sender->CurrentActionState--; + } +} + +void GameScript::RunningAttackNoSound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_NO_SOUND|AC_RUNNING); +} + +void GameScript::AttackNoSound( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_NO_SOUND); +} + +void GameScript::RunningAttack( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + /*if (!parameters->objects[1]) { + GameControl *gc = core->GetGameControl(); + tar = gc->GetTarget(); + } else {*/ + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + /*}*/ + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, AC_RUNNING); +} + +void GameScript::Attack( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + //using auto target! + Scriptable* tar; + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) || tar == Sender) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, 0); +} + +void GameScript::ForceAttack( Scriptable* Sender, Action* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!scr || scr->Type != ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2], GA_NO_DEAD ); + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + return; + } + //this is a hack, we use a gui variable for our own hideous reasons? + if (tar->Type==ST_ACTOR) { + GameControl *gc = core->GetGameControl(); + if (gc) { + //saving the target object ID from the gui variable + char Tmp[40]; + strncpy(Tmp,"NIDSpecial3()",sizeof(Tmp) ); + scr->AddAction( GenerateActionDirect(Tmp, (Actor *) tar) ); + } + } else { + char Tmp[80]; + snprintf(Tmp, sizeof(Tmp), "BashDoor(%s)", tar->GetScriptName()); + scr->AddAction ( GenerateAction(Tmp) ); + } +} + +void GameScript::AttackReevaluate( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!Sender->CurrentActionState) { + Sender->CurrentActionState = parameters->int0Parameter; + // TODO: reevaluate target (set CurrentActionTarget to 0) if we are not actively in combat + } + + Scriptable* tar; + tar = GetStoredActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar || (tar->Type != ST_ACTOR && tar->Type !=ST_DOOR && tar->Type !=ST_CONTAINER) ) { + Sender->ReleaseCurrentAction(); + return; + } + + //actor is already incapable of attack + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + Sender->ReleaseCurrentAction(); + return; + } + + AttackCore(Sender, tar, 0); + + Sender->CurrentActionState--; +} + +void GameScript::Explore( Scriptable* Sender, Action* /*parameters*/) +{ + Sender->GetCurrentArea( )->Explore(-1); +} + +void GameScript::UndoExplore( Scriptable* Sender, Action* /*parameters*/) +{ + Sender->GetCurrentArea( )->Explore(0); +} + +void GameScript::ExploreMapChunk( Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + /* + There is a mode flag in int1Parameter, but i don't know what is it, + our implementation uses it for LOS=1, or no LOS=0 + ExploreMapChunk will reveal both visibility/explored map, but the + visibility will fade in the next update cycle (which is quite frequent) + */ + map->ExploreMapChunk(parameters->pointParameter, parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::StartStore( Scriptable* Sender, Action* parameters) +{ + if (core->GetCurrentStore() ) { + return; + } + core->SetCurrentStore( parameters->string0Parameter, Sender->GetGlobalID()); + core->SetEventFlag(EF_OPENSTORE); + //sorry, i have absolutely no idea when i should do this :) + Sender->ReleaseCurrentAction(); +} + +//The integer parameter is a GemRB extension, if set to 1, the player +//gains experience for learning the spell +void GameScript::AddSpecialAbility( Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LearnSpell (parameters->string0Parameter, parameters->int0Parameter|LS_MEMO|LS_LEARN); + core->SetEventFlag(EF_ACTION); +} + +//actually this just depletes a spell, doesn't remove it from the book +//GemRB extension: the first/second int parameter can also make it removed +//from the spell memorization schedule (also from the spellbook) +void GameScript::RemoveSpell( Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + int type; + + if (Sender->Type!=ST_ACTOR) { + return; + } + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + type = parameters->int1Parameter; + } else { + type = parameters->int0Parameter; + } + if (type==2) { + //remove spell from both book and memorization + actor->spellbook.RemoveSpell(spellres); + return; + } + //type == 1 remove spell only from memorization + //type == 0 original behaviour: deplete only + actor->spellbook.UnmemorizeSpell(spellres, type); +} + +void GameScript::SetScriptName( Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + tar->SetScriptName(parameters->string0Parameter); +} + +//iwd2 +//advance time with a constant +//This is in seconds according to IESDP +void GameScript::AdvanceTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->AdvanceTime(parameters->int0Parameter*1000/AI_UPDATE_TIME); +} + +//advance at least one day, then stop at next day/dusk/night/morning +//oops, not TimeODay is used but Time (this means we got hours) +//i'm not sure if we should add a whole day either, needs more research +void GameScript::DayNight(Scriptable* /*Sender*/, Action* parameters) +{ + // first, calculate the current number of hours. + int padding = ((core->GetGame()->GameTime / AI_UPDATE_TIME) % 7200) / 300; + // then, calculate the offset (in hours) required to take us to the desired hour. + padding = (24 + parameters->int0Parameter - padding) % 24; + // then, advance one day (7200), plus the desired number of hours. + core->GetGame()->AdvanceTime(AI_UPDATE_TIME*(7200 + padding*300)); +} + +//implement pst style parameters: +//suggested dream - unused +//if suggested dream is 0, then area flags determine the 'movie' +//hp - number of hps healed +//renting - crashes pst, we simply ignore it +void GameScript::RestParty(Scriptable* Sender, Action* parameters) +{ + Game *game = core->GetGame(); + game->RestParty(REST_NOAREA|REST_NOMOVE|REST_NOCRITTER, parameters->int0Parameter, parameters->int1Parameter); + Sender->ReleaseCurrentAction(); +} + +//doesn't advance game time, just refreshes spells of target +//this is a non-blocking action +void GameScript::Rest(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->spellbook.ChargeAllSpells(); + //check if this should be a full heal + actor->Heal(0); + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//doesn't advance game time (unsure), just refreshes spells of target +void GameScript::RestNoSpells(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + //check if this should be a full heal + actor->Heal(0); + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//this is most likely advances time +void GameScript::RestUntilHealed(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->Heal(1); + //not sure if this should remove timed effects + //more like execute them hour by hour :> +} + +//iwd2 +//removes all delayed/duration/semi permanent effects (like a ctrl-r) +void GameScript::ClearPartyEffects(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + tar->fxqueue.RemoveExpiredEffects(0xffffffff); + } +} + +//iwd2 removes effects from a single sprite +void GameScript::ClearSpriteEffects(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) tar; + actor->fxqueue.RemoveExpiredEffects(0xffffffff); +} + +//IWD2 special, can mark only actors, hope it is enough +void GameScript::MarkObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + //unsure, could mark dead objects? + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1], GA_NO_DEAD ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LastMarked = tar->GetGlobalID(); + //if this doesn't modify LastSeen, then remove this line + actor->LastSeen = actor->LastMarked; +} + +void GameScript::MarkSpellAndObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *me = (Actor *) Sender; + if (me->LastMarkedSpell) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1]); + Actor *actor = NULL; + if (tar->Type == ST_ACTOR) { + actor = (Actor *) tar; + } + + int flags = parameters->int0Parameter; + if (!(flags & MSO_IGNORE_NULL) && !actor) { + return; + } + if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget() ) { + return; + } + if (!(flags & MSO_IGNORE_SEE) && actor && !CanSee(Sender, actor, true, 0) ) { + return; + } + int len = strlen(parameters->string0Parameter); + // + if (len&3) { + return; + } + len/=4; + int max = len; + int pos; + if (flags & MSO_RANDOM_SPELL) { + pos = core->Roll(1,len,0); + } else { + pos = 0; + } + while(len--) { + char spl[5]; + + memcpy(spl, parameters->string0Parameter+pos*4, 4); + spl[4]=0; + int splnum = atoi(spl); + + if (!(flags & MSO_IGNORE_HAVE) && !me->spellbook.HaveSpell(splnum, 0) ) { + goto end_mso_loop; + } + int range; + if ((flags & MSO_IGNORE_RANGE) || !actor) { + range = 0; + } else { + range = Distance(me, actor); + } + if (!(flags & MSO_IGNORE_INVALID) && actor->InvalidSpellTarget(splnum, me, range)) { + goto end_mso_loop; + } + //mark spell and target + me->LastMarkedSpell = splnum; + me->LastMarked = actor->GetGlobalID(); + break; +end_mso_loop: + pos++; + if (pos==max) { + pos = 0; + } + } +} + +void GameScript::ForceMarkedSpell(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->LastMarkedSpell = parameters->int0Parameter; +} + +void GameScript::SetMarkedSpell(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + if (parameters->int0Parameter) { + if (actor->LastMarkedSpell) { + return; + } + if (!actor->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return; + } + } + + //TODO: check if spell exists (not really important) + actor->LastMarkedSpell = parameters->int0Parameter; + return; +} + +void GameScript::SetDialogueRange(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase( IE_DIALOGRANGE, parameters->int0Parameter ); +} + +void GameScript::SetGlobalTint(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetVideoDriver()->SetFadeColor(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::SetArmourLevel(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetBase( IE_ARMOR_TYPE, parameters->int0Parameter ); +} + +void GameScript::RandomWalk(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( true, false ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::RandomRun(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( true, true ); + Sender->ReleaseCurrentAction(); +} + +void GameScript::RandomWalkContinuous(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + actor->RandomWalk( false, false ); +} + +void GameScript::RandomFly(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + int x = rand()&31; + if (x<10) { + actor->SetOrientation(actor->GetOrientation()-1, false); + } else if (x>20) { + actor->SetOrientation(actor->GetOrientation()+1, false); + } + //fly in this direction for 5 steps + actor->MoveLine(5, GL_PASS, actor->GetOrientation() ); + //readding the action to the end of the queue + //Sender->AddAction( parameters ); + //Sender->ReleaseCurrentAction(); +} + +//UseContainer uses the predefined target (like Nidspecial1 dialog hack) +void GameScript::UseContainer(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *)Sender; + Container *container = core->GetCurrentContainer(); + if (!container) { + printMessage("GameScript","No container selected!", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + + ieDword distance = PersonalDistance(Sender, container); + ieDword needed = MAX_OPERATING_DISTANCE; + if (container->Type==IE_CONTAINER_PILE) { + needed = 0; // less than a search square (width) + } + if (distance<=needed) + { + //check if the container is unlocked + if (!container->TryUnlock(actor)) { + //playsound can't open container + //display string, etc + displaymsg->DisplayConstantString(STR_CONTLOCKED,0xd7d7be,container); + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *)Sender; + actor->SetModal(MS_NONE); + container->TriggerTrap(0, actor->GetGlobalID()); + core->SetCurrentContainer(actor, container, true); + Sender->ReleaseCurrentAction(); + return; + } + MoveNearerTo(Sender, container, needed); +} + +//call the usecontainer action in target (not used) +void GameScript::ForceUseContainer(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); //why blocking??? + return; + } + char Tmp[256]; + sprintf( Tmp, "UseContainer()"); + Action *newaction = GenerateAction(Tmp); + tar->AddActionInFront(newaction); + Sender->ReleaseCurrentAction(); //why blocking??? +} + +//these actions directly manipulate a game variable (as the original engine) +void GameScript::SetMazeEasier(Scriptable* Sender, Action* /*parameters*/) +{ + int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL"); + if (value>0) { + SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value-1); + } +} + +void GameScript::SetMazeHarder(Scriptable* Sender, Action* /*parameters*/) +{ + int value = CheckVariable( Sender, "MAZEDIFFICULTY","GLOBAL"); + if (value<2) { + SetVariable(Sender, "MAZEDIFFICULTY", "GLOBAL", value+1); + } +} + +void GameScript::StartRainNow(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->GetGame()->StartRainOrSnow( false, WB_RAIN|WB_LIGHTNING); +} + +void GameScript::Weather(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + switch(parameters->int0Parameter & WB_FOG) { + case WB_NORMAL: + game->StartRainOrSnow( false, 0); + break; + case WB_RAIN: + game->StartRainOrSnow( true, WB_RAIN|WB_LIGHTNING); + break; + case WB_SNOW: + game->StartRainOrSnow( true, WB_SNOW); + break; + case WB_FOG: + game->StartRainOrSnow( true, WB_FOG); + break; + } +} + +void GameScript::CopyGroundPilesTo(Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + Map *othermap = core->GetGame()->GetMap( parameters->string0Parameter, false ); + if (!othermap) { + return; + } + map->CopyGroundPiles( othermap, parameters->pointParameter ); +} + +//iwd specific +void GameScript::PlayBardSong(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + //actually this one must use int0Parameter to set a bardsong + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_BATTLESONG); +} + +void GameScript::BattleSong(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_BATTLESONG); +} + +void GameScript::FindTraps(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + actor->SetModal( MS_DETECTTRAPS); +} + +void GameScript::Hide(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + + if (actor->TryToHide()) { + actor->SetModal(MS_STEALTH); + } + //TODO: expiry isn't instant (skill based transition?) + +} + +void GameScript::Turn(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *) Sender; + + if (actor->Modified[IE_DISABLEDBUTTON] & (1<GetStat(IE_TURNUNDEADLEVEL); + if (skill < 1) return; + + actor->SetModal(MS_TURNUNDEAD); + +} + +void GameScript::TurnAMT(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation(actor->GetOrientation()+parameters->int0Parameter, true); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::RandomTurn(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *actor = (Actor *) Sender; + actor->SetOrientation(rand() % MAX_ORIENT, true); + actor->SetWait( 1 ); + Sender->ReleaseCurrentAction(); // todo, blocking? +} + +void GameScript::AttachTransitionToDoor(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type != ST_DOOR) { + return; + } + Door* door = ( Door* ) tar; + strnspccpy(door->LinkedInfo, parameters->string0Parameter, 32); +} + +/*getting a handle of a temporary actor resource to copy its selected attributes*/ +void GameScript::ChangeAnimation(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,1); +} + +void GameScript::ChangeAnimationNoEffect(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + ChangeAnimationCore((Actor *) Sender, parameters->string0Parameter,0); +} + +void GameScript::Polymorph(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + act->SetBase(IE_ANIMATION_ID, parameters->int0Parameter); +} + +void GameScript::PolymorphCopy(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + PolymorphCopyCore((Actor *) tar, (Actor *) Sender, false); +} + +/* according to IESDP this only copies the animation ID */ +void GameScript::PolymorphCopyBase(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *act = (Actor *) Sender; + Actor *actor = (Actor *) tar; + act->SetBase(IE_ANIMATION_ID, actor->GetBase(IE_ANIMATION_ID) ); +} + +void GameScript::ExportParty(Scriptable* /*Sender*/, Action* parameters) +{ + char FileName[_MAX_PATH]; + + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *actor = game->GetPC(i, false); + snprintf(FileName,_MAX_PATH,"%s%d",parameters->string0Parameter,i+1); + core->WriteCharacter(FileName, actor); + } + displaymsg->DisplayConstantString(STR_EXPORTED, 0xbcefbc); +} + +void GameScript::SaveGame(Scriptable* /*Sender*/, Action* parameters) +{ + if (core->HasFeature(GF_STRREF_SAVEGAME)) { + const char *basename = "Auto-Save"; + AutoTable tab("savegame"); + if (tab) { + basename = tab->QueryDefault(); + } + char * str = core->GetString( parameters->int0Parameter, IE_STR_STRREFOFF); + char FolderName[_MAX_PATH]; + snprintf (FolderName, sizeof(FolderName), "%s - %s", basename, str); + core->FreeString( str ); + + core->GetSaveGameIterator()->CreateSaveGame(core->GetSaveGameIterator()->GetSaveGame(FolderName), FolderName); + } else { + core->GetSaveGameIterator()->CreateSaveGame(parameters->int0Parameter); + } +} + +/*EscapeAreaMove(S:Area*,I:X*,I:Y*,I:Face*)*/ +void GameScript::EscapeArea(Scriptable* Sender, Action* parameters) +{ + if (InDebug&ID_ACTIONS) { + printf("EscapeArea/EscapeAreaMove\n"); + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction + //Sender->ReleaseCurrentAction(); +} + +void GameScript::EscapeAreaNoSee(Scriptable* Sender, Action* parameters) +{ + if (InDebug&ID_ACTIONS) { + printf("EscapeAreaNoSee\n"); + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction + //Sender->ReleaseCurrentAction(); +} + +void GameScript::EscapeAreaDestroy(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + //find nearest exit + Point p = Sender->Pos; + map->TMap->AdjustNearestTravel(p); + //EscapeAreaCore will do its ReleaseCurrentAction + EscapeAreaCore( Sender, p, parameters->string0Parameter, p, EA_DESTROY, parameters->int0Parameter ); +} + +/*EscapeAreaObjectMove(S:Area*,I:X*,I:Y*,I:Face*)*/ +void GameScript::EscapeAreaObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Point p = tar->Pos; + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, 0, p, EA_DESTROY, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction +} + +//This one doesn't require the object to be seen? +//We don't have that feature yet, so this is the same as EscapeAreaObject +void GameScript::EscapeAreaObjectNoSee(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Point p = tar->Pos; + Sender->SetWait(parameters->int0Parameter); + if (parameters->string0Parameter[0]) { + Point q((short) parameters->int0Parameter, (short) parameters->int1Parameter); + EscapeAreaCore( Sender, p, parameters->string0Parameter, q, 0, parameters->int2Parameter ); + } else { + EscapeAreaCore( Sender, p, 0, p, EA_DESTROY|EA_NOSEE, parameters->int0Parameter ); + } + //EscapeAreaCore will do its ReleaseCurrentAction +} + +//takes first fitting item from container at feet, doesn't seem to be working in the original engines +void GameScript::PickUpItem(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + Map *map = scr->GetCurrentArea(); + Container *c = map->GetPile(scr->Pos); + if (!c) { //this shouldn't happen, but lets prepare for the worst + return; + } + + //the following part is coming from GUISCript.cpp with trivial changes + int Slot = c->inventory.FindItem(parameters->string0Parameter, 0); + if (Slot<0) { + return; + } + int res = core->CanMoveItem(c->inventory.GetSlotItem(Slot) ); + if (!res) { //cannot move + return; + } + CREItem *item = c->RemoveItem(Slot,0); + if (!item) { + return; + } + if (res!=-1 && scr->InParty) { //it is gold and we got the party pool! + goto item_is_gold; + } + res = scr->inventory.AddSlotItem(item, SLOT_ONLYINVENTORY); + if (res !=ASI_SUCCESS) { //putting it back + c->AddItem(item); + } + return; +item_is_gold: //we take gold! + if (scr->InParty) { + core->GetGame()->PartyGold += res; + //if you want message here then use + //core->GetGame()->AddGold(res); + } else { + scr->SetBase( IE_GOLD, scr->GetBase(IE_GOLD) + res ); + } + delete item; +} + +void GameScript::ChangeStoreMarkup(Scriptable* /*Sender*/, Action* parameters) +{ + bool has_current = false; + ieResRef current; + ieDword owner; + + Store *store = core->GetCurrentStore(); + if (!store) { + store = core->SetCurrentStore(parameters->string0Parameter, 0); + } else { + if (strnicmp(store->Name, parameters->string0Parameter, 8) ) { + //not the current store, we need some dirty hack + has_current = true; + strnlwrcpy(current, store->Name, 8); + owner = store->GetOwnerID(); + } + } + store->BuyMarkup = parameters->int0Parameter; + store->SellMarkup = parameters->int1Parameter; + //additional markup, is this depreciation??? + store->DepreciationRate = parameters->int2Parameter; + if (has_current) { + //setting back old store (this will save our current store) + core->SetCurrentStore(current, owner); + } +} + +void GameScript::SetEncounterProbability(Scriptable* /*Sender*/, Action* parameters) +{ + WorldMap *wmap = core->GetWorldMap(parameters->string0Parameter); + if (!wmap) { + //no such starting area + return; + } + WMPAreaLink *link = wmap->GetLink(parameters->string0Parameter, parameters->string1Parameter); + if (!link) { + return; + } + link->EncounterChance = parameters->int0Parameter; +} + +void GameScript::SpawnPtActivate(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 1; + } + } +} + +void GameScript::SpawnPtDeactivate(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 0; + } + } +} + +void GameScript::SpawnPtSpawn(Scriptable* Sender, Action* parameters) +{ + if (parameters->objects[1]) { + Map *map = Sender->GetCurrentArea(); + Spawn *spawn = map->GetSpawn(parameters->objects[1]->objectName); + if (spawn) { + spawn->Enabled = 1; //??? maybe use an unconditionality flag + map->TriggerSpawn(spawn); + } + } +} + +void GameScript::ApplySpell(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + return; + } + if (tar->Type==ST_ACTOR) { + //apply spell on target +/* + Actor *owner; + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = (Actor *) tar; + } +*/ + //core->ApplySpell(spellres, (Actor *) tar, owner, parameters->int1Parameter); + core->ApplySpell(spellres, (Actor *) tar, Sender, parameters->int1Parameter); + } else { + //no idea about this one +/* + Actor *owner; + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = NULL; + } +*/ + //apply spell on point + Point d; + GetPositionFromScriptable(tar, d, false); + //core->ApplySpellPoint(spellres, tar->GetCurrentArea(), d, owner, parameters->int1Parameter); + core->ApplySpellPoint(spellres, tar->GetCurrentArea(), d, Sender, parameters->int1Parameter); + } +} + +void GameScript::ApplySpellPoint(Scriptable* Sender, Action* parameters) +{ + ieResRef spellres; + Actor *owner; + + if (!ResolveSpellName( spellres, parameters) ) { + return; + } + + if (Sender->Type==ST_ACTOR) { + owner = (Actor *) Sender; + } else { + owner = NULL; + } + core->ApplySpellPoint(spellres, Sender->GetCurrentArea(), parameters->pointParameter, owner, parameters->int1Parameter); +} + +//this is a gemrb extension +//sets a variable to the stat value +void GameScript::GetStat(Scriptable* Sender, Action* parameters) +{ + ieDword value; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + value = 0; + } else { + Actor* actor = ( Actor* ) tar; + value = actor->GetStat( parameters->int0Parameter ); + } + SetVariable( Sender, parameters->string0Parameter, value ); +} + +void GameScript::BreakInstants(Scriptable* Sender, Action* /*parameters*/) +{ + //don't do anything, apparently the point of this action is to + //delay the execution of further actions to the next AI cycle + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // this doesn't really need to block +} + +//an interesting improvement would be to pause game for a given duration +void GameScript::PauseGame(Scriptable* Sender, Action* /*parameters*/) +{ + GameControl *gc = core->GetGameControl(); + if (gc) { + gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + displaymsg->DisplayConstantString(STR_SCRIPTPAUSED,0xff0000); + } + // releasing this action allows actions to continue executing, + // so we force a wait + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); // does this need to block? +} + +void GameScript::SetNoOneOnTrigger(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip || (ip->Type!=ST_TRIGGER && ip->Type!=ST_TRAVEL && ip->Type!=ST_PROXIMITY)) { + printf("Script error: No Trigger Named \"%s\"\n", parameters->objects[1]->objectName); + return; + } + ip->LastEntered = 0; + ip->LastTrigger = 0; + ip->LastTriggerObject = 0; +} + +void GameScript::UseDoor(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + + gc->ResetTargetMode(); + OpenDoor(Sender, parameters); + + Sender->ReleaseCurrentAction(); // this is blocking, OpenDoor is not +} + +//this will force bashing the door +void GameScript::BashDoor(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable *target = GetActorFromObject(Sender, parameters->objects[1]); + TileMap *tmap = Sender->GetCurrentArea()->TMap; + Door *door = NULL; + Container *container = NULL; + Point pos; + if (target->Type == ST_DOOR) { + // FIXME: actually it chooses from two possible points + pos = target->Pos; + door = tmap->GetDoorByPosition(pos); + } else if(target->Type == ST_CONTAINER) { + pos = target->Pos; + container = tmap->GetContainerByPosition(pos); + } else { + Sender->ReleaseCurrentAction(); + return; + } + + // TODO: "sets a field in the door/container to 1" + + if (SquaredPersonalDistance(pos, Sender) > MAX_OPERATING_DISTANCE*MAX_OPERATING_DISTANCE) { + MoveNearerTo(Sender, pos, MAX_OPERATING_DISTANCE, 0); + return; + } + + gc->SetTargetMode(TARGET_MODE_ATTACK); //for bashing doors too + + // try to bash it + if (door) { + door->TryBashLock((Actor *) Sender); + } else if (container) { + container->TryBashLock((Actor *) Sender); + } + + Sender->ReleaseCurrentAction(); +} + +//pst action +void GameScript::ActivatePortalCursor(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip) { + return; + } + if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) { + return; + } + InfoPoint *tar = (InfoPoint *) ip; + if (parameters->int0Parameter) { + tar->Trapped|=PORTAL_CURSOR; + } else { + tar->Trapped&=~PORTAL_CURSOR; + } +} + +//pst action +void GameScript::EnablePortalTravel(Scriptable* Sender, Action* parameters) +{ + Scriptable* ip; + + if (!parameters->objects[1]) { + ip=Sender; + } else { + ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + } + if (!ip) { + return; + } + if (ip->Type!=ST_PROXIMITY && ip->Type!=ST_TRAVEL) { + return; + } + InfoPoint *tar = (InfoPoint *) ip; + if (parameters->int0Parameter) { + tar->Trapped|=PORTAL_TRAVEL; + } else { + tar->Trapped&=~PORTAL_TRAVEL; + } +} + +//unhardcoded iwd action (for the forge entrance change) +void GameScript::ChangeDestination(Scriptable* Sender, Action* parameters) +{ + InfoPoint *ip = Sender->GetCurrentArea()->TMap->GetInfoPoint(parameters->objects[1]->objectName); + if (ip && (ip->Type==ST_TRAVEL) ) { + strnlwrcpy(ip->Destination, parameters->string0Parameter, 32); + } +} + +void GameScript::MoveCursorPoint(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetVideoDriver()->MoveMouse(parameters->pointParameter.x, parameters->pointParameter.y); +} + +//false means, no talk +void GameScript::DialogueInterrupt(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + if ( parameters->int0Parameter != 0 ) { + actor->SetMCFlag(MC_NO_TALK, BM_NAND); + } else { + actor->SetMCFlag(MC_NO_TALK, BM_OR); + } +} + +void GameScript::EquipMostDamagingMelee(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_MELEE); +} + +void GameScript::EquipRanged(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_RANGED); +} + +//will equip best weapon regardless of range considerations +void GameScript::EquipWeapon(Scriptable* Sender, Action* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + actor->inventory.EquipBestWeapon(EQUIP_MELEE|EQUIP_RANGED); +} + +void GameScript::SetBestWeapon(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor* actor = ( Actor* ) Sender; + + Actor *target = (Actor *) tar; + if (PersonalDistance(actor,target)>(unsigned int) parameters->int0Parameter) { + actor->inventory.EquipBestWeapon(EQUIP_RANGED); + } else { + actor->inventory.EquipBestWeapon(EQUIP_MELEE); + } +} + +void GameScript::FakeEffectExpiryCheck(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *target = (Actor *) tar; + target->fxqueue.RemoveExpiredEffects(parameters->int0Parameter); +} + +void GameScript::SetInterrupt(Scriptable* Sender, Action* parameters) +{ + if (parameters->int0Parameter) { + Sender->Interrupt(); + } else { + Sender->NoInterrupt(); + } +} + +void GameScript::SelectWeaponAbility(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + int slot = parameters->int0Parameter; + int wslot = scr->inventory.GetWeaponSlot(); + //weapon + if (core->QuerySlotType(slot)&SLOT_WEAPON) { + slot-=wslot; + if (slot<0 || slot>=MAX_QUICKWEAPONSLOT) { + return; + } + scr->SetEquippedQuickSlot(slot, parameters->int1Parameter); + return; + } + //quick item + wslot = scr->inventory.GetQuickSlot(); + if (core->QuerySlotType(slot)&SLOT_ITEM) { + slot-=wslot; + if (slot<0 || slot>=MAX_QUICKITEMSLOT) { + return; + } + if (scr->PCStats) { + scr->PCStats->QuickItemHeaders[slot]=(ieWord) parameters->int1Parameter; + } + } +} + +void GameScript::UseItem(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *act = (Actor *) Sender; + int Slot; + ieDword header, flags; + ieResRef itemres; + + if (parameters->string0Parameter[0]) { + Slot = act->inventory.FindItem(parameters->string0Parameter, 0); + //this IS in the original game code (ability) + header = parameters->int0Parameter; + flags = parameters->int1Parameter; + } else { + Slot = parameters->int0Parameter; + //this is actually not in the original game code + header = parameters->int1Parameter; + flags = parameters->int2Parameter; + } + + if (Slot == -1) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!ResolveItemName( itemres, act, Slot) ) { + Sender->ReleaseCurrentAction(); + return; + } + + unsigned int dist = GetItemDistance(itemres, header); + + if (PersonalDistance(tar->Pos, Sender) > dist) { + MoveNearerTo(Sender, tar, dist); + return; + } + + act->UseItem(Slot, header, tar, flags); + Sender->ReleaseCurrentAction(); +} + +void GameScript::UseItemPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Actor *act = (Actor *) Sender; + int Slot; + ieDword header; + ieResRef itemres; + ieDword flags; + + if (parameters->string0Parameter[0]) { + Slot = act->inventory.FindItem(parameters->string0Parameter, 0); + //this IS in the original game code (ability) + header = parameters->int0Parameter; + flags = parameters->int1Parameter; + } else { + Slot = parameters->int0Parameter; + //this is actually not in the original game code + header = parameters->int1Parameter; + flags = parameters->int2Parameter; + } + + if (Slot == -1) { + Sender->ReleaseCurrentAction(); + return; + } + + if (!ResolveItemName( itemres, act, Slot) ) { + Sender->ReleaseCurrentAction(); + return; + } + + unsigned int dist = GetItemDistance(itemres, header); + + if (PersonalDistance(parameters->pointParameter, Sender) > dist) { + MoveNearerTo(Sender, parameters->pointParameter, dist, 0); + return; + } + + act->UseItemPoint(Slot, header, parameters->pointParameter, flags); + Sender->ReleaseCurrentAction(); +} + +//addfeat will be able to remove feats too +//(the second int parameter is a bitmode) +void GameScript::AddFeat(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + actor->SetFeat(parameters->int0Parameter, parameters->int1Parameter); +} + +void GameScript::MatchHP(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + switch (parameters->int0Parameter) { + case 1: //sadly the hpflags are not the same as stats + actor->SetBase(IE_HITPOINTS,scr->GetBase(IE_HITPOINTS)); + break; + case 0: + actor->SetBase(IE_MAXHITPOINTS, scr->GetBase(IE_MAXHITPOINTS)); + break; + default: //this is gemrb extension + actor->SetBase(parameters->int0Parameter, scr->GetBase(parameters->int0Parameter)); + break; + } +} + +void GameScript::ChangeColor(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + ieDword stat = parameters->int0Parameter; + if (stat<9 || stat>14) { + return; + } + stat += IE_COLORS - 9; + scr->SetBase(stat, (scr->GetBase(stat)&~255)|(parameters->int1Parameter&255)); +} + +//removes previous kit, adds new +void GameScript::AddKit(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + //remove previous kit stuff + scr->ApplyKit(true); + //this adds the current level abilities + scr->SetBase(IE_KIT, parameters->int0Parameter); + scr->ApplyKit(false); +} + +//doesn't remove old kit +void GameScript::AddSuperKit(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Actor *scr = (Actor *) Sender; + scr->SetBase(IE_KIT, parameters->int0Parameter); + scr->ApplyKit(false); +} + +void GameScript::SetSelection(Scriptable* /*Sender*/, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + return; + } + gc->SelectActor(parameters->int0Parameter, parameters->int1Parameter); +} + +//this action is weird in the original game, because it overwrites ALL +//IDS stats. +//in this version, if a stat is set to 0, it won't change +//it will alter only the main IDS stats +void GameScript::ChangeAIType(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return; + } + Object *ob = parameters->objects[1]; + if (!ob) { + return; + } + Actor *scr = (Actor *) Sender; + for (int i=0;iobjectFields[i]; + if (!val) continue; + if (!strnicmp(ObjectIDSTableNames[i],"ea",8)) { + scr->SetBase(IE_EA, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"general",8)) { + scr->SetBase(IE_GENERAL, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"race",8)) { + scr->SetBase(IE_RACE, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"class",8)) { + scr->SetBase(IE_CLASS, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"gender",8)) { + scr->SetBase(IE_SEX, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"specific",8)) { + scr->SetBase(IE_SPECIFIC, val); + continue; + } + if (!strnicmp(ObjectIDSTableNames[i],"align",8)) { + scr->SetBase(IE_ALIGNMENT, val); + continue; + } + } +} + +//same as MoveToPoint, but not blocking +void GameScript::Leader(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + + char Tmp[256]; + + snprintf(Tmp, 256, "MoveToPoint([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y); + Action *newact = GenerateAction(Tmp); + Sender->AddAction(newact); +} + +//same as MoveToPointNoRecticle, but not blocking +void GameScript::Follow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return; + } + + char Tmp[256]; + + snprintf(Tmp, 256, "MoveToPointNoRecticle([%d.%d])", parameters->pointParameter.x, parameters->pointParameter.y); + Action *newact = GenerateAction(Tmp); + Sender->AddAction(newact); +} + +void GameScript::FollowCreature(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->FollowOffset.empty(); + if (!scr->InMove() || scr->Destination != actor->Pos) { + scr->WalkTo(actor->Pos, 0, 1); + } +} + +void GameScript::RunFollow(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->FollowOffset.empty(); + if (!scr->InMove() || scr->Destination != actor->Pos) { + scr->WalkTo(actor->Pos, IF_RUNNING, 1); + } +} + +void GameScript::ProtectPoint(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + if (!scr->InMove() || scr->Destination != parameters->pointParameter) { + scr->WalkTo( parameters->pointParameter, 0, 1 ); + } + // we should handle 'Protect' here rather than just unblocking + Sender->ReleaseCurrentAction(); +} + +void GameScript::ProtectObject(Scriptable* Sender, Action* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + scr->LastProtected = actor->GetGlobalID(); + //not exactly range + scr->FollowOffset.x = parameters->int0Parameter; + scr->FollowOffset.y = parameters->int0Parameter; + if (!scr->InMove() || scr->Destination != tar->Pos) { + scr->WalkTo( tar->Pos, 0, MAX_OPERATING_DISTANCE ); + } + // we should handle 'Protect' here rather than just unblocking + Sender->ReleaseCurrentAction(); +} + +//keeps following the object in formation +void GameScript::FollowObjectFormation(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + Actor *actor = (Actor *)tar; + scr->LastFollowed = actor->GetGlobalID(); + ieDword formation = parameters->int0Parameter; + ieDword pos = parameters->int1Parameter; + scr->FollowOffset = gc->GetFormationOffset(formation, pos); + if (!scr->InMove() || scr->Destination != tar->Pos) { + scr->WalkTo( tar->Pos, 0, 1 ); + } + Sender->ReleaseCurrentAction(); +} + +//walks to a specific offset of target (quite like movetoobject) +void GameScript::Formation(Scriptable* Sender, Action* parameters) +{ + GameControl *gc = core->GetGameControl(); + if (!gc) { + Sender->ReleaseCurrentAction(); + return; + } + if (Sender->Type!=ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* tar = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); + return; + } + Actor *scr = (Actor *)Sender; + ieDword formation = parameters->int0Parameter; + ieDword pos = parameters->int1Parameter; + Point FollowOffset = gc->GetFormationOffset(formation, pos); + FollowOffset.x+=tar->Pos.x; + FollowOffset.y+=tar->Pos.y; + if (!scr->InMove() || scr->Destination != FollowOffset) { + scr->WalkTo( FollowOffset, 0, 1 ); + } +} + +void GameScript::TransformItem(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + TransformItemCore((Actor *)tar, parameters, true); +} + +void GameScript::TransformPartyItem(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + TransformItemCore(tar, parameters, true); + } +} + +void GameScript::TransformItemAll(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + TransformItemCore((Actor *)tar, parameters, false); +} + +void GameScript::TransformPartyItemAll(Scriptable* /*Sender*/, Action* parameters) +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Actor *tar = game->GetPC(i, false); + TransformItemCore(tar, parameters, false); + } +} + +void GameScript::GeneratePartyMember(Scriptable* /*Sender*/, Action* parameters) +{ + AutoTable pcs("bios"); + if (!pcs) { + return; + } + const char* string = pcs->QueryField( parameters->int0Parameter, 0 ); + int pos = gamedata->LoadCreature(string,0,false); + if (pos<0) { + return; + } + Actor *actor = core->GetGame()->GetNPC(pos); + if (!actor) { + return; + } + actor->SetOrientation(parameters->int1Parameter, false); + actor->MoveTo(parameters->pointParameter); +} + +void GameScript::EnableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar|=FOG_DRAWFOG; +} + +void GameScript::DisableFogDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar&=~FOG_DRAWFOG; +} + +void DeleteAllSpriteCovers() +{ + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while (i--) { + Selectable *tar = (Selectable *) game->GetPC(i, false); + tar->SetSpriteCover(NULL); + } +} + +void GameScript::EnableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar&=~FOG_DITHERSPRITES; + DeleteAllSpriteCovers(); +} + +void GameScript::DisableSpriteDither(Scriptable* /*Sender*/, Action* /*parameters*/) +{ + core->FogOfWar|=~FOG_DITHERSPRITES; + DeleteAllSpriteCovers(); +} + +//the PST crew apparently loved hardcoding stuff +ieResRef RebusResRef={"DABUS1"}; + +void GameScript::FloatRebus(Scriptable* Sender, Action* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + Actor *actor = (Actor *)tar; + RebusResRef[5]=(char) core->Roll(1,5,'0'); + ScriptedAnimation *vvc = gamedata->GetScriptedAnimation(RebusResRef, 0); + if (vvc) { + //setting the height + vvc->ZPos=actor->size*20; + vvc->PlayOnce(); + //maybe this needs setting up some time + vvc->SetDefaultDuration(20); + actor->AddVVCell(vvc); + } +} + +void GameScript::IncrementKillStat(Scriptable* Sender, Action* parameters) +{ + DataFileMgr * ini = core->GetBeastsINI(); + if (!ini) { + return; + } + char key[5]; + sprintf(key,"%d", parameters->int0Parameter); + const char *variable = ini->GetKeyAsString( key, "killvar", NULL ); + if (!variable) { + return; + } + ieDword value = CheckVariable( Sender, variable, "GLOBAL" ) + 1; + SetVariable( Sender, variable, "GLOBAL", value ); +} + +void GameScript::SpellCastEffect(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + //TODO: finish this +} + +//this action plays a vvc animation over target +//we simply apply the appropriate opcode on the target (see iwdopcodes) +//the list of vvcs is in iwdshtab.2da +EffectRef fx_iwd_visual_spell_hit_ref={"IWDVisualSpellHit",NULL,-1}; + +void GameScript::SpellHitEffectSprite(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objects[2] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref); + Effect *fx = core->GetEffect(opcode); + if (!fx) { + //invalid effect name didn't resolve to opcode + return; + } + + //vvc type + fx->Parameter2 = parameters->int0Parameter; + //height (not sure if this is in the opcode, but seems acceptable) + fx->Parameter1 = parameters->int1Parameter; + fx->Probability1=100; + fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES; + core->ApplyEffect(fx, (Actor *) tar, src); +} + +void GameScript::SpellHitEffectPoint(Scriptable* Sender, Action* parameters) +{ + Scriptable* src = GetActorFromObject( Sender, parameters->objects[1] ); + if (!src) { + return; + } + + int opcode = EffectQueue::ResolveEffect(fx_iwd_visual_spell_hit_ref); + Effect *fx = core->GetEffect(opcode); + if (!fx) { + //invalid effect name didn't resolve to opcode + return; + } + + //vvc type + fx->Parameter2 = parameters->int0Parameter; + //height (not sure if this is in the opcode, but seems acceptable) + fx->Parameter1 = parameters->int1Parameter; + fx->Probability1=100; + fx->TimingMode=FX_DURATION_INSTANT_PERMANENT_AFTER_BONUSES; + fx->PosX=parameters->pointParameter.x; + fx->PosY=parameters->pointParameter.y; + core->ApplyEffect(fx, NULL, src); +} + + +void GameScript::ClickLButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_ACTION, parameters->int0Parameter); +} + +void GameScript::ClickLButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_ACTION, parameters->int0Parameter); +} + +void GameScript::ClickRButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_MENU, parameters->int0Parameter); +} + +void GameScript::ClickRButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_MENU, parameters->int0Parameter); +} + +void GameScript::DoubleClickLButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_ACTION|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickLButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_ACTION|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickRButtonObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar) { + Sender->ReleaseCurrentAction(); // this is blocking for some reason? + return; + } + ClickCore(Sender, tar->Pos, GEM_MB_MENU|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +void GameScript::DoubleClickRButtonPoint(Scriptable* Sender, Action* parameters) +{ + ClickCore(Sender, parameters->pointParameter, GEM_MB_MENU|GEM_MB_DOUBLECLICK, parameters->int0Parameter); +} + +//Picks 5 lines from wish.2da +//Gets the 5 values (column is int0parameter) from the table. +//Sets the five wishpowerNN to 1, while resets the rest to 0. +//TODO: investigate what happens with * values +void GameScript::SetupWish(Scriptable* Sender, Action* parameters) +{ + SetupWishCore(Sender, parameters->int0Parameter, parameters->int1Parameter); +} + +//The same as the previous action, except that the column parameter comes from +//the target object's wisdom directly (this action is not used in the original) +void GameScript::SetupWishObject(Scriptable* Sender, Action* parameters) +{ + Scriptable *tar = GetActorFromObject(Sender, parameters->objects[1] ); + if (!tar || tar->Type!=ST_ACTOR) { + return; + } + SetupWishCore(Sender, ((Actor *)tar)->GetStat(IE_WIS), parameters->int0Parameter); +} + +//GemRB specific action +//Sets up multiple tokens randomly (one per 2da row) +//the row label column sets the token names +void GameScript::SetToken2DA(Scriptable* /*Sender*/, Action* parameters) +{ + int count; + int i,j; + ieVariable tokenname; + + AutoTable tm(parameters->string0Parameter); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find %s.2da.\n", parameters->string0Parameter); + return; + } + + count = tm->GetRowCount(); + for(i=0;iRoll(1,tm->GetColumnCount(i),-1); + strnuprcpy(tokenname, tm->GetRowName(i), 32); + core->GetTokenDictionary()->SetAtCopy( tokenname, tm->QueryField(i, j) ); + } +} + +//this is a gemrb extension for scriptable tracks +void GameScript::SetTrackString(Scriptable* Sender, Action* parameters) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) return; + map->SetTrackString(parameters->int0Parameter, parameters->int1Parameter, parameters->int2Parameter); +} + +void GameScript::StateOverrideFlag(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->StateOverrideFlag = parameters->int0Parameter; +} + +void GameScript::StateOverrideTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->StateOverrideTime = parameters->int0Parameter; +} + +void GameScript::BanterBlockFlag(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->BanterBlockFlag = parameters->int0Parameter; +} + +void GameScript::BanterBlockTime(Scriptable* /*Sender*/, Action* parameters) +{ + core->GetGame()->BanterBlockTime = parameters->int0Parameter; +} + +void GameScript::SetNamelessDeath(Scriptable* Sender, Action* parameters) +{ + ieResRef area; + + snprintf(area,8,"AR%04d", parameters->int0Parameter); + IniSpawn *sp = Sender->GetCurrentArea()->INISpawn; + if (!sp) { + return; + } + sp->SetNamelessDeath(area, parameters->pointParameter, parameters->int1Parameter); +} diff --git a/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp b/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp new file mode 100644 index 000000000..f283c2d62 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GSUtils.cpp @@ -0,0 +1,2332 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * 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 "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "strrefs.h" +#include "defsounds.h" + +#include "Audio.h" +#include "DialogHandler.h" +#include "DisplayMessage.h" +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Item.h" +#include "Map.h" +#include "Spell.h" +#include "StringMgr.h" +#include "TileMap.h" +#include "Video.h" +#include "GUI/GameControl.h" + +#include + +//these tables will get freed by Core +Holder triggersTable; +Holder actionsTable; +Holder overrideActionsTable; +Holder objectsTable; +TriggerFunction triggers[MAX_TRIGGERS]; +ActionFunction actions[MAX_ACTIONS]; +short actionflags[MAX_ACTIONS]; +short triggerflags[MAX_TRIGGERS]; +ObjectFunction objects[MAX_OBJECTS]; +IDSFunction idtargets[MAX_OBJECT_FIELDS]; +Cache SrcCache; //cache for string resources (pst) +Cache BcsCache; //cache for scripts +int ObjectIDSCount = 7; +int MaxObjectNesting = 5; +bool HasAdditionalRect = false; +bool HasTriggerPoint = false; +//released by ReleaseMemory +ieResRef *ObjectIDSTableNames; +int ObjectFieldsCount = 7; +int ExtraParametersCount = 0; +int InDebug = 0; +int happiness[3][20]; +int RandomNumValue; +int *SkillStats=NULL; +int SkillCount=-1; +// reaction modifiers (by reputation and charisma) +int rmodrep[20]; +int rmodchr[25]; +Gem_Polygon **polygons; + +void InitScriptTables() +{ + //initializing the skill->stats conversion table + { + AutoTable tab("skillsta"); + if (tab) { + int rowcount = tab->GetRowCount(); + SkillCount = rowcount; + if (rowcount) { + SkillStats = (int *) malloc(rowcount * sizeof(int) ); + while(rowcount--) { + SkillStats[rowcount]=strtol(tab->QueryField(rowcount,0), NULL, 0); + } + } + } + } + //initializing the happiness table + { + AutoTable tab("happy"); + if (tab) { + for (int alignment=0;alignment<3;alignment++) { + for (int reputation=0;reputation<20;reputation++) { + happiness[alignment][reputation]=strtol(tab->QueryField(reputation,alignment), NULL, 0); + } + } + } + } + + //initializing the reaction mod. reputation table + AutoTable rmr("rmodrep"); + if (rmr) { + for (int reputation=0; reputation<20; reputation++) { + rmodrep[reputation] = strtol(rmr->QueryField(0, reputation), NULL, 0); + } + } + + //initializing the reaction mod. charisma table + AutoTable rmc("rmodchr"); + if (rmc) { + for (int charisma=0; charisma<25; charisma++) { + rmodchr[charisma] = strtol(rmc->QueryField(0, charisma), NULL, 0); + } + } +} + +int GetReaction(Actor *target, Scriptable *Sender) +{ + int chr, rep, reaction; + chr = target->GetStat(IE_CHR)-1; + if (target->GetStat(IE_EA) == EA_PC) { + rep = core->GetGame()->Reputation/10; + } else { + rep = target->GetStat(IE_REPUTATION); + } + reaction = 10 + rmodrep[rep] + rmodchr[chr]; + + // add -4 penalty when dealing with racial enemies + if (Sender && target->GetRangerLevel() && Sender->Type == ST_ACTOR && target->IsRacialEnemy((Actor *)Sender)) { + reaction -= 4; + } + + return reaction; +} + +int GetHappiness(Scriptable* Sender, int reputation) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor* ab = ( Actor* ) Sender; + int alignment = ab->GetStat(IE_ALIGNMENT)&AL_GE_MASK; //good / evil + if (reputation > 200) { + reputation = 200; + } + return happiness[alignment][reputation/10-1]; +} + +int GetHPPercent(Scriptable* Sender) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor* ab = ( Actor* ) Sender; + int hp1 = ab->GetStat(IE_MAXHITPOINTS); + if (hp1<1) { + return 0; + } + int hp2 = ab->GetBase(IE_HITPOINTS); + if (hp2<1) { + return 0; + } + return hp2*100/hp1; +} + +void HandleBitMod(ieDword &value1, ieDword value2, int opcode) +{ + switch(opcode) { + case BM_AND: + value1 = ( value1& value2 ); + break; + case BM_OR: + value1 = ( value1| value2 ); + break; + case BM_XOR: + value1 = ( value1^ value2 ); + break; + case BM_NAND: //this is a GemRB extension + value1 = ( value1& ~value2 ); + break; + case BM_SET: //this is a GemRB extension + value1 = value2; + break; + } +} + +// SPIT is not in the original engine spec, it is reserved for the +// enchantable items feature +// 0 1 2 3 4 +static const char *spell_suffices[]={"SPIT","SPPR","SPWI","SPIN","SPCL"}; + +//this function handles the polymorphism of Spell[RES] actions +//it returns spellres +bool ResolveSpellName(ieResRef spellres, Action *parameters) +{ + if (parameters->string0Parameter[0]) { + strnlwrcpy(spellres, parameters->string0Parameter, 8); + } else { + //resolve spell + int type = parameters->int0Parameter/1000; + int spellid = parameters->int0Parameter%1000; + if (type>4) { + return false; + } + sprintf(spellres, "%s%03d", spell_suffices[type], spellid); + } + return gamedata->Exists(spellres, IE_SPL_CLASS_ID); +} + +void ResolveSpellName(ieResRef spellres, ieDword number) +{ + //resolve spell + unsigned int type = number/1000; + int spellid = number%1000; + if (type>4) { + type=0; + } + sprintf(spellres, "%s%03d", spell_suffices[type], spellid); +} + +ieDword ResolveSpellNumber(const ieResRef spellres) +{ + int i; + + for(i=0;i<5;i++) { + if(!strnicmp(spellres, spell_suffices[i], 4)) { + int n = -1; + sscanf(spellres+4,"%d", &n); + if (n<0) { + return 0xffffffff; + } + return i*1000+n; + } + } + return 0xffffffff; +} + +bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot) +{ + CREItem *itm = act->inventory.GetSlotItem(Slot); + if(itm) { + strnlwrcpy(itemres, itm->ItemResRef, 8); + return gamedata->Exists(itemres, IE_ITM_CLASS_ID); + } + return false; +} + +bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname) +{ + bool had_nostore=false; + bool has_current=false; + ieResRef current; + ieDword owner = 0; + CREItem item; + + Store *store = core->GetCurrentStore(); + if (!store) { + had_nostore = true; + store = core->SetCurrentStore(storename, 0); + } else { + if (strnicmp(store->Name, storename, 8) ) { + //not the current store, we need some dirty hack + has_current = true; + strnlwrcpy(current, store->Name, 8); + owner = store->GetOwnerID(); + } + } + if (!store) { + printMessage("GameScript","Store cannot be opened!\n", LIGHT_RED); + return false; + } + + bool ret = false; + //don't use triggers (pst style), it would be possible to create infinite loops + if (store->FindItem(itemname, false) != (unsigned int)-1) { + ret=true; + } + if (has_current) { + //setting back old store (this will save our current store) + core->SetCurrentStore(current, owner); + } else if (had_nostore) { + core->CloseCurrentStore(); + } + return ret; +} + +//don't pass this point by reference, it is subject to change +void ClickCore(Scriptable *Sender, Point point, int type, int speed) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) { + Sender->ReleaseCurrentAction(); + return; + } + Point p=map->TMap->GetMapSize(); + if (!p.PointInside(point)) { + Sender->ReleaseCurrentAction(); + return; + } + Video *video = core->GetVideoDriver(); + GlobalTimer *timer = core->timer; + timer->SetMoveViewPort( point.x, point.y, speed, true ); + timer->DoStep(0); + if (timer->ViewportIsMoving()) { + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + return; + } + + video->ConvertToScreen(point.x, point.y); + GameControl *win = core->GetGameControl(); + + point.x+=win->XPos; + point.y+=win->YPos; + video->MoveMouse(point.x, point.y); + video->ClickMouse(type); + Sender->ReleaseCurrentAction(); +} + +void TransformItemCore(Actor *actor, Action *parameters, bool onlyone) +{ + int i = actor->inventory.GetSlotCount(); + while(i--) { + CREItem *item = actor->inventory.GetSlotItem(i); + if (!item) { + continue; + } + if (strnicmp(item->ItemResRef, parameters->string0Parameter, 8) ) { + continue; + } + actor->inventory.SetSlotItemRes(parameters->string1Parameter,i,parameters->int0Parameter,parameters->int1Parameter,parameters->int2Parameter); + if (onlyone) { + break; + } + } +} + +//check if an inventory (container or actor) has item (could be recursive ?) +bool HasItemCore(Inventory *inventory, const ieResRef itemname, ieDword flags) +{ + if (inventory->HasItem(itemname, flags)) { + return true; + } + int i=inventory->GetSlotCount(); + while (i--) { + //maybe we could speed this up if we mark bag items with a flags bit + CREItem *itemslot = inventory->GetSlotItem(i); + if (!itemslot) + continue; + Item *item = gamedata->GetItem(itemslot->ItemResRef); + if (!item) + continue; + bool ret = false; + if (core->CanUseItemType(SLOT_BAG,item,NULL) ) { + //the store is the same as the item's name + ret = StoreHasItemCore(itemslot->ItemResRef, itemname); + } + gamedata->FreeItem(item, itemslot->ItemResRef); + if (ret) { + return true; + } + } + return false; +} + +void DisplayStringCore(Scriptable* Sender, int Strref, int flags) +{ + StringBlock sb; + char Sound[_MAX_PATH]; + + //no one hears you when you are in the Limbo! + if (!Sender->GetCurrentArea()) { + return; + } + + memset(&sb,0,sizeof(sb)); + Sound[0]=0; + printf( "Displaying string on: %s\n", Sender->GetScriptName() ); + if (flags & DS_CONST) { + if (Sender->Type!=ST_ACTOR) { + printMessage("GameScript","Verbal constant not supported for non actors!\n", LIGHT_RED); + return; + } + Actor* actor = ( Actor* ) Sender; + if ((ieDword) Strref>=VCONST_COUNT) { + printMessage("GameScript","Invalid verbal constant!\n", LIGHT_RED); + return; + } + + int tmp=(int) actor->GetVerbalConstant(Strref); + if (tmp <= 0 || (actor->GetStat(IE_MC_FLAGS) & MC_EXPORTABLE)) { + //get soundset based string constant + actor->ResolveStringConstant( sb.Sound, (unsigned int) Strref); + if (actor->PCStats && actor->PCStats->SoundFolder[0]) { + snprintf(Sound, _MAX_PATH, "%s/%s", + actor->PCStats->SoundFolder, sb.Sound); + } else { + memcpy(Sound, sb.Sound, sizeof(ieResRef) ); + } + } + Strref = tmp; + + //display the verbal constants in the console + ieDword charactersubtitles = 0; + core->GetDictionary()->Lookup("Subtitles", charactersubtitles); + if (charactersubtitles) { + flags |= DS_CONSOLE; + } + } + + if ((Strref != -1) && !sb.Sound[0]) { + sb = core->strings->GetStringBlock( Strref ); + memcpy(Sound, sb.Sound, sizeof(ieResRef) ); + if (sb.text[0] && strcmp(sb.text," ") && (flags & DS_CONSOLE)) { + //can't play the sound here, we have to delay action + //and for that, we have to know how long the text takes + if(flags&DS_NONAME) { + displaymsg->DisplayString( sb.text ); + } else { + displaymsg->DisplayStringName( Strref, 0xf0f0f0, Sender, 0); + } + } + if (sb.text[0] && strcmp(sb.text," ") && (flags & (DS_HEAD | DS_AREA))) { + Sender->DisplayHeadText( sb.text ); + //don't free sb.text, it is residing in Sender + if (flags & DS_AREA) { + Sender->FixHeadTextPos(); + } + } else { + core->FreeString( sb.text ); + } + } + if (Sound[0] && !(flags&DS_SILENT) ) { + ieDword speech = GEM_SND_RELATIVE; //disable position + if (flags&DS_SPEECH) speech|=GEM_SND_SPEECH; + unsigned int len = 0; + core->GetAudioDrv()->Play( Sound,0,0,speech,&len ); + ieDword counter = ( AI_UPDATE_TIME * len ) / 1000; + if ((counter != 0) && (flags &DS_WAIT) ) + Sender->SetWait( counter ); + } +} + +int CanSee(Scriptable* Sender, Scriptable* target, bool range, int seeflag) +{ + Map *map; + + if (target->Type==ST_ACTOR) { + Actor *tar = (Actor *) target; + + if (!tar->ValidTarget(seeflag)) { + return 0; + } + } + + map = target->GetCurrentArea(); + //if (!(seeflag&GA_GLOBAL)) { + if (!map || map!=Sender->GetCurrentArea() ) { + return 0; + } + //} + + if (range) { + unsigned int dist; + + if (Sender->Type == ST_ACTOR) { + Actor* snd = ( Actor* ) Sender; + dist = snd->Modified[IE_VISUALRANGE]; + } else { + dist = 30; + } + + if (Distance(target->Pos, Sender->Pos) > dist * 15) { + return 0; + } + } + + return map->IsVisible(target->Pos, Sender->Pos); +} + +//non actors can see too (reducing function to LOS) +//non actors can be seen too (reducing function to LOS) +int SeeCore(Scriptable* Sender, Trigger* parameters, int justlos) +{ + //see dead + int flags; + + if (parameters->int0Parameter) { + flags = GA_DETECT; + } else { + flags = GA_NO_DEAD; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter, flags ); + /* don't set LastSeen if this isn't an actor */ + if (!tar) { + return 0; + } + //both are actors + if (CanSee(Sender, tar, true, flags) ) { + if (justlos) { + return 1; + } + if (Sender->Type==ST_ACTOR && tar->Type==ST_ACTOR) { + Actor* snd = ( Actor* ) Sender; + //additional checks for invisibility? + snd->LastSeen = tar->GetGlobalID(); + } + return 1; + } + return 0; +} + +//transfering item from Sender to target +//if target has no inventory, the item will be destructed +//if target can't get it, it will be dropped at its feet +int MoveItemCore(Scriptable *Sender, Scriptable *target, const char *resref, int flags, int setflag) +{ + Inventory *myinv; + Map *map; + // track whether we are dealing with our party and need to display feedback + bool lostitem = false; + bool gotitem = false; + + if (!target) { + return MIC_INVALID; + } + map=Sender->GetCurrentArea(); + switch(Sender->Type) { + case ST_ACTOR: + myinv=&((Actor *) Sender)->inventory; + if (((Actor *)Sender)->InParty) lostitem = true; + break; + case ST_CONTAINER: + myinv=&((Container *) Sender)->inventory; + break; + default: + return MIC_INVALID; + } + CREItem *item; + myinv->RemoveItem(resref, flags, &item); + if (!item) { + // nothing was removed + return MIC_NOITEM; + } + + item->Flags|=setflag; + + switch(target->Type) { + case ST_ACTOR: + myinv=&((Actor *) target)->inventory; + if (((Actor *) target)->InParty) gotitem = true; + break; + case ST_CONTAINER: + myinv=&((Container *) target)->inventory; + break; + default: + myinv = NULL; + break; + } + if (!myinv) { + delete item; + if (lostitem) displaymsg->DisplayConstantString(STR_LOSTITEM, 0xbcefbc); + return MIC_GOTITEM; // actually it was lost, not gained + } + if ( myinv->AddSlotItem(item, SLOT_ONLYINVENTORY) !=ASI_SUCCESS) { + // drop it at my feet + map->AddItemToLocation(target->Pos, item); + if (gotitem) displaymsg->DisplayConstantString(STR_INVFULL_ITEMDROP, 0xbcefbc); + return MIC_FULL; + } + if (gotitem) displaymsg->DisplayConstantString(STR_GOTITEM, 0xbcefbc); + return MIC_GOTITEM; +} + +/*FIXME: what is 'base'*/ +void PolymorphCopyCore(Actor *src, Actor *tar, bool base) +{ + tar->SetBase(IE_ANIMATION_ID, src->GetStat(IE_ANIMATION_ID) ); + if (!base) { + tar->SetBase(IE_ARMOR_TYPE, src->GetStat(IE_ARMOR_TYPE) ); + for (int i=0;i<7;i++) { + tar->SetBase(IE_COLORS+i, src->GetStat(IE_COLORS+i) ); + } + } + tar->SetName(src->GetName(0),0); + tar->SetName(src->GetName(1),1); + //add more attribute copying +} + +void CreateCreatureCore(Scriptable* Sender, Action* parameters, int flags) +{ + Scriptable *tmp = GetActorFromObject( Sender, parameters->objects[1] ); + //if there is nothing to copy, don't spawn anything + if (flags & CC_COPY) { + if (!tmp || tmp->Type != ST_ACTOR) { + return; + } + } + + Actor* ab; + if (flags & CC_STRING1) { + ab = gamedata->GetCreature(parameters->string1Parameter); + } + else { + ab = gamedata->GetCreature(parameters->string0Parameter); + } + + if (!ab) { + printMessage("GameScript","Failed to create creature! ",LIGHT_RED); + printf("(missing creature file %s?)\n", parameters->string0Parameter); + // maybe this should abort()? + return; + } + + //iwd2 allows an optional scriptname to be set + //but bg2 doesn't have this feature + //this way it works for both games + if ((flags & CC_SCRIPTNAME) && parameters->string1Parameter[0]) { + ab->SetScriptName(parameters->string1Parameter); + } + + int radius; + Point pnt; + + radius=0; + switch (flags & CC_MASK) { + //creates creature just off the screen + case CC_OFFSCREEN: + { + Region vp = core->GetVideoDriver()->GetViewport(); + radius=vp.w/2; //actually it must be further divided by the tile size, hmm 16? + } + //falling through + case CC_OBJECT://use object + offset + if (tmp) Sender=tmp; + //falling through + case CC_OFFSET://use sender + offset + pnt.x = parameters->pointParameter.x+Sender->Pos.x; + pnt.y = parameters->pointParameter.y+Sender->Pos.y; + break; + default: //absolute point, but -1,-1 means AtFeet + pnt.x = parameters->pointParameter.x; + pnt.y = parameters->pointParameter.y; + if (pnt.isempty()) { + pnt.x = Sender->Pos.x; + pnt.y = Sender->Pos.y; + } + break; + } + + Map *map = Sender->GetCurrentArea(); + map->AddActor( ab ); + ab->SetPosition( pnt, flags&CC_CHECK_IMPASSABLE, radius ); + ab->SetOrientation(parameters->int0Parameter, false ); + + //if string1 is animation, then we can't use it for a DV too + if (flags & CC_PLAY_ANIM) { + CreateVisualEffectCore( ab, ab->Pos, parameters->string1Parameter, 1); + } else { + //setting the deathvariable if it exists (iwd2) + if (parameters->string1Parameter[0]) { + ab->SetScriptName(parameters->string1Parameter); + } + } + + if (flags & CC_COPY) { + PolymorphCopyCore ( (Actor *) tmp, ab, false); + } +} + +static ScriptedAnimation *GetVVCEffect(const char *effect, int iterations) +{ + if (effect[0]) { + ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(effect, false); + if (!vvc) { + printMessage("GameScript","Failed to create effect.",LIGHT_RED); + return NULL; + } + if (iterations) { + vvc->SetDefaultDuration( vvc->GetSequenceDuration(AI_UPDATE_TIME * iterations)); + } else { + vvc->PlayOnce(); + } + return vvc; + } + return NULL; +} + +void CreateVisualEffectCore(Actor *target, const char *effect, int iterations) +{ + ScriptedAnimation *vvc = GetVVCEffect(effect, iterations); + if (vvc) { + target->AddVVCell( vvc ); + } +} + +void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations) +{ + ScriptedAnimation *vvc = GetVVCEffect(effect, iterations); + if (vvc) { + vvc->XPos +=position.x; + vvc->YPos +=position.y; + Sender->GetCurrentArea( )->AddVVCell( vvc ); + } +} + +//this destroys the current actor and replaces it with another +void ChangeAnimationCore(Actor *src, const char *resref, bool effect) +{ + Actor *tar = gamedata->GetCreature(resref); + if (tar) { + Map *map = src->GetCurrentArea(); + map->AddActor( tar ); + Point pos = src->Pos; + tar->SetOrientation(src->GetOrientation(), false ); + src->DestroySelf(); + // can't SetPosition while the old actor is taking the spot + tar->SetPosition(pos, 1); + if (effect) { + CreateVisualEffectCore(tar, tar->Pos,"smokepuffeffect",1); + } + } +} + +void EscapeAreaCore(Scriptable* Sender, const Point &p, const char* area, const Point &enter, int flags, int wait) +{ + char Tmp[256]; + + if ( !p.isempty() && PersonalDistance(p, Sender)>MAX_OPERATING_DISTANCE) { + //MoveNearerTo will return 0, if the actor is in move + //it will return 1 (the fourth parameter) if the target is unreachable + if (!MoveNearerTo(Sender, p, MAX_OPERATING_DISTANCE,1) ) { + if (!Sender->InMove()) printf("At least it said so...\n"); + return; + } + } + + if (flags &EA_DESTROY) { + //this must be put into a non-const variable + sprintf( Tmp, "DestroySelf()" ); + } else { + // last parameter is 'face', which should be passed from relevant action parameter.. + sprintf( Tmp, "MoveBetweenAreas(\"%s\",[%hd.%hd],%d)", area, enter.x, enter.y, 0 ); + } + printMessage("GSUtils"," ", WHITE); + printf("Executing %s in EscapeAreaCore\n", Tmp); + //drop this action, but add another (destroyself or movebetweenareas) + //between the arrival and the final escape, there should be a wait time + //that wait time could be handled here + if (wait) { + printf("But wait a bit... (%d)\n", wait); + Sender->SetWait(wait); + } + Sender->ReleaseCurrentAction(); + Action * action = GenerateAction( Tmp); + Sender->AddActionInFront( action ); +} + +void GetTalkPositionFromScriptable(Scriptable* scr, Point &position) +{ + switch (scr->Type) { + case ST_AREA: case ST_GLOBAL: + position = scr->Pos; //fake + break; + case ST_ACTOR: + //if there are other moveables, put them here + position = ((Movable *) scr)->GetMostLikelyPosition(); + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) { + position=((InfoPoint *) scr)->UsePoint; + break; + } + position=((InfoPoint *) scr)->TrapLaunch; + break; + case ST_DOOR: case ST_CONTAINER: + position=((Highlightable *) scr)->TrapLaunch; + break; + } +} + +void GetPositionFromScriptable(Scriptable* scr, Point &position, bool dest) +{ + if (!dest) { + position = scr->Pos; + return; + } + switch (scr->Type) { + case ST_AREA: case ST_GLOBAL: + position = scr->Pos; //fake + break; + case ST_ACTOR: + //if there are other moveables, put them here + position = ((Movable *) scr)->GetMostLikelyPosition(); + break; + case ST_TRIGGER: case ST_PROXIMITY: case ST_TRAVEL: + if (((InfoPoint *) scr)->Flags & TRAP_USEPOINT) { + position=((InfoPoint *) scr)->UsePoint; + break; + } + case ST_DOOR: case ST_CONTAINER: + position=((Highlightable *) scr)->TrapLaunch; + } +} + +int CheckInteract(const char *talker, const char *target) +{ + AutoTable interact("interact"); + if(!interact) + return 0; + const char *value = interact->QueryField(talker, target); + if(!value) + return 0; + switch(value[0]) { + case 's': + return I_SPECIAL; + case 'c': + return I_COMPLIMENT; + case 'i': + return I_INSULT; + } + return 0; +} + +static ieResRef PlayerDialogRes = "PLAYERx\0"; + +void BeginDialog(Scriptable* Sender, Action* parameters, int Flags) +{ + Scriptable* tar, *scr; + int seeflag = GA_NO_DEAD; + + if (InDebug&ID_VARIABLES) { + printf("BeginDialog core\n"); + } + if (Flags & BD_OWN) { + tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag); + scr = tar; + } else { + tar = GetStoredActorFromObject( Sender, parameters->objects[1], seeflag); + scr = Sender; + } + if (!scr) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Speaker for dialog couldn't be found (Sender: %s, Type: %d) Flags:%d.\n", Sender->GetScriptName(), Sender->Type, Flags); + Sender->ReleaseCurrentAction(); + return; + } + + if (!tar || tar->Type!=ST_ACTOR) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Target for dialog couldn't be found (Sender: %s, Type: %d).\n", Sender->GetScriptName(), Sender->Type); + if (Sender->Type == ST_ACTOR) { + ((Actor *) Sender)->DebugDump(); + } + printf ("Target object: "); + if (parameters->objects[1]) { + parameters->objects[1]->Dump(); + } else { + printf("\n"); + } + Sender->ReleaseCurrentAction(); + return; + } + + Actor *speaker, *target; + + speaker = NULL; + target = (Actor *) tar; + if ((Flags & BD_CHECKDIST) && !CanSee(scr, target, false, seeflag) ) { + printMessage("GameScript"," ",LIGHT_RED); + printf("CanSee returned false! Speaker (%s, type %d) and target are:\n", scr->GetScriptName(), scr->Type); + if (scr->Type == ST_ACTOR) { + ((Actor *) scr)->DebugDump(); + } + ((Actor *) tar)->DebugDump(); + Sender->ReleaseCurrentAction(); + return; + } + bool swap = false; + if (scr->Type==ST_ACTOR) { + speaker = (Actor *) scr; + if (speaker->GetStat(IE_STATE_ID)&STATE_DEAD) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Speaker is dead, cannot start dialogue. Speaker and target are:\n"); + speaker->DebugDump(); + target->DebugDump(); + Sender->ReleaseCurrentAction(); + return; + } + //DialogueRange is set in IWD + ieDword range = MAX_OPERATING_DISTANCE + speaker->GetBase(IE_DIALOGRANGE); + //making sure speaker is the protagonist, player, actor + if ( target->InParty == 1) swap = true; + else if ( speaker->InParty !=1 && target->InParty) swap = true; + //CHECKDIST works only for mobile scriptables + if (Flags&BD_CHECKDIST) { + if ( scr->GetCurrentArea()!=target->GetCurrentArea() || + PersonalDistance(scr, target)>range) { + MoveNearerTo(Sender, target, MAX_OPERATING_DISTANCE); + return; + } + } + } else { + //pst style dialog with trigger points + swap=true; + if (Flags&BD_CHECKDIST) { + Point TalkPos; + + if (target->InMove()) { + //waiting for target + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->ReleaseCurrentAction(); + Sender->SetWait(1); + return; + } + GetTalkPositionFromScriptable(scr, TalkPos); + if (PersonalDistance(TalkPos, target)>MAX_OPERATING_DISTANCE ) { + //try to force the target to come closer??? + GoNear(target, TalkPos); + Sender->AddActionInFront( Sender->GetCurrentAction() ); + Sender->ReleaseCurrentAction(); + Sender->SetWait(1); + return; + } + } + } + + GameControl* gc = core->GetGameControl(); + if (!gc) { + printMessage( "GameScript","Dialog cannot be initiated because there is no GameControl.", YELLOW ); + Sender->ReleaseCurrentAction(); + return; + } + //can't initiate dialog, because it is already there + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + if (Flags & BD_INTERRUPT) { + //break the current dialog if possible + gc->dialoghandler->EndDialog(true); + } + //check if we could manage to break it, not all dialogs are breakable! + if (gc->GetDialogueFlags()&DF_IN_DIALOG) { + printMessage( "GameScript","Dialog cannot be initiated because there is already one.", YELLOW ); + Sender->ReleaseCurrentAction(); + return; + } + } + + // starting a dialog ends cutscenes! + core->SetCutSceneMode(false); + + const char* Dialog = NULL; + AutoTable pdtable; + + switch (Flags & BD_LOCMASK) { + case BD_STRING0: + Dialog = parameters->string0Parameter; + if (Flags & BD_SETDIALOG) { + scr->SetDialog( Dialog ); + } + break; + case BD_SOURCE: + case BD_TARGET: + if (swap) Dialog = scr->GetDialog(); + else Dialog = target->GetDialog(GD_FEEDBACK); + break; + case BD_RESERVED: + //what if playerdialog was initiated from Player2? + PlayerDialogRes[5] = '1'; + Dialog = ( const char * ) PlayerDialogRes; + break; + case BD_INTERACT: //using the source for the dialog + const char* scriptingname = scr->GetScriptName(); + + /* use interact.2da for short, inlined dialogue */ + int type = CheckInteract(scriptingname, target->GetScriptName()); + if(type) { + //TODO increase interact counter in scr + speaker->Interact(type); + target->Response(type); + Sender->ReleaseCurrentAction(); + return; + } + /* banter dialogue */ + pdtable.load("interdia"); + //Dialog is a borrowed reference, we cannot free pdtable while it is being used + if (pdtable) { + Dialog = pdtable->QueryField( scriptingname, "FILE" ); + } + break; + } + + + //dialog is not meaningful + if (!Dialog || Dialog[0]=='*') { + Sender->ReleaseCurrentAction(); + return; + } + + //maybe we should remove the action queue, but i'm unsure + //no, we shouldn't even call this! + //Sender->ReleaseCurrentAction(); + + // moved this here from InitDialog, because InitDialog doesn't know which side is which + // post-swap (and non-actors always have IF_NOINT set) .. also added a check that it's + // actually busy doing something, for the same reason + if (target->GetInternalFlag()&IF_NOINT && (target->GetCurrentAction() || target->GetNextAction())) { + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + Sender->ReleaseCurrentAction(); + return; + } + + if (speaker!=target) { + if (swap) { + Scriptable *tmp = tar; + tar = scr; + scr = tmp; + } else { + if (!(Flags & BD_INTERRUPT)) { + // added CurrentAction as part of blocking action fixes + if (tar->GetCurrentAction() || tar->GetNextAction()) { + displaymsg->DisplayConstantString(STR_TARGETBUSY,0xff0000); + Sender->ReleaseCurrentAction(); + return; + } + } + } + } + + //don't clear target's actions, because a sequence like this will be broken: + //StartDialog([PC]); SetGlobal("Talked","LOCALS",1); + if (scr!=tar) { + if (scr->Type==ST_ACTOR) { + ((Actor *) scr)->SetOrientation(GetOrient( tar->Pos, scr->Pos), true); + } + if (tar->Type==ST_ACTOR) { + ((Actor *) tar)->SetOrientation(GetOrient( scr->Pos, tar->Pos), true); + } + } + + int ret; + + if (Dialog[0]) { + //increasing NumTimesTalkedTo or NumTimesInteracted + if (Flags & BD_TALKCOUNT) { + gc->SetDialogueFlags(DF_TALKCOUNT, BM_OR); + } else if ((Flags & BD_LOCMASK) == BD_INTERACT) { + gc->SetDialogueFlags(DF_INTERACT, BM_OR); + } + + core->GetDictionary()->SetAt("DialogChoose",(ieDword) -1); + ret = gc->dialoghandler->InitDialog( scr, tar, Dialog); + } + else { + ret = -1; + } + + if (ret<0) { + Sender->ReleaseCurrentAction(); + if (Flags & BD_NOEMPTY) { + return; + } + displaymsg->DisplayConstantStringName(STR_NOTHINGTOSAY,0xff0000,tar); + return; + } + + //this is a bit fishy + Sender->SetWait(1); + Sender->ReleaseCurrentAction(); + +} + +void MoveBetweenAreasCore(Actor* actor, const char *area, const Point &position, int face, bool adjust) +{ + printMessage("GameScript", " ", WHITE); + printf("MoveBetweenAreas: %s to %s [%d.%d] face: %d\n", actor->GetName(0), area,position.x,position.y, face); + Map* map2; + Game* game = core->GetGame(); + if (area[0]) { //do we need to switch area? + Map* map1 = actor->GetCurrentArea(); + //we have to change the pathfinder + //to the target area if adjust==true + map2 = game->GetMap(area, false); + if ( map1!=map2 ) { + if (map1) { + map1->RemoveActor( actor ); + } + map2->AddActor( actor ); + } + } + actor->SetPosition(position, adjust); + if (face !=-1) { + actor->SetOrientation( face, false ); + } + // should this perhaps be a 'selected' check or similar instead? + if (actor->InParty) { + GameControl *gc=core->GetGameControl(); + gc->SetScreenFlags(SF_CENTERONACTOR,BM_OR); + game->ChangeSong(false, true); + } +} + +//repeat movement, until goal isn't reached +//if int0parameter is !=0, then it will try only x times +void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, bool untilsee) +{ + if (Sender->Type != ST_ACTOR) { + Sender->ReleaseCurrentAction(); + return; + } + Scriptable* target = GetStoredActorFromObject( Sender, parameters->objects[1] ); + if (!target) { + Sender->ReleaseCurrentAction(); + return; + } + Actor* actor = ( Actor* ) Sender; + if (untilsee && CanSee(actor, target, true, 0) ) { + Sender->ReleaseCurrentAction(); + return; + } else { + if (PersonalDistance(actor, target)ReleaseCurrentAction(); + return; + } + } + if (!actor->InMove() || actor->Destination != target->Pos) { + actor->WalkTo( target->Pos, flags, 0 ); + } + //hopefully this hack will prevent lockups + if (!actor->InMove()) { + Sender->ReleaseCurrentAction(); + return; + } + + //repeat movement... + Action *newaction = ParamCopyNoOverride(parameters); + if (newaction->int0Parameter!=1) { + if (newaction->int0Parameter) { + newaction->int0Parameter--; + } + actor->AddActionInFront(newaction); + actor->SetWait(1); + } + + Sender->ReleaseCurrentAction(); +} + +void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c) +{ + strncpy(item->ItemResRef, resref, 8); + core->ResolveRandomItem(item); + if (a==-1) { + Item *origitem = gamedata->GetItem(resref); + for(int i=0;i<3;i++) { + ITMExtHeader *e = origitem->GetExtHeader(i); + item->Usages[i]=e?e->Charges:0; + } + gamedata->FreeItem(origitem, resref, false); + } else { + item->Usages[0]=(ieWord) a; + item->Usages[1]=(ieWord) b; + item->Usages[2]=(ieWord) c; + } + item->Flags=0; +} + +//It is possible to attack CONTAINERS/DOORS as well!!! +void AttackCore(Scriptable *Sender, Scriptable *target, int flags) +{ + //this is a dangerous cast, make sure actor is Actor * !!! + Actor *actor = (Actor *) Sender; + + WeaponInfo wi; + ITMExtHeader *header = NULL; + ITMExtHeader *hittingheader = NULL; + int tohit; + ieDword Flags; + int DamageBonus, CriticalBonus; + int speed, style; + + //bool leftorright = (bool) ((attacksperround-attackcount)&1); + bool leftorright = false; + + //will return false on any errors (eg, unusable weapon) + if (!actor->GetCombatDetails(tohit, leftorright, wi, header, hittingheader, Flags, DamageBonus, speed, CriticalBonus, style)) { + Sender->ReleaseCurrentAction(); + return; + } + + if (header) wi.range *= 10; + else wi.range = 0; + + if ( target->Type == ST_DOOR || target->Type == ST_CONTAINER) { + wi.range += 10; + } + Actor *tar = NULL; + ieDword targetID = 0; + if (target->Type==ST_ACTOR) { + tar = (Actor *) target; + targetID = tar->GetGlobalID(); + } + if (actor == tar) { + Sender->ReleaseCurrentAction(); + return; + } + if (!(flags&AC_NO_SOUND) ) { + if (actor->LastTarget != targetID) { + //play attack sound for party members + if (actor->InParty) { + //pick from all 5 possible verbal constants + actor->VerbalConstant(VB_ATTACK, 5); + //DisplayStringCore(Sender, VB_ATTACK, DS_CONSOLE|DS_CONST ); + } + //display attack message + displaymsg->DisplayConstantStringAction(STR_ACTION_ATTACK,0xf0f0f0, Sender, target); + } + } + //action performed + if(target->Type == ST_ACTOR) { + actor->SetTarget( target ); + } + if ( Sender->GetCurrentArea()!=target->GetCurrentArea() || + (PersonalDistance(Sender, target) > wi.range) ) { + MoveNearerTo(Sender, target, wi.range); + return; + } else if (target->Type == ST_DOOR) { + //Forcing a lock does not launch the trap... + Door* door = (Door*) target; + if(door->Flags & DOOR_LOCKED) { + door->TryBashLock(actor); + } + Sender->ReleaseCurrentAction(); + return; + } else if (target->Type == ST_CONTAINER) { + Container* cont = (Container*) target; + if(cont->Flags & CONT_LOCKED) { + cont->TryBashLock(actor); + } + Sender->ReleaseCurrentAction(); + return; + } + + actor->PerformAttack(core->GetGame()->GameTime); +} + +//we need this because some special characters like _ or * are also accepted +inline bool ismysymbol(const char letter) +{ + if (letter==']') return false; + if (letter=='[') return false; + if (letter==')') return false; + if (letter=='(') return false; + if (letter=='.') return false; + if (letter==',') return false; + return true; +} + +//this function returns a value, symbol could be a numeric string or +//a symbol from idsname +static int GetIdsValue(const char *&symbol, const char *idsname) +{ + int idsfile=core->LoadSymbol(idsname); + Holder valHook = core->GetSymbol(idsfile); + if (!valHook) { + //FIXME:missing ids file!!! + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Missing IDS file %s for symbol %s!\n",idsname, symbol); + } + return -1; + } + char *newsymbol; + int value=strtol(symbol, &newsymbol, 0); + if (symbol!=newsymbol) { + symbol=newsymbol; + return value; + } + char symbolname[64]; + int x; + for (x=0;ismysymbol(*symbol) && x<(int) sizeof(symbolname)-1;x++) { + symbolname[x]=*symbol; + symbol++; + } + symbolname[x]=0; + return valHook->GetValue(symbolname); +} + +static void ParseIdsTarget(const char *&src, Object *&object) +{ + for (int i=0;iobjectFields[i]=GetIdsValue(src, ObjectIDSTableNames[i]); + if (*src!='.') { + break; + } + src++; + } + src++; //skipping ] +} + +//this will skip to the next element in the prototype of an action/trigger +#define SKIP_ARGUMENT() while (*str && ( *str != ',' ) && ( *str != ')' )) str++ + +static void ParseObject(const char *&str,const char *&src, Object *&object) +{ + SKIP_ARGUMENT(); + object = new Object(); + switch (*src) { + case '"': + //Scriptable Name + src++; + int i; + for (i=0;i<(int) sizeof(object->objectName)-1 && *src && *src!='"';i++) + { + object->objectName[i] = *src; + src++; + } + object->objectName[i] = 0; + src++; + break; + case '[': + src++; //skipping [ + ParseIdsTarget(src, object); + break; + default: //nested object filters + int Nesting=0; + + while (NestingobjectFilters+1, object->objectFilters, (int) sizeof(int) *(MaxObjectNesting-1) ); + object->objectFilters[0]=GetIdsValue(src,"object"); + if (*src!='(') { + break; + } + src++; //skipping ( + if (*src==')') { + break; + } + Nesting++; + } + if (*src=='[') { + ParseIdsTarget(src, object); + } + src+=Nesting; //skipping ) + } +} + +/* this function was lifted from GenerateAction, to make it clearer */ +Action* GenerateActionCore(const char *src, const char *str, unsigned short actionID) +{ + Action *newAction = new Action(true); + newAction->actionID = actionID; + //this flag tells us to merge 2 consecutive strings together to get + //a variable (context+variablename) + int mergestrings = actionflags[newAction->actionID]&AF_MERGESTRINGS; + int objectCount = ( newAction->actionID == 1 ) ? 0 : 1; + int stringsCount = 0; + int intCount = 0; + if (actionflags[newAction->actionID]&AF_DIRECT) { + Object *tmp = new Object(); + tmp->objectFields[0] = -1; + //tmp->objectFields[1] = core->GetGameControl()->targetID; + newAction->objects[objectCount++] = tmp; + } + //Here is the Action; Now we need to evaluate the parameters, if any + if (*str!=')') while (*str) { + if (*(str+1)!=':') { + printf("Warning, parser was sidetracked: %s\n",str); + } + switch (*str) { + default: + printf("Invalid type: %s\n",str); + //str++; + delete newAction; + return NULL; + break; + + case 'p': //Point + SKIP_ARGUMENT(); + src++; //Skip [ + newAction->pointParameter.x = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip . + newAction->pointParameter.y = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip ] + break; + + case 'i': //Integer + { + //going to the variable name + while (*str != '*' && *str !=',' && *str != ')' ) { + str++; + } + int value; + if (*str=='*') { //there may be an IDS table + str++; + ieVariable idsTabName; + char* tmp = idsTabName; + while (( *str != ',' ) && ( *str != ')' )) { + *tmp = *str; + tmp++; + str++; + } + *tmp = 0; + if (idsTabName[0]) { + value = GetIdsValue(src, idsTabName); + } + else { + value = strtol( src, (char **) &src, 0); + } + } + else { //no IDS table + value = strtol( src, (char **) &src, 0); + } + if (!intCount) { + newAction->int0Parameter = value; + } else if (intCount == 1) { + newAction->int1Parameter = value; + } else { + newAction->int2Parameter = value; + } + intCount++; + } + break; + + case 'a': + //Action + { + SKIP_ARGUMENT(); + char action[257]; + int i = 0; + int openParenthesisCount = 0; + while (true) { + if (*src == ')') { + if (!openParenthesisCount) + break; + openParenthesisCount--; + } else { + if (*src == '(') { + openParenthesisCount++; + } else { + if (( *src == ',' ) && + !openParenthesisCount) + break; + } + } + action[i] = *src; + i++; + src++; + } + action[i] = 0; + Action* act = GenerateAction( action); + if (!act) { + delete newAction; + return NULL; + } + act->objects[0] = newAction->objects[0]; + newAction->objects[0] = NULL; //avoid freeing of object + delete newAction; //freeing action + newAction = act; + } + break; + + case 'o': //Object + if (objectCount==3) { + printf("Invalid object count!\n"); + //abort(); + delete newAction; + return NULL; + } + ParseObject(str, src, newAction->objects[objectCount++]); + break; + + case 's': //String + { + SKIP_ARGUMENT(); + src++; + int i; + char* dst; + if (!stringsCount) { + dst = newAction->string0Parameter; + } else { + dst = newAction->string1Parameter; + } + //if there are 3 strings, the first 2 will be merged, + //the last one will be left alone + if (*str==')') { + mergestrings = 0; + } + //skipping the context part, which + //is to be readed later + if (mergestrings) { + for (i=0;i<6;i++) { + *dst++='*'; + } + } + else { + i=0; + } + //breaking on ',' in case of a monkey attack + //fixes bg1:melicamp.dlg, bg1:sharte.dlg + //if strings ever need a , inside, this is a FIXME + while (*src != '"' && *src !=',') { + if (*src == 0) { + delete newAction; + return NULL; + } + //sizeof(context+name) = 40 + if (i<40) { + *dst++ = (char) tolower(*src); + i++; + } + src++; + } + *dst = 0; + //reading the context part + if (mergestrings) { + str++; + if (*str!='s') { + printf("Invalid mergestrings:%s\n",str); + //abort(); + delete newAction; + return NULL; + } + SKIP_ARGUMENT(); + if (!stringsCount) { + dst = newAction->string0Parameter; + } else { + dst = newAction->string1Parameter; + } + + //this works only if there are no spaces + if (*src++!='"' || *src++!=',' || *src++!='"') { + break; + } + //reading the context string + i=0; + while (*src != '"') { + if (*src == 0) { + delete newAction; + return NULL; + } + if (i++<6) { + *dst++ = (char) tolower(*src); + } + src++; + } + } + src++; //skipping " + stringsCount++; + } + break; + } + str++; + if (*src == ',' || *src==')') + src++; + } + return newAction; +} + +void GoNear(Scriptable *Sender, const Point &p) +{ + if (Sender->GetCurrentAction()) { + printMessage("GameScript","Target busy???\n",LIGHT_RED); + return; + } + char Tmp[256]; + sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y ); + Action * action = GenerateAction( Tmp); + Sender->AddActionInFront( action ); +} + +void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance) +{ + Point p; + Map *myarea, *hisarea; + + if (Sender->Type != ST_ACTOR) { + printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED); + Sender->ReleaseCurrentAction(); + return; + } + + myarea = Sender->GetCurrentArea(); + hisarea = target->GetCurrentArea(); + if (hisarea!=myarea) { + target = myarea->GetTileMap()->GetTravelTo(hisarea->GetScriptName()); + + if (!target) { + printMessage("GameScript", "MoveNearerTo failed to find an exit\n", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + ((Actor *) Sender)->UseExit(true); + } else { + ((Actor *) Sender)->UseExit(false); + } + // we deliberately don't try GetLikelyPosition here for now, + // maybe a future idea if we have a better implementation + // (the old code used it - by passing true not 0 below - when target was a movable) + GetPositionFromScriptable(target, p, 0); + + // account for PersonalDistance (which caller uses, but pathfinder doesn't) + if (distance && Sender->Type == ST_ACTOR) { + distance += ((Actor *)Sender)->size*10; + } + if (distance && target->Type == ST_ACTOR) { + distance += ((Actor *)target)->size*10; + } + + MoveNearerTo(Sender, p, distance, 0); +} + +//It is not always good to release the current action if target is unreachable +//we should also raise the trigger TargetUnreachable (if this is an Attack, at least) +//i hacked only this low level function, didn't need the higher ones so far +int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int dont_release) +{ + if (Sender->Type != ST_ACTOR) { + printMessage("GameScript","MoveNearerTo only works with actors\n",LIGHT_RED); + Sender->ReleaseCurrentAction(); + return 0; + } + + Actor *actor = (Actor *)Sender; + + if (!actor->InMove() || actor->Destination != p) { + actor->WalkTo(p, 0, distance); + } + + if (!actor->InMove()) { + //didn't release + if (dont_release) { + return dont_release; + } + // we can't walk any nearer to destination, give up + Sender->ReleaseCurrentAction(); + } + return 0; +} +/* +void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool flag, int distance) +{ + Point p; + GetPositionFromScriptable(target,p,flag); + GoNearAndRetry(Sender, p, distance); +} + +void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance) +{ + if (!Sender->GetCurrentAction() ) { + printMessage("GameScript","NULL action retried???\n",LIGHT_RED); + return; + } + Sender->AddActionInFront( Sender->GetCurrentAction() ); + char Tmp[256]; + sprintf( Tmp, "MoveToPoint([%hd.%hd])", p.x, p.y ); + Action * action = GenerateAction( Tmp); + //experimental hack, this value means, + //MoveToPoint shall pop the next action too if movement fails + //and the actor is farther than distance + //this will prevent deadlocks + //(we have to add 1 because otherwise distance==0 fails, we subtract it in MoveToPoint) + action->int0Parameter = distance+1; + Sender->AddActionInFront( action ); +} +*/ +void FreeSrc(SrcVector *poi, const ieResRef key) +{ + int res = SrcCache.DecRef((void *) poi, key, true); + if (res<0) { + printMessage( "GameScript", "Corrupted Src cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Src name is: %.8s\n", key); + abort(); + } + if (!res) { + delete poi; + } +} + +SrcVector *LoadSrc(const ieResRef resname) +{ + SrcVector *src = (SrcVector *) SrcCache.GetResource(resname); + if (src) { + return src; + } + DataStream* str = gamedata->GetResource( resname, IE_SRC_CLASS_ID ); + if ( !str) { + return NULL; + } + ieDword size=0; + str->ReadDword(&size); + src = new SrcVector(size); + SrcCache.SetAt( resname, (void *) src ); + while (size--) { + ieDword tmp; + str->ReadDword(&tmp); + src->at(size)=tmp; + str->ReadDword(&tmp); + } + delete ( str ); + return src; +} + +#define MEMCPY(a,b) memcpy((a),(b),sizeof(a) ) + +static Object *ObjectCopy(Object *object) +{ + if (!object) return NULL; + Object *newObject = new Object(); + MEMCPY( newObject->objectFields, object->objectFields ); + MEMCPY( newObject->objectFilters, object->objectFilters ); + MEMCPY( newObject->objectRect, object->objectRect ); + MEMCPY( newObject->objectName, object->objectName ); + return newObject; +} + +Action *ParamCopy(Action *parameters) +{ + Action *newAction = new Action(true); + newAction->actionID = parameters->actionID; + newAction->int0Parameter = parameters->int0Parameter; + newAction->int1Parameter = parameters->int1Parameter; + newAction->int2Parameter = parameters->int2Parameter; + newAction->pointParameter = parameters->pointParameter; + MEMCPY( newAction->string0Parameter, parameters->string0Parameter ); + MEMCPY( newAction->string1Parameter, parameters->string1Parameter ); + for (int c=0;c<3;c++) { + newAction->objects[c]= ObjectCopy( parameters->objects[c] ); + } + return newAction; +} + +Action *ParamCopyNoOverride(Action *parameters) +{ + Action *newAction = new Action(true); + newAction->actionID = parameters->actionID; + newAction->int0Parameter = parameters->int0Parameter; + newAction->int1Parameter = parameters->int1Parameter; + newAction->int2Parameter = parameters->int2Parameter; + newAction->pointParameter = parameters->pointParameter; + MEMCPY( newAction->string0Parameter, parameters->string0Parameter ); + MEMCPY( newAction->string1Parameter, parameters->string1Parameter ); + newAction->objects[0]= NULL; + newAction->objects[1]= ObjectCopy( parameters->objects[1] ); + newAction->objects[2]= ObjectCopy( parameters->objects[2] ); + return newAction; +} + +Trigger *GenerateTriggerCore(const char *src, const char *str, int trIndex, int negate) +{ + Trigger *newTrigger = new Trigger(); + newTrigger->triggerID = (unsigned short) triggersTable->GetValueIndex( trIndex )&0x3fff; + newTrigger->flags = (unsigned short) negate; + int mergestrings = triggerflags[newTrigger->triggerID]&TF_MERGESTRINGS; + int stringsCount = 0; + int intCount = 0; + //Here is the Trigger; Now we need to evaluate the parameters + if (*str!=')') while (*str) { + if (*(str+1)!=':') { + printf("Warning, parser was sidetracked: %s\n",str); + } + switch (*str) { + default: + printf("Invalid type: %s\n",str); + //str++; + delete newTrigger; + return NULL; + break; + + case 'p': //Point + SKIP_ARGUMENT(); + src++; //Skip [ + newTrigger->pointParameter.x = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip . + newTrigger->pointParameter.y = (short) strtol( src, (char **) &src, 10 ); + src++; //Skip ] + break; + + case 'i': //Integer + { + //going to the variable name + while (*str != '*' && *str !=',' && *str != ')' ) { + str++; + } + int value; + if (*str=='*') { //there may be an IDS table + str++; + ieVariable idsTabName; + char* tmp = idsTabName; + while (( *str != ',' ) && ( *str != ')' )) { + *tmp = *str; + tmp++; + str++; + } + *tmp = 0; + if (idsTabName[0]) { + value = GetIdsValue(src, idsTabName); + } + else { + value = strtol( src, (char **) &src, 0); + } + } + else { //no IDS table + value = strtol( src, (char **) &src, 0); + } + if (!intCount) { + newTrigger->int0Parameter = value; + } else if (intCount == 1) { + newTrigger->int1Parameter = value; + } else { + newTrigger->int2Parameter = value; + } + intCount++; + } + break; + + case 'o': //Object + ParseObject(str, src, newTrigger->objectParameter); + break; + + case 's': //String + { + SKIP_ARGUMENT(); + src++; + int i; + char* dst; + if (!stringsCount) { + dst = newTrigger->string0Parameter; + } else { + dst = newTrigger->string1Parameter; + } + //skipping the context part, which + //is to be readed later + if (mergestrings) { + for (i=0;i<6;i++) { + *dst++='*'; + } + } + else { + i=0; + } + while (*src != '"') { + if (*src == 0) { + delete newTrigger; + return NULL; + } + + //sizeof(context+name) = 40 + if (i<40) { + *dst++ = (char) tolower(*src); + i++; + } + src++; + } + *dst = 0; + //reading the context part + if (mergestrings) { + str++; + if (*str!='s') { + printf("Invalid mergestrings:%s\n",str); + //abort(); + delete newTrigger; + return NULL; + } + SKIP_ARGUMENT(); + if (!stringsCount) { + dst = newTrigger->string0Parameter; + } else { + dst = newTrigger->string1Parameter; + } + + //this works only if there are no spaces + if (*src++!='"' || *src++!=',' || *src++!='"') { + break; + } + //reading the context string + i=0; + while (*src != '"') { + if (*src == 0) { + delete newTrigger; + return NULL; + } + + if (i++<6) { + *dst++ = (char) tolower(*src); + } + src++; + } + } + src++; //skipping " + stringsCount++; + } + break; + } + str++; + if (*src == ',' || *src==')') + src++; + } + return newTrigger; +} + +void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, ieDword value) +{ + char newVarName[8+33]; + + if (InDebug&ID_VARIABLES) { + printf( "Setting variable(\"%s%s\", %d)\n", Context, + VarName, value ); + } + strncpy( newVarName, Context, 6 ); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->SetAt( VarName, value ); + return; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->SetAt( VarName, value ); + return; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->SetAt( VarName, value ); + return; + } + + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->SetAt( VarName, value); + } + else if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s %s in setvariable\n",Context, VarName); + } + } + else { + game->locals->SetAt( VarName, ( ieDword ) value ); + } +} + +void SetVariable(Scriptable* Sender, const char* VarName, ieDword value) +{ + char newVarName[8]; + const char *poi; + + poi = &VarName[6]; + //some HoW triggers use a : to separate the scope from the variable name + if (*poi==':') { + poi++; + } + + if (InDebug&ID_VARIABLES) { + printf( "Setting variable(\"%s\", %d)\n", VarName, value ); + } + strncpy( newVarName, VarName, 6 ); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->SetAt( poi, value ); + return; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->SetAt( poi, value ); + return; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->SetAt( poi, value ); + return; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->SetAt( poi, value); + } + else if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s in setvariable\n",VarName); + } + } + else { + game->locals->SetAt( poi, ( ieDword ) value ); + } +} + +ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid) +{ + char newVarName[8]; + const char *poi; + ieDword value = 0; + + strncpy( newVarName, VarName, 6 ); + newVarName[6]=0; + poi = &VarName[6]; + //some HoW triggers use a : to separate the scope from the variable name + if (*poi==':') { + poi++; + } + + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->Lookup( poi, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->Lookup( poi, value); + } else { + if (valid) { + *valid=false; + } + if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s in checkvariable\n",VarName); + } + } + } else { + game->locals->Lookup( poi, value ); + } + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s: %d\n",VarName, value); + } + return value; +} + +ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid) +{ + char newVarName[8]; + ieDword value = 0; + + strncpy(newVarName, Context, 6); + newVarName[6]=0; + if (strnicmp( newVarName, "MYAREA", 6 ) == 0) { + Sender->GetCurrentArea()->locals->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + if (strnicmp( newVarName, "LOCALS", 6 ) == 0) { + Sender->locals->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + Game *game = core->GetGame(); + if (!strnicmp(newVarName,"KAPUTZ",6) && core->HasFeature(GF_HAS_KAPUTZ) ) { + game->kaputz->Lookup( VarName, value ); + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; + } + if (strnicmp(newVarName,"GLOBAL",6) ) { + Map *map=game->GetMap(game->FindMap(newVarName)); + if (map) { + map->locals->Lookup( VarName, value); + } else { + if (valid) { + *valid=false; + } + if (InDebug&ID_VARIABLES) { + printMessage("GameScript"," ",YELLOW); + printf("Invalid variable %s %s in checkvariable\n",Context, VarName); + } + } + } else { + game->locals->Lookup( VarName, value ); + } + if (InDebug&ID_VARIABLES) { + printf("CheckVariable %s%s: %d\n",Context, VarName, value); + } + return value; +} + +int DiffCore(ieDword a, ieDword b, int diffmode) +{ + switch (diffmode) { + case LESS_THAN: + if (ab) { + return 1; + } + break; + case GREATER_OR_EQUALS: + if (a>=b) { + return 1; + } + break; + case NOT_EQUALS: + if (a!=b) { + return 1; + } + break; + case BINARY_LESS_OR_EQUALS: + if ((a&b) == a) { + return 1; + } + break; + case BINARY_MORE: + if ((a&b) != a) { + return 1; + } + break; + case BINARY_MORE_OR_EQUALS: + if ((a&b) == b) { + return 1; + } + break; + case BINARY_LESS: + if ((a&b) != b) { + return 1; + } + break; + case BINARY_INTERSECT: + if (a&b) { + return 1; + } + break; + case BINARY_NOT_INTERSECT: + if (!(a&b)) { + return 1; + } + break; + default: //less or equals + if (a<=b) { + return 1; + } + break; + } + return 0; +} + +int GetGroup(Actor *actor) +{ + int type = 2; //neutral, has no enemies + if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) { + type = 1; //PC + } + if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) { + type = 0; + } + return type; +} + +Point GetEntryPoint(const char *areaname, const char *entryname) +{ + Point p; + + AutoTable tab("entries"); + if (!tab) { + return p; + } + const char *tmpstr = tab->QueryField(areaname, entryname); + int x=-1; + int y=-1; + sscanf(tmpstr, "%d.%d", &x, &y); + p.x=(short) x; + p.y=(short) y; + return p; +} + +/* returns a spell's casting distance, it depends on the caster (level), and targeting mode too + the used header is calculated from the caster level */ +unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender) +{ + unsigned int dist; + + Spell* spl = gamedata->GetSpell( spellres ); + if (!spl) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Spell couldn't be found:%.8s.\n", spellres); + return 0; + } + dist = spl->GetCastingDistance(Sender); + //make possible special return values (like 0xffffffff means the spell doesn't need distance) + //this is used with special targeting mode (3) + if (dist>0xff000000) { + return dist; + } + + gamedata->FreeSpell(spl, spellres, false); + return dist*15; +} + +/* returns an item's casting distance, it depends on the used header, and targeting mode too + the used header is explictly given */ +unsigned int GetItemDistance(const ieResRef itemres, int header) +{ + unsigned int dist; + + Item* itm = gamedata->GetItem( itemres ); + if (!itm) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Item couldn't be found:%.8s.\n", itemres); + return 0; + } + dist=itm->GetCastingDistance(header); + //make possible special return values (like 0xffffffff means the item doesn't need distance) + //this is used with special targeting mode (3) + if (dist>0xff000000) { + return dist; + } + + gamedata->FreeItem(itm, itemres, false); + return dist*15; +} + +//read the wish 2da +void SetupWishCore(Scriptable *Sender, int column, int picks) +{ + int count; + ieVariable varname; + int *selects; + int i,j; + + //FIXME: find out what the original really used the picks parameter for + if (picks == 1) picks = 5; + + AutoTable tm("wish"); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find wish.2da.\n"); + return; + } + + selects = (int *) malloc(picks*sizeof(int)); + count = tm->GetRowCount(); + + for(i=0;i<99;i++) { + snprintf(varname,32, "wishpower%02d", i); + if(CheckVariable(Sender, varname, "GLOBAL") ) { + SetVariable(Sender, varname, "GLOBAL", 0); + } + } + + if (countQueryField( selects[i], column-1 ) ); + snprintf(varname,32,"wishpower%02d", spnum); + SetVariable(Sender, varname, "GLOBAL",1); + } + free(selects); +} + +#define MAX_ISLAND_POLYGONS 10 + +//read a polygon 2da +Gem_Polygon *GetPolygon2DA(ieDword index) +{ + ieResRef resref; + + if (index>=MAX_ISLAND_POLYGONS) { + return NULL; + } + + if (!polygons) { + polygons = (Gem_Polygon **) calloc(MAX_ISLAND_POLYGONS, sizeof(Gem_Polygon *) ); + } + if (polygons[index]) { + return polygons[index]; + } + snprintf(resref, sizeof(ieResRef), "ISLAND%02d", index); + AutoTable tm(resref); + if (!tm) { + return NULL; + } + int cnt = tm->GetRowCount(); + if (!cnt) { + return NULL; + } + Point *p = new Point[cnt]; + + int i = cnt; + while(i--) { + p[i].x = atoi(tm->QueryField(i, 0)); + p[i].y = atoi(tm->QueryField(i, 1)); + } + + polygons[index] = new Gem_Polygon(p, cnt, NULL); + delete [] p; + return polygons[index]; +} + diff --git a/project/jni/application/gemrb/src/core/GameScript/GSUtils.h b/project/jni/application/gemrb/src/core/GameScript/GSUtils.h new file mode 100644 index 000000000..afe9b1ae5 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GSUtils.h @@ -0,0 +1,142 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 GSUTILS_H +#define GSUTILS_H + +#include "GameScript/GameScript.h" + +#include "defsounds.h" +#include "exports.h" +#include "strrefs.h" + +#include "Interface.h" + +//indebug flags +#define ID_REFERENCE 1 +#define ID_CUTSCENE 2 +#define ID_VARIABLES 4 +#define ID_ACTIONS 8 +#define ID_TRIGGERS 16 + +extern Holder triggersTable; +extern Holder actionsTable; +extern Holder overrideActionsTable; +extern Holder objectsTable; +extern TriggerFunction triggers[MAX_TRIGGERS]; +extern ActionFunction actions[MAX_ACTIONS]; +extern short actionflags[MAX_ACTIONS]; +extern short triggerflags[MAX_TRIGGERS]; +extern ObjectFunction objects[MAX_OBJECTS]; +extern IDSFunction idtargets[MAX_OBJECT_FIELDS]; +extern Cache SrcCache; //cache for string resources (pst) +extern Cache BcsCache; //cache for scripts +extern int ObjectIDSCount; +extern int MaxObjectNesting; +extern bool HasAdditionalRect; +extern bool HasTriggerPoint; +extern ieResRef *ObjectIDSTableNames; +extern int ObjectFieldsCount; +extern int ExtraParametersCount; +extern int InDebug; +extern int *SkillStats; +extern int SkillCount; +extern Gem_Polygon **polygons; + +#define MIC_INVALID -2 +#define MIC_FULL -1 +#define MIC_NOITEM 0 +#define MIC_GOTITEM 1 + +GEM_EXPORT int GetReaction(Actor *target, Scriptable *Sender); +int GetHappiness(Scriptable *Sender, int reputation); +int GetHPPercent(Scriptable *Sender); +bool StoreHasItemCore(const ieResRef storename, const ieResRef itemname); +bool HasItemCore(Inventory *inventory, const ieResRef itemname, ieDword flags); +void ClickCore(Scriptable *Sender, Point point, int type, int speed); +void TransformItemCore(Actor *actor, Action *parameters, bool onlyone); +void CreateVisualEffectCore(Actor *target, const char *effect, int iterations); +void CreateVisualEffectCore(Scriptable *Sender, const Point &position, const char *effect, int iterations); +void GetPositionFromScriptable(Scriptable* scr, Point &position, bool trap); +void BeginDialog(Scriptable* Sender, Action* parameters, int flags); +void ChangeAnimationCore(Actor *src, const char *resref, bool effect); +void PolymorphCopyCore(Actor *src, Actor *tar, bool base); +void CreateCreatureCore(Scriptable* Sender, Action* parameters, int flags); +int MoveItemCore(Scriptable *Sender, Scriptable *target, const char *resref, int flags, int setflag); +void MoveToObjectCore(Scriptable *Sender, Action *parameters, ieDword flags, bool untilsee); +void CreateItemCore(CREItem *item, const char *resref, int a, int b, int c); +void AttackCore(Scriptable *Sender, Scriptable *target, int flags); +void InitScriptTables(); +void HandleBitMod(ieDword &value1, ieDword value2, int opcode); +bool ResolveSpellName(ieResRef spellres, Action *parameter); +GEM_EXPORT void ResolveSpellName(ieResRef spellres, ieDword number); +GEM_EXPORT ieDword ResolveSpellNumber(const ieResRef spellres); +bool ResolveItemName(ieResRef itemres, Actor *act, ieDword Slot); +void EscapeAreaCore(Scriptable *Sender, const Point &p, const char *area, const Point &enter, int flags, int wait); +void GoNear(Scriptable *Sender, const Point &p); +void MoveNearerTo(Scriptable *Sender, Scriptable *target, int distance); +int MoveNearerTo(Scriptable *Sender, const Point &p, int distance, int no_release); +void GoNearAndRetry(Scriptable *Sender, Scriptable *target, bool destination, int distance); +void GoNearAndRetry(Scriptable *Sender, const Point &p, int distance); + +#define NO_OPERATION -1 +#define LESS_OR_EQUALS 0 +//iwd2 diffmode with gemrb enhancements +#define EQUALS 1 +#define LESS_THAN 2 +#define GREATER_THAN 3 +#define GREATER_OR_EQUALS 4 +#define NOT_EQUALS 5 +#define BINARY_LESS_OR_EQUALS 6 //(left has only bits in right) +#define BINARY_MORE_OR_EQUALS 7 //(left has equal or more bits than right) +#define BINARY_INTERSECT 8 //(left and right has at least one common bit) +#define BINARY_NOT_INTERSECT 9 //(no common bits) +#define BINARY_MORE 10 //left has more bits than right +#define BINARY_LESS 11 //left has less bits than right + +GEM_EXPORT int GetGroup(Actor *actor); + +void FreeSrc(SrcVector *poi, const ieResRef key); +SrcVector *LoadSrc(const ieResRef resname); +Action *ParamCopy(Action *parameters); +Action *ParamCopyNoOverride(Action *parameters); +void SetVariable(Scriptable* Sender, const char* VarName, ieDword value); +Point GetEntryPoint(const char *areaname, const char *entryname); +//these are used from other plugins +GEM_EXPORT int CanSee(Scriptable* Sender, Scriptable* target, bool range, int nodead); +GEM_EXPORT int SeeCore(Scriptable* Sender, Trigger* parameters, int justlos); +GEM_EXPORT int DiffCore(ieDword a, ieDword b, int diffmode); +GEM_EXPORT void DisplayStringCore(Scriptable* Sender, int Strref, int flags); +GEM_EXPORT void SetVariable(Scriptable* Sender, const char* VarName, const char* Context, ieDword value); +GEM_EXPORT void MoveBetweenAreasCore(Actor* actor, const char *area, const Point &position, int face, bool adjust); +GEM_EXPORT ieDword CheckVariable(Scriptable* Sender, const char* VarName, bool *valid = NULL); +GEM_EXPORT ieDword CheckVariable(Scriptable* Sender, const char* VarName, const char* Context, bool *valid = NULL); +Action* GenerateActionCore(const char *src, const char *str, unsigned short actionID); +Trigger *GenerateTriggerCore(const char *src, const char *str, int trIndex, int negate); +unsigned int GetSpellDistance(const ieResRef spellres, Scriptable *Sender); +unsigned int GetItemDistance(const ieResRef itemres, int header); +void SetupWishCore(Scriptable *Sender, int column, int picks); +Gem_Polygon *GetPolygon2DA(ieDword index); + +inline int Bones(ieDword value) +{ + return core->Roll((value&0xf000)>>12, (value&0xff0)>>8, value&15); +} + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp b/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp new file mode 100644 index 000000000..f7c900069 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/GameScript.cpp @@ -0,0 +1,2337 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "PluginMgr.h" + +//debug flags +// 1 - cache +// 2 - cutscene ID +// 4 - globals +// 8 - action execution +//16 - trigger evaluation + +//Make this an ordered list, so we could use bsearch! +static const TriggerLink triggernames[] = { + {"actionlistempty", GameScript::ActionListEmpty, 0}, + {"actuallyincombat", GameScript::ActuallyInCombat, 0}, + {"acquired", GameScript::Acquired, 0}, + {"alignment", GameScript::Alignment, 0}, + {"allegiance", GameScript::Allegiance, 0}, + {"animstate", GameScript::AnimState, 0}, + {"anypconmap", GameScript::AnyPCOnMap, 0}, + {"anypcseesenemy", GameScript::AnyPCSeesEnemy, 0}, + {"areacheck", GameScript::AreaCheck, 0}, + {"areacheckobject", GameScript::AreaCheckObject, 0}, + {"areaflag", GameScript::AreaFlag, 0}, + {"arearestdisabled", GameScript::AreaRestDisabled, 0}, + {"areatype", GameScript::AreaType, 0}, + {"atlocation", GameScript::AtLocation, 0}, + {"assaltedby", GameScript::AttackedBy, 0},//pst + {"attackedby", GameScript::AttackedBy, 0}, + {"becamevisible", GameScript::BecameVisible, 0}, + {"bitcheck", GameScript::BitCheck,TF_MERGESTRINGS}, + {"bitcheckexact", GameScript::BitCheckExact,TF_MERGESTRINGS}, + {"bitglobal", GameScript::BitGlobal_Trigger,TF_MERGESTRINGS}, + {"breakingpoint", GameScript::BreakingPoint, 0}, + {"calanderday", GameScript::CalendarDay, 0}, //illiterate developers O_o + {"calendarday", GameScript::CalendarDay, 0}, + {"calanderdaygt", GameScript::CalendarDayGT, 0}, + {"calendardaygt", GameScript::CalendarDayGT, 0}, + {"calanderdaylt", GameScript::CalendarDayLT, 0}, + {"calendardaylt", GameScript::CalendarDayLT, 0}, + {"calledbyname", GameScript::CalledByName, 0}, //this is still a question + {"chargecount", GameScript::ChargeCount, 0}, + {"charname", GameScript::CharName, 0}, //not scripting name + {"checkareadifflevel", GameScript::DifficultyLT, 0},//iwd2 guess + {"checkdoorflags", GameScript::CheckDoorFlags, 0}, + {"checkpartyaveragelevel", GameScript::CheckPartyAverageLevel, 0}, + {"checkpartylevel", GameScript::CheckPartyLevel, 0}, + {"checkskill", GameScript::CheckSkill, 0}, + {"checkskillgt", GameScript::CheckSkillGT, 0}, + {"checkskilllt", GameScript::CheckSkillLT, 0}, + {"checkspellstate", GameScript::CheckSpellState, 0}, + {"checkstat", GameScript::CheckStat, 0}, + {"checkstatgt", GameScript::CheckStatGT, 0}, + {"checkstatlt", GameScript::CheckStatLT, 0}, + {"class", GameScript::Class, 0}, + {"classex", GameScript::ClassEx, 0}, //will return true for multis + {"classlevel", GameScript::ClassLevel, 0}, //pst + {"classlevelgt", GameScript::ClassLevelGT, 0}, + {"classlevellt", GameScript::ClassLevelLT, 0}, + {"clicked", GameScript::Clicked, 0}, + {"closed", GameScript::Closed, 0}, + {"combatcounter", GameScript::CombatCounter, 0}, + {"combatcountergt", GameScript::CombatCounterGT, 0}, + {"combatcounterlt", GameScript::CombatCounterLT, 0}, + {"contains", GameScript::Contains, 0}, + {"currentareais", GameScript::CurrentAreaIs, 0},//checks object + {"creaturehidden", GameScript::CreatureHidden, 0},//this is the engine level hiding feature, not the skill + {"creatureinarea", GameScript::AreaCheck, 0}, //pst, checks this object + {"damagetaken", GameScript::HPLost, 0}, + {"damagetakengt", GameScript::HPLostGT, 0}, + {"damagetakenlt", GameScript::HPLostLT, 0}, + {"dead", GameScript::Dead, 0}, + {"delay", GameScript::Delay, 0}, + {"detect", GameScript::Detect, 0}, //so far i see no difference + {"die", GameScript::Die, 0}, + {"died", GameScript::Died, 0}, + {"difficulty", GameScript::Difficulty, 0}, + {"difficultygt", GameScript::DifficultyGT, 0}, + {"difficultylt", GameScript::DifficultyLT, 0}, + {"disarmed", GameScript::Disarmed, 0}, + {"disarmfailed", GameScript::DisarmFailed, 0}, + {"entered", GameScript::Entered, 0}, + {"entirepartyonmap", GameScript::EntirePartyOnMap, 0}, + {"exists", GameScript::Exists, 0}, + {"extendedstatecheck", GameScript::ExtendedStateCheck, 0}, + {"extraproficiency", GameScript::ExtraProficiency, 0}, + {"extraproficiencygt", GameScript::ExtraProficiencyGT, 0}, + {"extraproficiencylt", GameScript::ExtraProficiencyLT, 0}, + {"faction", GameScript::Faction, 0}, + {"failedtoopen", GameScript::OpenFailed, 0}, + {"fallenpaladin", GameScript::FallenPaladin, 0}, + {"fallenranger", GameScript::FallenRanger, 0}, + {"false", GameScript::False, 0}, + {"forcemarkedspell", GameScript::ForceMarkedSpell_Trigger, 0}, + {"frame", GameScript::Frame, 0}, + {"g", GameScript::G_Trigger, 0}, + {"gender", GameScript::Gender, 0}, + {"general", GameScript::General, 0}, + {"ggt", GameScript::GGT_Trigger, 0}, + {"glt", GameScript::GLT_Trigger, 0}, + {"global", GameScript::Global,TF_MERGESTRINGS}, + {"globalandglobal", GameScript::GlobalAndGlobal_Trigger,TF_MERGESTRINGS}, + {"globalband", GameScript::BitCheck,TF_MERGESTRINGS}, + {"globalbandglobal", GameScript::GlobalBAndGlobal_Trigger,TF_MERGESTRINGS}, + {"globalbandglobalexact", GameScript::GlobalBAndGlobalExact,TF_MERGESTRINGS}, + {"globalbitglobal", GameScript::GlobalBitGlobal_Trigger,TF_MERGESTRINGS}, + {"globalequalsglobal", GameScript::GlobalsEqual,TF_MERGESTRINGS}, //this is the same + {"globalgt", GameScript::GlobalGT,TF_MERGESTRINGS}, + {"globalgtglobal", GameScript::GlobalGTGlobal,TF_MERGESTRINGS}, + {"globallt", GameScript::GlobalLT,TF_MERGESTRINGS}, + {"globalltglobal", GameScript::GlobalLTGlobal,TF_MERGESTRINGS}, + {"globalorglobal", GameScript::GlobalOrGlobal_Trigger,TF_MERGESTRINGS}, + {"globalsequal", GameScript::GlobalsEqual, 0}, + {"globalsgt", GameScript::GlobalsGT, 0}, + {"globalslt", GameScript::GlobalsLT, 0}, + {"globaltimerexact", GameScript::GlobalTimerExact, 0}, + {"globaltimerexpired", GameScript::GlobalTimerExpired, 0}, + {"globaltimernotexpired", GameScript::GlobalTimerNotExpired, 0}, + {"globaltimerstarted", GameScript::GlobalTimerStarted, 0}, + {"happiness", GameScript::Happiness, 0}, + {"happinessgt", GameScript::HappinessGT, 0}, + {"happinesslt", GameScript::HappinessLT, 0}, + {"harmlessclosed", GameScript::Closed, 0}, //pst, not sure + {"harmlessentered", GameScript::HarmlessEntered, 0}, //??? + {"harmlessopened", GameScript::Opened, 0}, //pst, not sure + {"hasbounceeffects", GameScript::HasBounceEffects, 0}, + {"hasimmunityeffects", GameScript::HasImmunityEffects, 0}, + {"hasinnateability", GameScript::HaveSpell, 0}, //these must be the same + {"hasitem", GameScript::HasItem, 0}, + {"hasitemequiped", GameScript::HasItemEquipped, 0}, //typo in bg2 + {"hasitemequipedreal", GameScript::HasItemEquipped, 0}, //not sure + {"hasitemequipped", GameScript::HasItemEquipped, 0}, + {"hasitemequippedreal", GameScript::HasItemEquipped, 0}, //not sure + {"hasiteminslot", GameScript::HasItemSlot, 0}, + {"hasitemslot", GameScript::HasItemSlot, 0}, + {"hasitemtypeslot", GameScript::HasItemTypeSlot, 0},//gemrb extension + {"hasweaponequiped", GameScript::HasWeaponEquipped, 0},//a typo again + {"hasweaponequipped", GameScript::HasWeaponEquipped, 0}, + {"haveanyspells", GameScript::HaveAnySpells, 0}, + {"havespell", GameScript::HaveSpell, 0}, //these must be the same + {"havespellparty", GameScript::HaveSpellParty, 0}, + {"havespellres", GameScript::HaveSpell, 0}, //they share the same ID + {"haveusableweaponequipped", GameScript::HaveUsableWeaponEquipped, 0}, + {"heard", GameScript::Heard, 0}, + {"help", GameScript::Help_Trigger, 0}, + {"helpex", GameScript::HelpEX, 0}, + {"hitby", GameScript::HitBy, 0}, + {"hotkey", GameScript::HotKey, 0}, + {"hp", GameScript::HP, 0}, + {"hpgt", GameScript::HPGT, 0}, + {"hplost", GameScript::HPLost, 0}, + {"hplostgt", GameScript::HPLostGT, 0}, + {"hplostlt", GameScript::HPLostLT, 0}, + {"hplt", GameScript::HPLT, 0}, + {"hppercent", GameScript::HPPercent, 0}, + {"hppercentgt", GameScript::HPPercentGT, 0}, + {"hppercentlt", GameScript::HPPercentLT, 0}, + {"inactivearea", GameScript::InActiveArea, 0}, + {"incutscenemode", GameScript::InCutSceneMode, 0}, + {"inline", GameScript::InLine, 0}, + {"inmyarea", GameScript::InMyArea, 0}, + {"inmygroup", GameScript::InMyGroup, 0}, + {"inparty", GameScript::InParty, 0}, + {"inpartyallowdead", GameScript::InPartyAllowDead, 0}, + {"inpartyslot", GameScript::InPartySlot, 0}, + {"internal", GameScript::Internal, 0}, + {"internalgt", GameScript::InternalGT, 0}, + {"internallt", GameScript::InternalLT, 0}, + {"interactingwith", GameScript::InteractingWith, 0}, + {"intrap", GameScript::InTrap, 0}, + {"inventoryfull", GameScript::InventoryFull, 0}, + {"inview", GameScript::LOS, 0}, //it seems the same, needs research + {"inwatcherskeep", GameScript::AreaStartsWith, 0}, + {"inweaponrange", GameScript::InWeaponRange, 0}, + {"isaclown", GameScript::IsAClown, 0}, + {"isactive", GameScript::IsActive, 0}, + {"isanimationid", GameScript::AnimationID, 0}, + {"iscreatureareaflag", GameScript::IsCreatureAreaFlag, 0}, + {"isfacingobject", GameScript::IsFacingObject, 0}, + {"isfacingsavedrotation", GameScript::IsFacingSavedRotation, 0}, + {"isgabber", GameScript::IsGabber, 0}, + {"isheartoffurymodeon", GameScript::NightmareModeOn, 0}, + {"islocked", GameScript::IsLocked, 0}, + {"isextendednight", GameScript::IsExtendedNight, 0}, + {"ismarkedspell", GameScript::IsMarkedSpell, 0}, + {"isoverme", GameScript::IsOverMe, 0}, + {"ispathcriticalobject", GameScript::IsPathCriticalObject, 0}, + {"isplayernumber", GameScript::IsPlayerNumber, 0}, + {"isrotation", GameScript::IsRotation, 0}, + {"isscriptname", GameScript::CalledByName, 0}, //seems the same + {"isspelltargetvalid", GameScript::IsSpellTargetValid, 0}, + {"isteambiton", GameScript::IsTeamBitOn, 0}, + {"isvalidforpartydialog", GameScript::IsValidForPartyDialog, 0}, + {"isvalidforpartydialogue", GameScript::IsValidForPartyDialog, 0}, + {"isweaponranged", GameScript::IsWeaponRanged, 0}, + {"isweather", GameScript::IsWeather, 0}, //gemrb extension + {"itemisidentified", GameScript::ItemIsIdentified, 0}, + {"joins", GameScript::Joins, 0}, + {"kit", GameScript::Kit, 0}, + {"knowspell", GameScript::KnowSpell, 0}, //gemrb specific + {"lastmarkedobject", GameScript::LastMarkedObject_Trigger, 0}, + {"lastpersontalkedto", GameScript::LastPersonTalkedTo, 0}, //pst + {"leaves", GameScript::Leaves, 0}, + {"level", GameScript::Level, 0}, + {"levelgt", GameScript::LevelGT, 0}, + {"levelinclass", GameScript::LevelInClass, 0}, //iwd2 + {"levelinclassgt", GameScript::LevelInClassGT, 0}, + {"levelinclasslt", GameScript::LevelInClassLT, 0}, + {"levellt", GameScript::LevelLT, 0}, + {"levelparty", GameScript::LevelParty, 0}, + {"levelpartygt", GameScript::LevelPartyGT, 0}, + {"levelpartylt", GameScript::LevelPartyLT, 0}, + {"localsequal", GameScript::LocalsEqual, 0}, + {"localsgt", GameScript::LocalsGT, 0}, + {"localslt", GameScript::LocalsLT, 0}, + {"los", GameScript::LOS, 0}, + {"modalstate", GameScript::ModalState, 0}, + {"morale", GameScript::Morale, 0}, + {"moralegt", GameScript::MoraleGT, 0}, + {"moralelt", GameScript::MoraleLT, 0}, + {"name", GameScript::CalledByName, 0}, //this is the same too? + {"namelessbitthedust", GameScript::NamelessBitTheDust, 0}, + {"nearbydialog", GameScript::NearbyDialog, 0}, + {"nearbydialogue", GameScript::NearbyDialog, 0}, + {"nearlocation", GameScript::NearLocation, 0}, + {"nearsavedlocation", GameScript::NearSavedLocation, 0}, + {"nightmaremodeon", GameScript::NightmareModeOn, 0}, + {"notstatecheck", GameScript::NotStateCheck, 0}, + {"nulldialog", GameScript::NullDialog, 0}, + {"nulldialogue", GameScript::NullDialog, 0}, + {"numcreature", GameScript::NumCreatures, 0}, + {"numcreaturegt", GameScript::NumCreaturesGT, 0}, + {"numcreaturelt", GameScript::NumCreaturesLT, 0}, + {"numcreaturesatmylevel", GameScript::NumCreaturesAtMyLevel, 0}, + {"numcreaturesgtmylevel", GameScript::NumCreaturesGTMyLevel, 0}, + {"numcreaturesltmylevel", GameScript::NumCreaturesLTMyLevel, 0}, + {"numcreaturevsparty", GameScript::NumCreatureVsParty, 0}, + {"numcreaturevspartygt", GameScript::NumCreatureVsPartyGT, 0}, + {"numcreaturevspartylt", GameScript::NumCreatureVsPartyLT, 0}, + {"numdead", GameScript::NumDead, 0}, + {"numdeadgt", GameScript::NumDeadGT, 0}, + {"numdeadlt", GameScript::NumDeadLT, 0}, + {"numinparty", GameScript::PartyCountEQ, 0}, + {"numinpartyalive", GameScript::PartyCountAliveEQ, 0}, + {"numinpartyalivegt", GameScript::PartyCountAliveGT, 0}, + {"numinpartyalivelt", GameScript::PartyCountAliveLT, 0}, + {"numinpartygt", GameScript::PartyCountGT, 0}, + {"numinpartylt", GameScript::PartyCountLT, 0}, + {"numitems", GameScript::NumItems, 0}, + {"numitemsgt", GameScript::NumItemsGT, 0}, + {"numitemslt", GameScript::NumItemsLT, 0}, + {"numitemsparty", GameScript::NumItemsParty, 0}, + {"numitemspartygt", GameScript::NumItemsPartyGT, 0}, + {"numitemspartylt", GameScript::NumItemsPartyLT, 0}, + {"numtimesinteracted", GameScript::NumTimesInteracted, 0}, + {"numtimesinteractedgt", GameScript::NumTimesInteractedGT, 0}, + {"numtimesinteractedlt", GameScript::NumTimesInteractedLT, 0}, + {"numtimesinteractedobject", GameScript::NumTimesInteractedObject, 0},//gemrb + {"numtimesinteractedobjectgt", GameScript::NumTimesInteractedObjectGT, 0},//gemrb + {"numtimesinteractedobjectlt", GameScript::NumTimesInteractedObjectLT, 0},//gemrb + {"numtimestalkedto", GameScript::NumTimesTalkedTo, 0}, + {"numtimestalkedtogt", GameScript::NumTimesTalkedToGT, 0}, + {"numtimestalkedtolt", GameScript::NumTimesTalkedToLT, 0}, + {"objectactionlistempty", GameScript::ObjectActionListEmpty, 0}, //same function + {"objitemcounteq", GameScript::NumItems, 0}, + {"objitemcountgt", GameScript::NumItemsGT, 0}, + {"objitemcountlt", GameScript::NumItemsLT, 0}, + {"oncreation", GameScript::OnCreation, 0}, + {"onisland", GameScript::OnIsland, 0}, + {"onscreen", GameScript::OnScreen, 0}, + {"opened", GameScript::Opened, 0}, + {"openfailed", GameScript::OpenFailed, 0}, + {"openstate", GameScript::OpenState, 0}, + {"or", GameScript::Or, 0}, + {"outofammo", GameScript::OutOfAmmo, 0}, + {"ownsfloatermessage", GameScript::OwnsFloaterMessage, 0}, + {"partycounteq", GameScript::PartyCountEQ, 0}, + {"partycountgt", GameScript::PartyCountGT, 0}, + {"partycountlt", GameScript::PartyCountLT, 0}, + {"partygold", GameScript::PartyGold, 0}, + {"partygoldgt", GameScript::PartyGoldGT, 0}, + {"partygoldlt", GameScript::PartyGoldLT, 0}, + {"partyhasitem", GameScript::PartyHasItem, 0}, + {"partyhasitemidentified", GameScript::PartyHasItemIdentified, 0}, + {"partyitemcounteq", GameScript::NumItemsParty, 0}, + {"partyitemcountgt", GameScript::NumItemsPartyGT, 0}, + {"partyitemcountlt", GameScript::NumItemsPartyLT, 0}, + {"partymemberdied", GameScript::PartyMemberDied, 0}, + {"partyrested", GameScript::PartyRested, 0}, + {"pccanseepoint", GameScript::PCCanSeePoint, 0}, + {"pcinstore", GameScript::PCInStore, 0}, + {"personalspacedistance", GameScript::PersonalSpaceDistance, 0}, + {"picklockfailed", GameScript::PickLockFailed, 0}, + {"pickpocketfailed", GameScript::PickpocketFailed, 0}, + {"proficiency", GameScript::Proficiency, 0}, + {"proficiencygt", GameScript::ProficiencyGT, 0}, + {"proficiencylt", GameScript::ProficiencyLT, 0}, + {"race", GameScript::Race, 0}, + {"randomnum", GameScript::RandomNum, 0}, + {"randomnumgt", GameScript::RandomNumGT, 0}, + {"randomnumlt", GameScript::RandomNumLT, 0}, + {"randomstatcheck", GameScript::RandomStatCheck, 0}, + {"range", GameScript::Range, 0}, + {"reaction", GameScript::Reaction, 0}, + {"reactiongt", GameScript::ReactionGT, 0}, + {"reactionlt", GameScript::ReactionLT, 0}, + {"realglobaltimerexact", GameScript::RealGlobalTimerExact, 0}, + {"realglobaltimerexpired", GameScript::RealGlobalTimerExpired, 0}, + {"realglobaltimernotexpired", GameScript::RealGlobalTimerNotExpired, 0}, + {"receivedorder", GameScript::ReceivedOrder, 0}, + {"reputation", GameScript::Reputation, 0}, + {"reputationgt", GameScript::ReputationGT, 0}, + {"reputationlt", GameScript::ReputationLT, 0}, + {"school", GameScript::School, 0}, //similar to kit + {"see", GameScript::See, 0}, + {"sequence", GameScript::Sequence, 0}, + {"setlastmarkedobject", GameScript::SetLastMarkedObject, 0}, + {"setmarkedspell", GameScript::SetMarkedSpell_Trigger, 0}, + {"specifics", GameScript::Specifics, 0}, + {"spellcast", GameScript::SpellCast, 0}, + {"spellcastinnate", GameScript::SpellCastInnate, 0}, + {"spellcastonme", GameScript::SpellCastOnMe, 0}, + {"spellcastpriest", GameScript::SpellCastPriest, 0}, + {"statecheck", GameScript::StateCheck, 0}, + {"stealfailed", GameScript::StealFailed, 0}, + {"storehasitem", GameScript::StoreHasItem, 0}, + {"stuffglobalrandom", GameScript::StuffGlobalRandom, 0},//hm, this is a trigger + {"subrace", GameScript::SubRace, 0}, + {"systemvariable", GameScript::SystemVariable_Trigger, 0}, //gemrb + {"targetunreachable", GameScript::TargetUnreachable, 0}, + {"team", GameScript::Team, 0}, + {"time", GameScript::Time, 0}, + {"timegt", GameScript::TimeGT, 0}, + {"timelt", GameScript::TimeLT, 0}, + {"timeofday", GameScript::TimeOfDay, 0}, + {"timeractive", GameScript::TimerActive, 0}, + {"timerexpired", GameScript::TimerExpired, 0}, + {"tookdamage", GameScript::TookDamage, 0}, + {"totalitemcnt", GameScript::TotalItemCnt, 0}, //iwd2 + {"totalitemcntexclude", GameScript::TotalItemCntExclude, 0}, //iwd2 + {"totalitemcntexcludegt", GameScript::TotalItemCntExcludeGT, 0}, //iwd2 + {"totalitemcntexcludelt", GameScript::TotalItemCntExcludeLT, 0}, //iwd2 + {"totalitemcntgt", GameScript::TotalItemCntGT, 0}, //iwd2 + {"totalitemcntlt", GameScript::TotalItemCntLT, 0}, //iwd2 + {"traptriggered", GameScript::TrapTriggered, 0}, + {"trigger", GameScript::TriggerTrigger, 0}, + {"triggerclick", GameScript::Clicked, 0}, //not sure + {"triggersetglobal", GameScript::TriggerSetGlobal,0}, //iwd2, but never used + {"true", GameScript::True, 0}, + {"turnedby", GameScript::TurnedBy, 0}, + {"unlocked", GameScript::Unlocked, 0}, + {"unselectablevariable", GameScript::UnselectableVariable, 0}, + {"unselectablevariablegt", GameScript::UnselectableVariableGT, 0}, + {"unselectablevariablelt", GameScript::UnselectableVariableLT, 0}, + {"unusable",GameScript::Unusable, 0}, + {"vacant",GameScript::Vacant, 0}, + {"walkedtotrigger", GameScript::WalkedToTrigger, 0}, + {"wasindialog", GameScript::WasInDialog, 0}, + {"xor", GameScript::Xor,TF_MERGESTRINGS}, + {"xp", GameScript::XP, 0}, + {"xpgt", GameScript::XPGT, 0}, + {"xplt", GameScript::XPLT, 0}, + { NULL,NULL,0} +}; + +//Make this an ordered list, so we could use bsearch! +static const ActionLink actionnames[] = { + {"actionoverride",NULL, AF_INVALID}, //will this function ever be reached + {"activate", GameScript::Activate, 0}, + {"activateportalcursor", GameScript::ActivatePortalCursor, 0}, + {"addareaflag", GameScript::AddAreaFlag, 0}, + {"addareatype", GameScript::AddAreaType, 0}, + {"addexperienceparty", GameScript::AddExperienceParty, 0}, + {"addexperiencepartycr", GameScript::AddExperiencePartyCR, 0}, + {"addexperiencepartyglobal", GameScript::AddExperiencePartyGlobal, 0}, + {"addfeat", GameScript::AddFeat, 0}, + {"addglobals", GameScript::AddGlobals, 0}, + {"addhp", GameScript::AddHP, 0}, + {"addjournalentry", GameScript::AddJournalEntry, 0}, + {"addkit", GameScript::AddKit, 0}, + {"addmapnote", GameScript::AddMapnote, 0}, + {"addpartyexperience", GameScript::AddExperienceParty, 0}, + {"addspecialability", GameScript::AddSpecialAbility, 0}, + {"addsuperkit", GameScript::AddSuperKit, 0}, + {"addwaypoint", GameScript::AddWayPoint,AF_BLOCKING}, + {"addxp2da", GameScript::AddXP2DA, 0}, + {"addxpobject", GameScript::AddXPObject, 0}, + {"addxpvar", GameScript::AddXP2DA, 0}, + {"advancetime", GameScript::AdvanceTime, 0}, + {"allowarearesting", GameScript::SetAreaRestFlag, 0},//iwd2 + {"ally", GameScript::Ally, 0}, + {"ambientactivate", GameScript::AmbientActivate, 0}, + {"ankhegemerge", GameScript::AnkhegEmerge, AF_ALIVE}, + {"ankheghide", GameScript::AnkhegHide, AF_ALIVE}, + {"applydamage", GameScript::ApplyDamage, 0}, + {"applydamagepercent", GameScript::ApplyDamagePercent, 0}, + {"applyspell", GameScript::ApplySpell, 0}, + {"applyspellpoint", GameScript::ApplySpellPoint, 0}, //gemrb extension + {"attachtransitiontodoor", GameScript::AttachTransitionToDoor, 0}, + {"attack", GameScript::Attack,AF_BLOCKING|AF_ALIVE}, + {"attacknosound", GameScript::AttackNoSound,AF_BLOCKING|AF_ALIVE}, //no sound yet anyway + {"attackoneround", GameScript::AttackOneRound,AF_BLOCKING|AF_ALIVE}, + {"attackreevaluate", GameScript::AttackReevaluate,AF_BLOCKING|AF_ALIVE}, + {"backstab", GameScript::Attack,AF_BLOCKING|AF_ALIVE},//actually hide+attack + {"banterblockflag", GameScript::BanterBlockFlag,0}, + {"banterblocktime", GameScript::BanterBlockTime,0}, + {"bashdoor", GameScript::BashDoor,AF_BLOCKING|AF_ALIVE}, //the same until we know better + {"battlesong", GameScript::BattleSong, AF_ALIVE}, + {"berserk", GameScript::Berserk, AF_ALIVE}, + {"bitclear", GameScript::BitClear,AF_MERGESTRINGS}, + {"bitglobal", GameScript::BitGlobal,AF_MERGESTRINGS}, + {"bitset", GameScript::GlobalBOr,AF_MERGESTRINGS}, //probably the same + {"breakinstants", GameScript::BreakInstants, AF_BLOCKING},//delay execution of instants to the next AI cycle??? + {"calllightning", GameScript::Kill, 0}, //TODO: call lightning projectile + {"calm", GameScript::Calm, 0}, + {"changeaiscript", GameScript::ChangeAIScript, 0}, + {"changeaitype", GameScript::ChangeAIType, 0}, + {"changealignment", GameScript::ChangeAlignment, 0}, + {"changeallegiance", GameScript::ChangeAllegiance, 0}, + {"changeanimation", GameScript::ChangeAnimation, 0}, + {"changeanimationnoeffect", GameScript::ChangeAnimationNoEffect, 0}, + {"changeclass", GameScript::ChangeClass, 0}, + {"changecolor", GameScript::ChangeColor, 0}, + {"changecurrentscript", GameScript::ChangeAIScript,AF_SCRIPTLEVEL}, + {"changedestination", GameScript::ChangeDestination,0}, //gemrb extension (iwd hack) + {"changedialog", GameScript::ChangeDialogue, 0}, + {"changedialogue", GameScript::ChangeDialogue, 0}, + {"changegender", GameScript::ChangeGender, 0}, + {"changegeneral", GameScript::ChangeGeneral, 0}, + {"changeenemyally", GameScript::ChangeAllegiance, 0}, //this is the same + {"changefaction", GameScript::SetFaction, 0}, //pst + {"changerace", GameScript::ChangeRace, 0}, + {"changespecifics", GameScript::ChangeSpecifics, 0}, + {"changestat", GameScript::ChangeStat, 0}, + {"changestatglobal", GameScript::ChangeStatGlobal, 0}, + {"changestoremarkup", GameScript::ChangeStoreMarkup, 0},//iwd2 + {"changeteam", GameScript::SetTeam, 0}, //pst + {"changetilestate", GameScript::ChangeTileState, 0}, //bg2 + {"chunkcreature", GameScript::Kill, 0}, //should be more graphical + {"clearactions", GameScript::ClearActions, 0}, + {"clearallactions", GameScript::ClearAllActions, 0}, + {"clearpartyeffects", GameScript::ClearPartyEffects, 0}, + {"clearspriteeffects", GameScript::ClearSpriteEffects, 0}, + {"clicklbuttonobject", GameScript::ClickLButtonObject, AF_BLOCKING}, + {"clicklbuttonpoint", GameScript::ClickLButtonPoint, AF_BLOCKING}, + {"clickrbuttonobject", GameScript::ClickLButtonObject, AF_BLOCKING}, + {"clickrbuttonpoint", GameScript::ClickLButtonPoint, AF_BLOCKING}, + {"closedoor", GameScript::CloseDoor,0}, + {"containerenable", GameScript::ContainerEnable, 0}, + {"continue", GameScript::Continue,AF_IMMEDIATE | AF_CONTINUE}, + {"copygroundpilesto", GameScript::CopyGroundPilesTo, 0}, + {"createcreature", GameScript::CreateCreature, 0}, //point is relative to Sender + {"createcreaturecopypoint", GameScript::CreateCreatureCopyPoint, 0}, //point is relative to Sender + {"createcreaturedoor", GameScript::CreateCreatureDoor, 0}, + {"createcreatureatfeet", GameScript::CreateCreatureAtFeet, 0}, + {"createcreatureatlocation", GameScript::CreateCreatureAtLocation, 0}, + {"createcreatureimpassable", GameScript::CreateCreatureImpassable, 0}, + {"createcreatureimpassableallowoverlap", GameScript::CreateCreatureImpassableAllowOverlap, 0}, + {"createcreatureobject", GameScript::CreateCreatureObjectOffset, 0}, //the same + {"createcreatureobjectcopy", GameScript::CreateCreatureObjectCopy, 0}, + {"createcreatureobjectcopyeffect", GameScript::CreateCreatureObjectCopy, 0}, //the same + {"createcreatureobjectdoor", GameScript::CreateCreatureObjectDoor, 0},//same as createcreatureobject, but with dimension door animation + {"createcreatureobjectoffscreen", GameScript::CreateCreatureObjectOffScreen, 0}, //same as createcreature object, but starts looking for a place far away from the player + {"createcreatureobjectoffset", GameScript::CreateCreatureObjectOffset, 0}, //the same + {"createcreatureoffscreen", GameScript::CreateCreatureOffScreen, 0}, + {"createitem", GameScript::CreateItem, 0}, + {"createitemglobal", GameScript::CreateItemNumGlobal, 0}, + {"createitemnumglobal", GameScript::CreateItemNumGlobal, 0}, + {"createpartygold", GameScript::CreatePartyGold, 0}, + {"createvisualeffect", GameScript::CreateVisualEffect, 0}, + {"createvisualeffectobject", GameScript::CreateVisualEffectObject, 0}, + {"createvisualeffectobjectSticky", GameScript::CreateVisualEffectObjectSticky, 0}, + {"cutsceneid", GameScript::CutSceneID,0}, + {"damage", GameScript::Damage, 0}, + {"daynight", GameScript::DayNight, 0}, + {"deactivate", GameScript::Deactivate, 0}, + {"debug", GameScript::Debug, 0}, + {"debugoutput", GameScript::Debug, 0}, + {"deletejournalentry", GameScript::RemoveJournalEntry, 0}, + {"demoend", GameScript::QuitGame, 0}, //same for now + {"destroyalldestructableequipment", GameScript::DestroyAllDestructableEquipment, 0}, + {"destroyallequipment", GameScript::DestroyAllEquipment, 0}, + {"destroygold", GameScript::DestroyGold, 0}, + {"destroyitem", GameScript::DestroyItem, 0}, + {"destroypartygold", GameScript::DestroyPartyGold, 0}, + {"destroypartyitem", GameScript::DestroyPartyItem, 0}, + {"destroyself", GameScript::DestroySelf, 0}, + {"detectsecretdoor", GameScript::DetectSecretDoor, 0}, + {"dialog", GameScript::Dialogue,AF_BLOCKING}, + {"dialogforceinterrupt", GameScript::DialogueForceInterrupt,AF_BLOCKING}, + {"dialoginterrupt", GameScript::DialogueInterrupt,0}, + {"dialogue", GameScript::Dialogue,AF_BLOCKING}, + {"dialogueforceinterrupt", GameScript::DialogueForceInterrupt,AF_BLOCKING}, + {"dialogueinterrupt", GameScript::DialogueInterrupt,0}, + {"disablefogdither", GameScript::DisableFogDither, 0}, + {"disablespritedither", GameScript::DisableSpriteDither, 0}, + {"displaymessage", GameScript::DisplayMessage, 0}, + {"displaystring", GameScript::DisplayString, 0}, + {"displaystringhead", GameScript::DisplayStringHead, 0}, + {"displaystringheadowner", GameScript::DisplayStringHeadOwner, 0}, + {"displaystringheaddead", GameScript::DisplayStringHead, 0}, //same? + {"displaystringnoname", GameScript::DisplayStringNoName, 0}, + {"displaystringnonamehead", GameScript::DisplayStringNoNameHead, 0}, + {"displaystringwait", GameScript::DisplayStringWait,AF_BLOCKING}, + {"doubleclicklbuttonobject", GameScript::DoubleClickLButtonObject, AF_BLOCKING}, + {"doubleclicklbuttonpoint", GameScript::DoubleClickLButtonPoint, AF_BLOCKING}, + {"doubleclickrbuttonobject", GameScript::DoubleClickLButtonObject, AF_BLOCKING}, + {"doubleclickrbuttonpoint", GameScript::DoubleClickLButtonPoint, AF_BLOCKING}, + {"dropinventory", GameScript::DropInventory, 0}, + {"dropinventoryex", GameScript::DropInventoryEX, 0}, + {"dropinventoryexexclude", GameScript::DropInventoryEX, 0}, //same + {"dropitem", GameScript::DropItem, AF_BLOCKING}, + {"enablefogdither", GameScript::EnableFogDither, 0}, + {"enableportaltravel", GameScript::EnablePortalTravel, 0}, + {"enablespritedither", GameScript::EnableSpriteDither, 0}, + {"endcredits", GameScript::EndCredits, 0},//movie + {"endcutscenemode", GameScript::EndCutSceneMode, 0}, + {"endgame", GameScript::QuitGame, 0}, //ending in iwd2 + {"enemy", GameScript::Enemy, 0}, + {"equipitem", GameScript::EquipItem, 0}, + {"equipmostdamagingmelee",GameScript::EquipMostDamagingMelee,0}, + {"equipranged", GameScript::EquipRanged,0}, + {"equipweapon", GameScript::EquipWeapon,0}, + {"erasejournalentry", GameScript::RemoveJournalEntry, 0}, + {"escapearea", GameScript::EscapeArea, AF_BLOCKING}, + {"escapeareadestroy", GameScript::EscapeAreaDestroy, AF_BLOCKING}, + {"escapeareanosee", GameScript::EscapeAreaNoSee, AF_BLOCKING}, + {"escapeareaobject", GameScript::EscapeAreaObject, AF_BLOCKING}, + {"escapeareaobjectnosee", GameScript::EscapeAreaObjectNoSee, AF_BLOCKING}, + {"exitpocketplane", GameScript::ExitPocketPlane, 0}, + {"expansionendcredits", GameScript::QuitGame, 0},//ends game too + {"explore", GameScript::Explore, 0}, + {"exploremapchunk", GameScript::ExploreMapChunk, 0}, + {"exportparty", GameScript::ExportParty, 0}, + {"face", GameScript::Face,AF_BLOCKING}, + {"faceobject", GameScript::FaceObject, AF_BLOCKING}, + {"facesavedlocation", GameScript::FaceSavedLocation, AF_BLOCKING}, + {"fadefromblack", GameScript::FadeFromColor, AF_BLOCKING}, //probably the same + {"fadefromcolor", GameScript::FadeFromColor, AF_BLOCKING}, + {"fadetoandfromcolor", GameScript::FadeToAndFromColor, AF_BLOCKING}, + {"fadetoblack", GameScript::FadeToColor, AF_BLOCKING}, //probably the same + {"fadetocolor", GameScript::FadeToColor, AF_BLOCKING}, + {"fakeeffectexpirycheck", GameScript::FakeEffectExpiryCheck, 0}, + {"fillslot", GameScript::FillSlot, 0}, + {"finalsave", GameScript::SaveGame, 0}, //synonym + {"findtraps", GameScript::FindTraps, 0}, + {"floatmessage", GameScript::DisplayStringHead, 0}, + {"floatmessagefixed", GameScript::FloatMessageFixed, 0}, + {"floatmessagefixedrnd", GameScript::FloatMessageFixedRnd, 0}, + {"floatmessagernd", GameScript::FloatMessageRnd, 0}, + {"floatrebus", GameScript::FloatRebus, 0}, + {"follow", GameScript::Follow, AF_ALIVE}, + {"followcreature", GameScript::FollowCreature, AF_BLOCKING|AF_ALIVE}, //pst + {"followobjectformation", GameScript::FollowObjectFormation, AF_BLOCKING|AF_ALIVE}, + {"forceaiscript", GameScript::ForceAIScript, 0}, + {"forceattack", GameScript::ForceAttack, 0}, + {"forcefacing", GameScript::ForceFacing, 0}, + {"forcehide", GameScript::ForceHide, 0}, + {"forceleavearealua", GameScript::ForceLeaveAreaLUA, 0}, + {"forcemarkedspell", GameScript::ForceMarkedSpell, 0}, + {"forcespell", GameScript::ForceSpell, AF_BLOCKING}, + {"forcespellpoint", GameScript::ForceSpellPoint, AF_BLOCKING}, + {"forceusecontainer", GameScript::ForceUseContainer,AF_BLOCKING}, + {"formation", GameScript::Formation, AF_BLOCKING}, + {"fullheal", GameScript::FullHeal, 0}, + {"fullhealex", GameScript::FullHeal, 0}, //pst, not sure what's different + {"generatepartymember", GameScript::GeneratePartyMember, 0}, + {"getitem", GameScript::GetItem, 0}, + {"getstat", GameScript::GetStat, 0}, //gemrb specific + {"giveexperience", GameScript::AddXPObject, 0}, + {"givegoldforce", GameScript::CreatePartyGold, 0}, //this is the same + {"giveitem", GameScript::GiveItem, 0}, + {"giveitemcreate", GameScript::CreateItem, 0}, //actually this is a targeted createitem + {"giveorder", GameScript::GiveOrder, 0}, + {"givepartyallequipment", GameScript::GivePartyAllEquipment, 0}, + {"givepartygold", GameScript::GivePartyGold, 0}, + {"givepartygoldglobal", GameScript::GivePartyGoldGlobal,0},//no mergestrings! + {"globaladdglobal", GameScript::GlobalAddGlobal,AF_MERGESTRINGS}, + {"globalandglobal", GameScript::GlobalAndGlobal,AF_MERGESTRINGS}, + {"globalband", GameScript::GlobalBAnd,AF_MERGESTRINGS}, + {"globalbandglobal", GameScript::GlobalBAndGlobal,AF_MERGESTRINGS}, + {"globalbitglobal", GameScript::GlobalBitGlobal, AF_MERGESTRINGS}, + {"globalbor", GameScript::GlobalBOr,AF_MERGESTRINGS}, + {"globalborglobal", GameScript::GlobalBOrGlobal,AF_MERGESTRINGS}, + {"globalmax", GameScript::GlobalMax,AF_MERGESTRINGS}, + {"globalmaxglobal", GameScript::GlobalMaxGlobal,AF_MERGESTRINGS}, + {"globalmin", GameScript::GlobalMin,AF_MERGESTRINGS}, + {"globalminglobal", GameScript::GlobalMinGlobal,AF_MERGESTRINGS}, + {"globalorglobal", GameScript::GlobalOrGlobal,AF_MERGESTRINGS}, + {"globalset", GameScript::SetGlobal,AF_MERGESTRINGS}, + {"globalsetglobal", GameScript::GlobalSetGlobal,AF_MERGESTRINGS}, + {"globalshl", GameScript::GlobalShL,AF_MERGESTRINGS}, + {"globalshlglobal", GameScript::GlobalShLGlobal,AF_MERGESTRINGS}, + {"globalshout", GameScript::GlobalShout, 0}, + {"globalshr", GameScript::GlobalShR,AF_MERGESTRINGS}, + {"globalshrglobal", GameScript::GlobalShRGlobal,AF_MERGESTRINGS}, + {"globalsubglobal", GameScript::GlobalSubGlobal,AF_MERGESTRINGS}, + {"globalxor", GameScript::GlobalXor,AF_MERGESTRINGS}, + {"globalxorglobal", GameScript::GlobalXorGlobal,AF_MERGESTRINGS}, + {"gotostartscreen", GameScript::QuitGame, 0},//ending + {"help", GameScript::Help, 0}, + {"hide", GameScript::Hide, 0}, + {"hideareaonmap", GameScript::HideAreaOnMap, 0}, + {"hidecreature", GameScript::HideCreature, 0}, + {"hidegui", GameScript::HideGUI, 0}, + {"incinternal", GameScript::IncInternal, 0}, //pst + {"incrementinternal", GameScript::IncInternal, 0},//iwd + {"incmoraleai", GameScript::IncMoraleAI, 0}, + {"incrementchapter", GameScript::IncrementChapter, AF_BLOCKING}, + {"incrementextraproficiency", GameScript::IncrementExtraProficiency, 0}, + {"incrementglobal", GameScript::IncrementGlobal,AF_MERGESTRINGS}, + {"incrementglobalonce", GameScript::IncrementGlobalOnce,AF_MERGESTRINGS}, + {"incrementkillstat", GameScript::IncrementKillStat, 0}, + {"incrementproficiency", GameScript::IncrementProficiency, 0}, + {"interact", GameScript::Interact, 0}, + {"joinparty", GameScript::JoinParty, 0}, //this action appears to be blocking in bg2 + {"journalentrydone", GameScript::SetQuestDone, 0}, + {"jumptoobject", GameScript::JumpToObject, 0}, + {"jumptopoint", GameScript::JumpToPoint, 0}, + {"jumptopointinstant", GameScript::JumpToPointInstant, 0}, + {"jumptosavedlocation", GameScript::JumpToSavedLocation, 0}, + {"kill", GameScript::Kill, 0}, + {"killfloatmessage", GameScript::KillFloatMessage, 0}, + {"leader", GameScript::Leader, AF_ALIVE}, + {"leavearea", GameScript::LeaveAreaLUA, 0}, //so far the same + {"leavearealua", GameScript::LeaveAreaLUA, 0}, + {"leavearealuaentry", GameScript::LeaveAreaLUAEntry,AF_BLOCKING}, + {"leavearealuapanic", GameScript::LeaveAreaLUAPanic, 0}, + {"leavearealuapanicentry", GameScript::LeaveAreaLUAPanicEntry,AF_BLOCKING}, + {"leaveparty", GameScript::LeaveParty, 0}, + {"lock", GameScript::Lock, 0},//key not checked at this time! + {"lockscroll", GameScript::LockScroll, 0}, + {"log", GameScript::Debug, 0}, //the same until we know better + {"makeglobal", GameScript::MakeGlobal, 0}, + {"makeunselectable", GameScript::MakeUnselectable, 0}, + {"markobject", GameScript::MarkObject, 0}, + {"markspellandobject", GameScript::MarkSpellAndObject, 0}, + {"moraledec", GameScript::MoraleDec, 0}, + {"moraleinc", GameScript::MoraleInc, 0}, + {"moraleset", GameScript::MoraleSet, 0}, + {"matchhp", GameScript::MatchHP, 0}, + {"movebetweenareas", GameScript::MoveBetweenAreas, 0}, + {"movebetweenareaseffect", GameScript::MoveBetweenAreas, 0}, + {"movecursorpoint", GameScript::MoveCursorPoint, 0},//immediate move + {"moveglobal", GameScript::MoveGlobal, 0}, + {"moveglobalobject", GameScript::MoveGlobalObject, 0}, + {"moveglobalobjectoffscreen", GameScript::MoveGlobalObjectOffScreen, 0}, + {"moveglobalsto", GameScript::MoveGlobalsTo, 0}, + {"transferinventory", GameScript::MoveInventory, 0}, + {"movetocenterofscreen", GameScript::MoveToCenterOfScreen,AF_BLOCKING}, + {"movetoexpansion", GameScript::MoveToExpansion,AF_BLOCKING}, + {"movetoobject", GameScript::MoveToObject,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectfollow", GameScript::MoveToObjectFollow,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectnointerrupt", GameScript::MoveToObjectNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"movetoobjectuntilsee", GameScript::MoveToObjectUntilSee,AF_BLOCKING|AF_ALIVE}, + {"movetooffset", GameScript::MoveToOffset,AF_BLOCKING|AF_ALIVE}, + {"movetopoint", GameScript::MoveToPoint,AF_BLOCKING|AF_ALIVE}, + {"movetopointnointerrupt", GameScript::MoveToPointNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"movetopointnorecticle", GameScript::MoveToPointNoRecticle,AF_BLOCKING|AF_ALIVE},//the same until we know better + {"movetosavedlocation", GameScript::MoveToSavedLocation,AF_MERGESTRINGS|AF_BLOCKING}, + //take care of the typo in the original bg2 action.ids + //FIXME: why doesn't this have MERGESTRINGS like the above entry? + {"movetosavedlocationn", GameScript::MoveToSavedLocation,AF_BLOCKING}, + {"moveviewobject", GameScript::MoveViewObject, AF_BLOCKING}, + {"moveviewpoint", GameScript::MoveViewPoint, AF_BLOCKING}, + {"moveviewpointuntildone", GameScript::MoveViewPoint, 0}, + {"nidspecial1", GameScript::NIDSpecial1,AF_BLOCKING|AF_DIRECT|AF_ALIVE},//we use this for dialogs, hack + {"nidspecial2", GameScript::NIDSpecial2,AF_BLOCKING},//we use this for worldmap, another hack + {"nidspecial3", GameScript::Attack,AF_BLOCKING|AF_DIRECT|AF_ALIVE},//this hack is for attacking preset target + {"nidspecial4", GameScript::ProtectObject,AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial5", GameScript::UseItem, AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial6", GameScript::Spell, AF_BLOCKING|AF_DIRECT|AF_ALIVE}, + {"nidspecial7", GameScript::UseItemPoint, AF_BLOCKING|AF_ALIVE}, + {"nidspecial8", GameScript::SpellPoint, AF_BLOCKING|AF_ALIVE}, + {"nidspecial9", GameScript::ToggleDoor, AF_BLOCKING},//another internal hack, maybe we should use UseDoor instead + {"noaction", GameScript::NoAction, 0}, + {"opendoor", GameScript::OpenDoor,0}, + {"panic", GameScript::Panic, AF_ALIVE}, + {"permanentstatchange", GameScript::PermanentStatChange, 0}, //pst + {"pausegame", GameScript::PauseGame, AF_BLOCKING}, //this is almost surely blocking + {"picklock", GameScript::PickLock,AF_BLOCKING}, + {"pickpockets", GameScript::PickPockets, AF_BLOCKING}, + {"pickupitem", GameScript::PickUpItem, 0}, + {"playbardsong", GameScript::PlayBardSong, AF_ALIVE}, + {"playdead", GameScript::PlayDead,AF_BLOCKING|AF_ALIVE}, + {"playdeadinterruptable", GameScript::PlayDeadInterruptable,AF_BLOCKING|AF_ALIVE}, + {"playerdialog", GameScript::PlayerDialogue,AF_BLOCKING}, + {"playerdialogue", GameScript::PlayerDialogue,AF_BLOCKING}, + {"playsequence", GameScript::PlaySequence, 0}, + {"playsequencetimed", GameScript::PlaySequenceTimed, 0},//pst + {"playsong", GameScript::StartSong, 0}, + {"playsound", GameScript::PlaySound, 0}, + {"playsoundnotranged", GameScript::PlaySoundNotRanged, 0}, + {"playsoundpoint", GameScript::PlaySoundPoint, 0}, + {"plunder", GameScript::Plunder,AF_BLOCKING|AF_ALIVE}, + {"polymorph", GameScript::Polymorph, 0}, + {"polymorphcopy", GameScript::PolymorphCopy, 0}, + {"polymorphcopybase", GameScript::PolymorphCopyBase, 0}, + {"protectobject", GameScript::ProtectObject, 0}, + {"protectpoint", GameScript::ProtectPoint, AF_BLOCKING}, + {"quitgame", GameScript::QuitGame, 0}, + {"randomfly", GameScript::RandomFly, AF_BLOCKING|AF_ALIVE}, + {"randomrun", GameScript::RandomRun, AF_BLOCKING|AF_ALIVE}, + {"randomturn", GameScript::RandomTurn, AF_BLOCKING}, + {"randomwalk", GameScript::RandomWalk, AF_BLOCKING|AF_ALIVE}, + {"randomwalkcontinuous", GameScript::RandomWalkContinuous, AF_BLOCKING|AF_ALIVE}, + {"realsetglobaltimer", GameScript::RealSetGlobalTimer,AF_MERGESTRINGS}, + {"reallyforcespell", GameScript::ReallyForceSpell, AF_BLOCKING}, + {"reallyforcespelldead", GameScript::ReallyForceSpellDead, AF_BLOCKING}, + {"reallyforcespelllevel", GameScript::ReallyForceSpell, AF_BLOCKING},//this is the same action + {"reallyforcespellpoint", GameScript::ReallyForceSpellPoint, AF_BLOCKING}, + {"recoil", GameScript::Recoil, AF_ALIVE}, + {"regainpaladinhood", GameScript::RegainPaladinHood, 0}, + {"regainrangerhood", GameScript::RegainRangerHood, 0}, + {"removeareaflag", GameScript::RemoveAreaFlag, 0}, + {"removeareatype", GameScript::RemoveAreaType, 0}, + {"removejournalentry", GameScript::RemoveJournalEntry, 0}, + {"removemapnote", GameScript::RemoveMapnote, 0}, + {"removepaladinhood", GameScript::RemovePaladinHood, 0}, + {"removerangerhood", GameScript::RemoveRangerHood, 0}, + {"removespell", GameScript::RemoveSpell, 0}, + {"removetraps", GameScript::RemoveTraps, AF_BLOCKING}, + {"reputationinc", GameScript::ReputationInc, 0}, + {"reputationset", GameScript::ReputationSet, 0}, + {"resetfogofwar", GameScript::UndoExplore, 0}, //pst + {"rest", GameScript::Rest, AF_ALIVE}, + {"restnospells", GameScript::RestNoSpells, 0}, + {"restorepartylocations", GameScript:: RestorePartyLocation, 0}, + {"restparty", GameScript::RestParty, 0}, + {"restuntilhealed", GameScript::RestUntilHealed, 0}, + //this is in iwd2, same as movetosavedlocation, but with stats + {"returntosavedlocation", GameScript::ReturnToSavedLocation, AF_BLOCKING|AF_ALIVE}, + {"returntosavedlocationdelete", GameScript::ReturnToSavedLocationDelete, AF_BLOCKING|AF_ALIVE}, + {"returntosavedplace", GameScript::ReturnToSavedLocation, AF_BLOCKING|AF_ALIVE}, + {"revealareaonmap", GameScript::RevealAreaOnMap, 0}, + {"runawayfrom", GameScript::RunAwayFrom,AF_BLOCKING|AF_ALIVE}, + {"runawayfromnointerrupt", GameScript::RunAwayFromNoInterrupt,AF_BLOCKING|AF_ALIVE}, + {"runawayfromnoleavearea", GameScript::RunAwayFromNoLeaveArea,AF_BLOCKING|AF_ALIVE}, + {"runawayfrompoint", GameScript::RunAwayFromPoint,AF_BLOCKING|AF_ALIVE}, + {"runfollow", GameScript::RunAwayFrom,AF_BLOCKING|AF_ALIVE}, + {"runningattack", GameScript::RunningAttack,AF_BLOCKING|AF_ALIVE}, + {"runningattacknosound", GameScript::RunningAttackNoSound,AF_BLOCKING|AF_ALIVE}, + {"runtoobject", GameScript::RunToObject,AF_BLOCKING|AF_ALIVE}, + {"runtopoint", GameScript::RunToPoint,AF_BLOCKING}, + {"runtopointnorecticle", GameScript::RunToPointNoRecticle,AF_BLOCKING|AF_ALIVE}, + {"runtosavedlocation", GameScript::RunToSavedLocation,AF_BLOCKING|AF_ALIVE}, + {"savegame", GameScript::SaveGame, 0}, + {"savelocation", GameScript::SaveLocation, 0}, + {"saveplace", GameScript::SaveLocation, 0}, + {"saveobjectlocation", GameScript::SaveObjectLocation, 0}, + {"screenshake", GameScript::ScreenShake,AF_BLOCKING}, + {"selectweaponability", GameScript::SelectWeaponAbility, 0}, + {"sendtrigger", GameScript::SendTrigger, 0}, + {"setanimstate", GameScript::PlaySequence, AF_ALIVE},//pst + {"setapparentnamestrref", GameScript::SetApparentName, 0}, + {"setareaflags", GameScript::SetAreaFlags, 0}, + {"setarearestflag", GameScript::SetAreaRestFlag, 0}, + {"setbeeninpartyflags", GameScript::SetBeenInPartyFlags, 0}, + {"setbestweapon", GameScript::SetBestWeapon, 0}, + {"setcorpseenabled", GameScript::AmbientActivate, 0},//another weird name + {"setcutsceneline", GameScript::SetCursorState, 0}, //same as next + {"setcursorstate", GameScript::SetCursorState, 0}, + {"setcreatureareaflag", GameScript::SetCreatureAreaFlag, 0}, + {"setcriticalpathobject", GameScript::SetCriticalPathObject, 0}, + {"setdialog", GameScript::SetDialogue,0}, + {"setdialogrange", GameScript::SetDialogueRange, 0}, + {"setdialogue", GameScript::SetDialogue,0}, + {"setdialoguerange", GameScript::SetDialogueRange, 0}, + {"setdoorflag", GameScript::SetDoorFlag,0}, + {"setdoorlocked", GameScript::SetDoorLocked,0}, + {"setencounterprobability", GameScript::SetEncounterProbability,0}, + {"setextendednight", GameScript::SetExtendedNight, 0}, + {"setfaction", GameScript::SetFaction, 0}, + {"setgabber", GameScript::SetGabber, 0}, + {"setglobal", GameScript::SetGlobal,AF_MERGESTRINGS}, + {"setglobalrandom", GameScript::SetGlobalRandom, AF_MERGESTRINGS}, + {"setglobaltimer", GameScript::SetGlobalTimer,AF_MERGESTRINGS}, + {"setglobaltimeronce", GameScript::SetGlobalTimerOnce,AF_MERGESTRINGS}, + {"setglobaltimerrandom", GameScript::SetGlobalTimerRandom,AF_MERGESTRINGS}, + {"setglobaltint", GameScript::SetGlobalTint, 0}, + {"sethomelocation", GameScript::SetSavedLocation, 0}, //bg2 + {"sethp", GameScript::SetHP, 0}, + {"sethppercent", GameScript::SetHPPercent, 0}, + {"setinternal", GameScript::SetInternal, 0}, + {"setinterrupt", GameScript::SetInterrupt, 0}, + {"setleavepartydialogfile", GameScript::SetLeavePartyDialogFile, 0}, + {"setleavepartydialoguefile", GameScript::SetLeavePartyDialogFile, 0}, + {"setmarkedspell", GameScript::SetMarkedSpell, 0}, + {"setmasterarea", GameScript::SetMasterArea, 0}, + {"setmazeeasier", GameScript::SetMazeEasier, 0}, //pst specific crap + {"setmazeharder", GameScript::SetMazeHarder, 0}, //pst specific crap + {"setmoraleai", GameScript::SetMoraleAI, 0}, + {"setmusic", GameScript::SetMusic, 0}, + {"setname", GameScript::SetApparentName, 0}, + {"setnamelessclass", GameScript::SetNamelessClass, 0}, + {"setnamelessdeath", GameScript::SetNamelessDeath, 0}, + {"setnamelessdisguise", GameScript::SetNamelessDisguise, 0}, + {"setnooneontrigger", GameScript::SetNoOneOnTrigger, 0}, + {"setnumtimestalkedto", GameScript::SetNumTimesTalkedTo, 0}, + {"setplayersound", GameScript::SetPlayerSound, 0}, + {"setquestdone", GameScript::SetQuestDone, 0}, + {"setregularnamestrref", GameScript::SetRegularName, 0}, + {"setrestencounterchance", GameScript::SetRestEncounterChance, 0}, + {"setrestencounterprobabilityday", GameScript::SetRestEncounterProbabilityDay, 0}, + {"setrestencounterprobabilitynight", GameScript::SetRestEncounterProbabilityNight, 0}, + {"setsavedlocation", GameScript::SetSavedLocation, 0}, + {"setsavedlocationpoint", GameScript::SetSavedLocationPoint, 0}, + {"setscriptname", GameScript::SetScriptName, 0}, + {"setselection", GameScript::SetSelection, 0}, + {"setsequence", GameScript::PlaySequence, 0}, //bg2 (only own) + {"setstartpos", GameScript::SetStartPos, 0}, + {"setteam", GameScript::SetTeam, 0}, + {"setteambit", GameScript::SetTeamBit, 0}, + {"settextcolor", GameScript::SetTextColor, 0}, + {"settrackstring", GameScript::SetTrackString, 0}, + {"settoken", GameScript::SetToken, 0}, + {"settoken2da", GameScript::SetToken2DA, 0}, //GemRB specific + {"settokenglobal", GameScript::SetTokenGlobal,AF_MERGESTRINGS}, + {"settokenobject", GameScript::SetTokenObject,0}, + {"setupwish", GameScript::SetupWish, 0}, + {"setupwishobject", GameScript::SetupWishObject, 0}, + {"setvisualrange", GameScript::SetVisualRange, 0}, + {"sg", GameScript::SG, 0}, + {"shout", GameScript::Shout, 0}, + {"sinisterpoof", GameScript::CreateVisualEffect, 0}, + {"smallwait", GameScript::SmallWait,AF_BLOCKING}, + {"smallwaitrandom", GameScript::SmallWaitRandom,AF_BLOCKING}, + {"soundactivate", GameScript::SoundActivate, 0}, + {"spawnptactivate", GameScript::SpawnPtActivate, 0}, + {"spawnptdeactivate", GameScript::SpawnPtDeactivate, 0}, + {"spawnptspawn", GameScript::SpawnPtSpawn, 0}, + {"spell", GameScript::Spell, AF_BLOCKING|AF_ALIVE}, + {"spellcasteffect", GameScript::SpellCastEffect, 0}, + {"spellhiteffectpoint", GameScript::SpellHitEffectPoint, 0}, + {"spellhiteffectsprite", GameScript::SpellHitEffectSprite, 0}, + {"spellnodec", GameScript::SpellNoDec, AF_BLOCKING|AF_ALIVE}, + {"spellpoint", GameScript::SpellPoint, AF_BLOCKING|AF_ALIVE}, + {"spellpointnodec", GameScript::SpellPointNoDec, AF_BLOCKING|AF_ALIVE}, + {"startcombatcounter", GameScript::StartCombatCounter, 0}, + {"startcutscene", GameScript::StartCutScene, 0}, + {"startcutsceneex", GameScript::StartCutScene, 0}, //pst (unknown) + {"startcutscenemode", GameScript::StartCutSceneMode, 0}, + {"startdialog", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialoginterrupt", GameScript::StartDialogueInterrupt,AF_BLOCKING}, + {"startdialogue", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialogueinterrupt", GameScript::StartDialogueInterrupt,AF_BLOCKING}, + {"startdialognoname", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialognoset", GameScript::StartDialogueNoSet,AF_BLOCKING}, + {"startdialognosetinterrupt", GameScript::StartDialogueNoSetInterrupt,AF_BLOCKING}, + {"startdialogoverride", GameScript::StartDialogueOverride,AF_BLOCKING}, + {"startdialogoverrideinterrupt", GameScript::StartDialogueOverrideInterrupt,AF_BLOCKING}, + {"startdialoguenoname", GameScript::StartDialogue,AF_BLOCKING}, + {"startdialoguenoset", GameScript::StartDialogueNoSet,AF_BLOCKING}, + {"startdialoguenosetinterrupt", GameScript::StartDialogueNoSetInterrupt,AF_BLOCKING}, + {"startdialogueoverride", GameScript::StartDialogueOverride,AF_BLOCKING}, + {"startdialogueoverrideinterrupt", GameScript::StartDialogueOverrideInterrupt,AF_BLOCKING}, + {"startmovie", GameScript::StartMovie,AF_BLOCKING}, + {"startmusic", GameScript::StartMusic, 0}, + {"startrainnow", GameScript::StartRainNow, 0}, + {"startrandomtimer", GameScript::StartRandomTimer, 0}, + {"startsong", GameScript::StartSong, 0}, + {"startstore", GameScript::StartStore, 0}, + {"starttimer", GameScript::StartTimer, 0}, + {"stateoverrideflag", GameScript::StateOverrideFlag, 0}, + {"stateoverridetime", GameScript::StateOverrideTime, 0}, + {"staticpalette", GameScript::StaticPalette, 0}, + {"staticsequence", GameScript::PlaySequence, 0},//bg2 animation sequence + {"staticstart", GameScript::StaticStart, 0}, + {"staticstop", GameScript::StaticStop, 0}, + {"stickysinisterpoof", GameScript::CreateVisualEffectObjectSticky, 0}, + {"stopmoving", GameScript::StopMoving, 0}, + {"storepartylocations", GameScript::StorePartyLocation, 0}, + {"swing", GameScript::Swing, AF_ALIVE}, + {"swingonce", GameScript::SwingOnce, AF_ALIVE}, + {"takeitemlist", GameScript::TakeItemList, 0}, + {"takeitemlistparty", GameScript::TakeItemListParty, 0}, + {"takeitemlistpartynum", GameScript::TakeItemListPartyNum, 0}, + {"takeitemreplace", GameScript::TakeItemReplace, 0}, + {"takepartygold", GameScript::TakePartyGold, 0}, + {"takepartyitem", GameScript::TakePartyItem, 0}, + {"takepartyitemall", GameScript::TakePartyItemAll, 0}, + {"takepartyitemnum", GameScript::TakePartyItemNum, 0}, + {"takepartyitemrange", GameScript::TakePartyItemRange, 0}, + {"teleportparty", GameScript::TeleportParty, 0}, + {"textscreen", GameScript::TextScreen, AF_BLOCKING}, + {"timedmovetopoint", GameScript::TimedMoveToPoint,AF_BLOCKING|AF_ALIVE}, + {"tomsstringdisplayer", GameScript::DisplayMessage, 0}, + {"transformitem", GameScript::TransformItem, 0}, + {"transformitemall", GameScript::TransformItemAll, 0}, + {"transformpartyitem", GameScript::TransformPartyItem, 0}, + {"transformpartyitemall", GameScript::TransformPartyItemAll, 0}, + {"triggeractivation", GameScript::TriggerActivation, 0}, + {"triggerwalkto", GameScript::MoveToObject,AF_BLOCKING|AF_ALIVE}, //something like this + {"turn", GameScript::Turn, 0}, + {"turnamt", GameScript::TurnAMT, AF_BLOCKING}, //relative Face() + {"undoexplore", GameScript::UndoExplore, 0}, + {"unhidegui", GameScript::UnhideGUI, 0}, + {"unloadarea", GameScript::UnloadArea, 0}, + {"unlock", GameScript::Unlock, 0}, + {"unlockscroll", GameScript::UnlockScroll, 0}, + {"unmakeglobal", GameScript::UnMakeGlobal, 0}, //this is a GemRB extension + {"usecontainer", GameScript::UseContainer,AF_BLOCKING}, + {"usedoor", GameScript::UseDoor,AF_BLOCKING}, + {"useitem", GameScript::UseItem,AF_BLOCKING}, + {"useitempoint", GameScript::UseItemPoint,AF_BLOCKING}, + {"useitempointslot", GameScript::UseItemPoint,AF_BLOCKING}, + {"useitemslot", GameScript::UseItem,AF_BLOCKING}, + {"vequip",GameScript::SetArmourLevel, 0}, + {"verbalconstant", GameScript::VerbalConstant, 0}, + {"verbalconstanthead", GameScript::VerbalConstantHead, 0}, + {"wait", GameScript::Wait, AF_BLOCKING}, + {"waitanimation", GameScript::WaitAnimation,AF_BLOCKING},//iwd2 + {"waitrandom", GameScript::WaitRandom, AF_BLOCKING}, + {"weather", GameScript::Weather, 0}, + {"xequipitem", GameScript::XEquipItem, 0}, + { NULL,NULL, 0} +}; + +//Make this an ordered list, so we could use bsearch! +static const ObjectLink objectnames[] = { + {"bestac", GameScript::BestAC}, + {"eighthnearest", GameScript::EighthNearest}, + {"eighthnearestdoor", GameScript::EighthNearestDoor}, + {"eighthnearestenemyof", GameScript::EighthNearestEnemyOf}, + {"eighthnearestenemyoftype", GameScript::EighthNearestEnemyOfType}, + {"eighthnearestmygroupoftype", GameScript::EighthNearestEnemyOfType}, + {"eigthnearestenemyof", GameScript::EighthNearestEnemyOf}, //typo in iwd + {"eigthnearestenemyoftype", GameScript::EighthNearestEnemyOfType}, //bg2 + {"eigthnearestmygroupoftype", GameScript::EighthNearestEnemyOfType},//bg2 + {"farthest", GameScript::Farthest}, + {"farthestenemyof", GameScript::FarthestEnemyOf}, + {"fifthnearest", GameScript::FifthNearest}, + {"fifthnearestdoor", GameScript::FifthNearestDoor}, + {"fifthnearestenemyof", GameScript::FifthNearestEnemyOf}, + {"fifthnearestenemyoftype", GameScript::FifthNearestEnemyOfType}, + {"fifthnearestmygroupoftype", GameScript::FifthNearestEnemyOfType}, + {"fourthnearest", GameScript::FourthNearest}, + {"fourthnearestdoor", GameScript::FourthNearestDoor}, + {"fourthnearestenemyof", GameScript::FourthNearestEnemyOf}, + {"fourthnearestenemyoftype", GameScript::FourthNearestEnemyOfType}, + {"fourthnearestmygroupoftype", GameScript::FourthNearestEnemyOfType}, + {"gabber", GameScript::Gabber}, + {"groupof", GameScript::GroupOf}, + {"lastattackerof", GameScript::LastAttackerOf}, + {"lastcommandedby", GameScript::LastCommandedBy}, + {"lastheardby", GameScript::LastHeardBy}, + {"lasthelp", GameScript::LastHelp}, + {"lasthitter", GameScript::LastHitter}, + {"lastmarkedobject", GameScript::LastMarkedObject}, + {"lastseenby", GameScript::LastSeenBy}, + {"lastsummonerof", GameScript::LastSummonerOf}, + {"lasttalkedtoby", GameScript::LastTalkedToBy}, + {"lasttargetedby", GameScript::LastTargetedBy}, + {"lasttrigger", GameScript::LastTrigger}, + {"leaderof", GameScript::LeaderOf}, + {"leastdamagedof", GameScript::LeastDamagedOf}, + {"marked", GameScript::LastMarkedObject}, //pst + {"mostdamagedof", GameScript::MostDamagedOf}, + {"myself", GameScript::Myself}, + {"mytarget", GameScript::MyTarget},//see lasttargetedby(myself) + {"nearest", GameScript::Nearest}, //actually this seems broken in IE and resolve as Myself + {"nearestdoor", GameScript::NearestDoor}, + {"nearestenemyof", GameScript::NearestEnemyOf}, + {"nearestenemyoftype", GameScript::NearestEnemyOfType}, + {"nearestenemysummoned", GameScript::NearestEnemySummoned}, + {"nearestmygroupoftype", GameScript::NearestMyGroupOfType}, + {"nearestpc", GameScript::NearestPC}, + {"ninthnearest", GameScript::NinthNearest}, + {"ninthnearestdoor", GameScript::NinthNearestDoor}, + {"ninthnearestenemyof", GameScript::NinthNearestEnemyOf}, + {"ninthnearestenemyoftype", GameScript::NinthNearestEnemyOfType}, + {"ninthnearestmygroupoftype", GameScript::NinthNearestMyGroupOfType}, + {"nothing", GameScript::Nothing}, + {"player1", GameScript::Player1}, + {"player1fill", GameScript::Player1Fill}, + {"player2", GameScript::Player2}, + {"player2fill", GameScript::Player2Fill}, + {"player3", GameScript::Player3}, + {"player3fill", GameScript::Player3Fill}, + {"player4", GameScript::Player4}, + {"player4fill", GameScript::Player4Fill}, + {"player5", GameScript::Player5}, + {"player5fill", GameScript::Player5Fill}, + {"player6", GameScript::Player6}, + {"player6fill", GameScript::Player6Fill}, + {"player7", GameScript::Player7}, + {"player7fill", GameScript::Player7Fill}, + {"player8", GameScript::Player8}, + {"player8fill", GameScript::Player8Fill}, + {"protectedby", GameScript::ProtectedBy}, + {"protectorof", GameScript::ProtectorOf}, + {"protagonist", GameScript::Protagonist}, + {"secondnearest", GameScript::SecondNearest}, + {"secondnearestdoor", GameScript::SecondNearestDoor}, + {"secondnearestenemyof", GameScript::SecondNearestEnemyOf}, + {"secondnearestenemyoftype", GameScript::SecondNearestEnemyOfType}, + {"secondnearestmygroupoftype", GameScript::SecondNearestMyGroupOfType}, + {"selectedcharacter", GameScript::SelectedCharacter}, + {"seventhnearest", GameScript::SeventhNearest}, + {"seventhnearestdoor", GameScript::SeventhNearestDoor}, + {"seventhnearestenemyof", GameScript::SeventhNearestEnemyOf}, + {"seventhnearestenemyoftype", GameScript::SeventhNearestEnemyOfType}, + {"seventhnearestmygroupoftype", GameScript::SeventhNearestMyGroupOfType}, + {"sixthnearest", GameScript::SixthNearest}, + {"sixthnearestdoor", GameScript::SixthNearestDoor}, + {"sixthnearestenemyof", GameScript::SixthNearestEnemyOf}, + {"sixthnearestenemyoftype", GameScript::SixthNearestEnemyOfType}, + {"sixthnearestmygroupoftype", GameScript::SixthNearestMyGroupOfType}, + {"strongestof", GameScript::StrongestOf}, + {"strongestofmale", GameScript::StrongestOfMale}, + {"tenthnearest", GameScript::TenthNearest}, + {"tenthnearestdoor", GameScript::TenthNearestDoor}, + {"tenthnearestenemyof", GameScript::TenthNearestEnemyOf}, + {"tenthnearestenemyoftype", GameScript::TenthNearestEnemyOfType}, + {"tenthnearestmygroupoftype", GameScript::TenthNearestMyGroupOfType}, + {"thirdnearest", GameScript::ThirdNearest}, + {"thirdnearestdoor", GameScript::ThirdNearestDoor}, + {"thirdnearestenemyof", GameScript::ThirdNearestEnemyOf}, + {"thirdnearestenemyoftype", GameScript::ThirdNearestEnemyOfType}, + {"thirdnearestmygroupoftype", GameScript::ThirdNearestMyGroupOfType}, + {"weakestof", GameScript::WeakestOf}, + {"worstac", GameScript::WorstAC}, + { NULL,NULL} +}; + +static const IDSLink idsnames[] = { + {"align", GameScript::ID_Alignment}, + {"alignmen", GameScript::ID_Alignment}, + {"alignmnt", GameScript::ID_Alignment}, + {"class20", GameScript::ID_AVClass}, + {"class", GameScript::ID_Class}, + {"classmsk", GameScript::ID_ClassMask}, + {"ea", GameScript::ID_Allegiance}, + {"faction", GameScript::ID_Faction}, + {"gender", GameScript::ID_Gender}, + {"general", GameScript::ID_General}, + {"race", GameScript::ID_Race}, + {"specific", GameScript::ID_Specific}, + {"subrace", GameScript::ID_Subrace}, + {"team", GameScript::ID_Team}, + { NULL,NULL} +}; + +static const TriggerLink* FindTrigger(const char* triggername) +{ + if (!triggername) { + return NULL; + } + int len = strlench( triggername, '(' ); + for (int i = 0; triggernames[i].Name; i++) { + if (!strnicmp( triggernames[i].Name, triggername, len )) { + if (!triggernames[i].Name[len]) { + return triggernames + i; + } + } + } + return NULL; +} + +static const ActionLink* FindAction(const char* actionname) +{ + if (!actionname) { + return NULL; + } + int len = strlench( actionname, '(' ); + for (int i = 0; actionnames[i].Name; i++) { + if (!strnicmp( actionnames[i].Name, actionname, len )) { + if (!actionnames[i].Name[len]) { + return actionnames + i; + } + } + } + return NULL; +} + +static const ObjectLink* FindObject(const char* objectname) +{ + if (!objectname) { + return NULL; + } + int len = strlench( objectname, '(' ); + for (int i = 0; objectnames[i].Name; i++) { + if (!strnicmp( objectnames[i].Name, objectname, len )) { + if (!objectnames[i].Name[len]) { + return objectnames + i; + } + } + } + return NULL; +} + +static const IDSLink* FindIdentifier(const char* idsname) +{ + if (!idsname) { + return NULL; + } + int len = (int)strlen( idsname ); + for (int i = 0; idsnames[i].Name; i++) { + if (!strnicmp( idsnames[i].Name, idsname, len )) { + return idsnames + i; + } + } + + printMessage( "GameScript"," ", YELLOW ); + printf( "Couldn't assign ids target: %.*s\n", len, idsname ); + return NULL; +} + +void SetScriptDebugMode(int arg) +{ + InDebug=arg; +} + + + +/********************** Targets **********************************/ + +int Targets::Count() const +{ + return (int)objects.size(); +} + +targettype *Targets::RemoveTargetAt(targetlist::iterator &m) +{ + m=objects.erase(m); + if (m!=objects.end() ) { + return &(*m); + } + return NULL; +} + +const targettype *Targets::GetLastTarget(int Type) +{ + targetlist::const_iterator m = objects.end(); + while (m--!=objects.begin() ) { + if ( (Type==-1) || ((*m).actor->Type==Type) ) { + return &(*(m)); + } + } + return NULL; +} + +const targettype *Targets::GetFirstTarget(targetlist::iterator &m, int Type) +{ + m=objects.begin(); + while (m!=objects.end() ) { + if ( (Type!=-1) && ( (*m).actor->Type!=Type)) { + m++; + continue; + } + return &(*m); + } + return NULL; +} + +const targettype *Targets::GetNextTarget(targetlist::iterator &m, int Type) +{ + m++; + while (m!=objects.end() ) { + if ( (Type!=-1) && ( (*m).actor->Type!=Type)) { + m++; + continue; + } + return &(*m); + } + return NULL; +} + +Scriptable *Targets::GetTarget(unsigned int index, int Type) +{ + targetlist::iterator m = objects.begin(); + while(m!=objects.end() ) { + if ( (Type==-1) || ((*m).actor->Type==Type)) { + if (!index) { + return (*m).actor; + } + index--; + } + m++; + } + return NULL; +} + +//this stuff should be refined, dead actors are sometimes targetable by script? +void Targets::AddTarget(Scriptable* target, unsigned int distance, int ga_flags) +{ + if (!target) { + return; + } + + switch (target->Type) { + case ST_ACTOR: + //i don't know if unselectable actors are targetable by script + //if yes, then remove GA_SELECT + if (ga_flags) { + if (!((Actor *) target)->ValidTarget(ga_flags) ) { + return; + } + } + break; + case ST_GLOBAL: + // this doesn't seem a good idea to allow + return; + default: + break; + } + targettype Target = {target, distance}; + targetlist::iterator m; + for (m = objects.begin(); m != objects.end(); ++m) { + if ( (*m).distance>distance) { + objects.insert( m, Target); + return; + } + } + objects.push_back( Target ); +} + +void Targets::Clear() +{ + objects.clear(); +} + +/** releasing global memory */ +static void CleanupIEScript() +{ + if (SkillStats) + free(SkillStats); + SkillStats = NULL; + SkillCount = -1; + if (ObjectIDSTableNames) + free(ObjectIDSTableNames); + ObjectIDSTableNames = NULL; +} + +void printFunction(Holder table, int index) +{ + const char *str = table->GetStringIndex(index); + int value = table->GetValueIndex(index); + + int len = strchr(str,'(')-str; + if (len<0) { + printf("%d %s\n", value, str); + } else { + printf("%d %.*s\n", value, len, str); + } +} + +void InitializeIEScript() +{ + std::list missing_triggers; + std::list missing_actions; + std::list missing_objects; + std::list::iterator l; + + PluginMgr::Get()->RegisterCleanup(CleanupIEScript); + + InitScriptTables(); + int tT = core->LoadSymbol( "trigger" ); + int aT = core->LoadSymbol( "action" ); + int oT = core->LoadSymbol( "object" ); + int gaT = core->LoadSymbol( "gemact" ); + AutoTable objNameTable("script"); + if (tT < 0 || aT < 0 || oT < 0 || !objNameTable) { + printMessage( "GameScript","A critical scripting file is missing!\n",LIGHT_RED ); + abort(); + } + triggersTable = core->GetSymbol( tT ); + actionsTable = core->GetSymbol( aT ); + objectsTable = core->GetSymbol( oT ); + overrideActionsTable = core->GetSymbol( gaT ); + if (!triggersTable || !actionsTable || !objectsTable || !objNameTable) { + printMessage( "GameScript","A critical scripting file is damaged!\n",LIGHT_RED ); + abort(); + } + + int i; + + /* Loading Script Configuration Parameters */ + + ObjectIDSCount = atoi( objNameTable->QueryField() ); + if (ObjectIDSCount<0 || ObjectIDSCount>MAX_OBJECT_FIELDS) { + printMessage("GameScript","The IDS Count shouldn't be more than 10!\n",LIGHT_RED); + abort(); + } + + ObjectIDSTableNames = (ieResRef *) malloc( sizeof(ieResRef) * ObjectIDSCount ); + for (i = 0; i < ObjectIDSCount; i++) { + const char *idsname; + idsname=objNameTable->QueryField( 0, i + 1 ); + const IDSLink *poi=FindIdentifier( idsname ); + if (poi==NULL) { + idtargets[i]=NULL; + } + else { + idtargets[i]=poi->Function; + } + strnlwrcpy(ObjectIDSTableNames[i], idsname, 8 ); + } + MaxObjectNesting = atoi( objNameTable->QueryField( 1 ) ); + if (MaxObjectNesting<0 || MaxObjectNesting>MAX_NESTING) { + printMessage("GameScript","The Object Nesting Count shouldn't be more than 5!\n", LIGHT_RED); + abort(); + } + HasAdditionalRect = ( atoi( objNameTable->QueryField( 2 ) ) != 0 ); + ExtraParametersCount = atoi( objNameTable->QueryField( 3 ) ); + HasTriggerPoint = ( atoi( objNameTable->QueryField( 4 ) ) != 0 ); + ObjectFieldsCount = ObjectIDSCount - ExtraParametersCount; + + /* Initializing the Script Engine */ + + memset( triggers, 0, sizeof( triggers ) ); + memset( triggerflags, 0, sizeof( triggerflags ) ); + memset( actions, 0, sizeof( actions ) ); + memset( actionflags, 0, sizeof( actionflags ) ); + memset( objects, 0, sizeof( objects ) ); + + int j; + + j = triggersTable->GetSize(); + while (j--) { + i = triggersTable->GetValueIndex( j ); + const TriggerLink* poi = FindTrigger(triggersTable->GetStringIndex( j )); + //maybe we should watch for this bit? + //bool triggerflag = i & 0x4000; + i &= 0x3fff; + if (i >= MAX_TRIGGERS) { + printMessage("GameScript"," ", RED); + printf("trigger %d (%s) is too high, ignoring\n", i, triggersTable->GetStringIndex( j ) ); + continue; + } + if (triggers[i]) { + if (poi && triggers[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", triggersTable->GetStringIndex( j ) ); + printFunction(triggersTable,triggersTable->FindValue(triggersTable->GetValueIndex( j ))); + //printFunction(triggersTable->GetStringIndex(triggersTable->FindValue(triggersTable->GetValueIndex( j )) )); + } else { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", triggersTable->GetStringIndex( j ) ); + printFunction(triggersTable,triggersTable->FindValue(triggersTable->GetValueIndex( j ))); + //printFunction(triggersTable->GetStringIndex(triggersTable->FindValue(triggersTable->GetValueIndex( j ) ) ) ); + } + } + continue; //we already found an alternative + } + if (poi == NULL) { + triggers[i] = NULL; + triggerflags[i] = 0; + missing_triggers.push_back(j); + continue; + } + triggers[i] = poi->Function; + triggerflags[i] = poi->Flags; + } + + for (l = missing_triggers.begin(); l!=missing_triggers.end();l++) { + j = *l; + // found later as a different name + int ii = triggersTable->GetValueIndex( j ) & 0x3fff; + if (ii >= MAX_TRIGGERS) { + continue; + } + + TriggerFunction f = triggers[ii]; + if (f) { + for (i = 0; triggernames[i].Name; i++) { + if (f == triggernames[i].Function) { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", triggersTable->GetStringIndex( j ), triggernames[i].Name ); + break; + } + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to trigger: ", YELLOW); + printFunction(triggersTable,j); +//->GetStringIndex(j) ); + } + + j = actionsTable->GetSize(); + while (j--) { + i = actionsTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("action %d (%s) is too high, ignoring\n", i, actionsTable->GetStringIndex( j ) ); + continue; + } + const ActionLink* poi = FindAction( actionsTable->GetStringIndex( j )); + if (actions[i]) { + if (poi && actions[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", actionsTable->GetStringIndex( j ) ); + printFunction(actionsTable, actionsTable->FindValue(actionsTable->GetValueIndex(j))); +//->GetStringIndex(actionsTable->FindValue(actionsTable->GetValueIndex( j )) ) ); + } else { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", actionsTable->GetStringIndex( j ) ); + printFunction(actionsTable, actionsTable->FindValue(actionsTable->GetValueIndex( j ))); +//actionsTable->GetStringIndex(actionsTable->FindValue(actionsTable->GetValueIndex( j )) ) ); + } + } + continue; //we already found an alternative + } + if (poi == NULL) { + actions[i] = NULL; + actionflags[i] = 0; + missing_actions.push_back(j); + continue; + } + actions[i] = poi->Function; + actionflags[i] = poi->Flags; + } + + if (overrideActionsTable) { + /* + * we add/replace some actions from gemact.ids + * right now you can't print or generate these actions! + */ + j = overrideActionsTable->GetSize(); + while (j--) { + i = overrideActionsTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("action %d (%s) is too high, ignoring\n", i, overrideActionsTable->GetStringIndex( j ) ); + continue; + } + const ActionLink *poi = FindAction( overrideActionsTable->GetStringIndex( j )); + if (!poi) { + continue; + } + if (actions[i]) { + printMessage("GameScript"," ", WHITE); + printf("%s overrides existing action ", overrideActionsTable->GetStringIndex( j ) ); + printFunction( actionsTable, actionsTable->FindValue(overrideActionsTable->GetValueIndex( j ))); + //printFunction( actionsTable->GetStringIndex(actionsTable->FindValue(overrideActionsTable->GetValueIndex( j )) ) ); + } + actions[i] = poi->Function; + actionflags[i] = poi->Flags; + } + } + + for (l = missing_actions.begin(); l!=missing_actions.end();l++) { + j = *l; + // found later as a different name + int ii = actionsTable->GetValueIndex( j ); + if (ii>=MAX_ACTIONS) { + continue; + } + + ActionFunction f = actions[ii]; + if (f) { + for (i = 0; actionnames[i].Name; i++) { + if (f == actionnames[i].Function) { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", actionsTable->GetStringIndex( j ), actionnames[i].Name ); + break; + } + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to action: ", YELLOW); + printFunction(actionsTable,j); + //printFunction(actionsTable->GetStringIndex(j) ); + } + + j = objectsTable->GetSize(); + while (j--) { + i = objectsTable->GetValueIndex( j ); + if (i >= MAX_OBJECTS) { + printMessage("GameScript"," ", RED); + printf("object %d (%s) is too high, ignoring\n", i, objectsTable->GetStringIndex( j ) ); + continue; + } + const ObjectLink* poi = FindObject( objectsTable->GetStringIndex( j )); + if (objects[i]) { + if (poi && objects[i]!=poi->Function) { + printMessage("GameScript"," ", YELLOW); + printf("%s is in collision with ", objectsTable->GetStringIndex( j ) ); + printFunction(objectsTable,objectsTable->FindValue(objectsTable->GetValueIndex( j ))); + //printFunction(objectsTable->GetStringIndex(objectsTable->FindValue(objectsTable->GetValueIndex( j )) ) ); + } else { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of ", objectsTable->GetStringIndex( j ) ); + printFunction(objectsTable, objectsTable->FindValue(objectsTable->GetValueIndex( j ))); + //printFunction(objectsTable->GetStringIndex(objectsTable->FindValue(objectsTable->GetValueIndex( j )) ) ); + } + continue; + } + if (poi == NULL) { + objects[i] = NULL; + missing_objects.push_back(j); + } else { + objects[i] = poi->Function; + } + } + + for (l = missing_objects.begin(); l!=missing_objects.end();l++) { + j = *l; + // found later as a different name + int ii = objectsTable->GetValueIndex( j ); + if (ii>=MAX_ACTIONS) { + continue; + } + + ObjectFunction f = objects[ii]; + if (f) { + for (i = 0; objectnames[i].Name; i++) { + if (f == objectnames[i].Function) { + printMessage("GameScript"," ", WHITE); + printf("%s is a synonym of %s\n", objectsTable->GetStringIndex( j ), objectnames[i].Name ); + break; + } + } + continue; + } + printMessage("GameScript","Couldn't assign function to object: ", YELLOW); + printFunction(objectsTable,j); + //printFunction(objectsTable->GetStringIndex(j) ); + } + + int instantTableIndex = core->LoadSymbol("instant"); + if (instantTableIndex < 0) { + printMessage("GameScript", "Couldn't find instant symbols!\n", LIGHT_RED); + abort(); + } + Holder instantTable = core->GetSymbol(instantTableIndex); + if (!instantTable) { + printMessage("GameScript", "Couldn't load instant symbols!\n", LIGHT_RED); + abort(); + } + j = instantTable->GetSize(); + while (j--) { + i = instantTable->GetValueIndex( j ); + if (i >= MAX_ACTIONS) { + printMessage("GameScript"," ", RED); + printf("instant action %d (%s) is too high, ignoring\n", i, instantTable->GetStringIndex( j ) ); + continue; + } + if (!actions[i]) { + printMessage("GameScript"," ", YELLOW); + printf("instant action %d (%s) doesn't exist, ignoring\n", i, instantTable->GetStringIndex( j ) ); + continue; + } + actionflags[i] |= AF_INSTANT; + } +} + +/********************** GameScript *******************************/ +GameScript::GameScript(const ieResRef ResRef, Scriptable* MySelf, + int ScriptLevel, bool AIScript) + : MySelf(MySelf) +{ + scriptlevel = ScriptLevel; + lastAction = (unsigned int) ~0; + + strnlwrcpy( Name, ResRef, 8 ); + + script = CacheScript( Name, AIScript); +} + +GameScript::~GameScript(void) +{ + if (script) { + //set 3. parameter to true if you want instant free + //and possible death + if (InDebug&ID_REFERENCE) { + printf("One instance of %s is dropped from %d.\n", Name, BcsCache.RefCount(Name) ); + } + int res = BcsCache.DecRef(script, Name, true); + + if (res<0) { + printMessage( "GameScript", "Corrupted Script cache encountered (reference count went below zero), ", LIGHT_RED ); + printf( "Script name is: %.8s\n", Name); + abort(); + } + if (!res) { + //printf("Freeing script %s because its refcount has reached 0.\n", Name); + script->Release(); + } + script = NULL; + } +} + +Script* GameScript::CacheScript(ieResRef ResRef, bool AIScript) +{ + char line[10]; + + SClass_ID type = AIScript ? IE_BS_CLASS_ID : IE_BCS_CLASS_ID; + + Script *newScript = (Script *) BcsCache.GetResource(ResRef); + if ( newScript ) { + if (InDebug&ID_REFERENCE) { + printf("Caching %s for the %d. time\n", ResRef, BcsCache.RefCount(ResRef) ); + } + return newScript; + } + + DataStream* stream = gamedata->GetResource( ResRef, type ); + if (!stream) { + return NULL; + } + stream->ReadLine( line, 10 ); + if (strncmp( line, "SC", 2 ) != 0) { + printMessage( "GameScript","Not a Compiled Script file\n", YELLOW ); + delete( stream ); + return NULL; + } + newScript = new Script( ); + BcsCache.SetAt( ResRef, (void *) newScript ); + if (InDebug&ID_REFERENCE) { + printf("Caching %s for the %d. time\n", ResRef, BcsCache.RefCount(ResRef) ); + } + + while (true) { + ResponseBlock* rB = ReadResponseBlock( stream ); + if (!rB) + break; + newScript->responseBlocks.push_back( rB ); + stream->ReadLine( line, 10 ); + } + delete( stream ); + return newScript; +} + +static int ParseInt(const char*& src) +{ + char number[33]; + + char* tmp = number; + while (isdigit(*src) || *src=='-') { + *tmp = *src; + tmp++; + src++; + } + *tmp = 0; + if (*src) + src++; + return atoi( number ); +} + +static void ParseString(const char*& src, char* tmp) +{ + while (*src != '"' && *src) { + *tmp = *src; + tmp++; + src++; + } + *tmp = 0; + if (*src) + src++; +} + +static Object* DecodeObject(const char* line) +{ + int i; + const char *origline = line; // for debug below + + Object* oB = new Object(); + for (i = 0; i < ObjectFieldsCount; i++) { + oB->objectFields[i] = ParseInt( line ); + } + for (i = 0; i < MaxObjectNesting; i++) { + oB->objectFilters[i] = ParseInt( line ); + } + //iwd tolerates the missing rectangle, so we do so too + if (HasAdditionalRect && (*line=='[') ) { + line++; //Skip [ + for (i = 0; i < 4; i++) { + oB->objectRect[i] = ParseInt( line ); + } + if (*line == ' ') + line++; //Skip ] (not really... it skips a ' ' since the ] was skipped by the ParseInt function + } + if (*line == '"') + line++; //Skip " + ParseString( line, oB->objectName ); + if (*line == '"') + line++; //Skip " (the same as above) + //this seems to be needed too + if (ExtraParametersCount && *line) { + line++; + } + for (i = 0; i < ExtraParametersCount; i++) { + oB->objectFields[i + ObjectFieldsCount] = ParseInt( line ); + } + if (*line != 'O' || *(line + 1) != 'B') { + printMessage( "GameScript","Got confused parsing object line: ", YELLOW ); + printf("%s\n", origline); + } + //let the object realize it has no future (in case of null objects) + if (oB->ReadyToDie()) { + oB = NULL; + } + return oB; +} + +static Trigger* ReadTrigger(DataStream* stream) +{ + char* line = ( char* ) malloc( 1024 ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "TR", 2 ) != 0) { + free( line ); + return NULL; + } + stream->ReadLine( line, 1024 ); + Trigger* tR = new Trigger(); + //this exists only in PST? + if (HasTriggerPoint) { + sscanf( line, "%hu %d %d %d %d [%hd,%hd] \"%[^\"]\" \"%[^\"]\" OB", + &tR->triggerID, &tR->int0Parameter, &tR->flags, + &tR->int1Parameter, &tR->int2Parameter, &tR->pointParameter.x, + &tR->pointParameter.y, tR->string0Parameter, tR->string1Parameter ); + } else { + sscanf( line, "%hu %d %d %d %d \"%[^\"]\" \"%[^\"]\" OB", + &tR->triggerID, &tR->int0Parameter, &tR->flags, + &tR->int1Parameter, &tR->int2Parameter, tR->string0Parameter, + tR->string1Parameter ); + } + strlwr(tR->string0Parameter); + strlwr(tR->string1Parameter); + tR->triggerID &= 0x3fff; + stream->ReadLine( line, 1024 ); + tR->objectParameter = DecodeObject( line ); + stream->ReadLine( line, 1024 ); + free( line ); + return tR; +} + +static Condition* ReadCondition(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "CO", 2 ) != 0) { + return NULL; + } + Condition* cO = new Condition(); + while (true) { + Trigger* tR = ReadTrigger( stream ); + if (!tR) + break; + cO->triggers.push_back( tR ); + } + return cO; +} + +/* + * if you pass non-NULL parameters, continuing is set to whether we Continue()ed + * (should start false and be passed to next script's Update), + * and done is set to whether we processed a block without Continue() + */ +bool GameScript::Update(bool *continuing, bool *done) +{ + if (!MySelf) + return false; + + if (!script) + return false; + + //ieDword thisTime = core->GetGame()->Ticks; + //if (( thisTime - lastRunTime ) < scriptRunDelay) { + // return false; + //} + + //lastRunTime = thisTime; + + if(!(MySelf->GetInternalFlag()&IF_ACTIVE) ) { + return true; + } + + bool continueExecution = false; + if (continuing) continueExecution = *continuing; + + RandomNumValue=rand(); + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + if (rB->condition->Evaluate(MySelf)) { + //if this isn't a continue-d block, we have to clear the queue + //we cannot clear the queue and cannot execute the new block + //if we already have stuff on the queue! + if (!continueExecution) { + if (MySelf->GetCurrentAction() || MySelf->GetNextAction()) { + if (MySelf->GetInternalFlag()&IF_NOINT) { + // we presumably don't want any further execution? + if (done) *done = true; + return true; + } + + if (lastAction==a) { + // we presumably don't want any further execution? + // this one is a bit more complicated, due to possible + // interactions with Continue() (lastAction here is always + // the first block encountered), needs more testing + //if (done) *done = true; + return true; + } + + //movetoobjectfollow would break if this isn't called + //(what is broken if it is here?) + MySelf->ClearActions(); + //IE even clears the path, shall we? + //yes we must :) + if (MySelf->Type == ST_ACTOR) { + ((Movable *)MySelf)->ClearPath(); + } + } + lastAction=a; + } + continueExecution = ( rB->responseSet->Execute(MySelf) != 0); + if (continuing) *continuing = continueExecution; + //clear triggers after response executed + //MySelf->ClearTriggers(); + if (!continueExecution) { + if (done) *done = true; + break; + } + //continueExecution = false; + } + } + return true; +} + +//IE simply takes the first action's object for cutscene object +//then adds these actions to its queue: +// SetInterrupt(false), , SetInterrupt(true) + +void GameScript::EvaluateAllBlocks() +{ + if (!MySelf || !(MySelf->GetInternalFlag()&IF_ACTIVE) ) { + return; + } + + if (!script) { + return; + } + +#ifdef GEMRB_CUTSCENES + // this is the (unused) more logical way of executing a cutscene, which + // evaluates conditions and doesn't just use the first response + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + if (rB->Condition->Evaluate(MySelf)) { + // TODO: this no longer works since the cutscene changes + rB->Execute(MySelf); + } + } +#else + // this is the original IE behaviour: + // cutscenes don't evaluate conditions - they just choose the + // first response, take the object from the first action, + // and then add the actions to that object's queue. + for (size_t a = 0; a < script->responseBlocks.size(); a++) { + ResponseBlock* rB = script->responseBlocks[a]; + ResponseSet * rS = rB->responseSet; + if (rS->responses.size()) { + Response *response = rS->responses[0]; + if (response->actions.size()) { + Action *action = response->actions[0]; + Scriptable *target = GetActorFromObject(MySelf, action->objects[1]); + if (target) { + // TODO: sometimes SetInterrupt(false) and SetInterrupt(true) are added before/after? + rS->responses[0]->Execute(target); + // TODO: this will break blocking instants, if there are any + target->ReleaseCurrentAction(); + } else if (InDebug&ID_CUTSCENE) { + printMessage("GameScript","Failed to find CutSceneID target!\n",YELLOW); + if (action->objects[1]) { + action->objects[1]->Dump(); + } + } + } + } + } +#endif +} + +ResponseBlock* GameScript::ReadResponseBlock(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "CR", 2 ) != 0) { + return NULL; + } + ResponseBlock* rB = new ResponseBlock(); + rB->condition = ReadCondition( stream ); + rB->responseSet = ReadResponseSet( stream ); + return rB; +} + +ResponseSet* GameScript::ReadResponseSet(DataStream* stream) +{ + char line[10]; + + stream->ReadLine( line, 10 ); + if (strncmp( line, "RS", 2 ) != 0) { + return NULL; + } + ResponseSet* rS = new ResponseSet(); + while (true) { + Response* rE = ReadResponse( stream ); + if (!rE) + break; + rS->responses.push_back( rE ); + } + return rS; +} + +//this is the border of the GameScript object (all subsequent functions are library functions) +//we can't make this a library function, because scriptlevel is set here +Response* GameScript::ReadResponse(DataStream* stream) +{ + char* line = ( char* ) malloc( 1024 ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "RE", 2 ) != 0) { + free( line ); + return NULL; + } + Response* rE = new Response(); + rE->weight = 0; + int count = stream->ReadLine( line, 1024 ); + char *poi; + rE->weight = (unsigned char)strtoul(line,&poi,10); + if (strncmp(poi,"AC",2)==0) + while (true) { + //not autofreed, because it is referenced by the Script + Action* aC = new Action(false); + count = stream->ReadLine( line, 1024 ); + aC->actionID = (unsigned short)strtoul(line, NULL,10); + for (int i = 0; i < 3; i++) { + stream->ReadLine( line, 1024 ); + Object* oB = DecodeObject( line ); + aC->objects[i] = oB; + if (i != 2) + stream->ReadLine( line, 1024 ); + } + stream->ReadLine( line, 1024 ); + sscanf( line, "%d %hd %hd %d %d\"%[^\"]\" \"%[^\"]\" AC", + &aC->int0Parameter, &aC->pointParameter.x, &aC->pointParameter.y, + &aC->int1Parameter, &aC->int2Parameter, aC->string0Parameter, + aC->string1Parameter ); + strlwr(aC->string0Parameter); + strlwr(aC->string1Parameter); + if (aC->actionID>=MAX_ACTIONS) { + aC->actionID=0; + printMessage("GameScript","Invalid script action ID!",LIGHT_RED); + } else { + if (actionflags[aC->actionID] & AF_SCRIPTLEVEL) { + aC->int0Parameter = scriptlevel; + } + } + rE->actions.push_back( aC ); + stream->ReadLine( line, 1024 ); + if (strncmp( line, "RE", 2 ) == 0) + break; + } + free( line ); + return rE; +} + +void GameScript::ExecuteString(Scriptable* Sender, char* String) +{ + if (String[0] == 0) { + return; + } + Action* act = GenerateAction( String ); + if (!act) { + return; + } + Sender->AddActionInFront(act); +} + +//This must return integer because Or(3) returns 3 +int GameScript::EvaluateString(Scriptable* Sender, char* String) +{ + if (String[0] == 0) { + return 0; + } + Trigger* tri = GenerateTrigger( String ); + if (tri) { + int ret = tri->Evaluate(Sender); + tri->Release(); + return ret; + } + return 0; +} + +bool Condition::Evaluate(Scriptable* Sender) +{ + int ORcount = 0; + unsigned int result = 0; + bool subresult = true; + + for (size_t i = 0; i < triggers.size(); i++) { + Trigger* tR = triggers[i]; + //do not evaluate triggers in an Or() block if one of them + //was already True() + if (!ORcount || !subresult) { + result = tR->Evaluate(Sender); + } + if (result > 1) { + //we started an Or() block + if (ORcount) { + printMessage( "GameScript","Unfinished OR block encountered!\n",YELLOW ); + } + ORcount = result; + subresult = false; + continue; + } + if (ORcount) { + subresult |= ( result != 0 ); + if (--ORcount) { + continue; + } + result = subresult; + } + if (!result) { + return 0; + } + } + if (ORcount) { + printMessage( "GameScript","Unfinished OR block encountered!\n",YELLOW ); + } + return 1; +} + +/* this may return more than a boolean, in case of Or(x) */ +int Trigger::Evaluate(Scriptable* Sender) +{ + if (!this) { + printMessage( "GameScript","Trigger evaluation fails due to NULL trigger.\n",LIGHT_RED ); + return 0; + } + TriggerFunction func = triggers[triggerID]; + const char *tmpstr=triggersTable->GetValue(triggerID); + if (!tmpstr) { + tmpstr=triggersTable->GetValue(triggerID|0x4000); + } + if (!func) { + triggers[triggerID] = GameScript::False; + printMessage("GameScript"," ",YELLOW); + printf("Unhandled trigger code: 0x%04x %s\n", + triggerID, tmpstr ); + return 0; + } + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",YELLOW); + printf( "Executing trigger code: 0x%04x %s\n", + triggerID, tmpstr ); + } + int ret = func( Sender, this ); + if (flags & NEGATE_TRIGGER) { + return !ret; + } + return ret; +} + +int ResponseSet::Execute(Scriptable* Sender) +{ + size_t i; + + switch(responses.size()) { + case 0: + return 0; + case 1: + return responses[0]->Execute(Sender); + } + /*default*/ + int randWeight; + int maxWeight = 0; + + for (i = 0; i < responses.size(); i++) { + maxWeight += responses[i]->weight; + } + if (maxWeight) { + randWeight = rand() % maxWeight; + } + else { + randWeight = 0; + } + + for (i = 0; i < responses.size(); i++) { + Response* rE = responses[i]; + if (rE->weight > randWeight) { + return rE->Execute(Sender); + /* this break is only symbolic */ + break; + } + randWeight-=rE->weight; + } + return 0; +} + +//continue is effective only as the last action in the block +int Response::Execute(Scriptable* Sender) +{ + int ret = 0; // continue or not + for (size_t i = 0; i < actions.size(); i++) { + Action* aC = actions[i]; + switch (actionflags[aC->actionID] & AF_MASK) { + case AF_IMMEDIATE: + GameScript::ExecuteAction( Sender, aC ); + ret = 0; + break; + case AF_NONE: + Sender->AddAction( aC ); + ret = 0; + break; + case AF_CONTINUE: + case AF_MASK: + ret = 1; + break; + } + } + return ret; +} + +void PrintAction(int actionID) +{ + printf("Action: %d %s\n", actionID , actionsTable->GetValue(actionID) ); +} + +void GameScript::ExecuteAction(Scriptable* Sender, Action* aC) +{ + int actionID = aC->actionID; + + if (aC->objects[0]) { + Scriptable *scr = GetActorFromObject(Sender, aC->objects[0]); + + aC->IncRef(); // if aC is us, we don't want it deleted! + Sender->ReleaseCurrentAction(); + + if (scr) { + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + printf("Sender: %s-->override: %s\n",Sender->GetScriptName(), scr->GetScriptName() ); + } + scr->ReleaseCurrentAction(); + scr->AddAction(ParamCopyNoOverride(aC)); + if (!(actionflags[actionID] & AF_INSTANT)) { + assert(scr->GetNextAction()); + // TODO: below was written before i added instants, this might be unnecessary now + + // there are plenty of places where it's vital that ActionOverride is not interrupted and if + // there are actions left on the queue after the release above, we can't instant-execute, + // so this is my best guess for now.. + scr->CurrentActionInterruptable = false; + } + } else { + printMessage("GameScript","Actionoverride failed for object: \n",LIGHT_RED); + aC->objects[0]->Dump(); + } + + aC->Release(); + return; + } + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + PrintAction(actionID); + printf("Sender: %s\n",Sender->GetScriptName() ); + } + ActionFunction func = actions[actionID]; + if (func) { + //turning off interruptable flag + //uninterruptable actions will set it back + if (Sender->Type==ST_ACTOR) { + Sender->Activate(); + if (actionflags[actionID]&AF_ALIVE) { + if (Sender->GetInternalFlag()&IF_STOPATTACK) { + printMessage("GameScript", "Aborted action due to death\n", YELLOW); + Sender->ReleaseCurrentAction(); + return; + } + } + } + func( Sender, aC ); + } else { + actions[actionID] = NoActionAtAll; + printMessage("GameScript", "Unknown ", YELLOW); + textcolor(YELLOW); + PrintAction(actionID); + Sender->ReleaseCurrentAction(); + textcolor(WHITE); + return; + } + + //don't bother with special flow control actions + if (actionflags[actionID] & AF_IMMEDIATE) { + //this action never entered the action queue, therefore shouldn't be freed + if (aC->GetRef()!=1) { + printf("Immediate action got queued!\n"); + PrintAction(actionID); + abort(); + } + return; + } + + //Releasing nonblocking actions, blocking actions will release themselves + if (!( actionflags[actionID] & AF_BLOCKING )) { + Sender->ReleaseCurrentAction(); + //aC is invalid beyond this point, so we return! + return; + } +} + +Trigger* GenerateTrigger(char* String) +{ + strlwr( String ); + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",YELLOW); + printf("Compiling:%s\n",String); + } + int negate = 0; + if (*String == '!') { + String++; + negate = 1; + } + int len = strlench(String,'(')+1; //including ( + int i = triggersTable->FindString(String, len); + if (i<0) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Invalid scripting trigger: %s\n", String); + return NULL; + } + char *src = String+len; + char *str = triggersTable->GetStringIndex( i )+len; + Trigger *trigger = GenerateTriggerCore(src, str, i, negate); + if (!trigger) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Malformed scripting trigger: %s\n", String); + return NULL; + } + return trigger; +} + +Action* GenerateAction(char* String) +{ + strlwr( String ); + if (InDebug&ID_ACTIONS) { + printMessage("GameScript"," ",YELLOW); + printf("Compiling:%s\n",String); + } + int len = strlench(String,'(')+1; //including ( + char *src = String+len; + int i = -1; + char *str; + unsigned short actionID; + if (overrideActionsTable) { + i = overrideActionsTable->FindString(String, len); + if (i >= 0) { + str = overrideActionsTable->GetStringIndex( i )+len; + actionID = overrideActionsTable->GetValueIndex(i); + } + } + if (i<0) { + i = actionsTable->FindString(String, len); + if (i < 0) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Invalid scripting action: %s\n", String); + return NULL; + } + str = actionsTable->GetStringIndex( i )+len; + actionID = actionsTable->GetValueIndex(i); + } + Action *action = GenerateActionCore( src, str, actionID); + if (!action) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Malformed scripting action: %s\n", String); + return NULL; + } + return action; +} + +Action* GenerateActionDirect(char *String, Scriptable *object) +{ + Action* action = GenerateAction(String); + Object *tmp = action->objects[1]; + if (tmp && tmp->objectFields[0]==-1) { + tmp->objectFields[1] = object->GetGlobalID(); + } + action->pointParameter.empty(); + return action; +} + +/** Self-destructing object if it is empty */ +bool Object::ReadyToDie() +{ + if (objectName[0]!=0) { + return false; + } + if (objectFilters[0]) { + return false; + } + for (int i=0;i +#include + +class Action; +class GameScript; + +//escapearea flags +#define EA_DESTROY 1 //destroy actor at the exit (otherwise move to new place) +#define EA_NOSEE 2 //no need to see the exit + +//displaystring flags +#define DS_WAIT 1 +#define DS_HEAD 2 +#define DS_CONSOLE 4 +#define DS_CONST 8 +#define DS_NONAME 16 +#define DS_SILENT 32 +#define DS_SPEECH 64 +#define DS_AREA 128 + +//verbal constant (bg2), we need a lookup table for other games +#define VB_PANIC 1 +#define VB_HAPPY 2 +#define VB_UNHAPPY 3 +#define VB_LEADER 6 +#define VB_TIRED 7 +#define VB_BORED 8 +#define VB_ATTACK 9 +#define VB_DAMAGE 18 +#define VB_DIE 19 +#define VB_SELECT 26 +#define VB_INSULT 44 +#define VB_COMPLIMENT 47 +#define VB_SPECIAL 50 +#define VB_REACT 53 +#define VB_REACT_S 54 +#define VB_RESP_COMP 55 +#define VB_RESP_INS 58 +#define VB_HOSTILE 59 +#define VB_DIALOG 60 +#define VB_CRITHIT 65 +#define VB_CRITMISS 66 +#define VB_TIMMUNE 67 +#define VB_INVENTORY 68 +#define VB_PP_SUCC 69 +#define VB_BIO 74 + +//diffmode (iwd2) +#define DM_EQUAL 1 +#define DM_LESS 2 +#define DM_GREATER 3 + +//markspellandobject (iwd2) +#define MSO_IGNORE_SEE 1 +#define MSO_IGNORE_INVALID 2 +#define MSO_RANDOM_SPELL 4 +#define MSO_IGNORE_HAVE 8 +#define MSO_IGNORE_RANGE 16 +#define MSO_IGNORE_NULL 32 + +//delta (pst) +#define DM_LOWER 1 +#define DM_RAISE 2 +#define DM_SET 3 + +//attack core flags +#define AC_NO_SOUND 1 +#define AC_RUNNING 2 + +//trigger flags stored in triggers in .bcs files +#define NEGATE_TRIGGER 1 + +#define MAX_OBJECT_FIELDS 10 +#define MAX_NESTING 5 + +#define GSASSERT(f,c) \ + if(!(f)) \ + { \ + printf("Assertion failed: %s [0x%08lX] Line %d",#f, c, __LINE__); \ + abort(); \ + } + +typedef std::vector SrcVector; + +struct targettype { + Scriptable *actor; //hmm, could be door + unsigned int distance; +}; + +typedef std::list targetlist; + +class GEM_EXPORT Targets { +public: + Targets() + { + } + + ~Targets() + { + Clear(); + } +private: + targetlist objects; +public: + int Count() const; + targettype *RemoveTargetAt(targetlist::iterator &m); + const targettype *GetNextTarget(targetlist::iterator &m, int Type); + const targettype *GetLastTarget(int Type); + const targettype *GetFirstTarget(targetlist::iterator &m, int Type); + Scriptable *GetTarget(unsigned int index, int Type); + void AddTarget(Scriptable* target, unsigned int distance, int flags); + void Clear(); +}; + +class GEM_EXPORT Object { +public: + Object() + { + objectName[0] = 0; + + memset( objectFields, 0, MAX_OBJECT_FIELDS * sizeof( int ) ); + memset( objectFilters, 0, MAX_NESTING * sizeof( int ) ); + memset( objectRect, 0, 4 * sizeof( int ) ); + + canary = (unsigned long) 0xdeadbeef; + } + ~Object() + { + } +public: + int objectFields[MAX_OBJECT_FIELDS]; + int objectFilters[MAX_NESTING]; + int objectRect[4]; + char objectName[65]; +private: + volatile unsigned long canary; +public: + void Dump() + { + int i; + + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + if(objectName[0]) { + printf("Object: %s\n",objectName); + return; + } + printf("IDS Targeting: "); + for(i=0;iRelease(); + objectParameter = NULL; + } + } + int Evaluate(Scriptable* Sender); +public: + unsigned short triggerID; + int int0Parameter; + int flags; + int int1Parameter; + int int2Parameter; + Point pointParameter; + char string0Parameter[65]; + char string1Parameter[65]; + Object* objectParameter; +private: + volatile unsigned long canary; +public: + void Dump() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + printf ("Trigger: %d\n", triggerID); + printf ("Int parameters: %d %d %d\n", int0Parameter, int1Parameter, int2Parameter); + printf ("Point: [%d.%d]\n", pointParameter.x, pointParameter.y); + printf ("String0: %s\n", string0Parameter); + printf ("String1: %s\n", string1Parameter); + if (objectParameter) { + objectParameter->Dump(); + } else { + printf("No object\n"); + } + printf("\n"); + } + + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Condition { +public: + Condition() + { + canary = (unsigned long) 0xdeadbeef; + } + ~Condition() + { + for (size_t c = 0; c < triggers.size(); ++c) { + if (triggers[c]) { + triggers[c]->Release(); + triggers[c] = NULL; + } + } + } + bool Evaluate(Scriptable* Sender); +public: + std::vector triggers; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Action { +public: + Action(bool autoFree) + { + actionID = 0; + objects[0] = NULL; + objects[1] = NULL; + objects[2] = NULL; + string0Parameter[0] = 0; + string1Parameter[0] = 0; + int0Parameter = 0; + pointParameter.null(); + int1Parameter = 0; + int2Parameter = 0; + //changed now + if (autoFree) { + RefCount = 0; //refcount will be increased by each AddAction + } else { + RefCount = 1; //one reference hold by the script + } + canary = (unsigned long) 0xdeadbeef; + } + ~Action() + { + for (int c = 0; c < 3; c++) { + if (objects[c]) { + objects[c]->Release(); + objects[c] = NULL; + } + } + } +public: + unsigned short actionID; + Object* objects[3]; + int int0Parameter; + Point pointParameter; + int int1Parameter; + int int2Parameter; + char string0Parameter[65]; + char string1Parameter[65]; +private: + int RefCount; + volatile unsigned long canary; +public: + int GetRef() { + return RefCount; + } + void Dump() + { + int i; + + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + printf("Int0: %d, Int1: %d, Int2: %d\n",int0Parameter, int1Parameter, int2Parameter); + printf("String0: %s, String1: %s\n", string0Parameter?string0Parameter:"", string1Parameter?string1Parameter:""); + for (i=0;i<3;i++) { + if (objects[i]) { + printf( "%d. ",i+1); + objects[i]->Dump(); + } else { + printf( "%d. Object - NULL\n",i+1); + } + } + + printf("RefCount: %d\n", RefCount); + } + + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + if (!RefCount) { + printf( "WARNING!!! Double Freeing in %s: Line %d\n", __FILE__, + __LINE__ ); + abort(); + } + RefCount--; + if (!RefCount) { + canary = 0xdddddddd; + delete this; + } + } + void IncRef() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + RefCount++; + if (RefCount >= 65536) { + printf( "Refcount increased to: %d in action %d\n", RefCount, + actionID ); + abort(); + } + } +}; + +class GEM_EXPORT Response { +public: + Response() + { + weight = 0; + canary = (unsigned long) 0xdeadbeef; + } + ~Response() + { + for (size_t c = 0; c < actions.size(); c++) { + if (actions[c]) { + if (actions[c]->GetRef()>2) { + printf("Residue action %d with refcount %d\n", actions[c]->actionID, actions[c]->GetRef()); + } + actions[c]->Release(); + actions[c] = NULL; + } + } + } + int Execute(Scriptable* Sender); +public: + unsigned char weight; + std::vector actions; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT ResponseSet { +public: + ResponseSet() + { + canary = (unsigned long) 0xdeadbeef; + } + ~ResponseSet() + { + for (size_t b = 0; b < responses.size(); b++) { + responses[b]->Release(); + responses[b] = NULL; + } + } + int Execute(Scriptable* Sender); +public: + std::vector responses; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT ResponseBlock { +public: + ResponseBlock() + { + condition = NULL; + responseSet = NULL; + canary = (unsigned long) 0xdeadbeef; + } + ~ResponseBlock() + { + if (condition) { + condition->Release(); + condition = NULL; + } + if (responseSet) { + responseSet->Release(); + responseSet = NULL; + } + } +public: + Condition* condition; + ResponseSet* responseSet; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +class GEM_EXPORT Script { +public: + Script() + { + canary = (unsigned long) 0xdeadbeef; + } + ~Script() + { + for (unsigned int i = 0; i < responseBlocks.size(); i++) { + if (responseBlocks[i]) { + responseBlocks[i]->Release(); + responseBlocks[i] = NULL; + } + } + } +public: + std::vector responseBlocks; +private: + volatile unsigned long canary; +public: + void Release() + { + GSASSERT( canary == (unsigned long) 0xdeadbeef, canary ); + canary = 0xdddddddd; + delete this; + } +}; + +typedef int (* TriggerFunction)(Scriptable*, Trigger*); +typedef void (* ActionFunction)(Scriptable*, Action*); +typedef Targets* (* ObjectFunction)(Scriptable *, Targets*, int ga_flags); +typedef int (* IDSFunction)(Actor *, int parameter); + +#define TF_NONE 0 +#define TF_CONDITION 1 //this isn't a trigger, just a condition (0x4000) +#define TF_MERGESTRINGS 8 //same value as actions' mergestring + +struct TriggerLink { + const char* Name; + TriggerFunction Function; + short Flags; +}; + +//createcreature flags +#define CC_OFFSET 1 +#define CC_OBJECT 2 +#define CC_OFFSCREEN 3 +#define CC_MASK 3 +#define CC_CHECK_IMPASSABLE 4 //adjust position (searchmap) +#define CC_PLAY_ANIM 8 //play animation +#define CC_STRING1 16 //resref is in second string +#define CC_CHECK_OVERLAP 32 //other actors +#define CC_COPY 64 //copy appearance +#define CC_SCRIPTNAME 128 //scriptname in 2nd string + +//begindialog flags +#define BD_STRING0 0 +#define BD_TARGET 1 +#define BD_SOURCE 2 +#define BD_RESERVED 3 //playerX resref +#define BD_INTERACT 4 //banter dialogs +#define BD_LOCMASK 7 //where is the dialog resref +#define BD_TALKCOUNT 8 //increases talkcount +#define BD_SETDIALOG 16 //also sets dialog (for string0) +#define BD_CHECKDIST 32 //checks distance, if needs, walks up +#define BD_OWN 64 //source == target, works for player only +#define BD_INTERRUPT 128 //interrupts action +#define BD_NUMERIC 256 //target is numeric +#define BD_ITEM 512 //talk to an item +#define BD_NOEMPTY 1024 //don't display '... has nothing to say to you' + +#define AF_NONE 0 +#define AF_IMMEDIATE 1 +#define AF_CONTINUE 2 +#define AF_MASK 3 //none, immediate or continue +#define AF_BLOCKING 4 +#define AF_MERGESTRINGS 8 +//we could use this flag to restrict player scripts from using dangerous +//opcodes, it would be a very useful and easy to implement feature! +#define AF_RESTRICTED 16 +//#define AF_RESTRICTED_LEVEL2 32 //maybe we could use 2 bits for this??? +#define AF_SCRIPTLEVEL 64 //this hack will transfer scriptlevel to int0parameter at runtime (changecurrentscript relies on it) +#define AF_INVALID 128 +#define AF_DIRECT 256 //this hack will transfer target from gamecontrol to object1 at compile time +#define AF_ALIVE 512 //only alive actors can do this +#define AF_INSTANT 1024 + +struct ActionLink { + const char* Name; + ActionFunction Function; + short Flags; +}; + +struct ObjectLink { + const char* Name; + ObjectFunction Function; +}; + +struct IDSLink { + const char* Name; + IDSFunction Function; +}; + +#define MAX_TRIGGERS 0xFF +#define MAX_ACTIONS 400 +#define MAX_OBJECTS 128 +#define AI_SCRIPT_LEVEL 4 //the script level of special ai scripts + +extern void SetScriptDebugMode(int arg); +extern int RandomNumValue; + +class GEM_EXPORT GameScript { +public: + GameScript(const ieResRef ResRef, Scriptable* Myself, + int ScriptLevel = 0, bool AIScript = false); + ~GameScript(); + const char *GetName() { return this?Name:"NONE\0\0\0\0"; } + static void ExecuteString(Scriptable* Sender, char* String); + static int EvaluateString(Scriptable* Sender, char* String); + static void ExecuteAction(Scriptable* Sender, Action* aC); +public: + bool Update(bool *continuing = NULL, bool *done = NULL); + void EvaluateAllBlocks(); +private: //Internal Functions + Script* CacheScript(ieResRef ResRef, bool AIScript); + ResponseBlock* ReadResponseBlock(DataStream* stream); + ResponseSet* ReadResponseSet(DataStream* stream); + Response* ReadResponse(DataStream* stream); + Trigger* ReadTrigger(DataStream* stream); + static int ParseInt(const char*& src); + static void ParseString(const char*& src, char* tmp); +private: //Internal variables + Scriptable* const MySelf; + ieResRef Name; + Script* script; + unsigned int lastAction; + int scriptlevel; +public: //Script Functions + static int ID_Alignment(Actor *actor, int parameter); + static int ID_Allegiance(Actor *actor, int parameter); + static int ID_AVClass(Actor *actor, int parameter); + static int ID_Class(Actor *actor, int parameter); + static int ID_ClassMask(Actor *actor, int parameter); + static int ID_Faction(Actor *actor, int parameter); + static int ID_Gender(Actor *actor, int parameter); + static int ID_General(Actor *actor, int parameter); + static int ID_Race(Actor *actor, int parameter); + static int ID_Specific(Actor *actor, int parameter); + static int ID_Subrace(Actor *actor, int parameter); + static int ID_Team(Actor *actor, int parameter); + + //Triggers + static int ActionListEmpty(Scriptable* Sender, Trigger* parameters); + static int ActuallyInCombat(Scriptable* Sender, Trigger* parameters); + static int Acquired(Scriptable* Sender, Trigger* parameters); + static int Alignment(Scriptable* Sender, Trigger* parameters); + static int Allegiance(Scriptable* Sender, Trigger* parameters); + static int AnimationID(Scriptable* Sender, Trigger* parameters); + static int AnimState(Scriptable* Sender, Trigger* parameters); + static int AnyPCOnMap(Scriptable* Sender, Trigger* parameters); + static int AnyPCSeesEnemy(Scriptable* Sender, Trigger* parameters); + static int AreaCheck(Scriptable* Sender, Trigger* parameter); + static int AreaCheckObject(Scriptable* Sender, Trigger* parameter); + static int AreaFlag(Scriptable* Sender, Trigger* parameter); + static int AreaRestDisabled(Scriptable* Sender, Trigger* parameter); + static int AreaStartsWith(Scriptable* Sender, Trigger* parameter); //InWatchersKeep + static int AreaType(Scriptable* Sender, Trigger* parameter); + static int AtLocation(Scriptable* Sender, Trigger* parameter); + static int AttackedBy(Scriptable* Sender, Trigger* parameters); + static int BecameVisible(Scriptable* Sender, Trigger* parameters); + static int BitCheck(Scriptable* Sender, Trigger* parameters); + static int BitCheckExact(Scriptable* Sender, Trigger* parameters); + static int BitGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int BreakingPoint(Scriptable* Sender, Trigger* parameters); + static int CalendarDay(Scriptable* Sender, Trigger* parameters); + static int CalendarDayGT(Scriptable* Sender, Trigger* parameters); + static int CalendarDayLT(Scriptable* Sender, Trigger* parameters); + static int CalledByName(Scriptable* Sender, Trigger* parameters); + static int ChargeCount(Scriptable* Sender, Trigger* parameters); + static int CharName(Scriptable* Sender, Trigger* parameters); + static int CheckDoorFlags(Scriptable* Sender, Trigger* parameters); + static int CheckPartyAverageLevel(Scriptable* Sender, Trigger* parameters); + static int CheckPartyLevel(Scriptable* Sender, Trigger* parameters); + static int CheckSkill(Scriptable* Sender, Trigger* parameters); + static int CheckSkillGT(Scriptable* Sender, Trigger* parameters); + static int CheckSkillLT(Scriptable* Sender, Trigger* parameters); + static int CheckSpellState(Scriptable* Sender, Trigger* parameters); + static int CheckStat(Scriptable* Sender, Trigger* parameters); + static int CheckStatGT(Scriptable* Sender, Trigger* parameters); + static int CheckStatLT(Scriptable* Sender, Trigger* parameters); + static int Class(Scriptable* Sender, Trigger* parameters); + static int ClassEx(Scriptable* Sender, Trigger* parameters); + static int ClassLevel(Scriptable* Sender, Trigger* parameters); + static int ClassLevelGT(Scriptable* Sender, Trigger* parameters); + static int ClassLevelLT(Scriptable* Sender, Trigger* parameters); + static int Clicked(Scriptable* Sender, Trigger* parameters); + static int Closed(Scriptable* Sender, Trigger* parameters); + static int CombatCounter(Scriptable* Sender, Trigger* parameters); + static int CombatCounterGT(Scriptable* Sender, Trigger* parameters); + static int CombatCounterLT(Scriptable* Sender, Trigger* parameters); + static int Contains(Scriptable* Sender, Trigger* parameters); + static int CreatureHidden( Scriptable* Sender, Trigger* parameters); + static int CurrentAreaIs(Scriptable* Sender, Trigger* parameters); + //static int DamageTaken(Scriptable* Sender, Trigger* parameters); + //static int DamageTakenGT(Scriptable* Sender, Trigger* parameters); + //static int DamageTakenLT(Scriptable* Sender, Trigger* parameters); + static int Dead(Scriptable* Sender, Trigger* parameters); + static int Delay(Scriptable* Sender, Trigger* parameters); + static int Detect(Scriptable* Sender, Trigger* parameters); + static int Die(Scriptable* Sender, Trigger* parameters); + static int Died(Scriptable* Sender, Trigger* parameters); + static int Difficulty(Scriptable* Sender, Trigger* parameters); + static int DifficultyGT(Scriptable* Sender, Trigger* parameters); + static int DifficultyLT(Scriptable* Sender, Trigger* parameters); + static int Disarmed(Scriptable* Sender, Trigger* parameters); + static int DisarmFailed(Scriptable* Sender, Trigger* parameters); + static int Entered(Scriptable* Sender, Trigger* parameters); + static int EntirePartyOnMap(Scriptable* Sender, Trigger* parameters); + static int Exists(Scriptable* Sender, Trigger* parameters); + static int ExtendedStateCheck(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiency(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiencyGT(Scriptable* Sender, Trigger* parameters); + static int ExtraProficiencyLT(Scriptable* Sender, Trigger* parameters); + static int Faction(Scriptable* Sender, Trigger* parameters); + static int FallenPaladin(Scriptable* Sender, Trigger* parameters); + static int FallenRanger(Scriptable* Sender, Trigger* parameters); + static int False(Scriptable* Sender, Trigger* parameters); + static int ForceMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters); + static int Frame(Scriptable* Sender, Trigger* parameters); + static int Gender(Scriptable* Sender, Trigger* parameters); + static int General(Scriptable* Sender, Trigger* parameters); + static int G_Trigger(Scriptable* Sender, Trigger* parameters); + static int Global(Scriptable* Sender, Trigger* parameters); + static int GlobalAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalBAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalBAndGlobalExact(Scriptable* Sender, Trigger* parameters); + static int GlobalBitGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalGT(Scriptable* Sender, Trigger* parameters); + static int GlobalGTGlobal(Scriptable* Sender, Trigger* parameters); + static int GlobalLT(Scriptable* Sender, Trigger* parameters); + static int GlobalLTGlobal(Scriptable* Sender, Trigger* parameters); + static int GlobalOrGlobal_Trigger(Scriptable* Sender, Trigger* parameters); + static int GlobalsEqual(Scriptable* Sender, Trigger* parameters); + static int GlobalsGT(Scriptable* Sender, Trigger* parameters); + static int GlobalsLT(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerExact(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerExpired(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters); + static int GlobalTimerStarted(Scriptable* Sender, Trigger* parameters); + static int GGT_Trigger(Scriptable* Sender, Trigger* parameters); + static int GLT_Trigger(Scriptable* Sender, Trigger* parameters); + static int Happiness(Scriptable* Sender, Trigger* parameters); + static int HappinessGT(Scriptable* Sender, Trigger* parameters); + static int HappinessLT(Scriptable* Sender, Trigger* parameters); + static int HarmlessEntered(Scriptable* Sender, Trigger* parameters); + static int HasBounceEffects(Scriptable* Sender, Trigger* parameters); + static int HasImmunityEffects(Scriptable* Sender, Trigger* parameters); + static int HasInnateAbility(Scriptable* Sender, Trigger* parameters); + static int HasItem(Scriptable* Sender, Trigger* parameters); + static int HasItemEquipped(Scriptable* Sender, Trigger* parameters); + static int HasItemSlot(Scriptable* Sender, Trigger* parameters); + static int HasItemTypeSlot(Scriptable* Sender, Trigger* parameters); + static int HasWeaponEquipped(Scriptable* Sender, Trigger* parameters); + static int HaveAnySpells(Scriptable* Sender, Trigger* parameters); + static int HaveSpellParty(Scriptable* Sender, Trigger* parameters); + static int HaveSpell(Scriptable* Sender, Trigger* parameters); + static int HaveUsableWeaponEquipped(Scriptable* Sender, Trigger* parameters); + static int Heard(Scriptable* Sender, Trigger* parameters); + static int Help_Trigger(Scriptable* Sender, Trigger* parameters); + static int HelpEX(Scriptable* Sender, Trigger* parameters); + static int HitBy(Scriptable* Sender, Trigger* parameters); + static int HotKey(Scriptable* Sender, Trigger* parameters); + static int HP(Scriptable* Sender, Trigger* parameters); + static int HPGT(Scriptable* Sender, Trigger* parameters); + static int HPLost(Scriptable* Sender, Trigger* parameters); + static int HPLostGT(Scriptable* Sender, Trigger* parameters); + static int HPLostLT(Scriptable* Sender, Trigger* parameters); + static int HPLT(Scriptable* Sender, Trigger* parameters); + static int HPPercent(Scriptable* Sender, Trigger* parameters); + static int HPPercentGT(Scriptable* Sender, Trigger* parameters); + static int HPPercentLT(Scriptable* Sender, Trigger* parameters); + static int InActiveArea(Scriptable* Sender, Trigger* parameter); + static int InCutSceneMode(Scriptable *Sender, Trigger* parameter); + static int InLine(Scriptable* Sender, Trigger* parameter); + static int InMyArea(Scriptable* Sender, Trigger* parameter); + static int InMyGroup(Scriptable* Sender, Trigger* parameter); + static int InParty(Scriptable* Sender, Trigger* parameters); + static int InPartyAllowDead(Scriptable* Sender, Trigger* parameters); + static int InPartySlot(Scriptable* Sender, Trigger* parameters); + static int InteractingWith(Scriptable* Sender, Trigger* parameters); + static int Internal(Scriptable* Sender, Trigger* parameters); + static int InternalGT(Scriptable* Sender, Trigger* parameters); + static int InternalLT(Scriptable* Sender, Trigger* parameters); + static int InTrap(Scriptable* Sender, Trigger* parameters); + static int InventoryFull(Scriptable* Sender, Trigger* parameter); + static int InWeaponRange(Scriptable* Sender, Trigger* parameter); + static int IsAClown(Scriptable* Sender, Trigger* parameters); + static int IsActive(Scriptable* Sender, Trigger* parameters); + static int IsCreatureAreaFlag( Scriptable* Sender, Trigger* parameters); + static int IsCreatureHiddenInShadows( Scriptable* Sender, Trigger* parameters); + static int IsGabber(Scriptable* Sender, Trigger* parameters); + static int IsExtendedNight(Scriptable* Sender, Trigger* parameters); + static int IsFacingObject(Scriptable* Sender, Trigger* parameters); + static int IsFacingSavedRotation(Scriptable* Sender, Trigger* parameters); + static int IsLocked(Scriptable* Sender, Trigger* parameters); + static int IsMarkedSpell(Scriptable* Sender, Trigger* parameters); + static int IsOverMe(Scriptable* Sender, Trigger* parameters); + static int IsPathCriticalObject( Scriptable* Sender, Trigger* parameters); + static int IsPlayerNumber( Scriptable* Sender, Trigger* parameters); + static int IsRotation(Scriptable* Sender, Trigger* parameters); + static int IsSpellTargetValid( Scriptable* Sender, Trigger* parameters); + static int IsTeamBitOn(Scriptable* Sender, Trigger* parameters); + static int IsValidForPartyDialog(Scriptable* Sender, Trigger* parameters); + static int IsWeaponRanged(Scriptable* Sender, Trigger* parameters); + static int IsWeather(Scriptable* Sender, Trigger* parameters); + static int ItemIsIdentified(Scriptable* Sender, Trigger* parameters); + static int Joins(Scriptable* Sender, Trigger* parameters); + static int Kit(Scriptable* Sender, Trigger* parameters); + static int KnowSpell(Scriptable* Sender, Trigger* parameters); + static int LastMarkedObject_Trigger(Scriptable* Sender, Trigger* parameters); + static int LastPersonTalkedTo(Scriptable* Sender, Trigger* parameters); + static int Leaves(Scriptable* Sender, Trigger* parameters); + static int Level(Scriptable* Sender, Trigger* parameters); + static int LevelGT(Scriptable* Sender, Trigger* parameters); + static int LevelLT(Scriptable* Sender, Trigger* parameters); + static int LevelInClass(Scriptable* Sender, Trigger* parameters); + static int LevelInClassGT(Scriptable* Sender, Trigger* parameters); + static int LevelInClassLT(Scriptable* Sender, Trigger* parameters); + static int LevelParty(Scriptable* Sender, Trigger* parameters); + static int LevelPartyGT(Scriptable* Sender, Trigger* parameters); + static int LevelPartyLT(Scriptable* Sender, Trigger* parameters); + static int LocalsEqual(Scriptable* Sender, Trigger* parameters); + static int LocalsGT(Scriptable* Sender, Trigger* parameters); + static int LocalsLT(Scriptable* Sender, Trigger* parameters); + static int LOS(Scriptable* Sender, Trigger* parameters); + static int ModalState(Scriptable* Sender, Trigger* parameters); + static int Morale(Scriptable* Sender, Trigger* parameters); + static int MoraleGT(Scriptable* Sender, Trigger* parameters); + static int MoraleLT(Scriptable* Sender, Trigger* parameters); + static int NamelessBitTheDust(Scriptable* Sender, Trigger* parameters); + static int NearbyDialog(Scriptable* Sender, Trigger* parameters); + static int NearLocation(Scriptable* Sender, Trigger* parameters); + static int NearSavedLocation(Scriptable* Sender, Trigger* parameters); + static int NightmareModeOn(Scriptable* Sender, Trigger* parameters); + static int NotStateCheck(Scriptable* Sender, Trigger* parameters); + static int NullDialog(Scriptable* Sender, Trigger* parameters); + static int NumCreatures(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesAtMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesGT(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesGTMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesLT(Scriptable* Sender, Trigger* parameters); + static int NumCreaturesLTMyLevel(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsParty(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsPartyGT(Scriptable* Sender, Trigger* parameters); + static int NumCreatureVsPartyLT(Scriptable* Sender, Trigger* parameters); + static int NumDead(Scriptable* Sender, Trigger* parameters); + static int NumDeadGT(Scriptable* Sender, Trigger* parameters); + static int NumDeadLT(Scriptable* Sender, Trigger* parameters); + static int NumItems(Scriptable* Sender, Trigger* parameters); + static int NumItemsGT(Scriptable* Sender, Trigger* parameters); + static int NumItemsLT(Scriptable* Sender, Trigger* parameters); + static int NumItemsParty(Scriptable* Sender, Trigger* parameters); + static int NumItemsPartyGT(Scriptable* Sender, Trigger* parameters); + static int NumItemsPartyLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteracted(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObject(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObjectGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesInteractedObjectLT(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedTo(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedToGT(Scriptable* Sender, Trigger* parameters); + static int NumTimesTalkedToLT(Scriptable* Sender, Trigger* parameters); + static int ObjectActionListEmpty(Scriptable* Sender, Trigger* parameters); + static int OnCreation(Scriptable* Sender, Trigger* parameters); + static int OnIsland(Scriptable* Sender, Trigger* parameters); + static int OnScreen(Scriptable* Sender, Trigger* parameters); + static int Opened(Scriptable* Sender, Trigger* parameters); + static int OpenFailed(Scriptable* Sender, Trigger* parameters); + static int OpenState(Scriptable* Sender, Trigger* parameters); + static int Or(Scriptable* Sender, Trigger* parameters); + static int OutOfAmmo(Scriptable* Sender, Trigger* parameters); + static int OwnsFloaterMessage(Scriptable* Sender, Trigger* parameters); + static int PartyCountEQ(Scriptable* Sender, Trigger* parameters); + static int PartyCountGT(Scriptable* Sender, Trigger* parameters); + static int PartyCountLT(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveEQ(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveGT(Scriptable* Sender, Trigger* parameters); + static int PartyCountAliveLT(Scriptable* Sender, Trigger* parameters); + static int PartyGold(Scriptable* Sender, Trigger* parameters); + static int PartyGoldGT(Scriptable* Sender, Trigger* parameters); + static int PartyGoldLT(Scriptable* Sender, Trigger* parameters); + static int PartyHasItem(Scriptable* Sender, Trigger* parameters); + static int PartyHasItemIdentified(Scriptable* Sender, Trigger* parameters); + static int PartyMemberDied(Scriptable* Sender, Trigger* parameters); + static int PartyRested(Scriptable* Sender, Trigger* parameters); + static int PCCanSeePoint(Scriptable* Sender, Trigger* parameters); + static int PCInStore(Scriptable* Sender, Trigger* parameters); + static int PersonalSpaceDistance(Scriptable* Sender, Trigger* parameters); + static int PickLockFailed(Scriptable* Sender, Trigger* parameters); + static int PickpocketFailed(Scriptable* Sender, Trigger* parameters); + static int Proficiency(Scriptable* Sender, Trigger* parameters); + static int ProficiencyGT(Scriptable* Sender, Trigger* parameters); + static int ProficiencyLT(Scriptable* Sender, Trigger* parameters); + static int Race(Scriptable* Sender, Trigger* parameters); + static int RandomNum(Scriptable* Sender, Trigger* parameters); + static int RandomNumGT(Scriptable* Sender, Trigger* parameters); + static int RandomNumLT(Scriptable* Sender, Trigger* parameters); + static int RandomStatCheck(Scriptable* Sender, Trigger* parameters); + static int Range(Scriptable* Sender, Trigger* parameters); + static int Reaction(Scriptable* Sender, Trigger* parameters); + static int ReactionLT(Scriptable* Sender, Trigger* parameters); + static int ReactionGT(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerExact(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerExpired(Scriptable* Sender, Trigger* parameters); + static int RealGlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters); + static int ReceivedOrder(Scriptable* Sender, Trigger* parameters); + static int Reputation(Scriptable* Sender, Trigger* parameters); + static int ReputationGT(Scriptable* Sender, Trigger* parameters); + static int ReputationLT(Scriptable* Sender, Trigger* parameters); + static int School(Scriptable* Sender, Trigger* parameters); + static int See(Scriptable* Sender, Trigger* parameters); + static int Sequence(Scriptable* Sender, Trigger* parameters); + static int SetLastMarkedObject(Scriptable* Sender, Trigger* parameters); + static int SetMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters); + static int Specifics(Scriptable* Sender, Trigger* parameters); + static int SpellCast(Scriptable* Sender, Trigger* parameters); + static int SpellCastInnate(Scriptable* Sender, Trigger* parameters); + static int SpellCastOnMe(Scriptable* Sender, Trigger* parameters); + static int SpellCastPriest(Scriptable* Sender, Trigger* parameters); + static int StateCheck(Scriptable* Sender, Trigger* parameters); + static int StealFailed(Scriptable* Sender, Trigger* parameters); + static int StoreHasItem(Scriptable* Sender, Trigger* parameters); + static int StuffGlobalRandom(Scriptable* Sender, Trigger* parameters); + static int SubRace(Scriptable* Sender, Trigger* parameters); + static int SystemVariable_Trigger(Scriptable* Sender, Trigger* parameters); + static int TargetUnreachable(Scriptable* Sender, Trigger* parameters); + static int Team(Scriptable* Sender, Trigger* parameters); + static int Time(Scriptable* Sender, Trigger* parameters); + static int TimeGT(Scriptable* Sender, Trigger* parameters); + static int TimeLT(Scriptable* Sender, Trigger* parameters); + static int TimeOfDay(Scriptable* Sender, Trigger* parameters); + static int TimerActive(Scriptable* Sender, Trigger* parameters); + static int TimerExpired(Scriptable* Sender, Trigger* parameters); + static int TookDamage(Scriptable* Sender, Trigger* parameters); + static int TotalItemCnt(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExclude(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExcludeGT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntExcludeLT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntGT(Scriptable* Sender, Trigger* parameters); + static int TotalItemCntLT(Scriptable* Sender, Trigger* parameters); + static int TrapTriggered(Scriptable* Sender, Trigger* parameters); + static int TriggerTrigger(Scriptable* Sender, Trigger* parameters); + static int TriggerSetGlobal(Scriptable* Sender, Trigger* parameters); + static int True(Scriptable* Sender, Trigger* parameters); + static int TurnedBy(Scriptable* Sender, Trigger* parameters); + static int Unlocked(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariable(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariableGT(Scriptable* Sender, Trigger* parameters); + static int UnselectableVariableLT(Scriptable* Sender, Trigger* parameters); + static int Unusable(Scriptable* Sender, Trigger* parameters); + static int Vacant(Scriptable* Sender, Trigger* parameters); + static int WalkedToTrigger(Scriptable* Sender, Trigger* parameters); + static int WasInDialog(Scriptable* Sender, Trigger* parameters); + static int Xor(Scriptable* Sender, Trigger* parameters); + static int XP(Scriptable* Sender, Trigger* parameters); + static int XPGT(Scriptable* Sender, Trigger* parameters); + static int XPLT(Scriptable* Sender, Trigger* parameters); +public: + //Actions + static void Activate(Scriptable* Sender, Action* parameters); + static void ActivatePortalCursor(Scriptable* Sender, Action* parameters); + static void AddAreaFlag(Scriptable* Sender, Action* parameters); + static void AddAreaType(Scriptable* Sender, Action* parameters); + static void AddExperienceParty(Scriptable *Sender, Action* parameters); + static void AddExperiencePartyCR(Scriptable *Sender, Action* parameters); + static void AddExperiencePartyGlobal(Scriptable *Sender, Action* parameters); + static void AddFeat(Scriptable *Sender, Action* parameters); + static void AddGlobals(Scriptable* Sender, Action* parameters); + static void AddHP(Scriptable* Sender, Action* parameters); + static void AddJournalEntry(Scriptable* Sender, Action* parameters); + static void AddKit(Scriptable* Sender, Action* parameters); + static void AddMapnote(Scriptable* Sender, Action* parameters); + static void AddSpecialAbility(Scriptable* Sender, Action* parameters); + static void AddSuperKit(Scriptable* Sender, Action* parameters); + static void AddWayPoint(Scriptable* Sender, Action* parameters); + static void AddXP2DA(Scriptable *Sender, Action* parameters); + static void AddXPObject(Scriptable *Sender, Action* parameters); + static void AdvanceTime(Scriptable *Sender, Action* parameters); + static void Ally(Scriptable* Sender, Action* parameters); + static void AmbientActivate(Scriptable* Sender, Action* parameters); + static void AnkhegEmerge(Scriptable* Sender, Action* parameters); + static void AnkhegHide(Scriptable* Sender, Action* parameters); + static void ApplyDamage(Scriptable* Sender, Action* parameters); + static void ApplyDamagePercent(Scriptable* Sender, Action* parameters); + static void ApplySpell(Scriptable* Sender, Action* parameters); + static void ApplySpellPoint(Scriptable* Sender, Action* parameters); + static void AttachTransitionToDoor(Scriptable* Sender, Action* parameters); + static void Attack(Scriptable* Sender, Action* parameters); + static void AttackNoSound(Scriptable* Sender, Action* parameters); + static void AttackOneRound(Scriptable* Sender, Action* parameters); + static void AttackReevaluate(Scriptable* Sender, Action* parameters); + static void BanterBlockFlag(Scriptable* Sender, Action* parameters); + static void BanterBlockTime(Scriptable* Sender, Action* parameters); + static void BashDoor(Scriptable* Sender, Action* parameters); + static void BattleSong(Scriptable* Sender, Action* parameters); + static void Berserk(Scriptable* Sender, Action* parameters); + static void BitClear(Scriptable* Sender, Action* parameters); + static void BitGlobal(Scriptable* Sender, Action* parameters); + static void BreakInstants(Scriptable* Sender, Action* parameters); + static void Calm(Scriptable* Sender, Action* parameters); + static void ChangeAIScript(Scriptable* Sender, Action* parameters); + static void ChangeAIType(Scriptable* Sender, Action* parameters); + static void ChangeAlignment(Scriptable* Sender, Action* parameters); + static void ChangeAllegiance(Scriptable* Sender, Action* parameters); + static void ChangeAnimation(Scriptable* Sender, Action* parameters); + static void ChangeAnimationNoEffect(Scriptable* Sender, Action* parameters); + static void ChangeClass(Scriptable* Sender, Action* parameters); + static void ChangeColor(Scriptable* Sender, Action* parameters); + static void ChangeCurrentScript(Scriptable* Sender, Action* parameters); + static void ChangeDestination(Scriptable* Sender, Action* parameters); + static void ChangeDialogue(Scriptable* Sender, Action* parameters); + static void ChangeGender(Scriptable* Sender, Action* parameters); + static void ChangeGeneral(Scriptable* Sender, Action* parameters); + static void ChangeRace(Scriptable* Sender, Action* parameters); + static void ChangeSpecifics(Scriptable* Sender, Action* parameters); + static void ChangeStat(Scriptable* Sender, Action* parameters); + static void ChangeStatGlobal(Scriptable* Sender, Action* parameters); + static void ChangeStoreMarkup(Scriptable* Sender, Action* parameters); + static void ChangeTileState(Scriptable* Sender, Action* parameters); + static void ClearActions(Scriptable* Sender, Action* parameters); + static void ClearAllActions(Scriptable* Sender, Action* parameters); + static void ClearPartyEffects(Scriptable* Sender, Action* parameters); + static void ClearSpriteEffects(Scriptable* Sender, Action* parameters); + static void ClickLButtonObject(Scriptable* Sender, Action* parameters); + static void ClickLButtonPoint(Scriptable* Sender, Action* parameters); + static void ClickRButtonObject(Scriptable* Sender, Action* parameters); + static void ClickRButtonPoint(Scriptable* Sender, Action* parameters); + static void CloseDoor(Scriptable* Sender, Action* parameters); + static void ContainerEnable(Scriptable* Sender, Action* parameters); + static void Continue(Scriptable* Sender, Action* parameters); + static void CopyGroundPilesTo(Scriptable* Sender, Action* parameters); + static void CreateCreature(Scriptable* Sender, Action* parameters); + static void CreateCreatureAtLocation(Scriptable* Sender, Action* parameters); + static void CreateCreatureAtFeet(Scriptable* Sender, Action* parameters); + static void CreateCreatureCopyPoint(Scriptable* Sender, Action* parameters); + static void CreateCreatureDoor(Scriptable* Sender, Action* parameters); + static void CreateCreatureImpassable(Scriptable* Sender, Action* parameters); + static void CreateCreatureImpassableAllowOverlap(Scriptable* Sender, + Action* parameters); + static void CreateCreatureObject(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectCopy(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectDoor(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectOffset(Scriptable* Sender, Action* parameters); + static void CreateCreatureObjectOffScreen(Scriptable* Sender, Action* parameters); + static void CreateCreatureOffScreen(Scriptable* Sender, Action* parameters); + static void CreateItem(Scriptable* Sender, Action* parameters); + static void CreateItemNumGlobal(Scriptable* Sender, Action* parameters); + static void CreatePartyGold(Scriptable *Sender, Action *parameters); + static void CreateVisualEffect(Scriptable* Sender, Action* parameters); + static void CreateVisualEffectObject(Scriptable* Sender, + Action* parameters); + static void CreateVisualEffectObjectSticky(Scriptable* Sender, + Action* parameters); + static void CutSceneID(Scriptable* Sender, Action* parameters); + static void Damage(Scriptable* Sender, Action* parameters); + static void DayNight(Scriptable *Sender, Action* parameters); + static void Deactivate(Scriptable* Sender, Action* parameters); + static void Debug(Scriptable* Sender, Action* parameters); + static void DestroyAllDestructableEquipment(Scriptable* Sender, + Action* parameters); + static void DestroyAllEquipment(Scriptable* Sender, Action* parameters); + static void DestroyGold(Scriptable* Sender, Action* parameters); + static void DestroyItem(Scriptable* Sender, Action* parameters); + static void DestroyPartyGold(Scriptable* Sender, Action* parameters); + static void DestroyPartyItem(Scriptable* Sender, Action* parameters); + static void DestroyPartyItemNum(Scriptable* Sender, Action* parameters); + static void DestroySelf(Scriptable* Sender, Action* parameters); + static void DetectSecretDoor(Scriptable* Sender, Action* parameters); + static void Dialogue(Scriptable* Sender, Action* parameters); + static void DialogueForceInterrupt(Scriptable* Sender, Action* parameters); + static void DialogueInterrupt(Scriptable* Sender, Action* parameters); + static void DisableFogDither(Scriptable* Sender, Action* parameters); + static void DisableSpriteDither(Scriptable* Sender, Action* parameters); + static void DisplayMessage(Scriptable* Sender, Action* parameters); + static void DisplayString(Scriptable* Sender, Action* parameters); + static void DisplayStringHead(Scriptable* Sender, Action* parameters); + static void DisplayStringHeadOwner(Scriptable* Sender, Action* parameters); + static void DisplayStringNoName(Scriptable* Sender, Action* parameters); + static void DisplayStringNoNameHead(Scriptable* Sender, Action* parameters); + static void DisplayStringWait(Scriptable* Sender, Action* parameters); + static void DoubleClickLButtonObject(Scriptable* Sender, Action* parameters); + static void DoubleClickLButtonPoint(Scriptable* Sender, Action* parameters); + static void DoubleClickRButtonObject(Scriptable* Sender, Action* parameters); + static void DoubleClickRButtonPoint(Scriptable* Sender, Action* parameters); + static void DropInventory(Scriptable* Sender, Action* parameters); + static void DropInventoryEX(Scriptable* Sender, Action* parameters); + static void DropItem(Scriptable* Sender, Action* parameters); + static void EnableFogDither(Scriptable* Sender, Action* parameters); + static void EnablePortalTravel(Scriptable* Sender, Action* parameters); + static void EnableSpriteDither(Scriptable* Sender, Action* parameters); + static void EndCredits(Scriptable* Sender, Action* parameters); + static void EndCutSceneMode(Scriptable* Sender, Action* parameters); + static void Enemy(Scriptable* Sender, Action* parameters); + static void EscapeArea(Scriptable* Sender, Action* parameters); + static void EscapeAreaDestroy(Scriptable* Sender, Action* parameters); + static void EscapeAreaNoSee(Scriptable* Sender, Action* parameters); + static void EscapeAreaObject(Scriptable* Sender, Action* parameters); + static void EscapeAreaObjectNoSee(Scriptable* Sender, Action* parameters); + static void EquipItem(Scriptable *Sender, Action *parameters); + static void EquipMostDamagingMelee(Scriptable *Sender, Action *parameters); + static void EquipRanged(Scriptable *Sender, Action *parameters); + static void EquipWeapon(Scriptable *Sender, Action *parameters); + static void ExitPocketPlane(Scriptable* Sender, Action* parameters); + static void ExpansionEndCredits(Scriptable* Sender, Action* parameters); + static void Explore(Scriptable *Sender, Action *parameters); + static void ExploreMapChunk(Scriptable *Sender, Action *parameters); + static void ExportParty(Scriptable *Sender, Action *parameters); + static void Face(Scriptable* Sender, Action* parameters); + static void FaceObject(Scriptable* Sender, Action* parameters); + static void FaceSavedLocation(Scriptable* Sender, Action* parameters); + static void FadeFromColor(Scriptable* Sender, Action* parameters); + static void FadeToAndFromColor(Scriptable* Sender, Action* parameters); + static void FadeToColor(Scriptable* Sender, Action* parameters); + static void FakeEffectExpiryCheck(Scriptable* Sender, Action* parameters); + static void FillSlot(Scriptable *Sender, Action* parameters); + static void FindTraps(Scriptable* Sender, Action* parameters); + static void FloatMessageFixed(Scriptable* Sender, Action* parameters); + static void FloatMessageFixedRnd(Scriptable* Sender, Action* parameters); + static void FloatMessageRnd(Scriptable* Sender, Action* parameters); + static void FloatRebus(Scriptable* Sender, Action* parameters); + static void Follow(Scriptable* Sender, Action* parameters); + static void FollowCreature(Scriptable* Sender, Action* parameters); + static void FollowObjectFormation(Scriptable* Sender, Action* parameters); + static void ForceAIScript(Scriptable* Sender, Action* parameters); + static void ForceAttack(Scriptable* Sender, Action* parameters); + static void ForceFacing(Scriptable* Sender, Action* parameters); + static void ForceHide(Scriptable* Sender, Action* parameters); + static void ForceLeaveAreaLUA(Scriptable* Sender, Action* parameters); + static void ForceMarkedSpell(Scriptable* Sender, Action* parameters); + static void ForceSpell(Scriptable* Sender, Action* parameters); + static void ForceSpellPoint(Scriptable* Sender, Action* parameters); + static void ForceUseContainer(Scriptable* Sender, Action* parameters); + static void Formation(Scriptable* Sender, Action* parameters); + static void FullHeal(Scriptable* Sender, Action* parameters); + static void GeneratePartyMember(Scriptable* Sender, Action* parameters); + static void GetItem(Scriptable* Sender, Action* parameters); + static void GetStat(Scriptable* Sender, Action* parameters); + static void GiveItem(Scriptable* Sender, Action* parameters); + static void GiveOrder(Scriptable* Sender, Action* parameters); + static void GivePartyAllEquipment(Scriptable* Sender, Action* parameters); + static void GivePartyGold(Scriptable* Sender, Action* parameters); + static void GivePartyGoldGlobal(Scriptable* Sender, Action* parameters); + static void GlobalAddGlobal(Scriptable* Sender, Action* parameters); + static void GlobalAndGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBAnd(Scriptable* Sender, Action* parameters); + static void GlobalBAndGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBitGlobal(Scriptable* Sender, Action* parameters); + static void GlobalBOr(Scriptable* Sender, Action* parameters); + static void GlobalBOrGlobal(Scriptable* Sender, Action* parameters); + static void GlobalMax(Scriptable* Sender, Action* parameters); + static void GlobalMaxGlobal(Scriptable* Sender, Action* parameters); + static void GlobalMin(Scriptable* Sender, Action* parameters); + static void GlobalMinGlobal(Scriptable* Sender, Action* parameters); + static void GlobalOrGlobal(Scriptable* Sender, Action* parameters); + static void GlobalSetGlobal(Scriptable* Sender, Action* parameters); + static void GlobalShL(Scriptable* Sender, Action* parameters); + static void GlobalShLGlobal(Scriptable* Sender, Action* parameters); + static void GlobalShout(Scriptable* Sender, Action* parameters); + static void GlobalShR(Scriptable* Sender, Action* parameters); + static void GlobalShRGlobal(Scriptable* Sender, Action* parameters); + static void GlobalSubGlobal(Scriptable* Sender, Action* parameters); + static void GlobalXor(Scriptable* Sender, Action* parameters); + static void GlobalXorGlobal(Scriptable* Sender, Action* parameters); + static void Help(Scriptable* Sender, Action* parameters); + static void Hide(Scriptable* Sender, Action* parameters); + static void HideAreaOnMap(Scriptable* Sender, Action* parameters); + static void HideCreature(Scriptable* Sender, Action* parameters); + static void HideGUI(Scriptable* Sender, Action* parameters); + static void IncInternal(Scriptable* Sender, Action* parameters); + static void IncMoraleAI(Scriptable* Sender, Action* parameters); + static void IncrementChapter(Scriptable* Sender, Action* parameters); + static void IncrementExtraProficiency(Scriptable* Sender, Action* parameters); + static void IncrementGlobal(Scriptable* Sender, Action* parameters); + static void IncrementGlobalOnce(Scriptable* Sender, Action* parameters); + static void IncrementKillStat(Scriptable* Sender, Action* parameters); + static void IncrementProficiency(Scriptable* Sender, Action* parameters); + static void Interact(Scriptable* Sender, Action* parameters); + static void JoinParty(Scriptable* Sender, Action* parameters); + static void JumpToObject(Scriptable* Sender, Action* parameters); + static void JumpToPoint(Scriptable* Sender, Action* parameters); + static void JumpToPointInstant(Scriptable* Sender, Action* parameters); + static void JumpToSavedLocation(Scriptable* Sender, Action* parameters); + static void Kill(Scriptable* Sender, Action* parameters); + static void KillFloatMessage(Scriptable* Sender, Action* parameters); + static void Leader(Scriptable* Sender, Action* parameters); + static void LeaveArea(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUA(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAEntry(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAPanic(Scriptable* Sender, Action* parameters); + static void LeaveAreaLUAPanicEntry(Scriptable* Sender, Action* parameters); + static void LeaveParty(Scriptable* Sender, Action* parameters); + static void Lock(Scriptable* Sender, Action* parameters); + static void LockScroll(Scriptable* Sender, Action* parameters); + static void MakeGlobal(Scriptable* Sender, Action* parameters); + static void MakeUnselectable(Scriptable* Sender, Action* parameters); + static void MarkObject(Scriptable* Sender, Action* parameters); + static void MarkSpellAndObject(Scriptable* Sender, Action* parameters); + static void MatchHP(Scriptable* Sender, Action* parameters); + static void MoraleDec(Scriptable* Sender, Action* parameters); + static void MoraleInc(Scriptable* Sender, Action* parameters); + static void MoraleSet(Scriptable* Sender, Action* parameters); + static void MoveBetweenAreas(Scriptable* Sender, Action* parameters); + static void MoveBetweenAreasEffect(Scriptable* Sender, Action* parameters); + static void MoveCursorPoint(Scriptable* Sender, Action* parameters); + static void MoveGlobal(Scriptable* Sender, Action* parameters); + static void MoveGlobalObject(Scriptable* Sender, Action* parameters); + static void MoveGlobalObjectOffScreen(Scriptable* Sender, Action* parameters); + static void MoveGlobalsTo(Scriptable* Sender, Action* parameters); + static void MoveInventory(Scriptable *Sender, Action* parameters); + static void MoveToCenterOfScreen(Scriptable* Sender, Action* parameters); + static void MoveToExpansion(Scriptable* Sender, Action* parameters); + static void MoveToObject(Scriptable* Sender, Action* parameters); + static void MoveToObjectFollow(Scriptable* Sender, Action* parameters); + static void MoveToObjectNoInterrupt(Scriptable* Sender, Action* parameters); + static void MoveToObjectUntilSee(Scriptable* Sender, Action* parameters); + static void MoveToOffset(Scriptable* Sender, Action* parameters); + static void MoveToPoint(Scriptable* Sender, Action* parameters); + static void MoveToPointNoInterrupt(Scriptable* Sender, Action* parameters); + static void MoveToPointNoRecticle(Scriptable* Sender, Action* parameters); + static void MoveToSavedLocation(Scriptable* Sender, Action* parameters); + static void MoveViewPoint(Scriptable* Sender, Action* parameters); + static void MoveViewObject(Scriptable* Sender, Action* parameters); + static void NIDSpecial1(Scriptable* Sender, Action* parameters); + static void NIDSpecial2(Scriptable* Sender, Action* parameters); + static void NoAction(Scriptable* Sender, Action* parameters); + static void NoActionAtAll(Scriptable* Sender, Action* parameters); + static void OpenDoor(Scriptable* Sender, Action* parameters); + static void Panic(Scriptable* Sender, Action* parameters); + static void PauseGame(Scriptable *Sender, Action* parameters); + static void PermanentStatChange(Scriptable* Sender, Action* parameters); + static void PickLock(Scriptable* Sender, Action* parameters); + static void PickPockets(Scriptable* Sender, Action* parameters); + static void PickUpItem(Scriptable* Sender, Action* parameters); + static void PlayBardSong(Scriptable* Sender, Action* parameters); + static void PlayDead(Scriptable* Sender, Action* parameters); + static void PlayDeadInterruptable(Scriptable* Sender, Action* parameters); + static void PlayerDialogue(Scriptable* Sender, Action* parameters); + static void PlaySequence(Scriptable* Sender, Action* parameters); + static void PlaySequenceTimed(Scriptable* Sender, Action* parameters); + static void PlaySound(Scriptable* Sender, Action* parameters); + static void PlaySoundNotRanged(Scriptable* Sender, Action* parameters); + static void PlaySoundPoint(Scriptable* Sender, Action* parameters); + static void Plunder(Scriptable* Sender, Action* parameters); + static void Polymorph(Scriptable* Sender, Action* parameters); + static void PolymorphCopy(Scriptable* Sender, Action* parameters); + static void PolymorphCopyBase(Scriptable* Sender, Action* parameters); + static void ProtectObject(Scriptable* Sender, Action* parameters); + static void ProtectPoint(Scriptable* Sender, Action* parameters); + static void QuitGame(Scriptable* Sender, Action* parameters); + static void RandomFly(Scriptable* Sender, Action* parameters); + static void RandomRun(Scriptable* Sender, Action* parameters); + static void RandomTurn(Scriptable* Sender, Action* parameters); + static void RandomWalk(Scriptable* Sender, Action* parameters); + static void RandomWalkContinuous(Scriptable* Sender, Action* parameters); + static void RealSetGlobalTimer(Scriptable* Sender, Action* parameters); + static void ReallyForceSpell(Scriptable* Sender, Action* parameters); + static void ReallyForceSpellDead(Scriptable* Sender, Action* parameters); + static void ReallyForceSpellPoint(Scriptable* Sender, Action* parameters); + static void Recoil(Scriptable* Sender, Action* parameters); + static void RegainPaladinHood(Scriptable* Sender, Action* parameters); + static void RegainRangerHood(Scriptable* Sender, Action* parameters); + static void RemoveAreaFlag(Scriptable* Sender, Action* parameters); + static void RemoveAreaType(Scriptable* Sender, Action* parameters); + static void RemoveJournalEntry(Scriptable* Sender, Action* parameters); + static void RemoveMapnote(Scriptable* Sender, Action* parameters); + static void RemovePaladinHood(Scriptable* Sender, Action* parameters); + static void RemoveRangerHood(Scriptable* Sender, Action* parameters); + static void RemoveSpell(Scriptable* Sender, Action* parameters); + static void RemoveTraps(Scriptable* Sender, Action* parameters); + static void ReputationInc(Scriptable* Sender, Action* parameters); + static void ReputationSet(Scriptable* Sender, Action* parameters); + static void RestorePartyLocation(Scriptable *Sender, Action* parameters); + static void Rest(Scriptable *Sender, Action* parameters); + static void RestNoSpells(Scriptable *Sender, Action* parameters); + static void RestParty(Scriptable *Sender, Action* parameters); + static void RestUntilHealed(Scriptable *Sender, Action* parameters); + static void ReturnToSavedLocation(Scriptable* Sender, Action* parameters); + static void ReturnToSavedLocationDelete(Scriptable* Sender, Action* parameters); + static void RevealAreaOnMap(Scriptable* Sender, Action* parameters); + static void RunAwayFrom(Scriptable* Sender, Action* parameters); + static void RunAwayFromNoInterrupt(Scriptable* Sender, Action* parameters); + static void RunAwayFromNoLeaveArea(Scriptable* Sender, Action* parameters); + static void RunFollow(Scriptable* Sender, Action* parameters); + static void RunningAttack(Scriptable* Sender, Action* parameters); + static void RunningAttackNoSound(Scriptable* Sender, Action* parameters); + static void RunToObject(Scriptable* Sender, Action* parameters); + static void RunToPoint(Scriptable* Sender, Action* parameters); + static void RunToPointNoRecticle(Scriptable* Sender, Action* parameters); + static void RunToSavedLocation(Scriptable* Sender, Action* parameters); + static void SaveGame(Scriptable* Sender, Action* parameters); + static void SaveLocation(Scriptable* Sender, Action* parameters); + static void SaveObjectLocation(Scriptable* Sender, Action* parameters); + static void ScreenShake(Scriptable* Sender, Action* parameters); + static void SelectWeaponAbility(Scriptable* Sender, Action* parameters); + static void SendTrigger(Scriptable* Sender, Action* parameters); + static void SetAnimState(Scriptable* Sender, Action* parameters); + static void SetApparentName(Scriptable* Sender, Action* parameters); + static void SetAreaFlags(Scriptable* Sender, Action* parameters); + static void SetAreaRestFlag(Scriptable* Sender, Action* parameters); + static void SetArmourLevel(Scriptable* Sender, Action* parameters); + static void SetBeenInPartyFlags(Scriptable* Sender, Action* parameters); + static void SetBestWeapon(Scriptable *Sender, Action *parameters); + static void SetCursorState(Scriptable* Sender, Action* parameters); + static void SetCreatureAreaFlag(Scriptable* Sender, Action* parameters); + static void SetCriticalPathObject(Scriptable* Sender, Action* parameters); + static void SetDialogue(Scriptable* Sender, Action* parameters); + static void SetDialogueRange(Scriptable* Sender, Action* parameters); + static void SetDoorFlag(Scriptable* Sender, Action* parameters); + static void SetDoorLocked(Scriptable* Sender, Action* parameters); + static void SetEncounterProbability(Scriptable* Sender, Action* parameters); + static void SetExtendedNight(Scriptable* Sender, Action* parameters); + static void SetFaction(Scriptable* Sender, Action* parameters); + static void SetGabber(Scriptable* Sender, Action* parameters); + static void SetGlobal(Scriptable* Sender, Action* parameters); + static void SetGlobalRandom(Scriptable* Sender, Action* parameters); + static void SetGlobalTimer(Scriptable* Sender, Action* parameters); + static void SetGlobalTimerOnce(Scriptable* Sender, Action* parameters); + static void SetGlobalTimerRandom(Scriptable* Sender, Action* parameters); + static void SetGlobalTint(Scriptable* Sender, Action* parameters); + static void SetHP(Scriptable* Sender, Action* parameters); + static void SetHPPercent(Scriptable* Sender, Action* parameters); + static void SetInternal(Scriptable* Sender, Action* parameters); + static void SetInterrupt(Scriptable* Sender, Action* parameters); + static void SetLeavePartyDialogFile(Scriptable* Sender, Action* parameters); + static void SetMarkedSpell(Scriptable* Sender, Action* parameters); + static void SetMasterArea(Scriptable* Sender, Action* parameters); + static void SetMazeEasier(Scriptable* Sender, Action* parameters); + static void SetMazeHarder(Scriptable* Sender, Action* parameters); + static void SetMoraleAI(Scriptable* Sender, Action* parameters); + static void SetMusic(Scriptable* Sender, Action* parameters); + static void SetNamelessClass(Scriptable* Sender, Action* parameters); + static void SetNamelessDeath(Scriptable* Sender, Action* parameters); + static void SetNamelessDisguise(Scriptable* Sender, Action* parameters); + static void SetNoOneOnTrigger(Scriptable* Sender, Action* parameters); + static void SetNumTimesTalkedTo(Scriptable* Sender, Action* parameters); + static void SetPlayerSound(Scriptable* Sender, Action* parameters); + static void SetQuestDone(Scriptable* Sender, Action* parameters); + static void SetRegularName(Scriptable* Sender, Action* parameters); + static void SetRestEncounterChance(Scriptable* Sender, Action* parameters); + static void SetRestEncounterProbabilityDay(Scriptable* Sender, Action* parameters); + static void SetRestEncounterProbabilityNight(Scriptable* Sender, Action* parameters); + static void SetSavedLocation(Scriptable* Sender, Action* parameters); + static void SetSavedLocationPoint(Scriptable* Sender, Action* parameters); + static void SetScriptName(Scriptable* Sender, Action* parameters); + static void SetSelection(Scriptable* Sender, Action* parameters); + static void SetStartPos(Scriptable* Sender, Action* parameters); + static void SetTeam(Scriptable* Sender, Action* parameters); + static void SetTeamBit(Scriptable* Sender, Action* parameters); + static void SetTextColor(Scriptable* Sender, Action* parameters); + static void SetToken(Scriptable* Sender, Action* parameters); + static void SetToken2DA(Scriptable* Sender, Action* parameters); + static void SetTokenGlobal(Scriptable* Sender, Action* parameters); + static void SetTokenObject(Scriptable* Sender, Action* parameters); + static void SetTrackString(Scriptable* Sender, Action* parameters); + static void SetupWish(Scriptable* Sender, Action* parameters); + static void SetupWishObject(Scriptable* Sender, Action* parameters); + static void SetVisualRange(Scriptable* Sender, Action* parameters); + static void SG(Scriptable* Sender, Action* parameters); + static void Shout(Scriptable* Sender, Action* parameters); + static void SmallWait(Scriptable* Sender, Action* parameters); + static void SmallWaitRandom(Scriptable* Sender, Action* parameters); + static void SoundActivate(Scriptable* Sender, Action* parameters); + static void SpawnPtActivate(Scriptable* Sender, Action* parameters); + static void SpawnPtDeactivate(Scriptable* Sender, Action* parameters); + static void SpawnPtSpawn(Scriptable* Sender, Action* parameters); + static void Spell(Scriptable* Sender, Action* parameters); + static void SpellCastEffect(Scriptable* Sender, Action* parameters); + static void SpellHitEffectPoint(Scriptable* Sender, Action* parameters); + static void SpellHitEffectSprite(Scriptable* Sender, Action* parameters); + static void SpellNoDec(Scriptable* Sender, Action* parameters); + static void SpellPoint(Scriptable* Sender, Action* parameters); + static void SpellPointNoDec(Scriptable* Sender, Action* parameters); + static void StartCombatCounter(Scriptable* Sender, Action* parameters); + static void StartCutScene(Scriptable* Sender, Action* parameters); + static void StartCutSceneMode(Scriptable* Sender, Action* parameters); + static void StartDialogue(Scriptable* Sender, Action* parameters); + static void StartDialogueInterrupt(Scriptable* Sender, Action* parameters); + static void StartDialogueNoSet(Scriptable* Sender, Action* parameters); + static void StartDialogueNoSetInterrupt(Scriptable* Sender, + Action* parameters); + static void StartDialogueOverride(Scriptable* Sender, Action* parameters); + static void StartDialogueOverrideInterrupt(Scriptable* Sender, + Action* parameters); + static void StartMovie(Scriptable* Sender, Action* parameters); + static void StartMusic(Scriptable* Sender, Action* parameters); + static void StartRainNow(Scriptable* Sender, Action* parameters); + static void StartRandomTimer(Scriptable* Sender, Action* parameters); + static void StartSong(Scriptable* Sender, Action* parameters); + static void StartStore(Scriptable* Sender, Action* parameters); + static void StartTimer(Scriptable* Sender, Action* parameters); + static void StateOverrideFlag(Scriptable* Sender, Action* parameters); + static void StateOverrideTime(Scriptable* Sender, Action* parameters); + static void StaticPalette(Scriptable* Sender, Action* parameters); + static void StaticStart(Scriptable* Sender, Action* parameters); + static void StaticStop(Scriptable* Sender, Action* parameters); + static void StopMoving(Scriptable* Sender, Action* parameters); + static void StorePartyLocation(Scriptable *Sender, Action* parameters); + static void Swing(Scriptable* Sender, Action* parameters); + static void SwingOnce(Scriptable* Sender, Action* parameters); + static void TakeItemList(Scriptable* Sender, Action* parameters); + static void TakeItemListParty(Scriptable* Sender, Action* parameters); + static void TakeItemListPartyNum(Scriptable* Sender, Action* parameters); + static void TakeItemReplace(Scriptable* Sender, Action* parameters); + static void TakePartyGold(Scriptable* Sender, Action* parameters); + static void TakePartyItem(Scriptable* Sender, Action* parameters); + static void TakePartyItemAll(Scriptable* Sender, Action* parameters); + static void TakePartyItemNum(Scriptable* Sender, Action* parameters); + static void TakePartyItemRange(Scriptable* Sender, Action* parameters); + static void TeleportParty(Scriptable* Sender, Action* parameters); + static void TextScreen(Scriptable* Sender, Action* parameters); + static void ToggleDoor(Scriptable* Sender, Action* parameters); + static void TimedMoveToPoint(Scriptable* Sender, Action* parameters); + static void TransformItem(Scriptable* Sender, Action* parameters); + static void TransformItemAll(Scriptable* Sender, Action* parameters); + static void TransformPartyItem(Scriptable* Sender, Action* parameters); + static void TransformPartyItemAll(Scriptable* Sender, Action* parameters); + static void TriggerActivation(Scriptable* Sender, Action* parameters); + static void Turn(Scriptable* Sender, Action* parameters); + static void TurnAMT(Scriptable* Sender, Action* parameters); + static void UndoExplore(Scriptable *Sender, Action *parameters); + static void UnhideGUI(Scriptable* Sender, Action* parameters); + static void Unlock(Scriptable* Sender, Action* parameters); + static void UnlockScroll(Scriptable* Sender, Action* parameters); + static void UseContainer(Scriptable* Sender, Action* parameters); + static void UseDoor(Scriptable* Sender, Action* parameters); + static void UseItem(Scriptable* Sender, Action* parameters); + static void UseItemPoint(Scriptable* Sender, Action* parameters); + static void VerbalConstant(Scriptable* Sender, Action* parameters); + static void VerbalConstantHead(Scriptable* Sender, Action* parameters); + static void Wait(Scriptable* Sender, Action* parameters); + static void WaitAnimation(Scriptable* Sender, Action* parameters); + static void WaitRandom(Scriptable* Sender, Action* parameters); + static void Weather(Scriptable* Sender, Action* parameters); + static void XEquipItem(Scriptable *Sender, Action *parameters); +public: + //Objects + static Targets *BestAC(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *EighthNearest(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestDoor(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestEnemyOf(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestEnemyOfType(Scriptable *Sender, Targets *parameter, int ga_flagss); + static Targets *EighthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Farthest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FarthestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FifthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *FourthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Gabber(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *GroupOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastAttackerOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastCommandedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHeardBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHelp(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastHitter(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastMarkedObject(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastSeenBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastSummonerOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTalkedToBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTargetedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LastTrigger(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LeaderOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *LeastDamagedOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *MostDamagedOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Myself(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *MyTarget(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Nearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestEnemySummoned(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NearestPC(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *NinthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Nothing(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player1(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player1Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player2(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player2Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player3(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player3Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player4(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player4Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player5(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player5Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player6(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player6Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Protagonist(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ProtectedBy(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ProtectorOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SecondNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SelectedCharacter(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SeventhNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *SixthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *StrongestOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *StrongestOfMale(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *TenthNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearest(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestDoor(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestEnemyOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestEnemyOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *ThirdNearestMyGroupOfType(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *WeakestOf(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *WorstAC(Scriptable *Sender, Targets *parameters, int ga_flags); + +public: + /*GemRB extensions/actions*/ + static void RunAwayFromPoint(Scriptable* Sender, Action* parameters); + static void UnMakeGlobal(Scriptable* Sender, Action* parameters); + static void UnloadArea(Scriptable* Sender, Action* parameters); + + /*GemRB extensions/objects*/ + static Targets *Player7(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player7Fill(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player8(Scriptable *Sender, Targets *parameters, int ga_flags); + static Targets *Player8Fill(Scriptable *Sender, Targets *parameters, int ga_flags); +}; + +GEM_EXPORT Action* GenerateAction(char* String); +Action* GenerateActionDirect(char* String, Scriptable *object); +GEM_EXPORT Trigger* GenerateTrigger(char* String); + +void InitializeIEScript(); + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Matching.cpp b/project/jni/application/gemrb/src/core/GameScript/Matching.cpp new file mode 100644 index 000000000..4665a8a2f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Matching.cpp @@ -0,0 +1,685 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * 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 "GameScript/Matching.h" + +#include "GameScript/GSUtils.h" + +#include "Interface.h" +#include "Game.h" +#include "TileMap.h" + +/* return a Targets object with a single actor inside */ +inline static Targets* ReturnActorAsTarget(Actor *aC) +{ + if (!aC) return NULL; + Targets *tgts = new Targets(); + tgts->AddTarget(aC, 0, 0); + return tgts; +} + +/*Actor *FindActorNearby(const char *name, Map *except, int ga_flags) +{ + Game *game = core->GetGame(); + size_t mc = game->GetLoadedMapCount(); + while(mc--) { + Map *map = game->GetMap(mc); + if (map==except) continue; + Actor * aC = map->GetActor(name, ga_flags); + if (aC) { + return aC; + } + } + return NULL; +}*/ + +/* do IDS filtering: [PC], [ENEMY], etc */ +inline static bool DoObjectIDSCheck(Object *oC, Actor *ac, bool *filtered) { + for (int j = 0; j < ObjectIDSCount; j++) { + if (!oC->objectFields[j]) { + continue; + } + *filtered = true; + IDSFunction func = idtargets[j]; + if (!func) { + printMessage("GameScript"," ", YELLOW); + printf("Unimplemented IDS targeting opcode: %d\n", j); + continue; + } + if (!func( ac, oC->objectFields[j] ) ) { + return false; + } + } + return true; +} + +/* do object filtering: Myself, LastAttackerOf(Player1), etc */ +inline static Targets *DoObjectFiltering(Scriptable *Sender, Targets *tgts, Object *oC, int ga_flags) { + for (int i = 0; i < MaxObjectNesting; i++) { + int filterid = oC->objectFilters[i]; + if (!filterid) break; + + ObjectFunction func = objects[filterid]; + if (!func) { + printMessage("GameScript"," ", YELLOW); + printf("Unknown object filter: %d %s\n", filterid, objectsTable->GetValue(filterid)); + continue; + } + + tgts = func(Sender, tgts, ga_flags); + if (!tgts->Count()) { + delete tgts; + return NULL; + } + } + return tgts; +} + +static EffectRef fx_protection_creature_ref = { "Protection:Creature", NULL, -1 }; + +inline static bool DoObjectChecks(Map *map, Scriptable *Sender, Actor *target, int &dist, bool ignoreinvis=false) +{ + dist = SquaredMapDistance(Sender, target); + + // TODO: what do we check for non-actors? + // non-actors have a visual range (15), we should do visual range and LOS + + if (Sender->Type == ST_ACTOR) { + Actor *source = (Actor *)Sender; + + // Detect() ignores invisibility completely + if (!ignoreinvis) { + // TODO: move this stuff into a shared function so it can be used elsewhere? + + // SEEINVISIBLE skips these checks :-) + if (source->Modified[IE_SEEINVISIBLE] == 0) { + ieDword state = target->Modified[IE_STATE_ID]; + // check for invisibility + if ((state & STATE_INVISIBLE) != 0) return false; + // check for improved invisibility? probably not + //if ((state & STATE_INVIS2) != 0) return false; + } + + // maybe this should be setting an invis flag? + // TODO: should SEEINVISIBLE ignore this? Detect()? + if (target->Modified[IE_AVATARREMOVAL]) return false; + } + + // visual range check + int visualrange = source->Modified[IE_VISUALRANGE]; + if (dist > visualrange*visualrange) return false; + + // LOS check + if (!map->IsVisible(Sender->Pos, target->Pos)) return false; + + // protection against creature + if (target->fxqueue.HasEffect(fx_protection_creature_ref)) { + // TODO: de-hardcode these (may not all be correct anyway) + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 2, source->Modified[IE_EA])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 3, source->Modified[IE_GENERAL])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 4, source->Modified[IE_RACE])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 5, source->Modified[IE_CLASS])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 6, source->Modified[IE_SPECIFIC])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 7, source->Modified[IE_SEX])) return false; + if (target->fxqueue.HasEffectWithParamPair(fx_protection_creature_ref, 8, source->Modified[IE_ALIGNMENT])) return false; + } + } + return true; +} + +/* returns actors that match the [x.y.z] expression */ +static Targets* EvaluateObject(Map *map, Scriptable* Sender, Object* oC, int ga_flags) +{ + // if you ActionOverride a global actor, they might not have a map :( + // TODO: don't allow this to happen? + if (!map) { + return NULL; + } + + if (oC->objectName[0]) { + //We want the object by its name... (doors/triggers don't play here!) + Actor* aC = map->GetActor( oC->objectName, ga_flags ); + + /*if (!aC && (ga_flags&GA_GLOBAL) ) { + aC = FindActorNearby(oC->objectName, map, ga_flags ); + }*/ + + //return here because object name/IDS targeting are mutually exclusive + return ReturnActorAsTarget(aC); + } + + if (oC->objectFields[0]==-1) { + // this is an internal hack, allowing us to pass actor ids around as objects + Actor* aC = map->GetActorByGlobalID( (ieDword) oC->objectFields[1] ); + /* TODO: this hack will throw away an invalid target */ + /* Consider putting this in GetActorByGlobalID */ + if (aC && !aC->ValidTarget(ga_flags)) { + aC = NULL; + } + return ReturnActorAsTarget(aC); + } + + Targets *tgts = NULL; + + //we need to get a subset of actors from the large array + //if this gets slow, we will need some index tables + int i = map->GetActorCount(true); + while (i--) { + Actor *ac = map->GetActor(i, true); + if (!ac) continue; // is this check really needed? + // don't return Sender in IDS targeting! + if (ac == Sender) continue; + bool filtered = false; + if (DoObjectIDSCheck(oC, ac, &filtered)) { + if (!filtered) { + // if no filters were applied.. + assert(!tgts); + return NULL; + } + int dist; + if (DoObjectChecks(map, Sender, ac, dist, (ga_flags & GA_DETECT) != 0)) { + if (!tgts) tgts = new Targets(); + tgts->AddTarget((Scriptable *) ac, dist, ga_flags); + } + } + } + + return tgts; +} + +Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags) +{ + if (!oC) { + return NULL; + } + Targets* tgts = EvaluateObject(map, Sender, oC, ga_flags); + //if we couldn't find an endpoint by name or object qualifiers + //it is not an Actor, but could still be a Door or Container (scriptable) + if (!tgts && oC->objectName[0]) { + return NULL; + } + //now lets do the object filter stuff, we create Targets because + //it is possible to start from blank sheets using endpoint filters + //like (Myself, Protagonist etc) + if (!tgts) { + tgts = new Targets(); + } + tgts = DoObjectFiltering(Sender, tgts, oC, ga_flags); + return tgts; +} + +/*Targets *GetAllObjectsNearby(Scriptable* Sender, Object* oC, int ga_flags) +{ + Game *game = core->GetGame(); + size_t mc = game->GetLoadedMapCount(); + while(mc--) { + Map *map = game->GetMap(mc); + if (map==Sender->GetCurrentArea()) continue; + Targets *tgts = GetAllObjects(map, Sender, oC, ga_flags); + if (tgts) { + return tgts; + } + } + return NULL; +}*/ + +Targets *GetAllActors(Scriptable *Sender, int ga_flags) +{ + Map *map = Sender->GetCurrentArea(); + + int i = map->GetActorCount(true); + Targets *tgts = new Targets(); + while (i--) { + Actor *ac = map->GetActor(i,true); + int dist = Distance(Sender->Pos, ac->Pos); + tgts->AddTarget((Scriptable *) ac, dist, ga_flags); + } + return tgts; +} + +/* get a non-actor object from a map, by name */ +Scriptable *GetActorObject(TileMap *TMap, const char *name) +{ + Scriptable * aC = TMap->GetDoor( name ); + if (aC) { + return aC; + } + + //containers should have a precedence over infopoints because otherwise + //AR1512 sanity test quest would fail + //If this order couldn't be maintained, then 'Contains' should have a + //unique call to get containers only + + //No... it was not an door... maybe a Container? + aC = TMap->GetContainer( name ); + if (aC) { + return aC; + } + + //No... it was not a container ... maybe an InfoPoint? + aC = TMap->GetInfoPoint( name ); + if (aC) { + return aC; + } + return aC; +} + +// blocking actions need to store some kinds of objects between ticks +Scriptable* GetStoredActorFromObject(Scriptable* Sender, Object* oC, int ga_flags) +{ + Scriptable *tar = NULL; + // retrieve an existing target if it still exists and is valid + if (Sender->CurrentActionTarget) { + tar = core->GetGame()->GetActorByGlobalID(Sender->CurrentActionTarget); + if (tar) { + // always an actor, check if it satisfies flags + if (((Actor *)tar)->ValidTarget(ga_flags)) { + return tar; + } + } + return NULL; // target invalid/gone + } + tar = GetActorFromObject(Sender, oC, ga_flags); + // maybe store the target if it's an actor.. + if (tar && tar->Type == ST_ACTOR) { + // .. but we only want objects created via objectFilters + if (oC->objectFilters[0]) { + Sender->CurrentActionTarget = tar->GetGlobalID(); + } + } + return tar; +} + +Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags) +{ + Scriptable *aC = NULL; + + if (!oC) { + return NULL; + } + Game *game = core->GetGame(); + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, ga_flags); + if (tgts) { + //now this could return other than actor objects + aC = tgts->GetTarget(0,-1); + delete tgts; + if (aC || oC->objectFields[0]!=-1) { + return aC; + } + + //global actors are always found by object ID! + return game->GetGlobalActorByGlobalID(oC->objectFields[1]); + } + + if (oC->objectName[0]) { + // if you ActionOverride a global actor, they might not have a map :( + // TODO: don't allow this to happen? + if (Sender->GetCurrentArea()) { + aC = GetActorObject(Sender->GetCurrentArea()->GetTileMap(), oC->objectName ); + if (aC) { + return aC; + } + } + + //global actors are always found by scripting name! + aC = game->FindPC(oC->objectName); + if (aC) { + return aC; + } + aC = game->FindNPC(oC->objectName); + if (aC) { + return aC; + } + } + return NULL; +} + +bool MatchActor(Scriptable *Sender, ieDword actorID, Object* oC) +{ + if (!Sender) { + return false; + } + Actor *ac = Sender->GetCurrentArea()->GetActorByGlobalID(actorID); + if (!ac) { + return false; + } + + // [0]/[ANYONE] can match all actors + if (!oC) { + return true; + } + + bool filtered = false; + + // name matching + if (oC->objectName[0]) { + if (strnicmp(ac->GetScriptName(), oC->objectName, 32) != 0) { + return false; + } + filtered = true; + } + + // IDS targeting + // (if we already matched by name, we don't do this) + // TODO: check distance? area? visibility? + if (!filtered && !DoObjectIDSCheck(oC, ac, &filtered)) return false; + + // globalID hack should never get here + assert(oC->objectFilters[0] != -1); + + // object filters + if (oC->objectFilters[0]) { + // object filters insist on having a stupid targets list, + // so we waste a lot of time here + Targets *tgts = new Targets(); + int ga_flags = 0; // TODO: correct? + + // handle already-filtered vs not-yet-filtered cases + // e.g. LastTalkedToBy(Myself) vs LastTalkedToBy + if (filtered) tgts->AddTarget(ac, 0, ga_flags); + + tgts = DoObjectFiltering(Sender, tgts, oC, ga_flags); + if (!tgts) return false; + + // and sometimes object filters are lazy and not only don't filter + // what we give them, they clear it and return a list :( + // so we have to search the whole list.. + bool ret = false; + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + if (actor->GetGlobalID() == actorID) { + ret = true; + break; + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + delete tgts; + if (!ret) return false; + } + return true; +} + +int GetObjectCount(Scriptable* Sender, Object* oC) +{ + if (!oC) { + return 0; + } + // EvaluateObject will return [PC] + // GetAllObjects will also return Myself (evaluates object filters) + // i believe we need the latter here + Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0); + int count = tgts->Count(); + delete tgts; + return count; +} + +//TODO: +//check numcreaturesatmylevel(myself, 1) +//when the actor is alone +//it should (obviously) return true if the trigger +//evaluates object filters +//also check numcreaturesgtmylevel(myself,0) with +//actor having at high level +int GetObjectLevelCount(Scriptable* Sender, Object* oC) +{ + if (!oC) { + return 0; + } + // EvaluateObject will return [PC] + // GetAllObjects will also return Myself (evaluates object filters) + // i believe we need the latter here + Targets* tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, oC, 0); + int count = 0; + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + count += ((Actor *) tt->actor)->GetXPLevel(true); + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return count; +} + +Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags) +{ + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTarget); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *XthNearestDoor(Targets *parameters, unsigned int count) +{ + //get the origin + Scriptable *origin = parameters->GetTarget(0, -1); + parameters->Clear(); + if (!origin) { + return parameters; + } + //get the doors based on it + Map *map = origin->GetCurrentArea(); + unsigned int i =(unsigned int) map->TMap->GetDoorCount(); + if (count>i) { + return parameters; + } + while (i--) { + Door *door = map->TMap->GetDoor(i); + unsigned int dist = Distance(origin->Pos, door->Pos); + parameters->AddTarget(door, dist, 0); + } + + //now get the xth door + origin = parameters->GetTarget(count, ST_DOOR); + parameters->Clear(); + if (!origin) { + return parameters; + } + parameters->AddTarget(origin, 0, 0); + return parameters; +} + +Targets *XthNearestOf(Targets *parameters, int count, int ga_flags) +{ + Scriptable *origin; + + if (count<0) { + const targettype *t = parameters->GetLastTarget(ST_ACTOR); + origin = t->actor; + } else { + origin = parameters->GetTarget(count, ST_ACTOR); + } + parameters->Clear(); + if (!origin) { + return parameters; + } + parameters->AddTarget(origin, 0, ga_flags); + return parameters; +} + +//mygroup means the same specifics as origin +Targets *XthNearestMyGroupOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the specifics of origin + ieDword type = actor->GetStat(IE_SPECIFIC); //my group + + while ( t ) { + if (t->actor->Type!=ST_ACTOR) { + t=parameters->RemoveTargetAt(m); + continue; + } + Actor *actor = (Actor *) (t->actor); + if (actor->GetStat(IE_SPECIFIC) != type) { + t=parameters->RemoveTargetAt(m); + continue; + } + t = parameters->GetNextTarget(m, ST_ACTOR); + } + return XthNearestOf(parameters,count, ga_flags); +} + +Targets *ClosestEnemySummoned(Scriptable *origin, Targets *parameters, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the allegiance of the origin + int type = GetGroup(actor); + + if (type==2) { + parameters->Clear(); + return parameters; + } + + actor = NULL; + while ( t ) { + Actor *tmp = (Actor *) (t->actor); + if (tmp->GetStat(IE_SEX) != SEX_SUMMON) { + continue; + } + if (type) { //origin is PC + if (tmp->GetStat(IE_EA) <= EA_GOODCUTOFF) { + continue; + } + } else { + if (tmp->GetStat(IE_EA) >= EA_EVILCUTOFF) { + continue; + } + } + actor = tmp; + t = parameters->GetNextTarget(m, ST_ACTOR); + } + parameters->Clear(); + parameters->AddTarget(actor, 0, ga_flags); + return parameters; +} + +Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags) +{ + if (origin->Type != ST_ACTOR) { + parameters->Clear(); + return parameters; + } + + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Actor *actor = (Actor *) origin; + //determining the allegiance of the origin + int type = GetGroup(actor); + + if (type==2) { + parameters->Clear(); + return parameters; + } + + while ( t ) { + if (t->actor->Type!=ST_ACTOR) { + t=parameters->RemoveTargetAt(m); + continue; + } + Actor *actor = (Actor *) (t->actor); + // IDS targeting already did object checks (unless we need to override Detect?) + if (type) { //origin is PC + if (actor->GetStat(IE_EA) <= EA_GOODCUTOFF) { + t=parameters->RemoveTargetAt(m); + continue; + } + } else { + if (actor->GetStat(IE_EA) >= EA_EVILCUTOFF) { + t=parameters->RemoveTargetAt(m); + continue; + } + } + t = parameters->GetNextTarget(m, ST_ACTOR); + } + return XthNearestOf(parameters,count, ga_flags); +} + +Targets *XthNearestEnemyOf(Targets *parameters, int count, int ga_flags) +{ + Actor *origin = (Actor *) parameters->GetTarget(0, ST_ACTOR); + parameters->Clear(); + if (!origin) { + return parameters; + } + //determining the allegiance of the origin + int type = GetGroup(origin); + + if (type==2) { + return parameters; + } + Map *map = origin->GetCurrentArea(); + int i = map->GetActorCount(true); + Actor *ac; + while (i--) { + ac=map->GetActor(i,true); + int distance; + //int distance = Distance(ac, origin); + // TODO: if it turns out you need to check Sender here, beware you take the right distance! + // (n the original games, this is only used for NearestEnemyOf(Player1) in obsgolem.bcs) + if (!DoObjectChecks(map, origin, ac, distance)) continue; + if (type) { //origin is PC + if (ac->GetStat(IE_EA) >= EA_EVILCUTOFF) { + parameters->AddTarget(ac, distance, ga_flags); + } + } + else { + if (ac->GetStat(IE_EA) <= EA_GOODCUTOFF) { + parameters->AddTarget(ac, distance, ga_flags); + } + } + } + return XthNearestOf(parameters,count, ga_flags); +} + diff --git a/project/jni/application/gemrb/src/core/GameScript/Matching.h b/project/jni/application/gemrb/src/core/GameScript/Matching.h new file mode 100644 index 000000000..6a6562191 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Matching.h @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 MATCHING_H +#define MATCHING_H + +#include "GameScript/GameScript.h" + +#include "exports.h" + +Targets* GetAllObjects(Map *map, Scriptable* Sender, Object* oC, int ga_flags); +Targets* GetAllActors(Scriptable* Sender, int ga_flags); +Scriptable* GetActorFromObject(Scriptable* Sender, Object* oC, int ga_flags = 0); +Scriptable* GetStoredActorFromObject(Scriptable* Sender, Object* oC, int ga_flags = 0); +Scriptable *GetActorObject(TileMap *TMap, const char *name); + +Targets *GetMyTarget(Scriptable *Sender, Actor *actor, Targets *parameters, int ga_flags); +Targets *XthNearestOf(Targets *parameters, int count, int ga_flags); +Targets *XthNearestDoor(Targets *parameters, unsigned int count); +Targets *XthNearestEnemyOf(Targets *parameters, int count, int ga_flags); +Targets *ClosestEnemySummoned(Scriptable *origin, Targets *parameters, int ga_flags); +Targets *XthNearestEnemyOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags); +Targets *XthNearestMyGroupOfType(Scriptable *origin, Targets *parameters, unsigned int count, int ga_flags); + +/* returns true if actor matches the object specs. */ +bool MatchActor(Scriptable *Sender, ieDword ID, Object* oC); +/* returns the number of actors matching the IDS targeting */ +int GetObjectCount(Scriptable* Sender, Object* oC); +int GetObjectLevelCount(Scriptable* Sender, Object* oC); + +#endif diff --git a/project/jni/application/gemrb/src/core/GameScript/Objects.cpp b/project/jni/application/gemrb/src/core/GameScript/Objects.cpp new file mode 100644 index 000000000..2cc9b3b31 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Objects.cpp @@ -0,0 +1,1158 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "DialogHandler.h" +#include "Game.h" +#include "GUI/GameControl.h" + +//------------------------------------------------------------- +// Object Functions +//------------------------------------------------------------- + +//in this implementation, Myself will drop the parameter array +//i think all object filters could be expected to do so +//they should remove unnecessary elements from the parameters +Targets *GameScript::Myself(Scriptable* Sender, Targets* parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(Sender, 0, ga_flags); + return parameters; +} + +Targets *GameScript::NearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 0); +} + +Targets *GameScript::SecondNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 1); +} + +Targets *GameScript::ThirdNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 2); +} + +Targets *GameScript::FourthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 3); +} + +Targets *GameScript::FifthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 4); +} + +Targets *GameScript::SixthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 5); +} + +Targets *GameScript::SeventhNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 6); +} + +Targets *GameScript::EighthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 7); +} + +Targets *GameScript::NinthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 8); +} + +Targets *GameScript::TenthNearestDoor(Scriptable* /*Sender*/, Targets *parameters, int /*ga_flags*/) +{ + return XthNearestDoor(parameters, 9); +} + +//in bg2 it is same as player1 so far +//in iwd2 this is the Gabber!!! +//but also, if there is no gabber, it is the first PC +//probably it is simply the nearest exportable character... +Targets *GameScript::Protagonist(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + //this sucks but IWD2 is like that... + static bool charnameisgabber = core->HasFeature(GF_CHARNAMEISGABBER); + if (charnameisgabber) { + GameControl* gc = core->GetGameControl(); + if (gc) { + parameters->AddTarget(gc->dialoghandler->GetSpeaker(), 0, ga_flags); + } + if (parameters->Count()) { + return parameters; + } + //ok, this will return the nearest PC in the first slot + Game *game = core->GetGame(); + int i = game->GetPartySize(false); + while(i--) { + Actor *target = game->GetPC(i,false); + parameters->AddTarget(target, Distance(Sender, target), ga_flags); + } + return parameters; + } + parameters->AddTarget(core->GetGame()->GetPC(0, false), 0, ga_flags); + return parameters; +} + +//last talker +Targets *GameScript::Gabber(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + GameControl* gc = core->GetGameControl(); + if (gc) { + parameters->AddTarget(gc->dialoghandler->GetSpeaker(), 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::LastTrigger(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + if (Sender->LastTriggerObject) { + Actor *target = Sender->GetCurrentArea()->GetActorByGlobalID(Sender->LastTriggerObject); + parameters->AddTarget(target, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::LastMarkedObject(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastMarked); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +//actions should always use LastMarkedObject, because LastSeen could be deleted +Targets *GameScript::LastSeenBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastSeen); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHelp(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHelp); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHeardBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHeard); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +//i was told that Group means the same specifics, so this is just an +//object selector for everyone with the same specifics as the current object +Targets *GameScript::GroupOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + ieDword tmp = actor->GetStat(IE_SPECIFIC); + Map *cm = Sender->GetCurrentArea(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *target=cm->GetActor(i,true); + if (target && (target->GetStat(IE_SPECIFIC)==tmp) ) { + parameters->AddTarget(target, 0, ga_flags); + } + } + } + return parameters; +} + +/*this one is tough, but done */ +Targets *GameScript::ProtectorOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + ieWord tmp = actor->LastProtected; + Map *cm = Sender->GetCurrentArea(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *target=cm->GetActor(i,true); + if (target && (target->LastProtected ==tmp) ) { + parameters->AddTarget(target, 0, ga_flags); + } + } + } + return parameters; +} + +Targets *GameScript::ProtectedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastProtected); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastCommandedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastCommander); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +// this is essentially a LastTargetedBy(0) - or MySelf +// but IWD2 defines it +Targets *GameScript::MyTarget(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + return GetMyTarget(Sender, NULL, parameters, ga_flags); +} + +Targets *GameScript::LastTargetedBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + return GetMyTarget(Sender, actor, parameters, ga_flags); +} + +Targets *GameScript::LastAttackerOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHitter); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastHitter(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastHitter); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LeaderOf(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastFollowed); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastTalkedToBy(Scriptable *Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastTalkedTo); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::LastSummonerOf(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + Actor *actor = (Actor *) parameters->GetTarget(0, ST_ACTOR); + if (!actor) { + if (Sender->Type==ST_ACTOR) { + actor = (Actor *) Sender; + } + } + parameters->Clear(); + if (actor) { + Actor *target = actor->GetCurrentArea()->GetActorByGlobalID(actor->LastSummoner); + if (target) { + parameters->AddTarget(target, 0, ga_flags); + } + } + return parameters; +} + +Targets *GameScript::Player1(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(0,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player1Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(1), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player2(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(1,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player2Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(2), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player3(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(2,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player3Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(3), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player4(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(3,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player4Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(4), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player5(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(4,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player5Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(5), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player6(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(5,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player6Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(6), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player7(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(6,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player7Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(7), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player8(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->GetPC(7,false), 0, ga_flags); + return parameters; +} + +Targets *GameScript::Player8Fill(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + parameters->AddTarget(core->GetGame()->FindPC(8), 0, ga_flags); + return parameters; +} + +Targets *GameScript::BestAC(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int bestac=actor->GetStat(IE_ARMORCLASS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int ac=actor->GetStat(IE_ARMORCLASS); + if (bestacactor; + } + } + + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +/*no idea why this object exists since the gender could be filtered easier*/ +Targets *GameScript::StrongestOfMale(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + int pos=-1; + int worsthp=-1; + Scriptable *scr = NULL; + //assignment intentional + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + Actor *actor = (Actor *) t->actor; + if (actor->GetStat(IE_SEX)!=SEX_MALE) continue; + int hp=actor->GetStat(IE_HITPOINTS); + if ((pos==-1) || (worsthpactor; + } + } + parameters->Clear(); + if (scr) { + parameters->AddTarget(scr, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::StrongestOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int besthp=actor->GetStat(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_HITPOINTS); + if (besthpactor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::WeakestOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int worsthp=actor->GetStat(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::WorstAC(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr=t->actor; + Actor *actor=(Actor *) scr; + int worstac=actor->GetStat(IE_ARMORCLASS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int ac=actor->GetStat(IE_ARMORCLASS); + if (worstac>ac) { + worstac=ac; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::MostDamagedOf(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + //Original engines restrict this to the PCs... + /*targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr = t->actor; + Actor *actor=(Actor *) scr; + int worsthp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=t->actor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters;*/ + Map* area = Sender->GetCurrentArea() ; + Game *game = core->GetGame(); + Scriptable* scr = NULL ; + int worsthp = 0xffff ; + int i = game->GetPartySize(false); + while (i--) { + Actor *actor = game->GetPC(i, false); + if(actor->GetCurrentArea() == area) { + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (worsthp>hp) { + worsthp=hp; + scr=actor; + } + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} +Targets *GameScript::LeastDamagedOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + targetlist::iterator m; + const targettype *t = parameters->GetFirstTarget(m, ST_ACTOR); + if (!t) { + return parameters; + } + Scriptable *scr = t->actor; + Actor *actor = (Actor *) scr; + int besthp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + // assignment in while + while ( (t = parameters->GetNextTarget(m, ST_ACTOR) ) ) { + actor = (Actor *) t->actor; + int hp=actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (besthpactor; + } + } + parameters->Clear(); + parameters->AddTarget(scr, 0, ga_flags); + return parameters; +} + +Targets *GameScript::Farthest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + const targettype *t = parameters->GetLastTarget(ST_ACTOR); + parameters->Clear(); + if (t) { + parameters->AddTarget(t->actor, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::FarthestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, -1, ga_flags); +} + +Targets *GameScript::NearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestEnemyOf(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOf(parameters, 9, ga_flags); +} + +Targets *GameScript::NearestEnemySummoned(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return ClosestEnemySummoned(Sender, parameters, ga_flags); +} + +Targets *GameScript::NearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestEnemyOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestEnemyOfType(Sender, parameters, 9, ga_flags); +} + +Targets *GameScript::NearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearestMyGroupOfType(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + return XthNearestMyGroupOfType(Sender, parameters, 9, ga_flags); +} + +/* returns only living PC's? if not, alter getpartysize/getpc flag*/ +Targets *GameScript::NearestPC(Scriptable* Sender, Targets *parameters, int ga_flags) +{ + parameters->Clear(); + Map *map = Sender->GetCurrentArea(); + Game *game = core->GetGame(); + int i = game->GetPartySize(true); + int mindist = -1; + Actor *ac = NULL; + while (i--) { + Actor *newactor=game->GetPC(i,true); + //NearestPC for PC's will not give themselves as a result + //this might be different from the original engine + if ((Sender->Type==ST_ACTOR) && (newactor == (Actor *) Sender)) { + continue; + } + if (newactor->GetCurrentArea()!=map) { + continue; + } + int dist = Distance(Sender, newactor); + if ( (mindist == -1) || (distAddTarget(ac, 0, ga_flags); + } + return parameters; +} + +Targets *GameScript::Nearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 0, ga_flags); +} + +Targets *GameScript::SecondNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 1, ga_flags); +} + +Targets *GameScript::ThirdNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 2, ga_flags); +} + +Targets *GameScript::FourthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 3, ga_flags); +} + +Targets *GameScript::FifthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 4, ga_flags); +} + +Targets *GameScript::SixthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 5, ga_flags); +} + +Targets *GameScript::SeventhNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 6, ga_flags); +} + +Targets *GameScript::EighthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 7, ga_flags); +} + +Targets *GameScript::NinthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 8, ga_flags); +} + +Targets *GameScript::TenthNearest(Scriptable* /*Sender*/, Targets *parameters, int ga_flags) +{ + return XthNearestOf(parameters, 9, ga_flags); +} + +Targets *GameScript::SelectedCharacter(Scriptable* Sender, Targets* parameters, int ga_flags) +{ + Map *cm = Sender->GetCurrentArea(); + parameters->Clear(); + int i = cm->GetActorCount(true); + while (i--) { + Actor *ac=cm->GetActor(i,true); + if (ac->GetCurrentArea()!=cm) { + continue; + } + if (ac->IsSelected()) { + parameters->AddTarget(ac, Distance(Sender, ac), ga_flags ); + } + } + return parameters; +} + +Targets *GameScript::Nothing(Scriptable* /*Sender*/, Targets* parameters, int /*ga_flags*/) +{ + parameters->Clear(); + return parameters; +} + +//------------------------------------------------------------- +// IDS Functions +//------------------------------------------------------------- + +int GameScript::ID_Alignment(Actor *actor, int parameter) +{ + int value = actor->GetStat( IE_ALIGNMENT ); + int a = parameter&15; + if (a) { + if (a != ( value & 15 )) { + return 0; + } + } + a = parameter & 240; + if (a) { + if (a != ( value & 240 )) { + return 0; + } + } + return 1; +} + +int GameScript::ID_Allegiance(Actor *actor, int parameter) +{ + int value = actor->GetStat( IE_EA ); + switch (parameter) { + case EA_GOODCUTOFF: + return value <= EA_GOODCUTOFF; + + case EA_NOTGOOD: + return value >= EA_NOTGOOD; + + case EA_NOTNEUTRAL: + return value >=EA_EVILCUTOFF || value <= EA_GOODCUTOFF; + + case EA_NOTEVIL: + return value <= EA_NOTEVIL; + + case EA_EVILCUTOFF: + return value >= EA_EVILCUTOFF; + + case 0: + case EA_ANYTHING: + return true; + + } + //default + return parameter == value; +} + +// *_ALL constants are different in iwd2 due to different classes (see note below) +// bard, cleric, druid, fighter, mage, paladin, ranger, thief +static const int all_bg_classes[] = { 206, 204, 208, 203, 202, 207, 209, 205 }; +static const int all_iwd2_classes[] = { 202, 203, 204, 205, 209, 206, 207, 208 }; + +// Dual-classed characters will detect only as their new class until their +// original class is re-activated, when they will detect as a multi-class +// GetClassLevel takes care of this automatically! +inline bool idclass(Actor *actor, int parameter, bool iwd2) { + int value = 0; + if (parameter < 202 || parameter > 209) { + value = actor->GetStat(IE_CLASS); + return parameter==value; + } + + const int *classes; + if (iwd2) { + classes = all_iwd2_classes; + } else { + classes = all_bg_classes; + } + + // we got one of the *_ALL values + if (parameter == classes[4]) { + // MAGE_ALL (also sorcerers) + value = actor->GetMageLevel() + actor->GetSorcererLevel(); + } else if (parameter == classes[3]) { + // FIGHTER_ALL (also monks) + value = actor->GetFighterLevel() + actor->GetMonkLevel(); + } else if (parameter == classes[1]) { + // CLERIC_ALL + value = actor->GetClericLevel(); + } else if (parameter == classes[7]) { + // THIEF_ALL + value = actor->GetThiefLevel(); + } else if (parameter == classes[0]) { + // BARD_ALL + value = actor->GetBardLevel(); + } else if (parameter == classes[5]) { + // PALADIN_ALL + value = actor->GetPaladinLevel(); + } else if (parameter == classes[2]) { + // DRUID_ALL + value = actor->GetDruidLevel(); + } else if (parameter == classes[6]) { + // RANGER_ALL + value = actor->GetRangerLevel(); + } + return value > 0; +} + +int GameScript::ID_Class(Actor *actor, int parameter) +{ + if (core->HasFeature(GF_3ED_RULES)) { + //iwd2 has different values, see also the note for AVClass + return idclass(actor, parameter, 1); + } + return idclass(actor, parameter, 0); +} + +// IE_CLASS holds only one class, not a bitmask like with iwd2 kits. The ids values +// are friendly to binary comparison, so we just need to build such a class value +int GameScript::ID_ClassMask(Actor *actor, int parameter) +{ + // maybe we're lucky... + int value = actor->GetStat(IE_CLASS); + if (parameter&(1<<(value-1))) return 1; + + // otherwise iterate over all the classes + value = actor->GetClassMask(); + + if (parameter&value) return 1; + return 0; +} + +// this is only present in iwd2 +// the function is identical to ID_Class, but uses the class20 IDS, +// iwd2's class.ids is different than the rest, while class20 is identical (remnant) +int GameScript::ID_AVClass(Actor *actor, int parameter) +{ + return idclass(actor, parameter, 0); +} + +int GameScript::ID_Race(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_RACE); + return parameter==value; +} + +int GameScript::ID_Subrace(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SUBRACE); + return parameter==value; +} + +int GameScript::ID_Faction(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_FACTION); + return parameter==value; +} + +int GameScript::ID_Team(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_TEAM); + return parameter==value; +} + +int GameScript::ID_Gender(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SEX); + return parameter==value; +} + +int GameScript::ID_General(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_GENERAL); + return parameter==value; +} + +int GameScript::ID_Specific(Actor *actor, int parameter) +{ + int value = actor->GetStat(IE_SPECIFIC); + return parameter==value; +} diff --git a/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp b/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp new file mode 100644 index 000000000..a780d818f --- /dev/null +++ b/project/jni/application/gemrb/src/core/GameScript/Triggers.cpp @@ -0,0 +1,4443 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * 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 "GameScript/GameScript.h" + +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" + +#include "win32def.h" + +#include "Calendar.h" +#include "DialogHandler.h" +#include "Game.h" +#include "GameData.h" +#include "Video.h" +#include "GUI/GameControl.h" +#include "math.h" //needs for acos + +//------------------------------------------------------------- +// Trigger Functions +//------------------------------------------------------------- +int GameScript::BreakingPoint(Scriptable* Sender, Trigger* /*parameters*/) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value < -300; +} + +int GameScript::Reaction(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value == parameters->int0Parameter; +} + +int GameScript::ReactionGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value > parameters->int0Parameter; +} + +int GameScript::ReactionLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + parameters->Dump(); + return 0; + } + int value = GetReaction(((Actor*) scr), Sender); + return value < parameters->int0Parameter; +} + +int GameScript::Happiness(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value == parameters->int0Parameter; +} + +int GameScript::HappinessGT(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value > parameters->int0Parameter; +} + +int GameScript::HappinessLT(Scriptable* Sender, Trigger* parameters) +{ + int value=GetHappiness(Sender, core->GetGame()->Reputation ); + return value < parameters->int0Parameter; +} + +int GameScript::Reputation(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 == (ieDword) parameters->int0Parameter; +} + +int GameScript::ReputationGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 > (ieDword) parameters->int0Parameter; +} + +int GameScript::ReputationLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->Reputation/10 < (ieDword) parameters->int0Parameter; +} + +int GameScript::Alignment(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Alignment( actor, parameters->int0Parameter); +} + +int GameScript::Allegiance(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Allegiance( actor, parameters->int0Parameter); +} + +//should return *_ALL stuff +int GameScript::Class(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Class( actor, parameters->int0Parameter); +} + +int GameScript::ClassEx(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_AVClass( actor, parameters->int0Parameter); +} + +int GameScript::Faction(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Faction( actor, parameters->int0Parameter); +} + +int GameScript::Team(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + return ID_Team( actor, parameters->int0Parameter); +} + +int GameScript::SubRace(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + //subrace trigger uses a weird system, cannot use ID_* + //return ID_Subrace( actor, parameters->int0Parameter); + int value = actor->GetStat(IE_SUBRACE); + if (value) { + value |= actor->GetStat(IE_RACE)<<16; + } + if (value == parameters->int0Parameter) { + return 1; + } + return 0; +} + +//if object parameter is given (gemrb) it is used +//otherwise it works on the current object (iwd2) +int GameScript::IsTeamBitOn(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = Sender; + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = (Actor*)scr; + if (actor->GetStat(IE_TEAM) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::NearbyDialog(Scriptable* Sender, Trigger* parameters) +{ + Actor *target = Sender->GetCurrentArea()->GetActorByDialog(parameters->string0Parameter); + if ( !target ) { + return 0; + } + return CanSee( Sender, target, true, GA_NO_DEAD | GA_NO_HIDDEN ); +} + +//atm this checks for InParty and See, it is unsure what is required +int GameScript::IsValidForPartyDialog(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor *target = (Actor *) scr; + //inparty returns -1 if not in party + if (core->GetGame()->InParty( target )<0) { + return 0; + } + //don't accept parties currently in dialog! + //this might disturb some modders, but this is the correct behaviour + //for example the aaquatah dialog in irenicus dungeon depends on it + GameControl *gc = core->GetGameControl(); + Actor *pc = (Actor *) scr; + if (pc->GetGlobalID() == gc->dialoghandler->targetID || pc->GetGlobalID()==gc->dialoghandler->speakerID) { + return 0; + } + + //don't accept parties with the no interrupt flag + //this fixes bug #2573808 on gamescript level + //(still someone has to turn the no interrupt flag off) + if(!pc->GetDialog(GD_CHECK)) { + return 0; + } + return CanSee( Sender, target, false, GA_NO_DEAD ); +} + +int GameScript::InParty(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + Actor *tar = (Actor *) scr; + if (core->GetGame()->InParty( tar ) <0) { + return 0; + } + //don't allow dead, don't allow maze and similar effects + if (tar->ValidTarget(GA_NO_DEAD|GA_NO_HIDDEN)) { + return 1; + } + return 0; +} + +int GameScript::InPartyAllowDead(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (!scr || scr->Type != ST_ACTOR) { + return 0; + } + return core->GetGame()->InParty( ( Actor * ) scr ) >= 0 ? 1 : 0; +} + +int GameScript::InPartySlot(Scriptable* Sender, Trigger* parameters) +{ + Actor *actor = core->GetGame()->GetPC(parameters->int0Parameter, false); + return MatchActor(Sender, actor->GetGlobalID(), parameters->objectParameter); +} + +int GameScript::Exists(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + return 1; +} + +int GameScript::IsAClown(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + return 1; +} + +int GameScript::IsGabber(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + if (scr->GetGlobalID() == core->GetGameControl()->dialoghandler->speakerID) + return 1; + return 0; +} + +int GameScript::IsActive(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->GetInternalFlag()&IF_ACTIVE) { + return 1; + } + return 0; +} + +int GameScript::InTrap(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->GetInternalFlag()&IF_INTRAP) { + return 1; + } + return 0; +} + +/* checks if targeted actor is in the specified region + GemRB allows different regions, referenced by int0Parameter + The polygons are stored in island.2da files */ +int GameScript::OnIsland(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + Gem_Polygon *p = GetPolygon2DA(parameters->int0Parameter); + if (!p) { + return 0; + } + return p->PointIn(scr->Pos); +} + +int GameScript::School(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + //only the low 2 bytes count + //the School values start from 1 to 9 and the first school value is 0x40 + //so this mild hack will do + if ( actor->GetStat(IE_KIT) == (ieDword) (0x20<int0Parameter)) { + return 1; + } + return 0; +} + +int GameScript::Kit(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + + ieDword kit = actor->GetStat(IE_KIT); + //TODO: fix baseclass / barbarian confusion + + //IWD2 style kit matching (also used for mage schools) + if (kit == (ieDword) (parameters->int0Parameter)) { + return 1; + } + //BG2 style kit matching (not needed anymore?), we do it on load + //kit = (kit>>16)|(kit<<16); + if ( kit == (ieDword) (parameters->int0Parameter)) { + return 1; + } + return 0; +} + +int GameScript::General(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor == NULL) { + return 0; + } + return ID_General(actor, parameters->int0Parameter); +} + +int GameScript::Specifics(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor == NULL) { + return 0; + } + return ID_Specific(actor, parameters->int0Parameter); +} + +int GameScript::BitCheck(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value & parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::BitCheckExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword tmp = (ieDword) parameters->int0Parameter ; + if ((value & tmp) == tmp) return 1; + } + return 0; +} + +//BM_OR would make sense only if this trigger changes the value of the variable +//should I do that??? +int GameScript::BitGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + HandleBitMod(value, parameters->int0Parameter, parameters->int1Parameter); + if (value!=0) return 1; + } + return 0; +} + +int GameScript::GlobalOrGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value1 ) return 1; + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable( Sender, parameters->string0Parameter, &valid ); + if (valid && value1) { + ieDword value2 = CheckVariable( Sender, parameters->string1Parameter, &valid ); + if (valid && value2) return 1; + } + return 0; +} + +int GameScript::GlobalBAndGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ((value1& value2 ) != 0) return 1; + } + } + return 0; +} + +int GameScript::GlobalBAndGlobalExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if (( value1& value2 ) == value2) return 1; + } + } + return 0; +} + +int GameScript::GlobalBitGlobal_Trigger(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + HandleBitMod( value1, value2, parameters->int1Parameter); + if (value1!=0) return 1; + } + } + return 0; +} + +//no what exactly this trigger would do, defined in iwd2, but never used +//i just assume it sets a global in the trigger block +int GameScript::TriggerSetGlobal(Scriptable* Sender, Trigger* parameters) +{ + SetVariable( Sender, parameters->string0Parameter, parameters->int0Parameter ); + return 1; +} + +//would this function also alter the variable? +int GameScript::Xor(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if (( value ^ parameters->int0Parameter ) != 0) return 1; + } + return 0; +} + +//TODO: +//no sprite is dead for iwd, they use KILL__CNT +int GameScript::NumDead(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value == (ieDword) parameters->int0Parameter ); +} + +int GameScript::NumDeadGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value > (ieDword) parameters->int0Parameter ); +} + +int GameScript::NumDeadLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + if (core->HasFeature(GF_HAS_KAPUTZ) ) { + value = CheckVariable(Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + ieVariable VariableName; + + snprintf(VariableName, 32, core->GetDeathVarFormat(), parameters->string0Parameter); + value = CheckVariable(Sender, VariableName, "GLOBAL" ); + } + return ( value < (ieDword) parameters->int0Parameter ); +} + +int GameScript::G_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + return ( value == parameters->int0Parameter ); +} + +int GameScript::Global(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value == parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GLT_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter,"GLOBAL" ); + return ( value < parameters->int0Parameter ); +} + +int GameScript::GlobalLT(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value < parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GGT_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + return ( value > parameters->int0Parameter ); +} + +int GameScript::GlobalGT(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + if ( value > parameters->int0Parameter ) return 1; + } + return 0; +} + +int GameScript::GlobalLTGlobal(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDwordSigned value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 < value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalGTGlobal(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDwordSigned value1 = CheckVariable(Sender, parameters->string0Parameter, &valid ); + if (valid) { + ieDwordSigned value2 = CheckVariable(Sender, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 > value2 ) return 1; + } + } + return 0; +} + +int GameScript::GlobalsEqual(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 == value2 ); +} + +int GameScript::GlobalsGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 > value2 ); +} + +int GameScript::GlobalsLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "GLOBAL" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "GLOBAL" ); + return ( value1 < value2 ); +} + +int GameScript::LocalsEqual(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 == value2 ); +} + +int GameScript::LocalsGT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 > value2 ); +} + +int GameScript::LocalsLT(Scriptable* Sender, Trigger* parameters) +{ + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, "LOCALS" ); + ieDword value2 = CheckVariable(Sender, parameters->string1Parameter, "LOCALS" ); + return ( value1 < value2 ); +} + +int GameScript::RealGlobalTimerExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + ieDword value2 = core->GetGame()->RealTime; + if ( value1 == value2 ) return 1; + } + return 0; +} + +int GameScript::RealGlobalTimerExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 < core->GetGame()->RealTime ) return 1; + } + return 0; +} + +int GameScript::RealGlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->RealTime ) return 1; + } + return 0; +} + +int GameScript::GlobalTimerExact(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid) { + if ( value1 == core->GetGame()->GameTime ) return 1; + } + return 0; +} + +int GameScript::GlobalTimerExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 < core->GetGame()->GameTime ) return 1; + } + return 0; +} + +//globaltimernotexpired returns false if the timer doesn't exist +int GameScript::GlobalTimerNotExpired(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->GameTime ) return 1; + } + return 0; +} + +//globaltimerstarted returns false if the timer doesn't exist +//is it the same as globaltimernotexpired? +int GameScript::GlobalTimerStarted(Scriptable* Sender, Trigger* parameters) +{ + bool valid=true; + + ieDword value1 = CheckVariable(Sender, parameters->string0Parameter, parameters->string1Parameter, &valid ); + if (valid && value1) { + if ( value1 > core->GetGame()->GameTime ) return 1; + } + return 0; +} + +int GameScript::WasInDialog(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_WASINDIALOG) { + Sender->SetBitTrigger(BT_WASINDIALOG); + return 1; + } + return 0; +} + +int GameScript::OnCreation(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_ONCREATION) { + Sender->SetBitTrigger(BT_ONCREATION); + return 1; + } + return 0; +} + +int GameScript::NumItemsParty(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cnt==parameters->int0Parameter; +} + +int GameScript::NumItemsPartyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cnt>parameters->int0Parameter; +} + +int GameScript::NumItemsPartyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int cnt = 0; + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + cnt+=actor->inventory.CountItems(parameters->string0Parameter,1); + } + return cntint0Parameter; +} + +int GameScript::NumItems(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cnt==parameters->int0Parameter; +} + +int GameScript::TotalItemCnt(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cnt==parameters->int0Parameter; +} + +int GameScript::TotalItemCntExclude(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cnt==parameters->int0Parameter; +} + +int GameScript::NumItemsGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cnt>parameters->int0Parameter; +} + +int GameScript::TotalItemCntGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cnt>parameters->int0Parameter; +} + +int GameScript::TotalItemCntExcludeGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cnt>parameters->int0Parameter; +} + +int GameScript::NumItemsLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + + Inventory *inv = NULL; + switch (tar->Type) { + case ST_ACTOR: + inv = &(((Actor *) tar)->inventory); + break; + case ST_CONTAINER: + inv = &(((Container *) tar)->inventory); + break; + default:; + } + if (!inv) { + return 0; + } + + int cnt = inv->CountItems(parameters->string0Parameter,1); + return cntint0Parameter; +} + +int GameScript::TotalItemCntLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1); //shall we count heaps or not? + return cntint0Parameter; +} + +int GameScript::TotalItemCntExcludeLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + int cnt = actor->inventory.CountItems("",1)-actor->inventory.CountItems(parameters->string0Parameter,1); //shall we count heaps or not? + return cntint0Parameter; +} + +//the int0 parameter is an addition, normally it is 0 +int GameScript::Contains(Scriptable* Sender, Trigger* parameters) +{ +//actually this should be a container + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !tar || tar->Type!=ST_CONTAINER) { + return 0; + } + Container *cnt = (Container *) tar; + if (HasItemCore(&cnt->inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::StoreHasItem(Scriptable* /*Sender*/, Trigger* parameters) +{ + return StoreHasItemCore(parameters->string0Parameter, parameters->string1Parameter); +} + +//the int0 parameter is an addition, normally it is 0 +int GameScript::HasItem(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr ) { + return 0; + } + Inventory *inventory; + switch (scr->Type) { + case ST_ACTOR: + inventory = &( (Actor *) scr)->inventory; + break; + case ST_CONTAINER: + inventory = &( (Container *) scr)->inventory; + break; + default: + inventory = NULL; + break; + } + if (inventory && HasItemCore(inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::ItemIsIdentified(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + if (HasItemCore(&actor->inventory, parameters->string0Parameter, IE_INV_ITEM_IDENTIFIED) ) { + return 1; + } + return 0; +} + +/** if the string is zero, then it will return true if there is any item in the slot (BG2)*/ +/** if the string is non-zero, it will return true, if the given item was in the slot (IWD2)*/ +int GameScript::HasItemSlot(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + //this might require a conversion of the slots + if (actor->inventory.HasItemInSlot(parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +//this is a GemRB extension +//HasItemTypeSlot(Object, SLOT, ItemType) +//returns true if the item in SLOT is of ItemType +int GameScript::HasItemTypeSlot(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Inventory *inv = &((Actor *) scr)->inventory; + if (parameters->int0Parameter>=inv->GetSlotCount()) { + return 0; + } + CREItem *slot = inv->GetSlotItem(parameters->int0Parameter); + if (!slot) { + return 0; + } + Item *itm = gamedata->GetItem(slot->ItemResRef); + int itemtype = itm->ItemType; + gamedata->FreeItem(itm, slot->ItemResRef, 0); + if (itemtype==parameters->int1Parameter) { + return 1; + } + return 0; +} + +int GameScript::HasItemEquipped(Scriptable * Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + if (actor->inventory.HasItem(parameters->string0Parameter, IE_INV_ITEM_EQUIPPED) ) { + return 1; + } + return 0; +} + +int GameScript::Acquired(Scriptable * Sender, Trigger* parameters) +{ + if ( Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (actor->inventory.HasItem(parameters->string0Parameter, IE_INV_ITEM_ACQUIRED) ) { + return 1; + } + return 0; +} + +/** this trigger accepts a numeric parameter, this number could be: */ +/** 0 - normal, 1 - equipped, 2 - identified, 3 - equipped&identified */ +/** this is a GemRB extension */ +int GameScript::PartyHasItem(Scriptable * /*Sender*/, Trigger* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (HasItemCore(&actor->inventory, parameters->string0Parameter, parameters->int0Parameter) ) { + return 1; + } + } + return 0; +} + +int GameScript::PartyHasItemIdentified(Scriptable * /*Sender*/, Trigger* parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + while(i--) { + Actor *actor = game->GetPC(i, true); + if (HasItemCore(&actor->inventory, parameters->string0Parameter, IE_INV_ITEM_IDENTIFIED) ) { + return 1; + } + } + return 0; +} + +int GameScript::InventoryFull( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (actor->inventory.FindCandidateSlot( SLOT_INVENTORY, 0 )==-1) { + return 1; + } + return 0; +} + +int GameScript::HasInnateAbility(Scriptable *Sender, Trigger *parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (parameters->string0Parameter[0]) { + return actor->spellbook.HaveSpell(parameters->string0Parameter, 0); + } + return actor->spellbook.HaveSpell(parameters->int0Parameter, 0); +} + +int GameScript::HaveSpell(Scriptable *Sender, Trigger *parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + return actor->spellbook.HaveSpell(parameters->string0Parameter, 0); + } + return actor->spellbook.HaveSpell(parameters->int0Parameter, 0); +} + +int GameScript::HaveAnySpells(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + return actor->spellbook.HaveSpell("", 0); +} + +int GameScript::HaveSpellParty(Scriptable* /*Sender*/, Trigger *parameters) +{ + Game *game=core->GetGame(); + + int i = game->GetPartySize(true); + + if (parameters->string0Parameter[0]) { + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->spellbook.HaveSpell(parameters->string0Parameter, 0) ) { + return 1; + } + } + } else { + while(i--) { + Actor *actor = game->GetPC(i, true); + if (actor->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return 1; + } + } + } + return 0; +} + +int GameScript::KnowSpell(Scriptable *Sender, Trigger *parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + if (parameters->string0Parameter[0]) { + return actor->spellbook.KnowSpell(parameters->string0Parameter); + } + return actor->spellbook.KnowSpell(parameters->int0Parameter); +} + +int GameScript::True(Scriptable * /* Sender*/, Trigger * /*parameters*/) +{ + return 1; +} + +//in fact this could be used only on Sender, but we want to enhance these +//triggers and actions to accept an object argument whenever possible. +//0 defaults to Myself (Sender) +int GameScript::NumTimesTalkedTo(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount == (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesTalkedToGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount > (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesTalkedToLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return actor->TalkCount < (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteracted(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] == (ieDword) parameters->int1Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] > (ieDword) parameters->int1Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + scr = Sender; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + ieDword npcid = parameters->int0Parameter; + if (npcid>=MAX_INTERACT) return 0; + if (!actor->PCStats) return 0; + return actor->PCStats->Interact[npcid] < (ieDword) parameters->int1Parameter ? 1 : 0; +} + +//GemRB specific +//interacting npc counts were restricted to 24 +//gemrb will increase a local variable in the interacting npc, with the scriptname of the +//target npc +int GameScript::NumTimesInteractedObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") == (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedObjectGT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") > (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::NumTimesInteractedObjectLT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* tar = ( Actor* ) scr; + return CheckVariable(Sender, tar->GetScriptName(), "LOCALS") < (ieDword) parameters->int0Parameter ? 1 : 0; +} + +int GameScript::ObjectActionListEmpty(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + + // added CurrentAction as part of blocking action fixes + if (scr->GetCurrentAction() || scr->GetNextAction()) { + return 0; + } + return 1; +} + +int GameScript::ActionListEmpty(Scriptable* Sender, Trigger* /*parameters*/) +{ + // added CurrentAction as part of blocking action fixes + if (Sender->GetCurrentAction() || Sender->GetNextAction()) { + return 0; + } + return 1; +} + +int GameScript::False(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + return 0; +} + +/* i guess this is a range of circle edges (instead of centers) */ +int GameScript::PersonalSpaceDistance(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + int range = parameters->int0Parameter; + + int distance = PersonalDistance(Sender, scr); + if (distance <= ( range * 10 )) { + return 1; + } + return 0; +} + +int GameScript::Range(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + int distance = SquaredMapDistance(Sender, scr); + return DiffCore(distance, (parameters->int0Parameter+1)*(parameters->int0Parameter+1), parameters->int1Parameter); +} + +int GameScript::InLine(Scriptable* Sender, Trigger* parameters) +{ + Map *map = Sender->GetCurrentArea(); + if (!map) { + return 0; + } + + Scriptable* scr1 = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr1) { + return 0; + } + + //looking for a scriptable by scriptname only + Scriptable* scr2 = map->GetActor( parameters->string0Parameter, 0 ); + if (!scr2) { + scr2 = GetActorObject(map->GetTileMap(), parameters->string0Parameter); + } + if (!scr2) { + return 0; + } + + double fdm1 = SquaredDistance(Sender, scr1); + double fdm2 = SquaredDistance(Sender, scr2); + double fd12 = SquaredDistance(scr1, scr2); + double dm1 = sqrt(fdm1); + double dm2 = sqrt(fdm2); + + if (fdm1>fdm2 || fd12>fdm2) { + return 0; + } + double angle = acos(( fdm2 + fdm1 - fd12 ) / (2*dm1*dm2)); + if (angle*180.0*M_PI<30.0) return 1; + return 0; +} + +//PST +int GameScript::AtLocation( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if ( (tar->Pos.x==parameters->pointParameter.x) && + (tar->Pos.y==parameters->pointParameter.y) ) { + return 1; + } + return 0; +} + +//in pst this is a point +//in iwd2 this is not a point +int GameScript::NearLocation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (parameters->pointParameter.isnull()) { + Point p((short) parameters->int0Parameter, (short) parameters->int1Parameter); + int distance = PersonalDistance(p, scr); + if (distance <= ( parameters->int2Parameter * 10 )) { + return 1; + } + return 0; + } + // should this be PersonalDistance? + int distance = Distance(parameters->pointParameter, scr); + if (distance <= ( parameters->int0Parameter * 10 )) { + return 1; + } + return 0; +} + +int GameScript::NearSavedLocation(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + if (core->HasFeature(GF_HAS_KAPUTZ)) { + // we don't understand how this works in pst yet + return 1; + } + Actor *actor = (Actor *) Sender; + Point p( (short) actor->GetStat(IE_SAVEDXPOS), (short) actor->GetStat(IE_SAVEDYPOS) ); + // should this be PersonalDistance? + int distance = Distance(p, Sender); + if (distance <= ( parameters->int0Parameter * 10 )) { + return 1; + } + return 0; +} + +int GameScript::Or(Scriptable* /*Sender*/, Trigger* parameters) +{ + return parameters->int0Parameter; +} + +int GameScript::TriggerTrigger(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->TriggerID==(ieDword) parameters->int0Parameter) { + Sender->AddTrigger (&Sender->TriggerID); + return 1; + } + return 0; +} + +int GameScript::WalkedToTrigger(Scriptable* Sender, Trigger* parameters) +{ + Actor *target = Sender->GetCurrentArea()->GetActorByGlobalID(Sender->LastTrigger); + if (!target) { + return 0; + } + if (PersonalDistance(target, Sender) > 3*MAX_OPERATING_DISTANCE ) { + return 0; + } + //now objects suicide themselves if they are empty objects + //so checking an empty object is easier + if (parameters->objectParameter == NULL) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::Clicked(Scriptable* Sender, Trigger* parameters) +{ + //now objects suicide themselves if they are empty objects + //so checking an empty object is easier + if (parameters->objectParameter == NULL) { + if (Sender->LastTrigger) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::Disarmed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: case ST_PROXIMITY: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmed) { + Sender->AddTrigger (&Sender->LastDisarmed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmed); + return 1; + } + return 0; +} + +//stealing from a store failed, owner triggered +int GameScript::StealFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_ACTOR: + break; + default: + return 0; + } + // maybe check if Sender is a shopkeeper??? + + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmFailed) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; +} + +int GameScript::PickpocketFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_ACTOR: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastOpenFailed) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastOpenFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; +} + +int GameScript::PickLockFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastPickLockFailed) { + Sender->AddTrigger (&Sender->LastPickLockFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastPickLockFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastPickLockFailed); + return 1; + } + return 0; +} + +int GameScript::OpenFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastOpenFailed) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastOpenFailed, parameters->objectParameter +)) { + Sender->AddTrigger (&Sender->LastOpenFailed); + return 1; + } + return 0; +} + +int GameScript::DisarmFailed(Scriptable* Sender, Trigger* parameters) +{ + switch(Sender->Type) { + case ST_DOOR: case ST_CONTAINER: case ST_PROXIMITY: + break; + default: + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastDisarmFailed) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastDisarmFailed, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastDisarmFailed); + return 1; + } + return 0; +} + +//opened for doors/containers (using lastEntered) +int GameScript::Opened(Scriptable* Sender, Trigger* parameters) +{ + Door *door; + + switch (Sender->Type) { + case ST_DOOR: + door = (Door *) Sender; + if (!door->IsOpen()) { + return 0; + } + break; + case ST_CONTAINER: + break; + default: + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +//closed for doors (using lastTrigger) +int GameScript::Closed(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_DOOR) { + return 0; + } + Door *door = (Door *) Sender; + if (door->IsOpen()) { + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastTrigger) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +//unlocked for doors/containers (using lastUnlocked) +int GameScript::Unlocked(Scriptable* Sender, Trigger* parameters) +{ + Door *door; + + switch (Sender->Type) { + case ST_DOOR: + door = (Door *) Sender; + if ((door->Flags&DOOR_LOCKED) ) { + return 0; + } + break; + case ST_CONTAINER: + break; + default: + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastUnlocked) { + Sender->AddTrigger (&Sender->LastUnlocked); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastUnlocked, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastUnlocked); + return 1; + } + return 0; +} + +int GameScript::Entered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + InfoPoint *ip = (InfoPoint *) Sender; + if (!ip->Trapped) { + return 0; + } + + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +int GameScript::HarmlessEntered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + if (parameters->objectParameter == NULL) { + if (Sender->LastEntered) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; + } + if (MatchActor(Sender, Sender->LastEntered, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastEntered); + return 1; + } + return 0; +} + +int GameScript::IsOverMe(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_PROXIMITY) { + return 0; + } + Highlightable *trap = (Highlightable *)Sender; + + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, parameters->objectParameter, GA_NO_DEAD); + int ret = 0; + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + if (trap->IsOver(actor->Pos)) { + ret = 1; + break; + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return ret; +} + +//this function is different in every engines, if you use a string0parameter +//then it will be considered as a variable check +//you can also use an object parameter (like in iwd) +int GameScript::Dead(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->string0Parameter[0]) { + ieDword value; + ieVariable Variable; + + if (core->HasFeature( GF_HAS_KAPUTZ )) { + value = CheckVariable( Sender, parameters->string0Parameter, "KAPUTZ"); + } else { + snprintf( Variable, 32, core->GetDeathVarFormat(), parameters->string0Parameter ); + } + value = CheckVariable( Sender, Variable, "GLOBAL" ); + if (value>0) { + return 1; + } + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 1; + } + if (target->Type != ST_ACTOR) { + return 1; + } + Actor* actor = ( Actor* ) target; + if (actor->GetStat( IE_STATE_ID ) & STATE_DEAD) { + return 1; + } + return 0; +} + +int GameScript::CreatureHidden(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + + //this stuff is not completely clear, but HoW has a flag for this + //and GemRB uses the avatarremoval stat for it. + //HideCreature also sets this stat, so... + if (act->GetStat(IE_AVATARREMOVAL)) { + return 1; + } + + if (act->GetInternalFlag()&IF_VISIBLE) { + return 0; + } + return 1; +} +int GameScript::BecameVisible(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + if (act->GetInternalFlag()&IF_BECAMEVISIBLE) { + //set trigger to erase + act->SetBitTrigger(BT_BECAMEVISIBLE); + return 1; + } + return 0; +} + +int GameScript::Die(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) Sender; + if (act->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to erase + act->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::Died(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor *act=(Actor *) tar; + if (act->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to erase + act->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::PartyMemberDied(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game = core->GetGame(); + int i = game->PartyMemberDied(); + if (i==-1) { + return 0; + } + //set trigger to erase + game->GetPC(i,false)->SetBitTrigger(BT_DIE); + return 1; +} + +int GameScript::NamelessBitTheDust(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Actor* actor = core->GetGame()->GetPC(0, false); + if (actor->GetInternalFlag()&IF_JUSTDIED) { + //set trigger to clear + actor->SetBitTrigger(BT_DIE); + return 1; + } + return 0; +} + +int GameScript::Race(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Race(actor, parameters->int0Parameter); +} + +int GameScript::Gender(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + return ID_Gender(actor, parameters->int0Parameter); +} + +int GameScript::HP(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ((signed) actor->GetBase( IE_HITPOINTS ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ( (signed) actor->GetBase( IE_HITPOINTS ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if ( (signed) actor->GetBase( IE_HITPOINTS ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +//these triggers work on the current damage (not the last damage) +/* they are identical to HPLost +int GameScript::DamageTaken(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage==(int) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::DamageTakenGT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage>(int) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::DamageTakenLT(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + int damage = actor->GetStat(IE_MAXHITPOINTS)-actor->GetBase(IE_HITPOINTS); + if (damage<(int) parameters->int0Parameter) { + return 1; + } + return 0; +} +*/ + +int GameScript::HPLost(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) == (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLostGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) > (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPLostLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + //max-current + if ( (signed) actor->GetStat(IE_MAXHITPOINTS)-(signed) actor->GetBase( IE_HITPOINTS ) < (signed) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercent(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercentGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::HPPercentLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (GetHPPercent( scr ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XP(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) == (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XPGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) > (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::XPLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr) { + return 0; + } + if (scr->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) scr; + if (actor->GetStat( IE_XP ) < (unsigned) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkill(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + if (target->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) target; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) == parameters->int0Parameter) { + return 1; + } + return 0; +} +int GameScript::CheckStat(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + if (target->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) target; + if ((signed) actor->GetStat( parameters->int1Parameter ) == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkillGT(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckStatGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( parameters->int1Parameter ) > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckSkillLT(Scriptable* Sender, Trigger* parameters) +{ + if (parameters->int1Parameter>=SkillCount) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( SkillStats[parameters->int1Parameter] ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CheckStatLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ((signed) actor->GetStat( parameters->int1Parameter ) < parameters->int0Parameter) { + return 1; + } + return 0; +} + +/* i believe this trigger is the same as 'MarkObject' action + except that if it cannot set the marked object, it returns false */ +int GameScript::SetLastMarkedObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + scr->LastMarked = tar->GetGlobalID(); + return 1; +} + +int GameScript::IsSpellTargetValid(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + Actor *actor = NULL; + if (tar->Type == ST_ACTOR) { + actor = (Actor *) tar; + } + + int flags = parameters->int1Parameter; + if (!(flags & MSO_IGNORE_NULL) && !actor) { + return 0; + } + if (!(flags & MSO_IGNORE_INVALID) && actor && actor->InvalidSpellTarget() ) { + return 0; + } + int splnum = parameters->int0Parameter; + if (!(flags & MSO_IGNORE_HAVE) && !scr->spellbook.HaveSpell(splnum, 0) ) { + return 0; + } + int range; + if ((flags & MSO_IGNORE_RANGE) || !actor) { + range = 0; + } else { + range = Distance(scr, actor); + } + if (!(flags & MSO_IGNORE_INVALID) && actor->InvalidSpellTarget(splnum, scr, range)) { + return 0; + } + return 1; +} + +//This trigger seems to always return true for actors... +//Always manages to set spell to 0, otherwise it sets if there was nothing set earlier +int GameScript::SetMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + if (parameters->int0Parameter) { + if (scr->LastMarkedSpell) { + return 1; + } + if (!scr->spellbook.HaveSpell(parameters->int0Parameter, 0) ) { + return 1; + } + } + + //TODO: check if spell exists (not really important) + scr->LastMarkedSpell = parameters->int0Parameter; + return 1; +} + +int GameScript::ForceMarkedSpell_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + scr->LastMarkedSpell = parameters->int0Parameter; + return 1; +} + +int GameScript::IsMarkedSpell(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + return scr->LastMarkedSpell == parameters->int0Parameter; +} + + +int GameScript::See(Scriptable* Sender, Trigger* parameters) +{ + int see = SeeCore(Sender, parameters, 0); + //don't mark LastSeen for clear!!! + if (Sender->Type==ST_ACTOR && see) { + Actor *act = (Actor *) Sender; + //save lastseen as lastmarked + act->LastMarked = act->LastSeen; + //Sender->AddTrigger (&act->LastSeen); + } + return see; +} + +int GameScript::Detect(Scriptable* Sender, Trigger* parameters) +{ + parameters->int0Parameter=1; //seedead/invis + int see = SeeCore(Sender, parameters, 0); + if (!see) { + return 0; + } + return 1; +} + +int GameScript::LOS(Scriptable* Sender, Trigger* parameters) +{ + int see=SeeCore(Sender, parameters, 1); + if (!see) { + return 0; + } + return Range(Sender, parameters); //same as range +} + +int GameScript::NumCreatures(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value == parameters->int0Parameter; +} + +int GameScript::NumCreaturesAtMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value == level; +} + +int GameScript::NumCreaturesLT(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value < parameters->int0Parameter; +} + +int GameScript::NumCreaturesLTMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value < level; +} + +int GameScript::NumCreaturesGT(Scriptable* Sender, Trigger* parameters) +{ + int value = GetObjectCount(Sender, parameters->objectParameter); + return value > parameters->int0Parameter; +} + +int GameScript::NumCreaturesGTMyLevel(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + int level = ((Actor *) Sender)->GetXPLevel(true); + int value; + + if (parameters->int0Parameter) { + value = GetObjectLevelCount(Sender, parameters->objectParameter); + } else { + value = GetObjectCount(Sender, parameters->objectParameter); + } + return value > level; +} + +int GameScript::NumCreatureVsParty(Scriptable* Sender, Trigger* parameters) +{ + //creating object on the spot + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value == parameters->int0Parameter; +} + +int GameScript::NumCreatureVsPartyGT(Scriptable* Sender, Trigger* parameters) +{ + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value > parameters->int0Parameter; +} + +int GameScript::NumCreatureVsPartyLT(Scriptable* Sender, Trigger* parameters) +{ + if (!parameters->objectParameter) { + parameters->objectParameter = new Object(); + } + int value = GetObjectCount(Sender, parameters->objectParameter); + value -= core->GetGame()->GetPartySize(true); + return value < parameters->int0Parameter; +} + +int GameScript::Morale(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) == parameters->int0Parameter; +} + +int GameScript::MoraleGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) > parameters->int0Parameter; +} + +int GameScript::MoraleLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_MORALEBREAK) < parameters->int0Parameter; +} + +int GameScript::CheckSpellState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (parameters->int0Parameter>255) { + return 0; + } + unsigned int position = parameters->int0Parameter>>5; + unsigned int bit = 1<<(parameters->int0Parameter&31); + if (actor->GetStat(IE_SPLSTATE_ID1+position) & bit) { + return 1; + } + return 0; +} + +int GameScript::StateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_STATE_ID) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::ExtendedStateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_EXTSTATE_ID) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::NotStateCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_STATE_ID) & ~parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::RandomNum(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 == RandomNumValue%parameters->int0Parameter; +} + +int GameScript::RandomNumGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 < RandomNumValue%parameters->int0Parameter; +} + +int GameScript::RandomNumLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + if (parameters->int0Parameter<0) { + return 0; + } + if (parameters->int1Parameter<0) { + return 0; + } + return parameters->int1Parameter-1 > RandomNumValue%parameters->int0Parameter; +} + +int GameScript::OpenState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + if (InDebug&ID_TRIGGERS) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Couldn't find door/container:%s\n", parameters->objectParameter? parameters->objectParameter->objectName:""); + printf("Sender: %s\n", Sender->GetScriptName() ); + } + return 0; + } + switch(tar->Type) { + case ST_DOOR: + { + Door *door =(Door *) tar; + return !door->IsOpen() == !parameters->int0Parameter; + } + case ST_CONTAINER: + { + Container *cont = (Container *) tar; + return !(cont->Flags&CONT_LOCKED) == !parameters->int0Parameter; + } + default:; //to remove a warning + } + printMessage("GameScript"," ",LIGHT_RED); + printf("Not a door/container:%s\n", tar->GetScriptName()); + return 0; +} + +int GameScript::IsLocked(Scriptable * Sender, Trigger *parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + printMessage("GameScript"," ",LIGHT_RED); + printf("Couldn't find door/container:%s\n", parameters->objectParameter? parameters->objectParameter->objectName:""); + printf("Sender: %s\n", Sender->GetScriptName() ); + return 0; + } + switch(tar->Type) { + case ST_DOOR: + { + Door *door =(Door *) tar; + return !!(door->Flags&DOOR_LOCKED); + } + case ST_CONTAINER: + { + Container *cont = (Container *) tar; + return !!(cont->Flags&CONT_LOCKED); + } + default:; //to remove a warning + } + printMessage("GameScript"," ",LIGHT_RED); + printf("Not a door/container:%s\n", tar->GetScriptName()); + return 0; +} + +int GameScript::Level(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + // FIXME: what about multiclasses or dualclasses? + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int0Parameter; +} + +//this is just a hack, actually multiclass should be available +int GameScript::ClassLevel(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + // FIXME: compare the requested level + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int1Parameter; +} + +// iwd2 and pst have different order of parameters: +// ClassLevelGT(Protagonist,MAGE,89) +// LevelInClass(Myself,10,CLERIC) +int GameScript::LevelInClass(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + // FIXME: compare the requested level + return actor->GetStat(IE_LEVEL) == (unsigned) parameters->int0Parameter; +} + +int GameScript::LevelGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int0Parameter; +} + +//this is just a hack, actually multiclass should be available +int GameScript::ClassLevelGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int1Parameter; +} + +int GameScript::LevelInClassGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) > (unsigned) parameters->int0Parameter; +} + +int GameScript::LevelLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int0Parameter; +} + +int GameScript::ClassLevelLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (!ID_Class( actor, parameters->int0Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int1Parameter; +} + +int GameScript::LevelInClassLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + if (!ID_ClassMask( actor, parameters->int1Parameter) ) + return 0; + return actor->GetStat(IE_LEVEL) < (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariable(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer == (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariableGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer > (unsigned) parameters->int0Parameter; +} + +int GameScript::UnselectableVariableLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->UnselectableTimer < (unsigned) parameters->int0Parameter; +} + +int GameScript::AreaCheck(Scriptable* Sender, Trigger* parameters) +{ + if (!strnicmp(Sender->GetCurrentArea()->GetScriptName(), parameters->string0Parameter, 8)) { + return 1; + } + return 0; +} + +int GameScript::AreaCheckObject(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), parameters->string0Parameter, 8)) { + return 1; + } + return 0; +} + +//lame iwd2 uses a numeric area identifier, this reduces its usability +int GameScript::CurrentAreaIs(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + ieResRef arearesref; + snprintf(arearesref, 8, "AR%04d", parameters->int0Parameter); + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), arearesref, 8)) { + return 1; + } + return 0; +} + +//lame bg2 uses a constant areaname prefix, this reduces its usability +//but in the spirit of flexibility, gemrb extension allows arbitrary prefixes +int GameScript::AreaStartsWith(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + + if (!tar) { + return 0; + } + ieResRef arearesref; + if (parameters->string0Parameter[0]) { + strnlwrcpy(arearesref, parameters->string0Parameter, 8); + } else { + strnlwrcpy(arearesref, "AR30", 8); //InWatchersKeep + } + int i = strlen(arearesref); + if (!strnicmp(tar->GetCurrentArea()->GetScriptName(), arearesref, i)) { + return 1; + } + return 0; +} + +int GameScript::EntirePartyOnMap(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + Game *game=core->GetGame(); + int i=game->GetPartySize(true); + while (i--) { + Actor *actor=game->GetPC(i,true); + if (actor->GetCurrentArea()!=map) { + return 0; + } + } + return 1; +} + +int GameScript::AnyPCOnMap(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map = Sender->GetCurrentArea(); + Game *game=core->GetGame(); + int i=game->GetPartySize(true); + while (i--) { + Actor *actor=game->GetPC(i,true); + if (actor->GetCurrentArea()==map) { + return 1; + } + } + return 0; +} + +int GameScript::InActiveArea(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (core->GetGame()->GetCurrentArea() == tar->GetCurrentArea()) { + return 1; + } + return 0; +} + +int GameScript::InMyArea(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (Sender->GetCurrentArea() == tar->GetCurrentArea()) { + return 1; + } + return 0; +} + +int GameScript::AreaType(Scriptable* Sender, Trigger* parameters) +{ + Map *map=Sender->GetCurrentArea(); + return (map->AreaType¶meters->int0Parameter)>0; +} + +int GameScript::IsExtendedNight( Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map=Sender->GetCurrentArea(); + if (map->AreaType&AT_EXTENDED_NIGHT) { + return 1; + } + return 0; +} + +int GameScript::AreaFlag(Scriptable* Sender, Trigger* parameters) +{ + Map *map=Sender->GetCurrentArea(); + return (map->AreaFlags¶meters->int0Parameter)>0; +} + +int GameScript::AreaRestDisabled(Scriptable* Sender, Trigger* /*parameters*/) +{ + Map *map=Sender->GetCurrentArea(); + if (map->AreaFlags&2) { + return 1; + } + return 0; +} + +//new optional parameter: size of actor (to reach target) +int GameScript::TargetUnreachable(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 1; //well, if it doesn't exist it is unreachable + } + Map *map=Sender->GetCurrentArea(); + if (!map) { + return 1; + } + unsigned int size = parameters->int0Parameter; + + if (!size) { + if (Sender->Type==ST_ACTOR) { + size = ((Movable *) Sender)->size; + } + else { + size = 1; + } + } + return map->TargetUnreachable( Sender->Pos, tar->Pos, size); +} + +int GameScript::PartyCountEQ(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)==parameters->int0Parameter; +} + +int GameScript::PartyCountLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)int0Parameter; +} + +int GameScript::PartyCountGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(0)>parameters->int0Parameter; +} + +int GameScript::PartyCountAliveEQ(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)==parameters->int0Parameter; +} + +int GameScript::PartyCountAliveLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)int0Parameter; +} + +int GameScript::PartyCountAliveGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartySize(1)>parameters->int0Parameter; +} + +int GameScript::LevelParty(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)==parameters->int0Parameter; +} + +int GameScript::LevelPartyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)int0Parameter; +} + +int GameScript::LevelPartyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->GetPartyLevel(1)>parameters->int0Parameter; +} + +int GameScript::PartyGold(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold == (ieDword) parameters->int0Parameter; +} + +int GameScript::PartyGoldGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold > (ieDword) parameters->int0Parameter; +} + +int GameScript::PartyGoldLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->PartyGold < (ieDword) parameters->int0Parameter; +} + +int GameScript::OwnsFloaterMessage(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + return tar->textDisplaying; +} + +int GameScript::InCutSceneMode(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + return core->InCutSceneMode(); +} + +int GameScript::Proficiency(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) == parameters->int1Parameter; +} + +int GameScript::ProficiencyGT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) > parameters->int1Parameter; +} + +int GameScript::ProficiencyLT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>31) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_PROFICIENCYBASTARDSWORD+idx) < parameters->int1Parameter; +} + +//this is a PST specific stat, shows how many free proficiency slots we got +//we use an unused stat for it +int GameScript::ExtraProficiency(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) == parameters->int0Parameter; +} + +int GameScript::ExtraProficiencyGT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) > parameters->int0Parameter; +} + +int GameScript::ExtraProficiencyLT(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_FREESLOTS) < parameters->int0Parameter; +} + +int GameScript::Internal(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) == parameters->int1Parameter; +} + +int GameScript::InternalGT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) > parameters->int1Parameter; +} + +int GameScript::InternalLT(Scriptable* Sender, Trigger* parameters) +{ + unsigned int idx = parameters->int0Parameter; + if (idx>15) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + return (signed) actor->GetStat(IE_INTERNAL_0+idx) < parameters->int1Parameter; +} + +//we check if target is currently in dialog or not +int GameScript::NullDialog(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + GameControl *gc = core->GetGameControl(); + if ( (tar->GetGlobalID() != gc->dialoghandler->targetID) && (tar->GetGlobalID() != gc->dialoghandler->speakerID) ) { + return 1; + } + return 0; +} + +//this one checks scriptname (deathvar), i hope it is right +//IsScriptName depends on this too +//Name is another (similar function) +int GameScript::CalledByName(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if (stricmp(actor->GetScriptName(), parameters->string0Parameter) ) { + return 0; + } + return 1; +} + +//This is checking on the character's name as it was typed in +int GameScript::CharName(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = GetActorFromObject( Sender, parameters->objectParameter ); + if (!scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = (Actor *) scr; + if (!strnicmp(actor->ShortName, parameters->string0Parameter, 32) ) { + return 1; + } + return 0; +} + +int GameScript::AnimationID(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + if ((ieWord) actor->GetStat(IE_ANIMATION_ID) == (ieWord) parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::AnimState(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + if (tar->Type != ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) tar; + return actor->GetStance() == parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::Time(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 == (ieDword) parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::TimeGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 > (ieDword) parameters->int0Parameter; +} + +//this trigger uses hours +int GameScript::TimeLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/300 < (ieDword) parameters->int0Parameter; +} + +int GameScript::HotKey(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + // FIXME: this is never going to work on 64 bit archs ... + int ret = (unsigned long) scr->HotKey == (unsigned long) parameters->int0Parameter; + //probably we need to implement a trigger mechanism, clear + //the hotkey only when the triggerblock was evaluated as true + if (ret) { + Sender->AddTrigger (&scr->HotKey); + } + return ret; +} + +int GameScript::CombatCounter(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter == (ieDword) parameters->int0Parameter; +} + +int GameScript::CombatCounterGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter > (ieDword) parameters->int0Parameter; +} + +int GameScript::CombatCounterLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + return core->GetGame()->CombatCounter < (ieDword) parameters->int0Parameter; +} + +int GameScript::TrapTriggered(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_TRIGGER) { + return 0; + } +/* matchactor would do this, hmm + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } +*/ + if (MatchActor(Sender, Sender->LastTrigger, parameters->objectParameter)) { + Sender->AddTrigger (&Sender->LastTrigger); + return 1; + } + return 0; +} + +int GameScript::InteractingWith(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + GameControl *gc = core->GetGameControl(); + if (Sender->GetGlobalID() != gc->dialoghandler->targetID && Sender->GetGlobalID() != gc->dialoghandler->speakerID) { + return 0; + } + if (tar->GetGlobalID() != gc->dialoghandler->targetID && tar->GetGlobalID() != gc->dialoghandler->speakerID) { + return 0; + } + return 1; +} + +int GameScript::LastPersonTalkedTo(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + if (MatchActor(Sender, scr->LastTalkedTo, parameters->objectParameter)) { + return 1; + } + return 0; +} + +int GameScript::IsRotation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if ( actor->GetOrientation() == parameters->int0Parameter ) { + return 1; + } + return 0; +} + +//GemRB currently stores the saved location in a local variable, but it is +//actually stored in the .gam structure (only for PCs) +int GameScript::IsFacingSavedRotation(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetOrientation() == actor->GetStat(IE_SAVEDFACE) ) { + return 1; + } + return 0; +} + +int GameScript::IsFacingObject(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type != ST_ACTOR) { + return 0; + } + Scriptable* target = GetActorFromObject( Sender, parameters->objectParameter ); + if (!target) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (actor->GetOrientation()==GetOrient( target->Pos, actor->Pos ) ) { + return 1; + } + return 0; +} + +int GameScript::AttackedBy(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *scr = (Actor *) Sender; + Targets *tgts = GetAllObjects(Sender->GetCurrentArea(), Sender, parameters->objectParameter, GA_NO_DEAD); + int ret = 0; + int AStyle = parameters->int0Parameter; + //iterate through targets to get the actor + if (tgts) { + targetlist::iterator m; + const targettype *tt = tgts->GetFirstTarget(m, ST_ACTOR); + while (tt) { + Actor *actor = (Actor *) tt->actor; + //if (actor->LastTarget == scr->GetID()) { + if (scr->LastAttacker == actor->GetGlobalID()) { + if (!AStyle || (AStyle==actor->GetAttackStyle()) ) { + scr->AddTrigger(&scr->LastAttacker); + ret = 1; + break; + } + } + tt = tgts->GetNextTarget(m, ST_ACTOR); + } + } + delete tgts; + return ret; +} + +int GameScript::TookDamage(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + //zero damage doesn't count? + if (actor->LastHitter && actor->LastDamage) { + Sender->AddTrigger(&actor->LastHitter); + return 1; + } + return 0; +} + +int GameScript::HitBy(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + if (!(parameters->int0Parameter&actor->LastDamageType) ) { + return 0; + } + } + if (MatchActor(Sender, actor->LastHitter, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHitter); + return 1; + } + return 0; +} + +int GameScript::Heard(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (parameters->int0Parameter) { + if (parameters->int0Parameter!=actor->LastShout) { + return 0; + } + } + if (MatchActor(Sender, actor->LastHeard, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHeard); + return 1; + } + return 0; +} + +int GameScript::LastMarkedObject_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastMarked, parameters->objectParameter)) { + //don't mark this object for clear + //Sender->AddTrigger(&actor->LastSeen); + return 1; + } + return 0; +} + +int GameScript::HelpEX(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + int stat; + switch (parameters->int0Parameter) { + case 1: stat = IE_EA; break; + case 2: stat = IE_GENERAL; break; + case 3: stat = IE_RACE; break; + case 4: stat = IE_CLASS; break; + case 5: stat = IE_SPECIFIC; break; + case 6: stat = IE_SEX; break; + case 7: stat = IE_ALIGNMENT; break; + default: return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + //a non actor checking for help? + return 0; + } + Actor* actor = ( Actor* ) tar; + Actor* help = Sender->GetCurrentArea()->GetActorByGlobalID(actor->LastHelp); + if (!help) { + //no help required + return 0; + } + if (actor->GetStat(stat)==help->GetStat(stat) ) { + Sender->AddTrigger(&actor->LastHelp); + return 1; + } + return 0; +} + +int GameScript::Help_Trigger(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastHelp, parameters->objectParameter)) { + Sender->AddTrigger(&actor->LastHelp); + return 1; + } + return 0; +} + +int GameScript::ReceivedOrder(Scriptable* Sender, Trigger* parameters) +{ + if (MatchActor(Sender, Sender->LastOrderer, parameters->objectParameter) && + parameters->int0Parameter==Sender->LastOrder) { + Sender->AddTrigger(&Sender->LastOrderer); + return 1; + } + return 0; +} + +int GameScript::Joins(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->Type!=ST_ACTOR) { + return 0; + } + Actor * actor = ( Actor* ) Sender; + //this trigger is sent only to PCs in a party + if(!actor->PCStats) { + return 0; + } + if (MatchActor(Sender, actor->PCStats->LastJoined, parameters->objectParameter)) { + Sender->AddTrigger(&actor->PCStats->LastJoined); + return 1; + } + return 0; +} + +int GameScript::Leaves(Scriptable* Sender, Trigger* parameters) +{ + if(Sender->Type!=ST_ACTOR) { + return 0; + } + Actor * actor = ( Actor* ) Sender; + //this trigger is sent only to PCs in a party + if(!actor->PCStats) { + return 0; + } + if (MatchActor(Sender, actor->PCStats->LastLeft, parameters->objectParameter)) { + Sender->AddTrigger(&actor->PCStats->LastLeft); + return 1; + } + return 0; +} + +int GameScript::FallenPaladin(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* act = ( Actor* ) Sender; + return (act->GetStat(IE_MC_FLAGS) & MC_FALLEN_PALADIN)!=0; +} + +int GameScript::FallenRanger(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* act = ( Actor* ) Sender; + return (act->GetStat(IE_MC_FLAGS) & MC_FALLEN_RANGER)!=0; +} + +int GameScript::NightmareModeOn(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Nightmare Mode", diff); + if (diff) { + return 1; + } + return 0; +} + +int GameScript::Difficulty(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + int mode = parameters->int1Parameter; + //hack for compatibility + if (!mode) { + mode = EQUALS; + } + return DiffCore(diff, (ieDword) parameters->int0Parameter, mode); +} + +int GameScript::DifficultyGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + return diff>(ieDword) parameters->int0Parameter; +} + +int GameScript::DifficultyLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword diff; + + core->GetDictionary()->Lookup("Difficulty Level", diff); + return diff<(ieDword) parameters->int0Parameter; +} + +int GameScript::Vacant(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_AREA) { + return 0; + } + Map *map = (Map *) Sender; + if ( map->CanFree() ) { + return 1; + } + return 0; +} + +//this trigger always checks the right hand weapon? +int GameScript::InWeaponRange(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar) { + return 0; + } + Actor *actor = (Actor *) Sender; + WeaponInfo wi; + unsigned int wrange = 0; + ITMExtHeader *header = actor->GetWeapon(wi, false); + if (header) { + wrange = wi.range; + } + header = actor->GetWeapon(wi, true); + if (header && (wi.range>wrange)) { + wrange = wi.range; + } + if ( PersonalDistance( Sender, tar ) <= wrange * 10 ) { + return 1; + } + return 0; +} + +//this implementation returns only true if there is a bow wielded +//but there is no ammo for it +//if the implementation should sign 'no ranged attack possible' +//then change some return values +//in bg2/iwd2 it doesn't accept an object (the object parameter is gemrb ext.) +int GameScript::OutOfAmmo(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* scr = Sender; + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } + if ( !scr || scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + WeaponInfo wi; + ITMExtHeader *header = actor->GetWeapon(wi, false); + //no bow wielded? + if (!header || header->AttackType!=ITEM_AT_BOW) { + return 0; + } + //we either have a projectile (negative) or an empty bow (positive) + //so we should find a negative slot, positive slot means: OutOfAmmo + if (actor->inventory.GetEquipped()<0) { + return 0; + } + //out of ammo + return 1; +} + +//returns true if a weapon is equipped (with more than 0 range) +//if a bow is equipped without projectile, it is useless! +//please notice how similar is this to OutOfAmmo +int GameScript::HaveUsableWeaponEquipped(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + WeaponInfo wi; + ITMExtHeader *header = actor->GetWeapon(wi, false); + + //bows are not usable (because if they are loaded, the equipped + //weapon is the projectile) + if (!header || header->AttackType==ITEM_AT_BOW) { + return 0; + } + //only fist we have, it is not qualified as weapon? + if (actor->inventory.GetEquippedSlot() == actor->inventory.GetFistSlot()) { + return 0; + } + return 1; +} + +int GameScript::HasWeaponEquipped(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->inventory.GetEquippedSlot() == IW_NO_EQUIPPED) { + return 0; + } + return 1; +} + +int GameScript::PCInStore( Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + if (core->GetCurrentStore()) { + return 1; + } + return 0; +} + +//this checks if the launch point is onscreen, a more elaborate check +//would see if any piece of the Scriptable is onscreen, what is the original +//behaviour? +int GameScript::OnScreen( Scriptable* Sender, Trigger* /*parameters*/) +{ + Region vp = core->GetVideoDriver()->GetViewport(); + if (vp.PointInside(Sender->Pos) ) { + return 1; + } + return 0; +} + +int GameScript::IsPlayerNumber( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->InParty == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::PCCanSeePoint( Scriptable* /*Sender*/, Trigger* parameters) +{ + Map* map = core->GetGame()->GetCurrentArea(); + if (map->IsVisible(parameters->pointParameter, false) ) { + return 1; + } + return 0; +} + +//i'm clueless about this trigger +int GameScript::StuffGlobalRandom( Scriptable* Sender, Trigger* parameters) +{ + unsigned int max=parameters->int0Parameter+1; + ieDword Value; + if (max) { + Value = RandomNumValue%max; + } else { + Value = RandomNumValue; + } + SetVariable( Sender, parameters->string0Parameter, Value ); + if (Value) { + return 1; + } + return 0; +} + +int GameScript::IsCreatureAreaFlag( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_MC_FLAGS) & parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::IsPathCriticalObject( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_MC_FLAGS) & MC_PLOT_CRITICAL) { + return 1; + } + return 0; +} + +// 0 - ability, 1 - number, 2 - mode +int GameScript::ChargeCount( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + int Slot = actor->inventory.FindItem(parameters->string0Parameter,0); + if (Slot<0) { + return 0; + } + CREItem *item = actor->inventory.GetSlotItem (Slot); + if (!item) {//bah + return 0; + } + if (parameters->int0Parameter>2) { + return 0; + } + int charge = item->Usages[parameters->int0Parameter]; + switch (parameters->int2Parameter) { + case DM_EQUAL: + if (charge == parameters->int1Parameter) + return 1; + break; + case DM_LESS: + if (charge < parameters->int1Parameter) + return 1; + break; + case DM_GREATER: + if (charge > parameters->int1Parameter) + return 1; + break; + default: + return 0; + } + return 0; +} + +// no idea if it checks only alive partymembers +int GameScript::CheckPartyLevel( Scriptable* /*Sender*/, Trigger* parameters) +{ + if (core->GetGame()->GetPartyLevel(false)int0Parameter) { + return 0; + } + return 1; +} + +// no idea if it checks only alive partymembers +int GameScript::CheckPartyAverageLevel( Scriptable* /*Sender*/, Trigger* parameters) +{ + int level = core->GetGame()->GetPartyLevel(false); + switch (parameters->int1Parameter) { + case DM_EQUAL: + if (level ==parameters->int0Parameter) { + return 1; + } + break; + case DM_LESS: + if (level < parameters->int0Parameter) { + return 1; + } + break; + case DM_GREATER: + if (level > parameters->int0Parameter) { + return 1; + } + break; + default: + return 0; + } + return 1; +} + +int GameScript::CheckDoorFlags( Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_DOOR) { + return 0; + } + Door* door = ( Door* ) tar; + if (door->Flags¶meters->int0Parameter) { + return 1; + } + return 0; +} + +// works only on animations? +// Be careful when converting to GetActorFromObject, it won't return animations (those are not scriptable) +int GameScript::Frame( Scriptable* Sender, Trigger* parameters) +{ + //to avoid a crash + if (!parameters->objectParameter) { + return 0; + } + AreaAnimation* anim = Sender->GetCurrentArea()->GetAnimation(parameters->objectParameter->objectName); + if (!anim) { + return 0; + } + int frame = anim->frame; + if ((frame>=parameters->int0Parameter) && + (frame<=parameters->int1Parameter) ) { + return 1; + } + return 0; +} + +//Modalstate in IWD2 allows specifying an object +int GameScript::ModalState( Scriptable* Sender, Trigger* parameters) +{ + Scriptable *scr; + + if (parameters->objectParameter) { + scr = GetActorFromObject( Sender, parameters->objectParameter ); + } else { + scr = Sender; + } + if (scr->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) scr; + + if (actor->ModalState==(ieDword) parameters->int0Parameter) { + return 1; + } + return 0; +} + +/* a special redundant trigger for iwd2 - could do something extra */ +int GameScript::IsCreatureHiddenInShadows( Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + + if (actor->ModalState==MS_STEALTH) { + return 1; + } + return 0; +} + +int GameScript::IsWeather( Scriptable* /*Sender*/, Trigger* parameters) +{ + Game *game = core->GetGame(); + ieDword weather = game->WeatherBits & parameters->int0Parameter; + if (weather == (ieDword) parameters->int1Parameter) { + return 1; + } + return 0; +} + +int GameScript::Delay( Scriptable* Sender, Trigger* parameters) +{ + ieDword delay = (ieDword) parameters->int0Parameter; + if (delay<=1) { + return 1; + } + ieDword time1=Sender->lastDelay/1000/delay; + ieDword time2=Sender->lastRunTime/1000/delay; + + if (time1!=time2) { + return 1; + } + return 0; +} + +int GameScript::TimeOfDay(Scriptable* /*Sender*/, Trigger* parameters) +{ + ieDword timeofday = (core->GetGame()->GameTime/AI_UPDATE_TIME)%7200/1800; + + if (timeofday==(ieDword) parameters->int0Parameter) { + return 1; + } + return 0; +} + +//this is a PST action, it's using delta, not diffmode +int GameScript::RandomStatCheck(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + + ieDword stat = actor->GetStat(parameters->int0Parameter); + ieDword value = Bones(parameters->int2Parameter); + switch(parameters->int1Parameter) { + case DM_SET: + if (stat==value) + return 1; + break; + case DM_LOWER: + if (statvalue) + return 1; + break; + } + return 0; +} + +int GameScript::PartyRested(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->GetInternalFlag()&IF_PARTYRESTED) { + Sender->SetBitTrigger(BT_PARTYRESTED); + return 1; + } + return 0; +} + +int GameScript::IsWeaponRanged(Scriptable* Sender, Trigger* parameters) +{ + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->inventory.GetEquipped()<0) { + return 1; + } + return 0; +} + +//HoW applies sequence on area animations +int GameScript::Sequence(Scriptable* Sender, Trigger* parameters) +{ + //to avoid a crash, check if object is NULL + if (parameters->objectParameter) { + AreaAnimation *anim = Sender->GetCurrentArea()->GetAnimation(parameters->objectParameter->objectName); + if (anim) { + //this is the cycle count for the area animation + //very much like stance for avatar anims + if (anim->sequence==parameters->int0Parameter) { + return 1; + } + return 0; + } + } + + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStance()==parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::TimerExpired(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->TimerExpired(parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::TimerActive(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->TimerActive(parameters->int0Parameter) ) { + return 1; + } + return 0; +} + +int GameScript::ActuallyInCombat(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game=core->GetGame(); + if (game->AnyPCInCombat()) return 1; + return 0; +} + +int GameScript::InMyGroup(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + + Scriptable* tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type!=ST_ACTOR) { + return 0; + } +/* IESDP SUCKS + if (GetGroup( (Actor *) tar)==GetGroup( (Actor *) Sender) ) { + return 1; + } +*/ + if ( ((Actor *) tar)->GetStat(IE_SPECIFIC)==((Actor *) tar)->GetStat(IE_SPECIFIC) ) { + return 1; + } + return 0; +} + +int GameScript::AnyPCSeesEnemy(Scriptable* /*Sender*/, Trigger* /*parameters*/) +{ + Game *game = core->GetGame(); + unsigned int i = (unsigned int) game->GetLoadedMapCount(); + while(i--) { + Map *map = game->GetMap(i); + if (map->AnyPCSeesEnemy()) { + return 1; + } + } + return 0; +} + +int GameScript::Unusable(Scriptable* Sender, Trigger* parameters) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor *actor = (Actor *) Sender; + + Item *item = gamedata->GetItem(parameters->string0Parameter); + int ret; + if (actor->Unusable(item)) { + ret = 0; + } else { + ret = 1; + } + gamedata->FreeItem(item, parameters->string0Parameter, true); + return ret; +} + +int GameScript::HasBounceEffects(Scriptable* Sender, Trigger* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_BOUNCE)) return 1; + return 0; +} + +int GameScript::HasImmunityEffects(Scriptable* Sender, Trigger* parameters) +{ + Scriptable *tar = GetActorFromObject( Sender, parameters->objectParameter ); + if (!tar || tar->Type != ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) tar; + if (actor->GetStat(IE_IMMUNITY)) return 1; + return 0; +} + +// this is a GemRB specific trigger, to transfer some system variables +// to a global (game variable), it will always return true, and the +// variable could be checked in a subsequent trigger (like triggersetglobal) + +#define SYSV_SCREENFLAGS 0 +#define SYSV_CONTROLSTATUS 1 +#define SYSV_REPUTATION 2 +#define SYSV_PARTYGOLD 3 + +int GameScript::SystemVariable_Trigger(Scriptable* Sender, Trigger* parameters) +{ + ieDword value; + + switch (parameters->int0Parameter) { + case SYSV_SCREENFLAGS: + value = core->GetGameControl()->GetScreenFlags(); + break; + case SYSV_CONTROLSTATUS: + value = core->GetGame()->ControlStatus; + break; + case SYSV_REPUTATION: + value = core->GetGame()->Reputation; + break; + case SYSV_PARTYGOLD: + value = core->GetGame()->PartyGold; + break; + default: + return 0; + } + + SetVariable(Sender, parameters->string0Parameter, value); + return 1; +} + +int GameScript::SpellCast(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 2000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastPriest(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 1000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastInnate(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + unsigned int param = 3000+parameters->int0Parameter%1000; + if (param!=Sender->LastSpellSeen) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterSeen, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterSeen); + return 1; + } + return 0; +} + +int GameScript::SpellCastOnMe(Scriptable* Sender, Trigger* parameters) +{ + if(parameters->int0Parameter) { + if ((ieDword) parameters->int0Parameter!=Sender->LastSpellOnMe) { + return 0; + } + } + if(MatchActor(Sender, Sender->LastCasterOnMe, parameters->objectParameter)) { + Sender->AddTrigger(&Sender->LastCasterOnMe); + return 1; + } + return 0; +} + +int GameScript::CalendarDay(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day == parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CalendarDayGT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day > parameters->int0Parameter) { + return 1; + } + return 0; +} + +int GameScript::CalendarDayLT(Scriptable* /*Sender*/, Trigger* parameters) +{ + int day = core->GetCalendar()->GetCalendarDay(core->GetGame()->GameTime/AI_UPDATE_TIME/7200); + if(day < parameters->int0Parameter) { + return 1; + } + return 0; +} + +//NT Returns true only if the active CRE was turned by the specified priest or paladin. +int GameScript::TurnedBy(Scriptable* Sender, Trigger* /*parameters*/) +{ + if (Sender->Type!=ST_ACTOR) { + return 0; + } + Actor* actor = ( Actor* ) Sender; + if (MatchActor(Sender, actor->LastTurner, NULL)) { + Sender->AddTrigger(&actor->LastTurner); + return 1; + } + return 0; +} diff --git a/project/jni/application/gemrb/src/core/GlobalTimer.cpp b/project/jni/application/gemrb/src/core/GlobalTimer.cpp new file mode 100644 index 000000000..1f71723d2 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GlobalTimer.cpp @@ -0,0 +1,339 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003-2005 The GemRB Project + * + * 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 "GlobalTimer.h" + +#include "ControlAnimation.h" +#include "Game.h" +#include "Interface.h" +#include "Video.h" +#include "GUI/GameControl.h" + +GlobalTimer::GlobalTimer(void) +{ + //AI_UPDATE_TIME: how many AI updates in a second + interval = ( 1000 / AI_UPDATE_TIME ); + Init(); +} + +GlobalTimer::~GlobalTimer(void) +{ + std::vector::iterator i; + for(i = animations.begin(); i != animations.end(); ++i) { + delete (*i); + } +} + +void GlobalTimer::Init() +{ + fadeToCounter = 0; + fadeFromCounter = 0; + fadeFromMax = 0; + fadeToMax = 0; + waitCounter = 0; + shakeCounter = 0; + startTime = 0; //forcing an update + speed = 0; + ClearAnimations(); +} + +void GlobalTimer::Freeze() +{ + unsigned long thisTime; + unsigned long advance; + + GetTime( thisTime ); + advance = thisTime - startTime; + if ( advance < interval) { + return; + } + startTime = thisTime; + Game* game = core->GetGame(); + if (!game) { + return; + } + game->RealTime+=advance; + + ieDword count = advance/interval; + // pst/bg2 do this, if you fix it for another game, wrap it in a check + DoFadeStep(count); + + // show scrolling cursor while paused + GameControl* gc = core->GetGameControl(); + if (gc) + gc->UpdateScrolling(); +} + +bool GlobalTimer::ViewportIsMoving() +{ + return (goal.x!=currentVP.x) || (goal.y!=currentVP.y); +} + +void GlobalTimer::SetMoveViewPort(ieDword x, ieDword y, int spd, bool center) +{ + speed=spd; + currentVP=core->GetVideoDriver()->GetViewport(); + if (center) { + x-=currentVP.w/2; + y-=currentVP.h/2; + } + goal.x=(short) x; + goal.y=(short) y; +} + +void GlobalTimer::DoStep(int count) +{ + Video *video = core->GetVideoDriver(); + + int x = currentVP.x; + int y = currentVP.y; + if ( (x != goal.x) || (y != goal.y)) { + if (speed) { + if (xgoal.x) x=goal.x; + } else { + x-=speed; + if (xgoal.y) y=goal.y; + } else { + y-=speed; + if (y>1); + y += (rand()%shakeY) - (shakeY>>1); + } + } + video->MoveViewportTo(x,y); +} + +void GlobalTimer::Update() +{ + Map *map; + Game *game; + GameControl* gc; + unsigned long thisTime; + unsigned long advance; + + gc = core->GetGameControl(); + if (gc) + gc->UpdateScrolling(); + + UpdateAnimations(); + + GetTime( thisTime ); + + if (!startTime) { + startTime = thisTime; + return; + } + + advance = thisTime - startTime; + if ( advance < interval) { + return; + } + ieDword count = advance/interval; + DoStep(count); + DoFadeStep(count); + if (!gc) { + goto end; + } + game = core->GetGame(); + if (!game) { + goto end; + } + map = game->GetCurrentArea(); + if (!map) { + goto end; + } + //do spell effects expire in dialogs? + //if yes, then we should remove this condition + if (!(gc->GetDialogueFlags()&DF_IN_DIALOG) ) { + map->UpdateFog(); + map->UpdateEffects(); + if (thisTime) { + //this measures in-world time (affected by effects, actions, etc) + game->AdvanceTime(count); + } + } + //this measures time spent in the game (including pauses) + if (thisTime) { + game->RealTime+=advance; + } +end: + startTime = thisTime; +} + + +void GlobalTimer::DoFadeStep(ieDword count) { + Video *video = core->GetVideoDriver(); + if (fadeToCounter) { + fadeToCounter-=count; + if (fadeToCounter<0) { + fadeToCounter=0; + } + video->SetFadePercent( ( ( fadeToMax - fadeToCounter ) * 100 ) / fadeToMax ); + //bug/patch #1837747 made this unneeded + //goto end; //hmm, freeze gametime? + } + //i think this 'else' is needed now because of the 'goto' cut above + else if (fadeFromCounter!=fadeFromMax) { + if (fadeFromCounter>fadeFromMax) { + fadeFromCounter-=count; + if (fadeFromCounterfadeFromMax) { + fadeToCounter=fadeFromMax; + } + video->SetFadePercent( ( ( fadeFromMax - fadeFromCounter ) * 100 ) / fadeFromMax ); + //bug/patch #1837747 made this unneeded + //goto end; //freeze gametime? + } + } + if (fadeFromCounter==fadeFromMax) { + video->SetFadePercent( 0 ); + } +} + +void GlobalTimer::SetFadeToColor(unsigned long Count) +{ + if(!Count) { + Count = 64; + } + fadeToCounter = Count; + fadeToMax = fadeToCounter; + //stay black for a while + fadeFromCounter = 128; + fadeFromMax = 0; +} + +void GlobalTimer::SetFadeFromColor(unsigned long Count) +{ + if(!Count) { + Count = 64; + } + fadeFromCounter = 0; + fadeFromMax = Count; +} + +void GlobalTimer::SetWait(unsigned long Count) +{ + waitCounter = Count; +} + +void GlobalTimer::AddAnimation(ControlAnimation* ctlanim, unsigned long time) +{ + AnimationRef* anim; + unsigned long thisTime; + + GetTime( thisTime ); + time += thisTime; + + // if there are no free animation reference objects, + // alloc one, else take the first free one + if (first_animation == 0) + anim = new AnimationRef; + else { + anim = animations.front (); + animations.erase (animations.begin()); + first_animation--; + } + + // fill in data + anim->time = time; + anim->ctlanim = ctlanim; + + // and insert it into list of other anim refs, sorted by time + for (std::vector::iterator it = animations.begin() + first_animation; it != animations.end (); it++) { + if ((*it)->time > time) { + animations.insert( it, anim ); + anim = NULL; + break; + } + } + if (anim) + animations.push_back( anim ); +} + +void GlobalTimer::RemoveAnimation(ControlAnimation* ctlanim) +{ + // Animation refs for given control are not physically removed, + // but just marked by erasing ptr to the control. They will be + // collected when they get to the front of the vector + for (std::vector::iterator it = animations.begin() + first_animation; it != animations.end (); it++) { + if ((*it)->ctlanim == ctlanim) { + (*it)->ctlanim = NULL; + } + } +} + +void GlobalTimer::UpdateAnimations() +{ + unsigned long thisTime; + GetTime( thisTime ); + while (animations.begin() + first_animation != animations.end()) { + AnimationRef* anim = animations[first_animation]; + if (anim->ctlanim == NULL) { + first_animation++; + continue; + } + + if (anim->time <= thisTime) { + anim->ctlanim->UpdateAnimation(); + first_animation++; + continue; + } + break; + } +} + +void GlobalTimer::ClearAnimations() +{ + first_animation = (unsigned int) animations.size(); +} + +void GlobalTimer::SetScreenShake(unsigned long shakeX, unsigned long shakeY, + unsigned long Count) +{ + this->shakeX = shakeX; + this->shakeY = shakeY; + shakeCounter = Count+1; +} diff --git a/project/jni/application/gemrb/src/core/GlobalTimer.h b/project/jni/application/gemrb/src/core/GlobalTimer.h new file mode 100644 index 000000000..ebf955c82 --- /dev/null +++ b/project/jni/application/gemrb/src/core/GlobalTimer.h @@ -0,0 +1,78 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 GLOBALTIMER_H +#define GLOBALTIMER_H + +#include "exports.h" +#include "win32def.h" + +#include "Region.h" + +#include + +class ControlAnimation; + +struct AnimationRef +{ + ControlAnimation *ctlanim; + unsigned long time; +}; + + +class GEM_EXPORT GlobalTimer { +private: + unsigned long startTime; + unsigned long interval; + + int fadeToCounter, fadeToMax; + int fadeFromCounter, fadeFromMax; + unsigned long waitCounter; + int shakeCounter; + unsigned long shakeX, shakeY; + unsigned int first_animation; + std::vector animations; + //move viewport to this coordinate + Point goal; + int speed; + Region currentVP; + + void DoFadeStep(ieDword count); +public: + GlobalTimer(void); + ~GlobalTimer(void); +public: + void Init(); + void Freeze(); + void Update(); + bool ViewportIsMoving(); + void DoStep(int count); + void SetMoveViewPort(ieDword x, ieDword y, int spd, bool center); + void SetFadeToColor(unsigned long Count); + void SetFadeFromColor(unsigned long Count); + void SetWait(unsigned long Count); + void SetScreenShake(unsigned long shakeX, unsigned long shakeY, + unsigned long Count); + void AddAnimation(ControlAnimation* ctlanim, unsigned long time); + void RemoveAnimation(ControlAnimation* ctlanim); + void ClearAnimations(); + void UpdateAnimations(); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Holder.h b/project/jni/application/gemrb/src/core/Holder.h new file mode 100644 index 000000000..170a4c87e --- /dev/null +++ b/project/jni/application/gemrb/src/core/Holder.h @@ -0,0 +1,95 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 HOLDER_H +#define HOLDER_H + +#include +#include + +template +class Held { +public: + Held() : RefCount(0) {} + void acquire() { ++RefCount; } + void release() { assert(RefCount && "Broken Held usage."); + if (!--RefCount) delete static_cast(this); } + size_t GetRefCount() { return RefCount; } +private: + size_t RefCount; +}; + +/** + * @class Holder + * Intrusive smart pointer. + * + * The class T must have member function acquire and release, such that + * acquire increases the refcount, and release decreses the refcount and + * frees the object if needed. + * + * Derived class of Holder shouldn't add member variables. That way, + * they can freely converted to Holder without slicing. + */ + +template +class Holder { +public: + Holder(T* ptr = NULL) + : ptr(ptr) + { + if (ptr) + ptr->acquire(); + } + ~Holder() + { + if (ptr) + ptr->release(); + } + Holder(const Holder& rhs) + : ptr(rhs.ptr) + { + if (ptr) + ptr->acquire(); + } + Holder& operator=(const Holder& rhs) + { + if (rhs.ptr) + rhs.ptr->acquire(); + if (ptr) + ptr->release(); + ptr = rhs.ptr; + return *this; + } + T& operator*() const { return *ptr; } + T* operator->() const { return ptr; } + bool operator!() const { return !ptr; } +#include "operatorbool.h" + OPERATOR_BOOL(Holder,T,ptr) + T* get() const { return ptr; } + void release() { + if (ptr) + ptr->release(); + ptr = NULL; + } +protected: + T *ptr; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/Image.cpp b/project/jni/application/gemrb/src/core/Image.cpp new file mode 100644 index 000000000..8b03e095b --- /dev/null +++ b/project/jni/application/gemrb/src/core/Image.cpp @@ -0,0 +1,47 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 "Image.h" + +#include "Interface.h" +#include "Video.h" + +Image::Image(unsigned int w, unsigned int h) + : height(h), width(w), data(new Color[height*width]) +{ +} + +Image::~Image() +{ + delete[] data; +} + +Sprite2D* Image::GetSprite2D() +{ + union { + Color color; + ieDword Mask; + } r = {{ 0xFF, 0x00, 0x00, 0x00 }}, + g = {{ 0x00, 0xFF, 0x00, 0x00 }}, + b = {{ 0x00, 0x00, 0xFF, 0x00 }}, + a = {{ 0x00, 0x00, 0x00, 0xFF }}; + void *pixels = malloc(sizeof(Color) * height*width); + memcpy(pixels, data, sizeof(Color)*height*width); + return core->GetVideoDriver()->CreateSprite(width, height, 32, + r.Mask, g.Mask, b.Mask, a.Mask, pixels); +} diff --git a/project/jni/application/gemrb/src/core/Image.h b/project/jni/application/gemrb/src/core/Image.h new file mode 100644 index 000000000..9628d6561 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Image.h @@ -0,0 +1,61 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 IMAGE_H +#define IMAGE_H + +#include "RGBAColor.h" +#include "exports.h" + +class Sprite2D; + +class GEM_EXPORT Image { +public: + Image(unsigned int height, unsigned int width); + ~Image(); + Color GetPixel(unsigned int x, unsigned int y) const + { + if (x >= width || y >= height) { + static const Color black = { 0, 0, 0, 0 }; + return black; + } + return data[width*y+x]; + + } + void SetPixel(unsigned int x, unsigned int y, Color idx) + { + if (x >= width || y >= height) + return; + data[width*y+x] = idx; + + } + unsigned int GetHeight() const + { + return height; + } + unsigned int GetWidth() const + { + return width; + } + Sprite2D *GetSprite2D(); +private: + unsigned int height, width; + Color *data; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageFactory.cpp b/project/jni/application/gemrb/src/core/ImageFactory.cpp new file mode 100644 index 000000000..17ad0d0c6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageFactory.cpp @@ -0,0 +1,42 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 "ImageFactory.h" + +#include "Interface.h" +#include "Video.h" + +ImageFactory::ImageFactory(const char* ResRef, Sprite2D* bitmap_) + : FactoryObject( ResRef, IE_BMP_CLASS_ID ), bitmap(bitmap_) +{ + +} + +ImageFactory::~ImageFactory(void) +{ + core->GetVideoDriver()->FreeSprite( bitmap ); +} + +Sprite2D* ImageFactory::GetSprite2D() const +{ + bitmap->acquire(); + return bitmap; +} + diff --git a/project/jni/application/gemrb/src/core/ImageFactory.h b/project/jni/application/gemrb/src/core/ImageFactory.h new file mode 100644 index 000000000..c0576d99f --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageFactory.h @@ -0,0 +1,40 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 IMAGEFACTORY_H +#define IMAGEFACTORY_H + +#include "exports.h" +#include "globals.h" + +#include "FactoryObject.h" +#include "Sprite2D.h" + +class GEM_EXPORT ImageFactory : public FactoryObject { +private: + Sprite2D* bitmap; +public: + ImageFactory(const char* ResRef, Sprite2D* bitmap); + ~ImageFactory(void); + + Sprite2D* GetSprite2D() const; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageMgr.cpp b/project/jni/application/gemrb/src/core/ImageMgr.cpp new file mode 100644 index 000000000..c38c4866f --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageMgr.cpp @@ -0,0 +1,92 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 "ImageMgr.h" + +#include "win32def.h" + +#include "ImageFactory.h" +#include "Interface.h" +#include "Video.h" + +const TypeID ImageMgr::ID = { "ImageMgr" }; + +ImageMgr::ImageMgr(void) +{ +} + +ImageMgr::~ImageMgr(void) +{ +} + +Bitmap* ImageMgr::GetBitmap() +{ + unsigned int height = GetHeight(); + unsigned int width = GetWidth(); + Bitmap *data = new Bitmap(width, height); + + printMessage("ImageMgr", "Don't know how to handle 24bit bitmap from ", WHITE); + printf( "%s...", str->filename ); + printStatus( "ERROR", LIGHT_RED ); + + Sprite2D *spr = GetSprite2D(); + + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + data->SetAt(x,y, spr->GetPixel(x,y).r); + } + } + + core->GetVideoDriver()->FreeSprite(spr); + + return data; +} + +Image* ImageMgr::GetImage() +{ + unsigned int height = GetHeight(); + unsigned int width = GetWidth(); + Image *data = new Image(width, height); + + Sprite2D *spr = GetSprite2D(); + + for (unsigned int y = 0; y < height; y++) { + for (unsigned int x = 0; x < width; x++) { + data->SetPixel(x,y, spr->GetPixel(x,y)); + } + } + + core->GetVideoDriver()->FreeSprite(spr); + + return data; +} + +void ImageMgr::GetPalette(int /*colors*/, Color* /*pal*/) +{ + printMessage("ImageMgr", "Can't get non-existant palette from ", WHITE); + printf("%s... ", str->filename); + printStatus("ERROR", LIGHT_RED); +} + +ImageFactory* ImageMgr::GetImageFactory(const char* ResRef) +{ + ImageFactory* fact = new ImageFactory( ResRef, GetSprite2D() ); + return fact; +} diff --git a/project/jni/application/gemrb/src/core/ImageMgr.h b/project/jni/application/gemrb/src/core/ImageMgr.h new file mode 100644 index 000000000..40d4709eb --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageMgr.h @@ -0,0 +1,68 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 IMAGEMGR_H +#define IMAGEMGR_H + +#include "exports.h" + +#include "Bitmap.h" +#include "Image.h" +#include "Resource.h" +#include "Sprite2D.h" +#include "System/DataStream.h" + +class ImageFactory; + +/** + * Base class for Image plugins. + */ +class GEM_EXPORT ImageMgr : public Resource { +public: + static const TypeID ID; +public: + ImageMgr(void); + virtual ~ImageMgr(void); + /** Returns a \ref Sprite2D containing the image. */ + virtual Sprite2D* GetSprite2D() = 0; + virtual Image* GetImage(); + virtual Bitmap* GetBitmap(); + /** + * Returns image palette. + * + * @param[in] colors Number of colors to return. + * @param[out] pal Array to fill with colors. + * + * This does nothing if there is no palette. + */ + virtual void GetPalette(int colors, Color* pal); + /** Returns the width of the image */ + virtual int GetWidth() = 0; + /** Returns the height of the image */ + virtual int GetHeight() = 0; + /** + * Returns a \ref ImageFactory for the current image. + * + * @param[in] ResRef name of image represented by factory. + */ + ImageFactory* GetImageFactory(const char* ResRef); +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/ImageWriter.cpp b/project/jni/application/gemrb/src/core/ImageWriter.cpp new file mode 100644 index 000000000..4c2eae78b --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageWriter.cpp @@ -0,0 +1,27 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 "ImageWriter.h" + +ImageWriter::ImageWriter(void) +{ +} + +ImageWriter::~ImageWriter(void) +{ +} diff --git a/project/jni/application/gemrb/src/core/ImageWriter.h b/project/jni/application/gemrb/src/core/ImageWriter.h new file mode 100644 index 000000000..a02a44b7b --- /dev/null +++ b/project/jni/application/gemrb/src/core/ImageWriter.h @@ -0,0 +1,35 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 IMAGEWRITER_H +#define IMAGEWRITER_H + +#include "Plugin.h" +#include "Sprite2D.h" +#include "System/DataStream.h" + +class GEM_EXPORT ImageWriter : public Plugin { +public: + ImageWriter(void); + ~ImageWriter(void); + + /** Writes an Sprite2D to a stream and frees the sprite. */ + virtual void PutImage(DataStream *output, Sprite2D *sprite) = 0; +}; + +#endif diff --git a/project/jni/application/gemrb/src/core/IniSpawn.cpp b/project/jni/application/gemrb/src/core/IniSpawn.cpp new file mode 100644 index 000000000..b7c1ee4d6 --- /dev/null +++ b/project/jni/application/gemrb/src/core/IniSpawn.cpp @@ -0,0 +1,716 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2007 The GemRB Project + * + * 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 class handles the special spawn structures of planescape torment +// (stored in .ini format) + +#include "IniSpawn.h" + +#include "win32def.h" + +#include "Game.h" +#include "GameData.h" +#include "Interface.h" +#include "Map.h" +#include "GameScript/GSUtils.h" +#include "GameScript/Matching.h" +#include "Scriptable/Actor.h" + +static const int StatValues[9]={ +IE_EA, IE_FACTION, IE_TEAM, IE_GENERAL, IE_RACE, IE_CLASS, IE_SPECIFIC, +IE_SEX, IE_ALIGNMENT }; + +IniSpawn::IniSpawn(Map *owner) +{ + map = owner; + NamelessSpawnArea[0] = 0; + NamelessState = 35; + NamelessVar = NULL; + namelessvarcount = 0; + Locals = NULL; + localscount = 0; + eventspawns = NULL; + eventcount = 0; + last_spawndate = 0; +} + +IniSpawn::~IniSpawn() +{ + if (eventspawns) { + delete[] eventspawns; + } +} + +Holder GetIniFile(const ieResRef DefaultArea) +{ + //the lack of spawn ini files is not a serious problem, happens all the time + if (!gamedata->Exists( DefaultArea, IE_INI_CLASS_ID)) { + return NULL; + } + + DataStream* inifile = gamedata->GetResource( DefaultArea, IE_INI_CLASS_ID ); + if (!inifile) { + return NULL; + } + if (!core->IsAvailable( IE_INI_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printMessage( "IniSpawn","No INI Importer Available.\n",LIGHT_RED ); + return NULL; + } + + PluginHolder ini(IE_INI_CLASS_ID); + ini->Open(inifile, true ); //autofree + return ini; +} + +/*** initializations ***/ + +inline int CountElements(const char *s, char separator) +{ + int ret = 1; + while(*s) { + if (*s==separator) ret++; + s++; + } + return ret; +} + +inline void GetElements(const char *s, ieResRef *storage, int count) +{ + while(count--) { + ieResRef *field = storage+count; + strnuprcpy(*field, s, sizeof(ieResRef)-1); + for(size_t i=0;iGetKeyAsString(crittername,"spec_var",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + strnuprcpy(critter.SpecContext, s, 6); + strnlwrcpy(critter.SpecVar, s+8, 32); + } else { + strnuprcpy(critter.SpecContext, "GLOBAL", 6); + strnlwrcpy(critter.SpecVar, s, 32); + } + } + + //add this to specvar at each spawn + ps = inifile->GetKeyAsInt(crittername,"spec_var_inc", 0); + critter.SpecVarInc=ps; + + //use this value with spec_var_operation to determine spawn + ps = inifile->GetKeyAsInt(crittername,"spec_var_value",0); + critter.SpecVarValue=ps; + //this operation uses DiffCore + s = inifile->GetKeyAsString(crittername,"spec_var_operation",""); + critter.SpecVarOperator=GetDiffMode(s); + //the amount of critters to spawn + critter.TotalQuantity = inifile->GetKeyAsInt(crittername,"spec_qty",1); + critter.SpawnCount = inifile->GetKeyAsInt(crittername,"create_qty",critter.TotalQuantity); + + //the creature resource(s) + s = inifile->GetKeyAsString(crittername,"cre_file",NULL); + if (s) { + critter.creaturecount = CountElements(s,','); + critter.CreFile=new ieResRef[critter.creaturecount]; + GetElements(s, critter.CreFile, critter.creaturecount); + } else { + printMessage( "IniSpawn"," ", LIGHT_RED); + printf("Invalid spawn entry: %s\n", crittername); + } + + s = inifile->GetKeyAsString(crittername,"point_select",NULL); + + if (s) { + ps=s[0]; + } else { + ps=0; + } + + s = inifile->GetKeyAsString(crittername,"spawn_point",NULL); + if (s) { + //expect more than one spawnpoint + if (ps=='r') { + //select one of the spawnpoints randomly + int count = core->Roll(1,CountElements(s,']'),-1); + //go to the selected spawnpoint + while(count--) { + while(*s++!=']') ; + } + } + //parse the selected spawnpoint + int x,y,o; + if (sscanf(s,"[%d.%d:%d]", &x, &y, &o)==3) { + critter.SpawnPoint.x=(short) x; + critter.SpawnPoint.y=(short) y; + critter.Orientation=o; + } else { + if (sscanf(s,"[%d.%d]", &x, &y)==2) { + critter.SpawnPoint.x=(short) x; + critter.SpawnPoint.y=(short) y; + critter.Orientation=core->Roll(1,16,-1); + } + } + } + + //store or retrieve spawn point + s = inifile->GetKeyAsString(crittername,"spawn_point_global", NULL); + if (s) { + switch (ps) { + case 'e': + critter.SpawnPoint.fromDword(CheckVariable(map, s+8,s)); + break; + default: + //see save_selected_point + //SetVariable(map, s+8, s, critter.SpawnPoint.asDword()); + break; + } + } + + //take facing from variable + s = inifile->GetKeyAsString(crittername,"spawn_facing_global", NULL); + if (s) { + switch (ps) { + case 'e': + critter.Orientation=(int) CheckVariable(map, s+8,s); + break; + default: + //see save_selected_point + //SetVariable(map, s+8, s, (ieDword) critter.Orientation); + break; + } + } + + s = inifile->GetKeyAsString(crittername,"save_selected_point",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + SetVariable(map, s+8, s, critter.SpawnPoint.asDword()); + } else { + SetVariable(map, s, "GLOBAL", critter.SpawnPoint.asDword()); + } + } + s = inifile->GetKeyAsString(crittername,"save_selected_facing",NULL); + if (s) { + if ((strlen(s)>9) && s[6]==':' && s[7]==':') { + SetVariable(map, s+8, s, (ieDword) critter.Orientation); + } else { + SetVariable(map, s, "GLOBAL", (ieDword) critter.Orientation); + } + } + + //sometimes only the orientation is given, the point is stored in a variable + ps = inifile->GetKeyAsInt(crittername,"facing",-1); + if (ps!=-1) critter.Orientation = ps; + ps = inifile->GetKeyAsInt(crittername, "ai_ea",-1); + if (ps!=-1) critter.SetSpec[AI_EA] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_team",-1); + if (ps!=-1) critter.SetSpec[AI_TEAM] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_general",-1); + if (ps!=-1) critter.SetSpec[AI_GENERAL] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_race",-1); + if (ps!=-1) critter.SetSpec[AI_RACE] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_class",-1); + if (ps!=-1) critter.SetSpec[AI_CLASS] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_specifics",-1); + if (ps!=-1) critter.SetSpec[AI_SPECIFICS] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_gender",-1); + if (ps!=-1) critter.SetSpec[AI_GENDER] = (ieByte) ps; + ps = inifile->GetKeyAsInt(crittername, "ai_alignment",-1); + if (ps!=-1) critter.SetSpec[AI_ALIGNMENT] = (ieByte) ps; + + s = inifile->GetKeyAsString(crittername,"spec",NULL); + if (s) { + int x[9]; + + ps = sscanf(s,"[%d.%d.%d.%d.%d.%d.%d.%d.%d]", x, x+1, x+2, x+3, x+4, x+5, + x+6, x+7, x+8); + if (ps == 0) { + strnuprcpy(critter.ScriptName, s, 32); + critter.Flags|=CF_CHECK_NAME; + memset(critter.Spec,-1,sizeof(critter.Spec)); + } else { + while(ps--) { + critter.Spec[ps]=(ieByte) x[ps]; + } + } + } + + s = inifile->GetKeyAsString(crittername,"script_name",NULL); + if (s) { + strnuprcpy(critter.ScriptName, s, 32); + } + + //iwd2 script names (override remains the same) + //special 1 == area + s = inifile->GetKeyAsString(crittername,"script_special_1",NULL); + if (s) { + strnuprcpy(critter.AreaScript,s, 8); + } + //special 2 == class + s = inifile->GetKeyAsString(crittername,"script_special_2",NULL); + if (s) { + strnuprcpy(critter.ClassScript,s, 8); + } + //special 3 == general + s = inifile->GetKeyAsString(crittername,"script_special_3",NULL); + if (s) { + strnuprcpy(critter.GeneralScript,s, 8); + } + //team == specific + s = inifile->GetKeyAsString(crittername,"script_team",NULL); + if (s) { + strnuprcpy(critter.SpecificScript,s, 8); + } + + //combat == race + s = inifile->GetKeyAsString(crittername,"script_combat",NULL); + if (s) { + strnuprcpy(critter.RaceScript,s, 8); + } + //movement == default + s = inifile->GetKeyAsString(crittername,"script_movement",NULL); + if (s) { + strnuprcpy(critter.DefaultScript,s, 8); + } + + //pst script names + s = inifile->GetKeyAsString(crittername,"script_override",NULL); + if (s) { + strnuprcpy(critter.OverrideScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_class",NULL); + if (s) { + strnuprcpy(critter.ClassScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_race",NULL); + if (s) { + strnuprcpy(critter.RaceScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_general",NULL); + if (s) { + strnuprcpy(critter.GeneralScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_default",NULL); + if (s) { + strnuprcpy(critter.DefaultScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_area",NULL); + if (s) { + strnuprcpy(critter.AreaScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"script_specifics",NULL); + if (s) { + strnuprcpy(critter.SpecificScript,s, 8); + } + s = inifile->GetKeyAsString(crittername,"dialog",NULL); + if (s) { + strnuprcpy(critter.Dialog,s, 8); + } + + //flags + if (inifile->GetKeyAsBool(crittername,"death_scriptname",false)) { + critter.Flags|=CF_DEATHVAR; + } + //don't spawn when spawnpoint is visible + if (inifile->GetKeyAsBool(crittername,"ignore_can_see",false)) { + critter.Flags|=CF_IGNORECANSEE; + } + //unsure, but could be similar to previous + if (inifile->GetKeyAsBool(crittername,"check_view_port", false)) { + critter.Flags|=CF_CHECKVIEWPORT; + } + //unknown, this is used only in pst + if (inifile->GetKeyAsBool(crittername,"check_crowd", false)) { + critter.Flags|=CF_CHECKCROWD; + } + //unknown, this is used only in pst + if (inifile->GetKeyAsBool(crittername,"find_safest_point", false)) { + critter.Flags|=CF_SAFESTPOINT; + } + //disable spawn based on game difficulty + if (inifile->GetKeyAsBool(crittername,"area_diff_1", false)) { + critter.Flags|=CF_NO_DIFF_1; + } + if (inifile->GetKeyAsBool(crittername,"area_diff_2", false)) { + critter.Flags|=CF_NO_DIFF_2; + } + if (inifile->GetKeyAsBool(crittername,"area_diff_3", false)) { + critter.Flags|=CF_NO_DIFF_3; + } +} + +void IniSpawn::ReadSpawnEntry(DataFileMgr *inifile, const char *entryname, SpawnEntry &entry) +{ + const char *s; + + entry.interval = (unsigned int) inifile->GetKeyAsInt(entryname,"interval",0); + //don't default to NULL here, some entries may be missing in original game + //an empty default string here will create an empty but consistent entry + s = inifile->GetKeyAsString(entryname,"critters",""); + int crittercount = CountElements(s,','); + entry.crittercount=crittercount; + entry.critters=new CritterEntry[crittercount]; + ieVariable *critters = new ieVariable[crittercount]; + GetElements(s, critters, crittercount); + while(crittercount--) { + ReadCreature(inifile, critters[crittercount], entry.critters[crittercount]); + } + delete[] critters; +} + +/* set by action */ +void IniSpawn::SetNamelessDeath(const ieResRef area, Point &pos, ieDword state) +{ + strnuprcpy(NamelessSpawnArea, area, 8); + NamelessSpawnPoint = pos; + NamelessState = state; +} + +void IniSpawn::InitSpawn(const ieResRef DefaultArea) +{ + const char *s; + + Holder inifile = GetIniFile(DefaultArea); + if (!inifile) { + strnuprcpy(NamelessSpawnArea, DefaultArea, 8); + return; + } + + s = inifile->GetKeyAsString("nameless","destare",DefaultArea); + strnuprcpy(NamelessSpawnArea, s, 8); + s = inifile->GetKeyAsString("nameless","point","[0.0]"); + int x,y; + if (sscanf(s,"[%d.%d]", &x, &y)!=2) { + x=0; + y=0; + } + NamelessSpawnPoint.x=x; + NamelessSpawnPoint.y=y; + //35 - already standing + //36 - getting up + NamelessState = inifile->GetKeyAsInt("nameless","state",36); + + namelessvarcount = inifile->GetKeysCount("namelessvar"); + if (namelessvarcount) { + NamelessVar = new VariableSpec[namelessvarcount]; + for (y=0;yGetKeyNameByIndex("namelessvar",y); + strnlwrcpy(NamelessVar[y].Name, Key, 32); + NamelessVar[y].Value = inifile->GetKeyAsInt("namelessvar",Key,0); + } + } + + localscount = inifile->GetKeysCount("locals"); + if (localscount) { + Locals = new VariableSpec[localscount]; + for (y=0;yGetKeyNameByIndex("locals",y); + strnlwrcpy(Locals[y].Name, Key, 32); + Locals[y].Value = inifile->GetKeyAsInt("locals",Key,0); + } + } + + s = inifile->GetKeyAsString("spawn_main","enter",NULL); + if (s) { + ReadSpawnEntry(inifile.get(), s, enterspawn); + } + s = inifile->GetKeyAsString("spawn_main","events",NULL); + if (s) { + eventcount = CountElements(s,','); + eventspawns = new SpawnEntry[eventcount]; + ieVariable *events = new ieVariable[eventcount]; + GetElements(s, events, eventcount); + int ec = eventcount; + while(ec--) { + ReadSpawnEntry(inifile.get(), events[ec], eventspawns[ec]); + } + delete[] events; + } + //maybe not correct + InitialSpawn(); +} + + +/*** events ***/ + +//respawn nameless after he bit the dust +void IniSpawn::RespawnNameless() +{ + Game *game = core->GetGame(); + Actor *nameless = game->GetPC(0, false); + + if (NamelessSpawnPoint.isnull()) { + core->GetGame()->JoinParty(nameless,JP_INITPOS); + NamelessSpawnPoint=nameless->Pos; + strnuprcpy(NamelessSpawnArea, nameless->Area, 8); + } + + nameless->Resurrect(); + //hardcoded!!! + if (NamelessState==36) { + nameless->SetStance(IE_ANI_PST_START); + } + int i; + + for (i=0;iGetPartySize(false);i++) { + MoveBetweenAreasCore(game->GetPC(i, false),NamelessSpawnArea,NamelessSpawnPoint,-1, true); + } + + //certain variables are set when nameless dies + for (i=0;i=0) { + // dunno if this should be negated + if (!DiffCore(specvar, critter.SpecVarValue, critter.SpecVarOperator) ) { + return; + } + } else { + //ar0203 in PST seems to want the check this way. + //if other areas conflict and you want to use (!specvar), + //please research further + //researched further - ar0203 respawns only if specvar is 1 + if (!specvar) { + return; + } + } + } + + if (!(critter.Flags&CF_IGNORECANSEE)) { + if (map->IsVisible(critter.SpawnPoint, false) ) { + return; + } + } + + if (critter.Flags&CF_NO_DIFF_MASK) { + ieDword difficulty; + ieDword diff_bit; + + core->GetDictionary()->Lookup("Difficulty Level", difficulty); + switch (difficulty) + { + case 0: + diff_bit = CF_NO_DIFF_1; + break; + case 1: + diff_bit = CF_NO_DIFF_2; + break; + case 2: + diff_bit = CF_NO_DIFF_3; + break; + default: + diff_bit = 0; + } + if (critter.Flags&diff_bit) { + return; + } + } + + if (critter.ScriptName[0] && (critter.Flags&CF_CHECK_NAME) ) { + //maybe this one needs to be using getobjectcount as well + //currently we cannot count objects with scriptname??? + if (map->GetActor( critter.ScriptName, 0 )) { + return; + } + } else { + //Object *object = new Object(); + Object object; + //objectfields based on spec + object.objectFields[0]=critter.Spec[0]; + object.objectFields[1]=critter.Spec[1]; + object.objectFields[2]=critter.Spec[2]; + object.objectFields[3]=critter.Spec[3]; + object.objectFields[4]=critter.Spec[4]; + object.objectFields[5]=critter.Spec[5]; + object.objectFields[6]=critter.Spec[6]; + object.objectFields[7]=critter.Spec[7]; + object.objectFields[8]=critter.Spec[8]; + int cnt = GetObjectCount(map, &object); + if (cnt>=critter.TotalQuantity) { + return; + } + } + + int x = core->Roll(1,critter.creaturecount,-1); + Actor* cre = gamedata->GetCreature(critter.CreFile[x]); + if (!cre) { + return; + } + + SetVariable(map, critter.SpecVar, critter.SpecContext, specvar+(ieDword) critter.SpecVarInc); + map->AddActor(cre); + for (x=0;x<9;x++) { + if (critter.SetSpec[x]) { + cre->SetBase(StatValues[x], critter.SetSpec[x]); + } + } + cre->SetPosition( critter.SpawnPoint, 0, 0);//maybe critters could be repositioned + cre->SetOrientation(critter.Orientation,false); + if (critter.ScriptName[0]) { + cre->SetScriptName(critter.ScriptName); + } + if (critter.OverrideScript[0]) { + cre->SetScript(critter.OverrideScript, SCR_OVERRIDE); + } + if (critter.ClassScript[0]) { + cre->SetScript(critter.ClassScript, SCR_CLASS); + } + if (critter.RaceScript[0]) { + cre->SetScript(critter.RaceScript, SCR_RACE); + } + if (critter.GeneralScript[0]) { + cre->SetScript(critter.GeneralScript, SCR_GENERAL); + } + if (critter.DefaultScript[0]) { + cre->SetScript(critter.DefaultScript, SCR_DEFAULT); + } + if (critter.AreaScript[0]) { + cre->SetScript(critter.AreaScript, SCR_AREA); + } + if (critter.SpecificScript[0]) { + cre->SetScript(critter.SpecificScript, SCR_SPECIFICS); + } + if (critter.Dialog[0]) { + cre->SetDialog(critter.Dialog); + } +} + +void IniSpawn::SpawnGroup(SpawnEntry &event) +{ + if (!event.critters) { + return; + } + unsigned int interval = event.interval; + if (interval) { + if(core->GetGame()->GameTime/interval<=last_spawndate/interval) { + return; + } + } + last_spawndate=core->GetGame()->GameTime; + + for(int i=0;iSpawnCount;j++) { + SpawnCreature(*critter); + } + } +} + +//execute the initial spawn +void IniSpawn::InitialSpawn() +{ + SpawnGroup(enterspawn); + //these variables are set when entering first + for (int i=0;i.ini files + * @author The GemRB Project + */ + +#ifndef INISPAWN_H +#define INISPAWN_H + +#include "exports.h" +#include "ie_types.h" + +#include "DataFileMgr.h" +#include "Region.h" + +class Map; + +/** + * @struct CritterEntry + */ + +//critter flags +#define CF_IGNORECANSEE 1 +#define CF_DEATHVAR 2 +#define CF_NO_DIFF_1 4 +#define CF_NO_DIFF_2 8 +#define CF_NO_DIFF_3 16 +#define CF_CHECKVIEWPORT 32 +#define CF_CHECKCROWD 64 +#define CF_SAFESTPOINT 128 +#define CF_NO_DIFF_MASK 28 +#define CF_CHECK_NAME 256 +//spec ids flags +#define AI_EA 0 +#define AI_FACTION 1 +#define AI_TEAM 2 +#define AI_GENERAL 3 +#define AI_RACE 4 +#define AI_CLASS 5 +#define AI_SPECIFICS 6 +#define AI_GENDER 7 +#define AI_ALIGNMENT 8 + +//spawn point could be: +// s - single +// r - random +// e - preset +// save_select_point saves the spawnpoint + +struct CritterEntry { + int creaturecount; + ieResRef *CreFile; //spawn one of these creatures + ieByte Spec[9]; //existance check IDS qualifier + ieByte SetSpec[9]; //set IDS qualifier + ieVariable ScriptName; //existance check scripting name + ieVariable SpecVar; //condition variable + ieResRef SpecContext; //condition variable context + ieResRef OverrideScript; //override override script + ieResRef ClassScript; //overrride class script + ieResRef RaceScript; //override race script + ieResRef GeneralScript; //override general script + ieResRef DefaultScript; //override default script + ieResRef AreaScript; //override area script + ieResRef SpecificScript; //override specific script + ieResRef Dialog; //override dialog + ieVariable SpawnPointVar; //spawn point saved location + Point SpawnPoint; //spawn point + int SpecVarOperator; //operation performed on spec var + int SpecVarValue; //using this value with the operation + int SpecVarInc; //add this to spec var at each spawn + int Orientation; //spawn orientation + int Flags; //CF_IGNORENOSEE, CF_DEATHVAR, etc + int TotalQuantity; //total number + int SpawnCount; //create quantity +}; + +/** + * @class SpawnEntry + */ +class SpawnEntry { +public: + ieDword interval; + int crittercount; + CritterEntry *critters; + SpawnEntry() { + interval = 0; + crittercount = 0; + critters = NULL; + } + ~SpawnEntry() { + if (critters) { + for (int i=0;i +#endif + +#include "Interface.h" + +#include "exports.h" +#include "globals.h" +#include "strrefs.h" +#include "win32def.h" + +#include "ActorMgr.h" +#include "AmbientMgr.h" +#include "AnimationMgr.h" +#include "ArchiveImporter.h" +#include "Audio.h" +#include "Calendar.h" +#include "DataFileMgr.h" +#include "DialogHandler.h" +#include "DialogMgr.h" +#include "DisplayMessage.h" +#include "EffectMgr.h" +#include "EffectQueue.h" +#include "Factory.h" +#include "Game.h" +#include "GameData.h" +#include "ImageMgr.h" +#include "ItemMgr.h" +#include "MapMgr.h" +#include "MoviePlayer.h" +#include "MusicMgr.h" +#include "Palette.h" +#include "PluginMgr.h" +#include "PluginMgr.h" +#include "ProjectileServer.h" +#include "SaveGameIterator.h" +#include "SaveGameMgr.h" +#include "ScriptEngine.h" +#include "ScriptedAnimation.h" +#include "SoundMgr.h" +#include "SpellMgr.h" +#include "StoreMgr.h" +#include "StringMgr.h" +#include "TileMap.h" +#include "Video.h" +#include "WorldMapMgr.h" +#include "GUI/Button.h" +#include "GUI/Console.h" +#include "GUI/GameControl.h" +#include "GUI/Label.h" +#include "GUI/MapControl.h" +#include "GUI/WorldMapControl.h" +#include "System/FileStream.h" +#include "System/VFS.h" + +#if defined(__HAIKU__) +#include +#endif + +#include +#include +#include + +GEM_EXPORT Interface* core; + +#ifdef WIN32 +GEM_EXPORT HANDLE hConsole; +#endif + +//use DialogF.tlk if the protagonist is female, that's why we leave space +static const char dialogtlk[] = "dialog.tlk\0"; + +static int MaximumAbility = 25; +static ieWordSigned *strmod = NULL; +static ieWordSigned *strmodex = NULL; +static ieWordSigned *intmod = NULL; +static ieWordSigned *dexmod = NULL; +static ieWordSigned *conmod = NULL; +static ieWordSigned *chrmod = NULL; +static ieWordSigned *lorebon = NULL; +static ieWordSigned *wisbon = NULL; +static int **reputationmod = NULL; +static ieVariable IWD2DeathVarFormat = "_DEAD%s"; +static ieVariable DeathVarFormat = "SPRITE_IS_DEAD%s"; + +Interface::Interface(int iargc, char* iargv[]) +{ + argc = iargc; + argv = iargv; +#ifdef WIN32 + hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); +#endif + textcolor( LIGHT_WHITE ); + printf( "GemRB Core Version v%s Loading...\n", VERSION_GEMRB ); + + // default to the correct endianswitch + ieWord endiantest = 1; + if (((char *)&endiantest)[1] == 1) { + // big-endian + DataStream::SetEndianSwitch(true); + } + + unsigned int i; + for(i=0;i<256;i++) { + pl_uppercase[i]=(ieByte) toupper(i); + pl_lowercase[i]=(ieByte) tolower(i); + } + + projserv = NULL; + VideoDriverName = "sdl"; + AudioDriverName = "openal"; + vars = NULL; + tokens = NULL; + lists = NULL; + RtRows = NULL; + sgiterator = NULL; + game = NULL; + calendar = NULL; + worldmap = NULL; + CurrentStore = NULL; + CurrentContainer = NULL; + UseContainer = false; + InfoTextPalette = NULL; + timer = NULL; + displaymsg = NULL; + evntmgr = NULL; + console = NULL; + slottypes = NULL; + slotmatrix = NULL; + + ModalWindow = NULL; + tooltip_x = 0; + tooltip_y = 0; + tooltip_currtextw = 0; + tooltip_ctrl = NULL; + plugin_flags = NULL; + + pal16 = NULL; + pal32 = NULL; + pal256 = NULL; + + GUIEnhancements = 0; + + CursorCount = 0; + Cursors = NULL; + + mousescrollspd = 10; + + ConsolePopped = false; + CheatFlag = false; + FogOfWar = 1; + QuitFlag = QF_NORMAL; + EventFlag = EF_CONTROL; +#ifndef WIN32 + CaseSensitive = true; //this is the default value, so CD1/CD2 will be resolved +#else + CaseSensitive = false; +#endif + GameOnCD = false; + SkipIntroVideos = false; + DrawFPS = false; + KeepCache = false; + TooltipDelay = 100; + FullScreen = 0; + GUIScriptsPath[0] = 0; + GamePath[0] = 0; + SavePath[0] = 0; + GemRBPath[0] = 0; + PluginsPath[0] = 0; + CachePath[0] = 0; + GemRBOverridePath[0] = 0; + GameName[0] = 0; + + strncpy( GameOverridePath, "override", sizeof(GameOverridePath) ); + strncpy( GameSoundsPath, "sounds", sizeof(GameSoundsPath) ); + strncpy( GameScriptsPath, "scripts", sizeof(GameScriptsPath) ); + strncpy( GamePortraitsPath, "portraits", sizeof(GamePortraitsPath) ); + strncpy( GameCharactersPath, "characters", sizeof(GameCharactersPath) ); + strncpy( GameDataPath, "data", sizeof(GameDataPath) ); + strncpy( INIConfig, "baldur.ini", sizeof(INIConfig) ); + strncpy( ButtonFont, "STONESML", sizeof(ButtonFont) ); + strncpy( TooltipFont, "STONESML", sizeof(TooltipFont) ); + strncpy( MovieFont, "STONESML", sizeof(MovieFont) ); + strncpy( ScrollCursorBam, "CURSARW", sizeof(ScrollCursorBam) ); + strncpy( GlobalScript, "BALDUR", sizeof(GlobalScript) ); + strncpy( WorldMapName[0], "WORLDMAP", sizeof(ieResRef) ); + memset( WorldMapName[1], 0, sizeof(ieResRef) ); + strncpy( Palette16, "MPALETTE", sizeof(Palette16) ); + strncpy( Palette32, "PAL32", sizeof(Palette32) ); + strncpy( Palette256, "MPAL256", sizeof(Palette256) ); + strcpy( TooltipBackResRef, "\0" ); + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + strcpy( GroundCircleBam[size], "\0" ); + GroundCircleScale[size] = 0; + } + TooltipColor.r = 0; + TooltipColor.g = 255; + TooltipColor.b = 0; + TooltipColor.a = 255; + TooltipMargin = 10; + + TooltipBack = NULL; + DraggedItem = NULL; + DraggedPortrait = 0; + DefSound = NULL; + DSCount = -1; + memset(GameFeatures, 0, sizeof( GameFeatures )); + //GameFeatures = 0; + //GameFeatures2 = 0; + memset( WindowFrames, 0, sizeof( WindowFrames )); + memset( GroundCircles, 0, sizeof( GroundCircles )); + memset(FogSprites, 0, sizeof( FogSprites )); + AreaAliasTable = NULL; + ItemExclTable = NULL; + ItemDialTable = NULL; + ItemDial2Table = NULL; + ItemTooltipTable = NULL; + update_scripts = false; + + gamedata = new GameData(); +} + +#define FreeResourceVector(type, variable) \ +{ \ + size_t i=variable.size(); \ + while(i--) { \ + if (variable[i]) { \ + delete variable[i]; \ + } \ + } \ + variable.clear(); \ +} + +//2da lists are ieDword lists allocated by malloc +static void Release2daList(void *poi) +{ + free( (ieDword *) poi); +} + +static void ReleaseItemList(void *poi) +{ + delete ((ItemList *) poi); +} + +void FreeAbilityTables() +{ + if (strmod) { + free(strmod); + } + strmod = NULL; + if (strmodex) { + free(strmodex); + } + strmodex = NULL; + if (intmod) { + free(intmod); + } + intmod = NULL; + if (dexmod) { + free(dexmod); + } + dexmod = NULL; + if (conmod) { + free(conmod); + } + conmod = NULL; + if (chrmod) { + free(chrmod); + } + chrmod = NULL; + if (lorebon) { + free(lorebon); + } + lorebon = NULL; + if (wisbon) { + free(wisbon); + } + wisbon = NULL; +} + +void Interface::FreeResRefTable(ieResRef *&table, int &count) +{ + if (table) { + free( table ); + count = -1; + } +} + +static void ReleaseItemTooltip(void *poi) +{ + free(poi); +} + +Interface::~Interface(void) +{ + DragItem(NULL,NULL); + delete AreaAliasTable; + + if (music) { + music->HardEnd(); + } + // stop any ambients which are still enqueued + if (AudioDriver) { + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + } + //destroy the highest objects in the hierarchy first! + delete game; + delete calendar; + delete worldmap; + + FreeAbilityTables(); + + if (reputationmod) { + for (unsigned int i=0; i<20; i++) { + if (reputationmod[i]) { + free(reputationmod[i]); + } + } + free(reputationmod); + reputationmod=NULL; + } + + PluginMgr::Get()->RunCleanup(); + + ReleaseMemoryActor(); + EffectQueue_ReleaseMemory(); + CharAnimations::ReleaseMemory(); + delete CurrentStore; + + FreeResRefTable(DefSound, DSCount); + + free( slottypes ); + free( slotmatrix ); + + delete sgiterator; + + if (Cursors) { + for (int i = 0; i < CursorCount; i++) { + video->FreeSprite( Cursors[i] ); + } + delete[] Cursors; + } + + FreeResourceVector( Font, fonts ); + FreeResourceVector( Window, windows ); + + size_t i; + for (i = 0; i < musiclist.size(); i++) { + free((void *)musiclist[i]); + } + + DamageInfoMap.clear(); + + ModalStates.clear(); + + delete plugin_flags; + + delete projserv; + + delete console; + + delete pal256; + delete pal32; + delete pal16; + + delete timer; + delete displaymsg; + + if (video) { + + for(i=0;iFreeSprite(FogSprites[i]); + } + + for(i=0;i<4;i++) { + video->FreeSprite(WindowFrames[i]); + } + + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + for(i=0;i<6;i++) { + video->FreeSprite(GroundCircles[size][i]); + } + } + + if (TooltipBack) { + for(i=0;i<3;i++) { + //freesprite checks for null pointer + video->FreeSprite(TooltipBack[i]); + } + delete[] TooltipBack; + } + if (InfoTextPalette) { + gamedata->FreePalette(InfoTextPalette); + } + + video->SetDragCursor(NULL); + } + + delete evntmgr; + + delete vars; + delete tokens; + if (lists) { + lists->RemoveAll(Release2daList); + delete lists; + } + + if (RtRows) { + RtRows->RemoveAll(ReleaseItemList); + delete RtRows; + } + if (ItemExclTable) { + ItemExclTable->RemoveAll(NULL); + delete ItemExclTable; + } + if (ItemDialTable) { + ItemDialTable->RemoveAll(NULL); + delete ItemDialTable; + } + if (ItemDial2Table) { + ItemDial2Table->RemoveAll(NULL); + delete ItemDial2Table; + } + if (ItemTooltipTable) { + ItemTooltipTable->RemoveAll(ReleaseItemTooltip); + delete ItemTooltipTable; + } + + Map::ReleaseMemory(); + Actor::ReleaseMemory(); + + gamedata->ClearCaches(); + delete gamedata; + gamedata = NULL; + + // Removing all stuff from Cache, except bifs + if (!KeepCache) DelTree((const char *) CachePath, true); +} + +void Interface::SetWindowFrame(int i, Sprite2D *Picture) +{ + video->FreeSprite(WindowFrames[i]); + WindowFrames[i]=Picture; +} + +GameControl* Interface::StartGameControl() +{ + //making sure that our window is the first one + if (ConsolePopped) { + PopupConsole(); + } + DelAllWindows();//deleting ALL windows + gamedata->DelTable(0xffffu); //dropping ALL tables + Window* gamewin = new Window( 0xffff, 0, 0, (ieWord) Width, (ieWord) Height ); + gamewin->WindowPack[0]=0; + GameControl* gc = new GameControl(); + gc->XPos = 0; + gc->YPos = 0; + gc->Width = (ieWord) Width; + gc->Height = (ieWord) Height; + gc->Owner = gamewin; + gc->ControlID = 0x00000000; + gc->ControlType = IE_GUI_GAMECONTROL; + gamewin->AddControl( gc ); + AddWindow( gamewin ); + SetVisible( 0, WINDOW_VISIBLE ); + //setting the focus to the game control + evntmgr->SetFocused(gamewin, gc); + if (guiscript->LoadScript( "MessageWindow" )) { + guiscript->RunFunction( "MessageWindow", "OnLoad" ); + gc->UnhideGUI(); + } + + return gc; +} + +/* handle main loop events that might destroy or create windows +thus cannot be called from DrawWindows directly +these events are pending until conditions are right +*/ +void Interface::HandleEvents() +{ + GameControl *gc = GetGameControl(); + if (gc && (!gc->Owner || !gc->Owner->Visible)) { + gc=NULL; + } + + if (EventFlag&EF_SELECTION) { + EventFlag&=~EF_SELECTION; + guiscript->RunFunction( "GUICommonWindows", "SelectionChanged", false); + } + + if (EventFlag&EF_UPDATEANIM) { + EventFlag&=~EF_UPDATEANIM; + guiscript->RunFunction( "GUICommonWindows", "UpdateAnimation", false); + } + + if (EventFlag&EF_PORTRAIT) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "PortraitWindow", tmp ); + if (tmp != (ieDword) ~0) { + EventFlag&=~EF_PORTRAIT; + guiscript->RunFunction( "GUICommonWindows", "UpdatePortraitWindow" ); + } + } + + if (EventFlag&EF_ACTION) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "ActionsWindow", tmp ); + if (tmp != (ieDword) ~0) { + EventFlag&=~EF_ACTION; + guiscript->RunFunction( "GUICommonWindows", "UpdateActionsWindow" ); + } + } + + if ((EventFlag&EF_CONTROL) && gc) { + EventFlag&=~EF_CONTROL; + guiscript->RunFunction( "MessageWindow", "UpdateControlStatus" ); + //this is the only value we can use here + if (game->ControlStatus & CS_HIDEGUI) + gc->HideGUI(); + else + gc->UnhideGUI(); + return; + } + if ((EventFlag&EF_SHOWMAP) && gc) { + ieDword tmp = (ieDword) ~0; + vars->Lookup( "OtherWindow", tmp ); + if (tmp == (ieDword) ~0) { + EventFlag &= ~EF_SHOWMAP; + guiscript->RunFunction( "GUIMA", "ShowMap" ); + } + return; + } + + if (EventFlag&EF_SEQUENCER) { + EventFlag&=~EF_SEQUENCER; + guiscript->RunFunction( "GUIMG", "OpenSequencerWindow" ); + return; + } + + if (EventFlag&EF_IDENTIFY) { + EventFlag&=~EF_IDENTIFY; + // FIXME: Implement this. + guiscript->RunFunction( "GUICommonWindows", "OpenIdentifyWindow" ); + return; + } + if (EventFlag&EF_OPENSTORE) { + EventFlag&=~EF_OPENSTORE; + guiscript->RunFunction( "GUISTORE", "OpenStoreWindow" ); + return; + } + + if (EventFlag&EF_EXPANSION) { + EventFlag&=~EF_EXPANSION; + guiscript->RunFunction( "MessageWindow", "GameExpansion", false ); + return; + } +} + +/* handle main loop events that might destroy or create windows +thus cannot be called from DrawWindows directly +*/ +void Interface::HandleFlags() +{ + //clear events because the context changed + EventFlag = EF_CONTROL; + + if (QuitFlag&(QF_QUITGAME|QF_EXITGAME) ) { + // when reaching this, quitflag should be 1 or 2 + // if Exitgame was set, we'll set Start.py too + QuitGame (QuitFlag&QF_EXITGAME); + QuitFlag &= ~(QF_QUITGAME|QF_EXITGAME); + } + + if (QuitFlag&QF_LOADGAME) { + QuitFlag &= ~QF_LOADGAME; + LoadGame(LoadGameIndex.get(), VersionOverride ); + LoadGameIndex.release(); + //after loading a game, always check if the game needs to be upgraded + } + + if (QuitFlag&QF_ENTERGAME) { + QuitFlag &= ~QF_ENTERGAME; + if (game) { + EventFlag|=EF_EXPANSION; + timer->Init(); + + //rearrange party slots + game->ConsolidateParty(); + GameControl* gc = StartGameControl(); + //switch map to protagonist + Actor* actor = GetFirstSelectedPC(true); + if (actor) { + gc->ChangeMap(actor, true); + } + } else { + printMessage("Core", "No game to enter...\n", LIGHT_RED); + QuitFlag = QF_QUITGAME; + } + } + + if (QuitFlag&QF_CHANGESCRIPT) { + QuitFlag &= ~QF_CHANGESCRIPT; + guiscript->LoadScript( NextScript ); + guiscript->RunFunction( NextScript, "OnLoad" ); + } +} + +bool GenerateAbilityTables() +{ + FreeAbilityTables(); + + //range is: 0 - maximumability + int tablesize = MaximumAbility+1; + strmod = (ieWordSigned *) malloc (tablesize * 4 * sizeof(ieWordSigned) ); + if (!strmod) + return false; + strmodex = (ieWordSigned *) malloc (101 * 4 * sizeof(ieWordSigned) ); + if (!strmodex) + return false; + intmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) ); + if (!intmod) + return false; + dexmod = (ieWordSigned *) malloc (tablesize * 3 * sizeof(ieWordSigned) ); + if (!dexmod) + return false; + conmod = (ieWordSigned *) malloc (tablesize * 5 * sizeof(ieWordSigned) ); + if (!conmod) + return false; + chrmod = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!chrmod) + return false; + lorebon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!lorebon) + return false; + wisbon = (ieWordSigned *) malloc (tablesize * 1 * sizeof(ieWordSigned) ); + if (!wisbon) + return false; + return true; +} + +bool Interface::ReadAbilityTable(const ieResRef tablename, ieWordSigned *mem, int columns, int rows) +{ + AutoTable tab(tablename); + if (!tab) { + return false; + } + //this is a hack for rows not starting at 0 in some cases + int fix = 0; + const char * tmp = tab->GetRowName(0); + if (tmp && (tmp[0]!='0')) { + fix = atoi(tmp); + for (int i=0;iQueryField(0,j),NULL,0 ); + } + } + } + for (int j=0;jQueryField(i,j),NULL,0 ); + } + } + return true; +} + +bool Interface::ReadAbilityTables() +{ + bool ret = GenerateAbilityTables(); + if (!ret) + return ret; + ret = ReadAbilityTable("strmod", strmod, 4, MaximumAbility + 1); + if (!ret) + return ret; + ret = ReadAbilityTable("strmodex", strmodex, 4, 101); + //3rd ed doesn't have strmodex, but has a maximum of 40 + if (!ret && (MaximumAbility<=25) ) + return ret; + ret = ReadAbilityTable("intmod", intmod, 3, MaximumAbility + 1); + if (!ret) + return ret; + ret = ReadAbilityTable("hpconbon", conmod, 5, MaximumAbility + 1); + if (!ret) + return ret; + if (!HasFeature(GF_3ED_RULES)) { + //no lorebon in iwd2??? + ret = ReadAbilityTable("lorebon", lorebon, 1, MaximumAbility + 1); + if (!ret) + return ret; + //no dexmod in iwd2??? + ret = ReadAbilityTable("dexmod", dexmod, 3, MaximumAbility + 1); + if (!ret) + return ret; + } + //this table is a single row (not a single column) + ret = ReadAbilityTable("chrmodst", chrmod, MaximumAbility + 1, 1); + if (!ret) + return ret; + if (HasFeature(GF_WISDOM_BONUS)) { + ret = ReadAbilityTable("wisxpbon", wisbon, 1, MaximumAbility + 1); + if (!ret) + return ret; + } + return true; +} + +bool Interface::ReadGameTimeTable() +{ + AutoTable table("gametime"); + if (!table) { + return false; + } + + Time.round_sec = atoi(table->QueryField("ROUND_SECONDS", "DURATION")); + Time.turn_sec = atoi(table->QueryField("TURN_SECONDS", "DURATION")); + Time.round_size = Time.round_sec * AI_UPDATE_TIME; + Time.rounds_per_turn = Time.turn_sec / Time.round_sec; + + return true; +} + +bool Interface::ReadAuxItemTables() +{ + int idx; + int table; + bool flag = true; + + if (ItemExclTable) { + ItemExclTable->RemoveAll(NULL); + } else { + ItemExclTable = new Variables(); + ItemExclTable->SetType(GEM_VARIABLES_INT); + } + table = gamedata->LoadTable( "itemexcl" ); + + AutoTable aa; + + //don't report error when the file doesn't exist + if (aa.load("itemexcl")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = strtol(aa->QueryField(idx,0),NULL,0); + ItemExclTable->SetAt(key, value); + } + } + if (ItemDialTable) { + ItemDialTable->RemoveAll(NULL); + } else { + ItemDialTable = new Variables(); + ItemDialTable->SetType(GEM_VARIABLES_INT); + } + if (ItemDial2Table) { + ItemDial2Table->RemoveAll(NULL); + } else { + ItemDial2Table = new Variables(); + ItemDial2Table->SetType(GEM_VARIABLES_STRING); + } + + //don't report error when the file doesn't exist + if (aa.load("itemdial")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key, dlgres; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = strtol(aa->QueryField(idx,0),NULL,0); + ItemDialTable->SetAt(key, value); + strnlwrcpy(dlgres,aa->QueryField(idx,1),8); + ItemDial2Table->SetAtCopy(key, dlgres); + } + } + + if (ItemTooltipTable) { + ItemTooltipTable->RemoveAll(ReleaseItemTooltip); + } else { + ItemTooltipTable = new Variables(); + ItemTooltipTable->SetType(GEM_VARIABLES_POINTER); + } + + //don't report error when the file doesn't exist + if (aa.load("tooltip")) { + idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + int *tmppoi = (int *) malloc(sizeof(int)*3); + + strnlwrcpy(key,aa->GetRowName(idx),8); + for (int i=0;i<3;i++) { + tmppoi[i] = atoi(aa->QueryField(idx,i)); + } + ItemTooltipTable->SetAt(key, (void*)tmppoi); + } + } + return flag; +} + +//Static +const char *Interface::GetDeathVarFormat() +{ + return DeathVarFormat; +} + +int Interface::GetItemExcl(const ieResRef itemname) const +{ + ieDword value; + + if (ItemExclTable && ItemExclTable->Lookup(itemname, value)) { + return (int) value; + } + return 0; +} + +int Interface::GetItemTooltip(const ieResRef itemname, int header, int identified) +{ + int *value = NULL; + + if (ItemTooltipTable) { + void* lookup = NULL; + ItemTooltipTable->Lookup(itemname, lookup); + value = (int*)lookup; + } + if (value && (value[header]>=0)) { + return value[header]; + } + Item *item = gamedata->GetItem(itemname); + if (!item) { + return -1; + } + int ret = identified?item->ItemNameIdentified:item->ItemName; + gamedata->FreeItem(item, itemname, 0); + return ret; +} + +int Interface::GetItemDialStr(const ieResRef itemname) const +{ + ieDword value; + + if (ItemDialTable && ItemDialTable->Lookup(itemname, value)) { + return (int) value; + } + return -1; +} + +//second value is the item dialog resource returned by this method +int Interface::GetItemDialRes(const ieResRef itemname, ieResRef retval) const +{ + if (ItemDial2Table && ItemDial2Table->Lookup(itemname, retval, sizeof(ieResRef))) { + return 1; + } + return 0; +} + +bool Interface::ReadAreaAliasTable(const ieResRef tablename) +{ + if (AreaAliasTable) { + AreaAliasTable->RemoveAll(NULL); + } else { + AreaAliasTable = new Variables(); + AreaAliasTable->SetType(GEM_VARIABLES_INT); + } + + AutoTable aa(tablename); + if (!aa) { + //don't report error when the file doesn't exist + return true; + } + + int idx = aa->GetRowCount(); + while (idx--) { + ieResRef key; + + strnlwrcpy(key,aa->GetRowName(idx),8); + ieDword value = atoi(aa->QueryField(idx,0)); + AreaAliasTable->SetAt(key, value); + } + return true; +} + +//this isn't const +int Interface::GetAreaAlias(const ieResRef areaname) const +{ + ieDword value; + + if (AreaAliasTable && AreaAliasTable->Lookup(areaname, value)) { + return (int) value; + } + return -1; +} + +bool Interface::ReadMusicTable(const ieResRef tablename, int col) { + AutoTable tm(tablename); + if (!tm) + return false; + + for (unsigned int i = 0; i < tm->GetRowCount(); i++) { + musiclist.push_back(strdup(tm->QueryField(i, col))); + } + + return true; +} + +bool Interface::ReadDamageTypeTable() { + AutoTable tm("dmgtypes"); + if (!tm) + return false; + + DamageInfoStruct di; + for (ieDword i = 0; i < tm->GetRowCount(); i++) { + di.strref = displaymsg->GetStringReference(atoi(tm->QueryField(i, 0))); + di.resist_stat = TranslateStat(tm->QueryField(i, 1)); + di.value = strtol(tm->QueryField(i, 2), (char **) NULL, 16); + di.iwd_mod_type = atoi(tm->QueryField(i, 3)); + DamageInfoMap.insert(std::make_pair ((ieDword)di.value, di)); + } + + return true; +} + +bool Interface::ReadReputationModTable() { + AutoTable tm("reputati"); + if (!tm) + return false; + + reputationmod = (int **) calloc(21, sizeof(int *)); + int cols = tm->GetColumnCount(); + for (unsigned int i=0; i<20; i++) { + reputationmod[i] = (int *) calloc(cols, sizeof(int)); + for (int j=0; jQueryField(i, j)); + } + } + + return true; +} + +bool Interface::ReadModalStates() +{ + AutoTable table("modal"); + if (!table) + return false; + + ModalStatesStruct ms; + for (unsigned short i = 0; i < table->GetRowCount(); i++) { + strncpy(ms.spell, table->QueryField(i, 0), 8); + strncpy(ms.action, table->QueryField(i, 1), 16); + ms.entering_str = atoi(table->QueryField(i, 2)); + ms.leaving_str = atoi(table->QueryField(i, 3)); + ms.failed_str = atoi(table->QueryField(i, 4)); + ms.aoe_spell = atoi(table->QueryField(i, 5)); + ModalStates.push_back(ms); + } + + return true; +} + +//Not a constant anymore, we let the caller set the entry to zero +char *Interface::GetMusicPlaylist(int SongType) const { + if (SongType < 0 || (unsigned int)SongType >= musiclist.size()) + return NULL; + + return musiclist[SongType]; +} + +static const Color white = {0xff,0xff,0xff,0xff}; +static const Color black = {0x00,0x00,0x00,0xff}; +static const Region bg( 0, 0, 100, 30 ); + +/** this is the main loop */ +void Interface::Main() +{ + ieDword brightness = 10; + ieDword contrast = 5; + ieDword speed = 10; + + vars->Lookup("Full Screen", FullScreen); + video->CreateDisplay( Width, Height, Bpp, FullScreen); + video->SetDisplayTitle( GameName, GameType ); + vars->Lookup("Brightness Correction", brightness); + vars->Lookup("Gamma Correction", contrast); + vars->Lookup("Mouse Scroll Speed", speed); + video->SetGamma(brightness, contrast); + SetMouseScrollSpeed((int) speed); + if (vars->Lookup("Tooltips", TooltipDelay)) { + // the games store the slider position*10, not the actual delay + TooltipDelay *= TOOLTIP_DELAY_FACTOR/10; + } + + Font* fps = GetFont( ( unsigned int ) 0 ); + char fpsstring[40]={"???.??? fps"}; + unsigned long frame = 0, time, timebase; + GetTime(timebase); + double frames = 0.0; + Palette* palette = CreatePalette( white, black ); + do { + //don't change script when quitting is pending + + while (QuitFlag) { + HandleFlags(); + } + //eventflags are processed only when there is a game + if (EventFlag && game) { + HandleEvents(); + } + HandleGUIBehaviour(); + + GameLoop(); + DrawWindows(); + if (DrawFPS) { + frame++; + GetTime( time ); + if (time - timebase > 1000) { + frames = ( frame * 1000.0 / ( time - timebase ) ); + timebase = time; + frame = 0; + sprintf( fpsstring, "%.3f fps", frames ); + } + video->DrawRect( bg, black ); + fps->Print( bg, + ( unsigned char * ) fpsstring, palette, + IE_FONT_ALIGN_LEFT | IE_FONT_ALIGN_MIDDLE, true ); + } + if (TickHook) + TickHook->call(); + } while (video->SwapBuffers() == GEM_OK); + gamedata->FreePalette( palette ); +} + +int Interface::ReadResRefTable(const ieResRef tablename, ieResRef *&data) +{ + int count = 0; + + if (data) { + free(data); + data = NULL; + } + AutoTable tm(tablename); + if (!tm) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find %s.2da.\n",tablename ); + return 0; + } + count = tm->GetRowCount(); + data = (ieResRef *) calloc( count, sizeof(ieResRef) ); + for (int i = 0; i < count; i++) { + strnlwrcpy( data[i], tm->QueryField( i, 0 ), 8 ); + //* marks an empty resource + if (data[i][0]=='*') { + data[i][0]=0; + } + } + return count; +} + +int Interface::LoadSprites() +{ + ieDword i; + int size; + if (!IsAvailable( IE_2DA_CLASS_ID )) { + printf( "No 2DA Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + //loading cursors + AnimationFactory* anim; + anim = (AnimationFactory*) gamedata->GetFactoryResource("cursors", IE_BAM_CLASS_ID); + if (anim) + { + CursorCount = anim->GetCycleCount(); + Cursors = new Sprite2D * [CursorCount]; + for (int i = 0; i < CursorCount; i++) { + Cursors[i] = anim->GetFrame( 0, (ieByte) i ); + } + } + printMessage( "Core", "Loading Cursors...", WHITE ); + + // this is the last existing cursor type + if (CursorCountSetCursor( Cursors[0], Cursors[1] ); + printStatus( "OK", LIGHT_GREEN ); + + // Load fog-of-war bitmaps + anim = (AnimationFactory*) gamedata->GetFactoryResource("fogowar", IE_BAM_CLASS_ID); + printMessage( "Core", "Loading Fog-Of-War bitmaps...", WHITE ); + if (!anim || anim->GetCycleSize( 0 ) != 8) { + // unknown type of fog anim + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + FogSprites[0] = NULL; + FogSprites[1] = anim->GetFrame( 0, 0 ); + FogSprites[2] = anim->GetFrame( 1, 0 ); + FogSprites[3] = anim->GetFrame( 2, 0 ); + + FogSprites[4] = video->MirrorSpriteVertical( FogSprites[1], false ); + + FogSprites[5] = NULL; + + FogSprites[6] = video->MirrorSpriteVertical( FogSprites[3], false ); + + FogSprites[7] = NULL; + + FogSprites[8] = video->MirrorSpriteHorizontal( FogSprites[2], false ); + + FogSprites[9] = video->MirrorSpriteHorizontal( FogSprites[3], false ); + + FogSprites[10] = NULL; + FogSprites[11] = NULL; + + FogSprites[12] = video->MirrorSpriteHorizontal( FogSprites[6], false ); + + FogSprites[16] = anim->GetFrame( 3, 0 ); + FogSprites[17] = anim->GetFrame( 4, 0 ); + FogSprites[18] = anim->GetFrame( 5, 0 ); + FogSprites[19] = anim->GetFrame( 6, 0 ); + + FogSprites[20] = video->MirrorSpriteVertical( FogSprites[17], false ); + + FogSprites[21] = NULL; + + FogSprites[23] = NULL; + + FogSprites[24] = video->MirrorSpriteHorizontal( FogSprites[18], false ); + + FogSprites[25] = anim->GetFrame( 7, 0 ); + + { + Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[25], false ); + FogSprites[22] = video->MirrorSpriteHorizontal( tmpsprite, false ); + video->FreeSprite( tmpsprite ); + } + + FogSprites[26] = NULL; + FogSprites[27] = NULL; + + { + Sprite2D *tmpsprite = video->MirrorSpriteVertical( FogSprites[19], false ); + FogSprites[28] = video->MirrorSpriteHorizontal( tmpsprite, false ); + video->FreeSprite( tmpsprite ); + } + + i = 0; + vars->Lookup("3D Acceleration", i); + if (i) { + for(i=0;iCreateAlpha( FogSprites[i] ); + video->FreeSprite ( FogSprites[i] ); + FogSprites[i] = alphasprite; + } + } + } + + printStatus( "OK", LIGHT_GREEN ); + + // Load ground circle bitmaps (PST only) + //block required due to msvc6.0 incompatibility + for (size = 0; size < MAX_CIRCLE_SIZE; size++) { + if (GroundCircleBam[size][0]) { + anim = (AnimationFactory*) gamedata->GetFactoryResource(GroundCircleBam[size], IE_BAM_CLASS_ID); + if (!anim || anim->GetCycleCount() != 6) { + // unknown type of circle anim + printMessage( "Core", "Loading Ground circle bitmaps...", WHITE ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + for (int i = 0; i < 6; i++) { + Sprite2D* sprite = anim->GetFrame( 0, (ieByte) i ); + if (GroundCircleScale[size]) { + GroundCircles[size][i] = video->SpriteScaleDown( sprite, GroundCircleScale[size] ); + video->FreeSprite( sprite ); + } else { + GroundCircles[size][i] = sprite; + } + } + } + } + + printMessage( "Core", "Loading Ground circle bitmaps...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Loading Fonts...\n", WHITE ); + AutoTable tab("fonts"); + if (!tab) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find fonts.2da.\nTermination in Progress...\n" ); + return GEM_ERROR; + } else { + PluginHolder bamint(IE_BAM_CLASS_ID); + if (!bamint) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No BAM Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + DataStream* str = NULL; + + int count = tab->GetRowCount(); + for (int i = 0; i < count; i++) { + const char* ResRef = tab->QueryField( i, 0 ); + int needpalette = atoi( tab->QueryField( i, 1 ) ); + int first_char = atoi( tab->QueryField( i, 2 ) ); + str = gamedata->GetResource( ResRef, IE_BAM_CLASS_ID ); + if (!bamint->Open( str, true )) { + continue; + } + Font* fnt = bamint->GetFont(); + if (!fnt) { + continue; + } + strnlwrcpy( fnt->ResRef, ResRef, 8 ); + if (needpalette) { + + Color fore = {0xff, 0xff, 0xff, 0}; + Color back = {0x00, 0x00, 0x00, 0}; + if (!strnicmp( TooltipFont, ResRef, 8) ) { + if (TooltipColor.a==0xff) { + fore = TooltipColor; + } else { + fore = back; + back = TooltipColor; + } + } + Palette* pal = CreatePalette( fore, back ); + pal->CreateShadedAlphaChannel(); + fnt->SetPalette(pal); + gamedata->FreePalette( pal ); + } + fnt->SetFirstChar( (ieByte) first_char ); + fonts.push_back( fnt ); + } + + if (fonts.size() == 0) { + printMessage( "Core", "No default font loaded! ", WHITE ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (GetFont( ButtonFont ) == NULL) { + printMessage( "Core", "ButtonFont not loaded: ", WHITE ); + printf("%s ", ButtonFont); + printStatus( "WARNING", YELLOW ); + } + if (GetFont( MovieFont ) == NULL) { + printMessage( "Core", "MovieFont not loaded: ", WHITE ); + printf("%s ", MovieFont); + printStatus( "WARNING", YELLOW ); + } + if (GetFont( TooltipFont ) == NULL) { + printMessage( "Core", "TooltipFont not loaded: ", WHITE ); + printf("%s ", TooltipFont); + printStatus( "WARNING", YELLOW ); + } + } + printMessage( "Core", "Fonts Loaded...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + + if (TooltipBackResRef[0]) { + anim = (AnimationFactory*) gamedata->GetFactoryResource(TooltipBackResRef, IE_BAM_CLASS_ID); + printMessage( "Core", "Initializing Tooltips...", WHITE ); + if (!anim) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + TooltipBack = new Sprite2D * [3]; + for (int i = 0; i < 3; i++) { + TooltipBack[i] = anim->GetFrame( 0, (ieByte) i ); + TooltipBack[i]->XPos = 0; + TooltipBack[i]->YPos = 0; + } + printStatus( "OK", LIGHT_GREEN ); + } + + return GEM_OK; +} + +int Interface::Init() +{ + plugin_flags = new Variables(); + plugin_flags->SetType( GEM_VARIABLES_INT ); + + printMessage( "Core", "Initializing the Event Manager...", WHITE ); + evntmgr = new EventMgr(); + + printMessage( "Core", "Initializing Lists Dictionary...", WHITE ); + lists = new Variables(); + if (!lists) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + lists->SetType( GEM_VARIABLES_POINTER ); + + printMessage( "Core", "Initializing Variables Dictionary...", WHITE ); + vars = new Variables(); + if (!vars) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + vars->SetType( GEM_VARIABLES_INT ); + vars->ParseKey(true); + + vars->SetAt( "Volume Ambients", 100 ); + vars->SetAt( "Volume Movie", 100 ); + vars->SetAt( "Volume Music", 100 ); + vars->SetAt( "Volume SFX", 100 ); + vars->SetAt( "Volume Voices", 100 ); + printStatus( "OK", LIGHT_GREEN ); + + if (!LoadConfig()) { + return GEM_ERROR; + } + printMessage( "Core", "Starting Plugin Manager...\n", WHITE ); + PluginMgr *plugin = PluginMgr::Get(); + plugin->LoadPlugins(PluginsPath); + if (plugin && plugin->GetPluginCount()) { + printMessage( "Core", "Plugin Loading Complete...", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + } else { + printMessage( "Core", "Plugin Loading Failed, check path...", YELLOW); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + plugin->RunInitializers(); + + time_t t; + t = time( NULL ); + srand( ( unsigned int ) t ); +#ifdef _DEBUG + FileStreamPtrCount = 0; + CachedFileStreamPtrCount = 0; +#endif + printMessage( "Core", "GemRB Core Initialization...\n", WHITE ); + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing Video Driver...", WHITE ); + video = ( Video * ) PluginMgr::Get()->GetDriver(&Video::ID, VideoDriverName.c_str()); + if (!video) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No Video Driver Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + if (video->Init() == GEM_ERROR) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot Initialize Video Driver.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + Color defcolor={255,255,255,200}; + SetInfoTextColor(defcolor); + printStatus( "OK", LIGHT_GREEN ); + + { + printMessage( "Core", "Initializing Search Path...", WHITE ); + if (!IsAvailable( PLUGIN_RESOURCE_DIRECTORY )) { + printf( "no DirectoryImporter! " ); + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + + char path[_MAX_PATH]; + + PathJoin( path, CachePath, NULL); + gamedata->AddSource(path, "Cache", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GemRBOverridePath, "override", GameType, NULL); + gamedata->AddSource(path, "GemRB Override", PLUGIN_RESOURCE_DIRECTORY); + + size_t i; + for (i = 0; i < ModPath.size(); ++i) + gamedata->AddSource(ModPath[i].c_str(), "Mod paths", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GemRBOverridePath, "override", "shared", NULL); + gamedata->AddSource(path, "shared GemRB Override", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameOverridePath, NULL); + gamedata->AddSource(path, "Override", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameSoundsPath, NULL); + gamedata->AddSource(path, "Sounds", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameScriptsPath, NULL); + gamedata->AddSource(path, "Scripts", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GamePortraitsPath, NULL); + gamedata->AddSource(path, "Portraits", PLUGIN_RESOURCE_DIRECTORY); + + PathJoin( path, GamePath, GameDataPath, NULL); + gamedata->AddSource(path, "Data", PLUGIN_RESOURCE_DIRECTORY); + + //IWD2 movies are on the CD but not in the BIF + for (i = 0; i < MAX_CD; i++) { + for (size_t j=0;jAddSource(path, description, PLUGIN_RESOURCE_DIRECTORY); + } + } + + printStatus( "OK", LIGHT_GREEN ); + } + + { + printMessage( "Core", "Initializing KEY Importer...", WHITE ); + char ChitinPath[_MAX_PATH]; + PathJoin( ChitinPath, GamePath, "chitin.key", NULL ); + if (!gamedata->AddSource(ChitinPath, "chitin.key", PLUGIN_RESOURCE_KEY)) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Reading Game Options...\n", WHITE ); + if (!LoadGemRBINI()) + { + printf( "Cannot Load INI\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + //loading baldur.ini + { + char ini_path[_MAX_PATH]; + PathJoin( ini_path, GamePath, INIConfig, NULL ); + LoadINI( ini_path ); + int i; + for (i = 0; i < 8; i++) { + if (INIConfig[i] == '.') + break; + GameNameResRef[i] = INIConfig[i]; + } + GameNameResRef[i] = 0; + } + + printMessage( "Core", "Creating Projectile Server...\n", WHITE ); + projserv = new ProjectileServer(); + if (!projserv->GetHighestProjectileNumber()) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No projectiles are available...\n" ); + } + + printMessage( "Core", "Checking for Dialogue Manager...", WHITE ); + if (!IsAvailable( IE_TLK_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No TLK Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strings = PluginHolder(IE_TLK_CLASS_ID); + printMessage( "Core", "Loading Dialog.tlk file...", WHITE ); + char strpath[_MAX_PATH]; + PathJoin( strpath, GamePath, dialogtlk, NULL ); + FileStream* fs = new FileStream(); + if (!fs->Open( strpath, true )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find Dialog.tlk.\nTermination in Progress...\n" ); + delete fs; + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strings->Open( fs, true ); + + { + printMessage( "Core", "Loading Palettes...\n", WHITE ); + ResourceHolder pal16im(Palette16); + if (pal16im) + pal16 = pal16im->GetImage(); + ResourceHolder pal32im(Palette32); + if (pal32im) + pal32 = pal32im->GetImage(); + ResourceHolder pal256im(Palette256); + if (pal256im) + pal256 = pal256im->GetImage(); + if (!pal16 || !pal32 || !pal256) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printMessage( "Core", "Palettes Loaded\n", WHITE ); + } + + if (!IsAvailable( IE_BAM_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "No BAM Importer Available.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + printMessage( "Core", "Initializing stock sounds...\n", WHITE ); + DSCount = ReadResRefTable ("defsound", DefSound); + if (DSCount == 0) { + printStatus( "ERROR", LIGHT_RED ); + printf( "Cannot find defsound.2da.\nTermination in Progress...\n" ); + return GEM_ERROR; + } + + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Broadcasting Event Manager...", WHITE ); + video->SetEventMgr( evntmgr ); + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing Window Manager...", WHITE ); + windowmgr = PluginHolder(IE_CHU_CLASS_ID); + if (windowmgr == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + printMessage( "Core", "Initializing GUI Script Engine...", WHITE ); + guiscript = PluginHolder(IE_GUI_SCRIPT_CLASS_ID); + if (guiscript == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (!guiscript->Init()) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + strcpy( NextScript, "Start" ); + + int ret = LoadSprites(); + if (ret) return ret; + + printMessage( "Core", "Setting up the Console...", WHITE ); + QuitFlag = QF_CHANGESCRIPT; + console = new Console(); + console->XPos = 0; + console->YPos = (ieWord) (Height - 25); + console->Width = (ieWord) Width; + console->Height = 25; + if (fonts.size() > 0) { + console->SetFont( fonts[0] ); + } + + Sprite2D *tmpsprite = GetCursorSprite(); + if (!tmpsprite) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + console->SetCursor (tmpsprite); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Starting up the Sound Driver...", WHITE ); + AudioDriver = ( Audio * ) PluginMgr::Get()->GetDriver(&Audio::ID, AudioDriverName.c_str()); + if (AudioDriver == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + if (!AudioDriver->Init()) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Allocating SaveGameIterator...", WHITE ); + sgiterator = new SaveGameIterator(); + if (sgiterator == NULL) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + //no need of strdup, variables do copy the key! + vars->SetAt( "SkipIntroVideos", (unsigned long)SkipIntroVideos ); + vars->SetAt( "GUIEnhancements", (unsigned long)GUIEnhancements ); + + printMessage( "Core", "Initializing Token Dictionary...", WHITE ); + tokens = new Variables(); + if (!tokens) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + tokens->SetType( GEM_VARIABLES_STRING ); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Initializing Music Manager...", WHITE ); + music = PluginHolder(IE_MUS_CLASS_ID); + if (!music) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + printMessage("Core", "Loading music list...\n", WHITE ); + if (HasFeature( GF_HAS_SONGLIST )) { + ret = ReadMusicTable("songlist", 1); + } else { + /*since bg1 and pst has no .2da for songlist, + we must supply one in the gemrb/override folder. + It should be: music.2da, first column is a .mus filename*/ + ret = ReadMusicTable("music", 0); + } + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } else { + printStatus( "NOT FOUND", YELLOW ); + } + + if (HasFeature( GF_RESDATA_INI )) { + printMessage( "Core", "Loading resource data File...", WHITE ); + INIresdata = PluginHolder(IE_INI_CLASS_ID); + DataStream* ds = gamedata->GetResource("resdata", IE_INI_CLASS_ID); + if (!INIresdata->Open( ds, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + + if (HasFeature( GF_HAS_PARTY_INI )) { + printMessage( "Core", "Loading precreated teams setup...\n", + WHITE ); + INIparty = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs = new FileStream(); + char tINIparty[_MAX_PATH]; + PathJoin( tINIparty, GamePath, "Party.ini", NULL ); + fs->Open( tINIparty, true ); + if (!INIparty->Open( fs, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + + if (HasFeature(GF_IWD2_DEATHVARFORMAT)) { + memcpy(DeathVarFormat, IWD2DeathVarFormat, sizeof(ieVariable)); + } + + if (HasFeature( GF_HAS_BEASTS_INI )) { + printMessage( "Core", "Loading beasts definition File...\n", + WHITE ); + INIbeasts = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs = new FileStream(); + char tINIbeasts[_MAX_PATH]; + PathJoin( tINIbeasts, GamePath, "beast.ini", NULL ); + // FIXME: crashes if file does not open + fs->Open( tINIbeasts, true ); + if (!INIbeasts->Open( fs, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Loading quests definition File...\n", + WHITE ); + INIquests = PluginHolder(IE_INI_CLASS_ID); + FileStream* fs2 = new FileStream(); + char tINIquests[_MAX_PATH]; + PathJoin( tINIquests, GamePath, "quests.ini", NULL ); + // FIXME: crashes if file does not open + fs2->Open( tINIquests, true ); + if (!INIquests->Open( fs2, true )) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + } + game = NULL; + calendar = NULL; + + timer = new GlobalTimer(); + printMessage( "Core", "Bringing up the Global Timer...", WHITE ); + if (!timer) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = Init_EffectQueue(); + printMessage( "Core", "Initializing effects...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = InitItemTypes(); + printMessage( "Core", "Initializing Inventory Management...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + displaymsg = new DisplayMessage(); + printMessage( "Core", "Initializing string constants...", WHITE ); + if (!displaymsg) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadRandomItems(); + printMessage( "Core", "Initializing random treasure...", WHITE ); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } + else { + printStatus( "ERROR", LIGHT_RED ); + } + + ret = ReadAbilityTables(); + printMessage( "Core", "Initializing ability tables...", WHITE ); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadReputationModTable(); + printMessage( "Core", "Reading reputation mod table...", WHITE); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } else { + printStatus( "NOT FOUND", LIGHT_RED ); + } + + if ( gamedata->Exists("WMAPLAY", IE_2DA_CLASS_ID) ) { + ret = ReadAreaAliasTable( "WMAPLAY" ); + printMessage( "Core", "Initializing area aliases...", WHITE ); + if (ret) { + printStatus( "OK", LIGHT_GREEN ); + } + else { + printStatus( "NOT FOUND", YELLOW ); + } + } + + ret = ReadGameTimeTable(); + printMessage( "Core", "Reading game time table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadAuxItemTables(); + printMessage( "Core", "Reading item tables...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } + printStatus( "OK", LIGHT_GREEN ); + + ret = ReadDamageTypeTable(); + printMessage( "Core", "Reading damage type table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + ret = ReadModalStates(); + printMessage( "Core", "Reading modal states table...", WHITE); + if (!ret) { + printStatus( "ERROR", LIGHT_RED ); + return GEM_ERROR; + } else { + printStatus( "OK", LIGHT_GREEN ); + } + + printMessage( "Core", "Reading game script tables...", WHITE); + InitializeIEScript(); + printStatus( "OK", LIGHT_GREEN ); + + printMessage( "Core", "Core Initialization Complete!\n", WHITE ); + + return GEM_OK; +} + +bool Interface::IsAvailable(SClass_ID filetype) const +{ + return PluginMgr::Get()->IsAvailable( filetype ); +} + +WorldMap *Interface::GetWorldMap(const char *map) +{ + int index = worldmap->FindAndSetCurrentMap(map?map:game->CurrentArea); + return worldmap->GetWorldMap(index); +} + +ProjectileServer* Interface::GetProjectileServer() const +{ + return projserv; +} + +Video* Interface::GetVideoDriver() const +{ + return video.get(); +} + +Audio* Interface::GetAudioDrv(void) const { + return AudioDriver.get(); +} + +const char* Interface::TypeExt(SClass_ID type) const +{ + switch (type) { + case IE_2DA_CLASS_ID: + return ".2da"; + + case IE_ACM_CLASS_ID: + return ".acm"; + + case IE_ARE_CLASS_ID: + return ".are"; + + case IE_BAM_CLASS_ID: + return ".bam"; + + case IE_BCS_CLASS_ID: + return ".bcs"; + + case IE_BS_CLASS_ID: + return ".bs"; + + case IE_BIF_CLASS_ID: + return ".bif"; + + case IE_BIO_CLASS_ID: + if (HasFeature(GF_BIOGRAPHY_RES)) { + return ".res"; + } + return ".bio"; + + case IE_BMP_CLASS_ID: + return ".bmp"; + + case IE_PNG_CLASS_ID: + return ".png"; + + case IE_CHR_CLASS_ID: + return ".chr"; + + case IE_CHU_CLASS_ID: + return ".chu"; + + case IE_CRE_CLASS_ID: + return ".cre"; + + case IE_DLG_CLASS_ID: + return ".dlg"; + + case IE_EFF_CLASS_ID: + return ".eff"; + + case IE_GAM_CLASS_ID: + return ".gam"; + + case IE_IDS_CLASS_ID: + return ".ids"; + + case IE_INI_CLASS_ID: + return ".ini"; + + case IE_ITM_CLASS_ID: + return ".itm"; + + case IE_MOS_CLASS_ID: + return ".mos"; + + case IE_MUS_CLASS_ID: + return ".mus"; + + case IE_MVE_CLASS_ID: + return ".mve"; + + case IE_OGG_CLASS_ID: + return ".ogg"; + + case IE_PLT_CLASS_ID: + return ".plt"; + + case IE_PRO_CLASS_ID: + return ".pro"; + + case IE_SAV_CLASS_ID: + return ".sav"; + + case IE_SPL_CLASS_ID: + return ".spl"; + + case IE_SRC_CLASS_ID: + return ".src"; + + case IE_STO_CLASS_ID: + return ".sto"; + + case IE_TIS_CLASS_ID: + return ".tis"; + + case IE_TLK_CLASS_ID: + return ".tlk"; + + case IE_TOH_CLASS_ID: + return ".toh"; + + case IE_TOT_CLASS_ID: + return ".tot"; + + case IE_VAR_CLASS_ID: + return ".var"; + + case IE_VVC_CLASS_ID: + return ".vvc"; + + case IE_WAV_CLASS_ID: + return ".wav"; + + case IE_WED_CLASS_ID: + return ".wed"; + + case IE_WFX_CLASS_ID: + return ".wfx"; + + case IE_WMP_CLASS_ID: + return ".wmp"; + } + return NULL; +} + +void Interface::FreeString(char *&str) const +{ + if (str) { + strings->FreeString(str); + } + str = NULL; +} + +ieStrRef Interface::UpdateString(ieStrRef strref, const char *text) const +{ + return strings->UpdateString( strref, text ); +} + +char* Interface::GetString(ieStrRef strref, ieDword options) const +{ + ieDword flags = 0; + + if (!(options & IE_STR_STRREFOFF)) { + vars->Lookup( "Strref On", flags ); + } + return strings->GetString( strref, flags | options ); +} + +void Interface::SetFeature(int flag, int position) +{ + if (flag) { + GameFeatures[position>>5] |= 1<<(position&31); + } else { + GameFeatures[position>>5] &= ~(1<<(position&31) ); + } +/* + if (position>=32) { + position-=32; + if (flag) { + GameFeatures2 |= 1 << position; + } else { + GameFeatures2 &= ~( 1 << position ); + } + return; + } + if (flag) { + GameFeatures |= 1 << position; + } else { + GameFeatures &= ~( 1 << position ); + } +*/ +} + +ieDword Interface::HasFeature(int position) const +{ + return GameFeatures[position>>5] & (1<<(position&31)); +/* + if (position>=64) { + return GameFeatures3 & ( 1 << (position-64) ); + } + if (position>=32) { + return GameFeatures2 & ( 1 << (position-32) ); + } + return GameFeatures & ( 1 << position ); +*/ +} + +/** Search directories and load a config file */ +bool Interface::LoadConfig(void) +{ +#ifndef WIN32 + char path[_MAX_PATH]; + char name[_MAX_PATH]; + + // Find directory where user stores GemRB configurations (~/.gemrb). + // FIXME: Create it if it does not exist + // Use current dir if $HOME is not defined (or bomb out??) + + char* s = getenv( "HOME" ); + if (s) { + strcpy( UserDir, s ); + strcat( UserDir, "/."PACKAGE"/" ); + } else { + strcpy( UserDir, "./" ); + } + + // Find basename of this program. It does the same as basename (3), + // but that's probably missing on some archs + s = strrchr( argv[0], PathDelimiter ); + if (s) { + s++; + } else { + s = argv[0]; + } + + strcpy( name, s ); + //if (!name[0]) // FIXME: could this happen? + // strcpy (name, PACKAGE); // ugly hack + + // If we were called as $0 -c , load config from filename + if (argc > 2 && ! strcmp("-c", argv[1])) { + if (LoadConfig( argv[2] )) { + return true; + } else { + // Explicitly specified cfg file HAS to be present + return false; + } + } + + // FIXME: temporary hack, to be deleted?? + if (LoadConfig( "GemRB.cfg" )) { + return true; + } + + PathJoin( path, UserDir, name, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } + +#ifdef SYSCONFDIR + PathJoin( path, SYSCONFDIR, name, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } +#endif + + // Don't try with default binary name if we have tried it already + if (!strcmp( name, PACKAGE )) { + return false; + } + + PathJoin( path, UserDir, PACKAGE, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } + +#ifdef SYSCONFDIR + PathJoin( path, SYSCONFDIR, PACKAGE, NULL ); + strcat( path, ".cfg" ); + + if (LoadConfig( path )) { + return true; + } +#endif + + return false; +#else // WIN32 + // If we were called as $0 -c , load config from filename + if (argc > 2 && ! strcmp("-c", argv[1])) { + return LoadConfig( argv[2] ); + // Explicitly specified cfg file HAS to be present + } + strcpy( UserDir, ".\\" ); + return LoadConfig( "GemRB.cfg" ); +#endif// WIN32 +} + +bool Interface::LoadConfig(const char* filename) +{ + FILE* config; + size_t i; + + printMessage("Config","Trying to open ", WHITE); + textcolor(LIGHT_WHITE); + printf("%s ", filename); + config = fopen( filename, "rb" ); + if (config == NULL) { + printStatus("NOT FOUND", LIGHT_RED); + return false; + } + char name[65], value[_MAX_PATH + 3]; + + //once GemRB own format is working well, this might be set to 0 + SaveAsOriginal = 1; + + while (!feof( config )) { + char rem; + + if (fread( &rem, 1, 1, config ) != 1) + break; + + if (rem == '#') { + //it should always return 0 + if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0) + break; + continue; + } + fseek( config, -1, SEEK_CUR ); + memset(value,'\0',_MAX_PATH + 3); + //the * element is not counted + if (fscanf( config, "%64[^= ] = %[^\r\n]%*[\r\n]", name, value )!=2) + continue; + for (i=_MAX_PATH + 2; i > 0; i--) { + if (value[i] == '\0') continue; + if (value[i] == ' ') { + value[i] = '\0'; + } else { + break; + } + } + + if (false) { +#define CONFIG_INT(str, var) \ + } else if (stricmp(name, str) == 0) { \ + var ( atoi(value) ) + CONFIG_INT("Bpp", Bpp = ); + CONFIG_INT("CaseSensitive", CaseSensitive = ); + CONFIG_INT("DoubleClickDelay", evntmgr->SetDCDelay); + CONFIG_INT("DrawFPS", DrawFPS = ); + CONFIG_INT("EnableCheatKeys", EnableCheatKeys); + CONFIG_INT("EndianSwitch", DataStream::SetEndianSwitch); + CONFIG_INT("FogOfWar", FogOfWar = ); + CONFIG_INT("FullScreen", FullScreen = ); + CONFIG_INT("GUIEnhancements", GUIEnhancements = ); + CONFIG_INT("GameOnCD", GameOnCD = ); + CONFIG_INT("Height", Height = ); + CONFIG_INT("KeepCache", KeepCache = ); + CONFIG_INT("MultipleQuickSaves", GameControl::MultipleQuickSaves); + CONFIG_INT("RepeatKeyDelay", evntmgr->SetRKDelay); + CONFIG_INT("SaveAsOriginal", SaveAsOriginal = ); + CONFIG_INT("ScriptDebugMode", SetScriptDebugMode); + CONFIG_INT("SkipIntroVideos", SkipIntroVideos = ); + CONFIG_INT("TooltipDelay", TooltipDelay = ); + CONFIG_INT("Width", Width = ); +#undef CONFIG_INT +#define CONFIG_STRING(str, var) \ + } else if (stricmp(name, str) == 0) { \ + strncpy(var, value, sizeof(var)) + CONFIG_STRING("GameCharactersPath", GameCharactersPath); + CONFIG_STRING("GameDataPath", GameDataPath); + CONFIG_STRING("GameName", GameName); + CONFIG_STRING("GameOverridePath", GameOverridePath); + CONFIG_STRING("GamePortraitsPath", GamePortraitsPath); + CONFIG_STRING("GameScriptsPath", GameScriptsPath); + CONFIG_STRING("GameSoundsPath", GameSoundsPath); + CONFIG_STRING("GameType", GameType); +#undef CONFIG_STRING +#define CONFIG_STRING(str, var) \ + } else if (stricmp(name, str) == 0) { \ + var = value + CONFIG_STRING("AudioDriver", AudioDriverName); + CONFIG_STRING("VideoDriver", VideoDriverName); +#undef CONFIG_STRING +#define CONFIG_PATH(str, var) \ + } else if (stricmp(name, str) == 0) { \ + strncpy(var, value, sizeof(var)); + CONFIG_PATH("CachePath", CachePath); + CONFIG_PATH("GUIScriptsPath", GUIScriptsPath); + CONFIG_PATH("GamePath", GamePath); + CONFIG_PATH("GemRBOverridePath", GemRBOverridePath); + CONFIG_PATH("GemRBPath", GemRBPath); + CONFIG_PATH("PluginsPath", PluginsPath); + CONFIG_PATH("SavePath", SavePath); +#undef CONFIG_PATH + } else if (stricmp( name, "ModPath" ) == 0) { + for (char *path = strtok(value,SPathListSeparator); + path; + path = strtok(NULL,SPathListSeparator)) { + ModPath.push_back(path); + } + } else if (stricmp( name, "SkipPlugin" ) == 0) { + plugin_flags->SetAt( value, PLF_SKIP ); + } else if (stricmp( name, "DelayPlugin" ) == 0) { + plugin_flags->SetAt( value, PLF_DELAY ); + } else { + for(i=0;iGetResource( "gemrb", IE_INI_CLASS_ID ); + if (! inifile) { + printStatus( "ERROR", LIGHT_RED ); + return false; + } + + printMessage( "Core", "Loading game type-specific GemRB setup...\n", WHITE ); + printf( "%s",inifile->originalfile); + + if (!IsAvailable( IE_INI_CLASS_ID )) { + printStatus( "ERROR", LIGHT_RED ); + printf( "[Core]: No INI Importer Available.\n" ); + return false; + } + PluginHolder ini(IE_INI_CLASS_ID); + ini->Open( inifile, true ); //autofree + + printStatus( "OK", LIGHT_GREEN ); + + const char *s; + + // Resrefs are already initialized in Interface::Interface() + s = ini->GetKeyAsString( "resources", "CursorBAM", NULL ); + if (s) + strnlwrcpy( CursorBam, s, 8 ); //console cursor + + s = ini->GetKeyAsString( "resources", "ScrollCursorBAM", NULL ); + if (s) + strnlwrcpy( ScrollCursorBam, s, 8 ); + + s = ini->GetKeyAsString( "resources", "ButtonFont", NULL ); + if (s) + strnlwrcpy( ButtonFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipFont", NULL ); + if (s) + strnlwrcpy( TooltipFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "MovieFont", NULL ); + if (s) + strnlwrcpy( MovieFont, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipBack", NULL ); + if (s) + strnlwrcpy( TooltipBackResRef, s, 8 ); + + s = ini->GetKeyAsString( "resources", "TooltipColor", NULL ); + if (s) { + if (s[0] == '#') { + unsigned long c = strtoul (s + 1, NULL, 16); + // FIXME: check errno + TooltipColor.r = (unsigned char) (c >> 24); + TooltipColor.g = (unsigned char) (c >> 16); + TooltipColor.b = (unsigned char) (c >> 8); + TooltipColor.a = (unsigned char) (c); + } + } + + //which stat determines the fist weapon (defaults to class) + Actor::SetFistStat(ini->GetKeyAsInt( "resources", "FistStat", IE_CLASS)); + + TooltipMargin = ini->GetKeyAsInt( "resources", "TooltipMargin", TooltipMargin ); + + // The format of GroundCircle can be: + // GroundCircleBAM1 = wmpickl/3 + // to denote that the bitmap should be scaled down 3x + for (int size = 0; size < MAX_CIRCLE_SIZE; size++) { + char name[30]; + sprintf( name, "GroundCircleBAM%d", size+1 ); + s = ini->GetKeyAsString( "resources", name, NULL ); + if (s) { + const char *pos = strchr( s, '/' ); + if (pos) { + GroundCircleScale[size] = atoi( pos+1 ); + strncpy( GroundCircleBam[size], s, pos - s ); + GroundCircleBam[size][pos - s] = '\0'; + } else { + strcpy( GroundCircleBam[size], s ); + } + } + } + + s = ini->GetKeyAsString( "resources", "NoteString", NULL ); + TextArea::SetNoteString(s); + + s = ini->GetKeyAsString( "resources", "INIConfig", NULL ); + if (s) + strcpy( INIConfig, s ); + + s = ini->GetKeyAsString( "resources", "Palette16", NULL ); + if (s) + strcpy( Palette16, s ); + + s = ini->GetKeyAsString( "resources", "Palette32", NULL ); + if (s) + strcpy( Palette32, s ); + + s = ini->GetKeyAsString( "resources", "Palette256", NULL ); + if (s) + strcpy( Palette256, s ); + + unsigned int i = (unsigned int) ini->GetKeyAsInt ("charset", "CharCount", 0); + if (i>99) i=99; + while(i--) { + char key[10]; + snprintf(key,9,"Letter%d", i+1); + s = ini->GetKeyAsString( "charset", key, NULL ); + if (s) { + const char *s2 = strchr(s,','); + if (s2) { + upperlower(atoi(s), atoi(s2+1) ); + } + } + } + + MaximumAbility = ini->GetKeyAsInt ("resources", "MaximumAbility", 25 ); + + RedrawTile = ini->GetKeyAsInt( "resources", "RedrawTile", 0 )!=0; + + for (i=0;iGetKeyAsInt( "resources", game_flags[i], 0 ), i ); + //printMessage("Option", "", GREEN); + //printf("%s = %s\n", game_flags[i], HasFeature(i)?"yes":"no"); + } + + ForceStereo = ini->GetKeyAsInt( "resources", "ForceStereo", 0 ); + + return true; +} + +Palette* Interface::CreatePalette(const Color &color, const Color &back) +{ + Palette* pal = new Palette(); + pal->col[0].r = 0; + pal->col[0].g = 0xff; + pal->col[0].b = 0; + pal->col[0].a = 0; + for (int i = 1; i < 256; i++) { + pal->col[i].r = back.r + + ( unsigned char ) ( ( ( color.r - back.r ) * ( i ) ) / 255 ); + pal->col[i].g = back.g + + ( unsigned char ) ( ( ( color.g - back.g ) * ( i ) ) / 255 ); + pal->col[i].b = back.b + + ( unsigned char ) ( ( ( color.b - back.b ) * ( i ) ) / 255 ); + pal->col[i].a = back.a + + ( unsigned char ) ( ( ( color.a - back.a ) * ( i ) ) / 255 ); + } + return pal; +} + +/** No descriptions */ +Color* Interface::GetPalette(unsigned index, int colors, Color *pal) const +{ + Image *img; + if (colors == 32) { + img = pal32; + } else if (colors <= 32) { + img = pal16; + } else if (colors == 256) { + img = pal256; + } else { + return pal; + } + if (index >= img->GetHeight()) { + index = 0; + } + for (int i = 0; i < colors; i++) { + pal[i] = img->GetPixel(i, index); + } + return pal; +} +/** Returns a preloaded Font */ +Font* Interface::GetFont(const char *ResRef) const +{ + for (unsigned int i = 0; i < fonts.size(); i++) { + if (strnicmp( fonts[i]->ResRef, ResRef, 8 ) == 0) { + return fonts[i]; + } + } + return NULL; +} + +Font* Interface::GetFont(unsigned int index) const +{ + if (index >= fonts.size()) { + return NULL; + } + return fonts[index]; +} + +Font* Interface::GetButtonFont() const +{ + return GetFont( ButtonFont ); +} + +/** Returns the Event Manager */ +EventMgr* Interface::GetEventMgr() const +{ + return evntmgr; +} + +/** Returns the Window Manager */ +WindowMgr* Interface::GetWindowMgr() const +{ + return windowmgr.get(); +} + +/** Get GUI Script Manager */ +ScriptEngine* Interface::GetGUIScriptEngine() const +{ + return guiscript.get(); +} + +static EffectRef fx_summon_disable_ref={"AvatarRemovalModifier",NULL,-1}; + +//NOTE: if there were more summoned creatures, it will return only the last +Actor *Interface::SummonCreature(const ieResRef resource, const ieResRef vvcres, Scriptable *Owner, Actor *target, const Point &position, int eamod, int level, Effect *fx, bool sexmod) +{ + //maximum number of monsters summoned + int cnt=10; + Actor * ab = NULL; + + //TODO: + //decrease the number of summoned creatures with the number of already summoned creatures here + //the summoned creatures have a special IE_SPECIFIC + + while(cnt--) { + ab = gamedata->GetCreature(resource); + if (!ab) { + return NULL; + } + + if (Owner && Owner->Type==ST_ACTOR) { + ab->LastSummoner = Owner->GetGlobalID(); + } + //Always use Base stats for the recently summoned creature + + int enemyally; + + if (eamod==EAM_SOURCEALLY || eamod==EAM_SOURCEENEMY) { + if (Owner && Owner->Type==ST_ACTOR) { + enemyally = ((Actor *) Owner)->GetStat(IE_EA)>EA_GOODCUTOFF; + } else { + enemyally = true; + } + } else { + if (target) { + enemyally = target->GetBase(IE_EA)>EA_GOODCUTOFF; + } else { + enemyally = true; + } + } + + switch (eamod) { + case EAM_SOURCEALLY: + case EAM_ALLY: + if (enemyally) { + ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA? + } else { + ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA? + } + break; + case EAM_SOURCEENEMY: + case EAM_ENEMY: + if (enemyally) { + ab->SetBase(IE_EA, EA_CONTROLLED); //is this the summoned EA? + } else { + ab->SetBase(IE_EA, EA_ENEMY); //is this the summoned EA? + } + break; + case EAM_NEUTRAL: + ab->SetBase(IE_EA, EA_NEUTRAL); + break; + default: + break; + } + + // mark the summon, but only if they don't have a special sex already + if (sexmod && ab->BaseStats[IE_SEX] < SEX_EXTRA) { + ab->SetBase(IE_SEX, SEX_SUMMON); + } + + Map *map; + if (target) { + map = target->GetCurrentArea(); + } else { + map = Owner->GetCurrentArea(); + } + map->AddActor(ab); + ab->SetPosition(position, true, 0); + ab->RefreshEffects(NULL); + + if (vvcres[0]) { + ScriptedAnimation* vvc = gamedata->GetScriptedAnimation(vvcres, false); + if (vvc) { + //This is the final position of the summoned creature + //not the original target point + vvc->XPos=ab->Pos.x; + vvc->YPos=ab->Pos.y; + //force vvc to play only once + vvc->PlayOnce(); + map->AddVVCell( vvc ); + + //set up the summon disable effect + Effect *newfx = EffectQueue::CreateEffect(fx_summon_disable_ref, 0, 1, FX_DURATION_ABSOLUTE); + if (newfx) { + newfx->Duration = vvc->GetSequenceDuration(AI_UPDATE_TIME)*9/10 + core->GetGame()->GameTime; + ApplyEffect(newfx, ab, ab); + } + } + } + + //remove the xp value of friendly summons + if (ab->BaseStats[IE_EA]SetBase(IE_XPVALUE, 0); + } + if (fx) { + ApplyEffect(fx, ab, Owner); + } + + //this check should happen after the fact + level -= ab->GetBase(IE_XP); + if(level<0 || ab->GetBase(IE_XP) == 0) { + break; + } + + } + return ab; +} + +void Interface::RedrawControls(const char *varname, unsigned int value) +{ + for (unsigned int i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win != NULL && win->Visible!=WINDOW_INVALID) { + win->RedrawControls(varname, value); + } + } +} + +void Interface::RedrawAll() +{ + for (unsigned int i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win != NULL && win->Visible!=WINDOW_INVALID) { + win->Invalidate(); + } + } +} + +/** Loads a WindowPack (CHUI file) in the Window Manager */ +bool Interface::LoadWindowPack(const char* name) +{ + DataStream* stream = gamedata->GetResource( name, IE_CHU_CLASS_ID ); + if (stream == NULL) { + printMessage( "Interface", "Error: Cannot find ", LIGHT_RED ); + printf( "%s.chu\n", name ); + return false; + } + if (!GetWindowMgr()->Open( stream, true )) { + printMessage( "Interface", "Error: Cannot Load ", LIGHT_RED ); + printf( "%s.chu\n", name ); + return false; + } + + strncpy( WindowPack, name, sizeof( WindowPack ) ); + WindowPack[sizeof( WindowPack ) - 1] = '\0'; + + return true; +} + +/** Loads a Window in the Window Manager */ +int Interface::LoadWindow(unsigned short WindowID) +{ + unsigned int i; + + for (i = 0; i < windows.size(); i++) { + Window *win = windows[i]; + if (win == NULL) + continue; + if (win->Visible==WINDOW_INVALID) { + continue; + } + if (win->WindowID == WindowID && + !strnicmp( WindowPack, win->WindowPack, sizeof(WindowPack) )) { + SetOnTop( i ); + win->Invalidate(); + return i; + } + } + Window* win = windowmgr->GetWindow( WindowID ); + if (win == NULL) { + return -1; + } + memcpy( win->WindowPack, WindowPack, sizeof(WindowPack) ); + + int slot = -1; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) { + slot = i; + break; + } + } + if (slot == -1) { + windows.push_back( win ); + slot = ( int ) windows.size() - 1; + } else { + windows[slot] = win; + } + win->Invalidate(); + return slot; +} +// FIXME: it's a clone of LoadWindow +/** Creates a Window in the Window Manager */ +int Interface::CreateWindow(unsigned short WindowID, int XPos, int YPos, unsigned int Width, unsigned int Height, char* Background) +{ + unsigned int i; + + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) + continue; + if (windows[i]->WindowID == WindowID && !stricmp( WindowPack, + windows[i]->WindowPack )) { + SetOnTop( i ); + windows[i]->Invalidate(); + return i; + } + } + + Window* win = new Window( WindowID, (ieWord) XPos, (ieWord) YPos, (ieWord) Width, (ieWord) Height ); + if (Background[0]) { + ResourceHolder mos(Background); + if (mos != NULL) { + win->SetBackGround( mos->GetSprite2D(), true ); + } else { + printf( "[Core]: Cannot Load BackGround, skipping\n" ); + } + } + + strcpy( win->WindowPack, WindowPack ); + + int slot = -1; + for (i = 0; i < windows.size(); i++) { + if (windows[i] == NULL) { + slot = i; + break; + } + } + if (slot == -1) { + windows.push_back( win ); + slot = ( int ) windows.size() - 1; + } else { + windows[slot] = win; + } + win->Invalidate(); + return slot; +} + +/** Sets a Window on the Top */ +void Interface::SetOnTop(int Index) +{ + std::vector::iterator t; + for(t = topwin.begin(); t != topwin.end(); ++t) { + if((*t) == Index) { + topwin.erase(t); + break; + } + } + if(topwin.size() != 0) + topwin.insert(topwin.begin(), Index); + else + topwin.push_back(Index); +} +/** Add a window to the Window List */ +void Interface::AddWindow(Window * win) +{ + int slot = -1; + for(unsigned int i = 0; i < windows.size(); i++) { + Window *w = windows[i]; + + if(w==NULL) { + slot = i; + break; + } + } + if(slot == -1) { + windows.push_back(win); + slot=(int)windows.size()-1; + } + else + windows[slot] = win; + win->Invalidate(); +} + +/** Get a Control on a Window */ +int Interface::GetControl(unsigned short WindowIndex, unsigned long ControlID) const +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + int i = 0; + while (true) { + Control* ctrl = win->GetControl( (unsigned short) i ); + if (ctrl == NULL) + return -1; + if (ctrl->ControlID == ControlID) + return i; + i++; + } +} +/** Adjust the Scrolling factor of a control (worldmap atm) */ +int Interface::AdjustScrolling(unsigned short WindowIndex, + unsigned short ControlIndex, short x, short y) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + switch(ctrl->ControlType) { + case IE_GUI_WORLDMAP: + ((WorldMapControl *) ctrl)->AdjustScrolling(x,y); + break; + default: //doesn't work for these + return -1; + } + return 0; +} + +/** Set the Text of a Control */ +int Interface::SetText(unsigned short WindowIndex, + unsigned short ControlIndex, const char* string) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + return ctrl->SetText( string ); +} +/** Set the Tooltip text of a Control */ +int Interface::SetTooltip(unsigned short WindowIndex, + unsigned short ControlIndex, const char* string) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + return ctrl->SetTooltip( string ); +} + +void Interface::DisplayTooltip(int x, int y, Control *ctrl) +{ + if (tooltip_ctrl && tooltip_ctrl == ctrl && tooltip_x == x && tooltip_y == y) + return; + tooltip_x = x; + tooltip_y = y; + tooltip_currtextw = 0; + tooltip_ctrl = ctrl; +} + +int Interface::GetVisible(unsigned short WindowIndex) const +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + return win->Visible; +} +/** Set a Window Visible Flag */ +int Interface::SetVisible(unsigned short WindowIndex, int visible) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + return -1; + } + if (visible!=WINDOW_FRONT) { + win->Visible = (char) visible; + } + switch (visible) { + case WINDOW_GRAYED: + win->Invalidate(); + //here is a fallthrough + case WINDOW_INVISIBLE: + //hiding the viewport if the gamecontrol window was made invisible + if (win->WindowID==65535) { + video->SetViewport( 0,0,0,0 ); + } + evntmgr->DelWindow( win ); + break; + + case WINDOW_VISIBLE: + if (win->WindowID==65535) { + video->SetViewport( win->XPos, win->YPos, win->Width, win->Height); + } + //here is a fallthrough + case WINDOW_FRONT: + if (win->Visible==WINDOW_VISIBLE) { + evntmgr->AddWindow( win ); + } + win->Invalidate(); + SetOnTop( WindowIndex ); + break; + } + return 0; +} + + +/** Set the Status of a Control in a Window */ +int Interface::SetControlStatus(unsigned short WindowIndex, + unsigned short ControlIndex, unsigned long Status) +{ + //don't set the status of an already invalidated window + Window* win = GetWindow(WindowIndex); + if (win == NULL) { + return -1; + } + Control* ctrl = win->GetControl( ControlIndex ); + if (ctrl == NULL) { + return -1; + } + if (Status&IE_GUI_CONTROL_FOCUSED) { + evntmgr->SetFocused( win, ctrl); + } + if (ctrl->ControlType != ((Status >> 24) & 0xff) ) { + return -2; + } + switch (ctrl->ControlType) { + case IE_GUI_BUTTON: + //Button + { + Button* btn = ( Button* ) ctrl; + btn->SetState( ( unsigned char ) ( Status & 0x7f ) ); + } + break; + default: + ctrl->Value = Status & 0x7f; + break; + } + return 0; +} + +/** Show a Window in Modal Mode */ +int Interface::ShowModal(unsigned short WindowIndex, int Shadow) +{ + if (WindowIndex >= windows.size()) { + printMessage( "Core", "Window not found", LIGHT_RED ); + return -1; + } + Window* win = windows[WindowIndex]; + if (win == NULL) { + printMessage( "Core", "Window already freed", LIGHT_RED ); + return -1; + } + win->Visible = WINDOW_FRONT; + //don't destroy the other window handlers + //evntmgr->Clear(); + SetOnTop( WindowIndex ); + evntmgr->AddWindow( win ); + evntmgr->SetFocused( win, NULL ); + + ModalWindow = NULL; + DrawWindows(); + win->Invalidate(); + + Color gray = { + 0, 0, 0, 128 + }; + Color black = { + 0, 0, 0, 255 + }; + + Region r( 0, 0, Width, Height ); + + if (Shadow == MODAL_SHADOW_GRAY) { + video->DrawRect( r, gray ); + } else if (Shadow == MODAL_SHADOW_BLACK) { + video->DrawRect( r, black ); + } + + ModalWindow = win; + return 0; +} + +bool Interface::IsFreezed() +{ + return !update_scripts; +} + +void Interface::GameLoop(void) +{ + update_scripts = false; + GameControl *gc = GetGameControl(); + if (gc) { + update_scripts = !(gc->GetDialogueFlags() & DF_FREEZE_SCRIPTS); + } + + GSUpdate(update_scripts); + + //i'm not sure if this should be here + + //in multi player (if we ever get to it), only the server must call this + if (update_scripts) { + if ( game->selected.size() > 0 ) { + gc->ChangeMap(GetFirstSelectedPC(true), false); + } + // the game object will run the area scripts as well + game->UpdateScripts(); + } +} + +/** handles hardcoded gui behaviour */ +void Interface::HandleGUIBehaviour(void) +{ + GameControl *gc = GetGameControl(); + if (gc) { + //this variable is used all over in the following hacks + int flg = gc->GetDialogueFlags(); + + //the following part is a series of hardcoded gui behaviour + + //initiating dialog + if (flg & DF_IN_DIALOG) { + // -3 noaction + // -2 close + // -1 open + // choose option + ieDword var = (ieDword) -3; + vars->Lookup("DialogChoose", var); + if ((int) var == -2) { + gc->dialoghandler->EndDialog(); + } else if ( (int)var !=-3) { + gc->dialoghandler->DialogChoose(var); + if (!(gc->GetDialogueFlags() & (DF_OPENCONTINUEWINDOW | DF_OPENENDWINDOW))) + guiscript->RunFunction( "GUIWORLD", "NextDialogState" ); + + // the last node of a dialog can have a new-dialog action! don't interfere in that case + ieDword newvar = 0; vars->Lookup("DialogChoose", newvar); + if (var == (ieDword) -1 || newvar != (ieDword) -1) { + vars->SetAt("DialogChoose", (ieDword) -3); + } + } + if (flg & DF_OPENCONTINUEWINDOW) { + guiscript->RunFunction( "GUIWORLD", "OpenContinueMessageWindow" ); + gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND); + } else if (flg & DF_OPENENDWINDOW) { + guiscript->RunFunction( "GUIWORLD", "OpenEndMessageWindow" ); + gc->SetDialogueFlags(DF_OPENCONTINUEWINDOW|DF_OPENENDWINDOW, BM_NAND); + } + } + + //handling container + if (CurrentContainer && UseContainer) { + if (!(flg & DF_IN_CONTAINER) ) { + gc->SetDialogueFlags(DF_IN_CONTAINER, BM_OR); + guiscript->RunFunction( "CommonWindow", "OpenContainerWindow" ); + } + } else { + if (flg & DF_IN_CONTAINER) { + gc->SetDialogueFlags(DF_IN_CONTAINER, BM_NAND); + guiscript->RunFunction( "CommonWindow", "CloseContainerWindow" ); + } + } + //end of gui hacks + } +} + +void Interface::DrawWindows(void) +{ + //here comes the REAL drawing of windows + if (ModalWindow) { + ModalWindow->DrawWindow(); + return; + } + size_t i = topwin.size(); + while(i--) { + unsigned int t = topwin[i]; + + if ( t >=windows.size() ) + continue; + + //visible ==1 or 2 will be drawn + Window* win = windows[t]; + if (win != NULL) { + if (win->Visible == WINDOW_INVALID) { + topwin.erase(topwin.begin()+i); + evntmgr->DelWindow( win ); + delete win; + windows[t]=NULL; + } else if (win->Visible) { + win->DrawWindow(); + } + } + } +} + +void Interface::DrawTooltip () +{ + if (! tooltip_ctrl || !tooltip_ctrl->Tooltip) + return; + + Font* fnt = GetFont( TooltipFont ); + char *tooltip_text = tooltip_ctrl->Tooltip; + + int w1 = 0; + int w2 = 0; + int strw = fnt->CalcStringWidth( tooltip_text ) + 8; + int w = strw; + int h = fnt->maxHeight; + + if (TooltipBack) { + // animate BG tooltips + // TODO: make tooltip animation an option instead + // of following hard-coded check! + if (TooltipMargin == 5) { + // TODO: make speed an option + int tooltip_anim_speed = 15; + if (tooltip_currtextw < strw) { + tooltip_currtextw += tooltip_anim_speed; + } + if (tooltip_currtextw > strw) { + tooltip_currtextw = strw; + } + w = tooltip_currtextw; + } + + h = TooltipBack[0]->Height; + w1 = TooltipBack[1]->Width; + w2 = TooltipBack[2]->Width; + w += TooltipMargin*2; + strw += TooltipMargin*2; + //multiline in case of too much text + if (w>TooltipBack[0]->Width) + strw=w=TooltipBack[0]->Width; + else if (strw>TooltipBack[0]->Width) + strw=TooltipBack[0]->Width; + } + + int strx = tooltip_x - strw / 2; + int y = tooltip_y - h / 2; + // Ensure placement within the screen + if (strx < 0) strx = 0; + else if (strx + strw + w1 + w2 > Width) + strx = Width - strw - w1 - w2; + if (y < 0) y = 0; + else if (y + h > Height) + y = Height - h; + + int x = strx + ((strw - w) / 2); + + // FIXME: take back[0] from center, not from left end + Region r2 = Region( x, y, w, h ); + if (TooltipBack) { + video->BlitSprite( TooltipBack[0], x + TooltipMargin, y, true, &r2 ); + video->BlitSprite( TooltipBack[1], x, y, true ); + video->BlitSprite( TooltipBack[2], x + w, y, true ); + } + + if (TooltipBack) { + r2.x+=TooltipMargin; + strx+=TooltipMargin; + } + Region textr = Region( strx, y, strw, h ); + fnt->Print( r2, textr, (ieByte *) tooltip_text, NULL, + IE_FONT_ALIGN_CENTER | IE_FONT_ALIGN_MIDDLE, true ); +} + +//interface for higher level functions, if the window was +//marked for deletion it is not returned +Window* Interface::GetWindow(unsigned short WindowIndex) const +{ + if (WindowIndex < windows.size()) { + Window *win = windows[WindowIndex]; + if (win && (win->Visible!=WINDOW_INVALID) ) { + return win; + } + } + return NULL; +} + +// this function will determine if wnd is a valid window pointer +// by checking if its WindowID is the same as the reference +bool Interface::IsValidWindow(unsigned short WindowID, Window *wnd) const +{ + size_t WindowIndex = windows.size(); + while (WindowIndex--) { + if (windows[WindowIndex] == wnd) { + return wnd->WindowID == WindowID; + } + } + return false; +} + +//this function won't delete the window, just mark it for deletion +//it will be deleted in the next DrawWindows cycle +//regardless, the window deleted is inaccessible for gui scripts and +//other high level functions from now +int Interface::DelWindow(unsigned short WindowIndex) +{ + if (WindowIndex >= windows.size()) { + return -1; + } + Window* win = windows[WindowIndex]; + if ((win == NULL) || (win->Visible==WINDOW_INVALID) ) { + printMessage( "Core", "Window deleted again", LIGHT_RED ); + return -1; + } + if (win == ModalWindow) { + ModalWindow = NULL; + RedrawAll(); //marking windows for redraw + } + evntmgr->DelWindow( win ); + win->release(); + //re-capturing new (old) modal window if any + size_t tw = topwin.size(); + for(size_t i=0;iVisible==WINDOW_FRONT) { + ModalWindow = tmp; + break; + } + } + return 0; +} + +void Interface::DelAllWindows() +{ + vars->SetAt("MessageWindow", (ieDword) ~0); + vars->SetAt("OptionsWindow", (ieDword) ~0); + vars->SetAt("PortraitWindow", (ieDword) ~0); + vars->SetAt("ActionsWindow", (ieDword) ~0); + vars->SetAt("TopWindow", (ieDword) ~0); + vars->SetAt("OtherWindow", (ieDword) ~0); + vars->SetAt("FloatWindow", (ieDword) ~0); + for(unsigned int WindowIndex=0; WindowIndexClear(); + ModalWindow = NULL; +} + +/** Popup the Console */ +void Interface::PopupConsole() +{ + ConsolePopped = !ConsolePopped; + RedrawAll(); + console->Changed = true; +} + +/** Draws the Console */ +void Interface::DrawConsole() +{ + console->Draw( 0, 0 ); +} + +/** Get the Sound Manager */ +SaveGameIterator* Interface::GetSaveGameIterator() const +{ + return sgiterator; +} +/** Sends a termination signal to the Video Driver */ +bool Interface::Quit(void) +{ + return video->Quit(); +} +/** Returns the variables dictionary */ +Variables* Interface::GetDictionary() const +{ + return vars; +} +/** Returns the token dictionary */ +Variables* Interface::GetTokenDictionary() const +{ + return tokens; +} +/** Get the Music Manager */ +MusicMgr* Interface::GetMusicMgr() const +{ + return music.get(); +} +/** Loads an IDS Table, returns -1 on error or the Symbol Table Index on success */ +int Interface::LoadSymbol(const char* ResRef) +{ + int ind = GetSymbolIndex( ResRef ); + if (ind != -1) { + return ind; + } + DataStream* str = gamedata->GetResource( ResRef, IE_IDS_CLASS_ID ); + if (!str) { + return -1; + } + PluginHolder sm(IE_IDS_CLASS_ID); + if (!sm) { + delete str; + return -1; + } + if (!sm->Open( str, true )) { + return -1; + } + Symbol s; + strncpy( s.ResRef, ResRef, 8 ); + s.sm = sm; + ind = -1; + for (size_t i = 0; i < symbols.size(); i++) { + if (!symbols[i].sm) { + ind = ( int ) i; + break; + } + } + if (ind != -1) { + symbols[ind] = s; + return ind; + } + symbols.push_back( s ); + return ( int ) symbols.size() - 1; +} +/** Gets the index of a loaded Symbol Table, returns -1 on error */ +int Interface::GetSymbolIndex(const char* ResRef) const +{ + for (size_t i = 0; i < symbols.size(); i++) { + if (!symbols[i].sm) + continue; + if (strnicmp( symbols[i].ResRef, ResRef, 8 ) == 0) + return ( int ) i; + } + return -1; +} +/** Gets a Loaded Symbol Table by its index, returns NULL on error */ +Holder Interface::GetSymbol(unsigned int index) const +{ + if (index >= symbols.size()) { + return Holder(); + } + if (!symbols[index].sm) { + return Holder(); + } + return symbols[index].sm; +} +/** Frees a Loaded Symbol Table, returns false on error, true on success */ +bool Interface::DelSymbol(unsigned int index) +{ + if (index >= symbols.size()) { + return false; + } + if (!symbols[index].sm) { + return false; + } + symbols[index].sm.release(); + return true; +} +/** Plays a Movie */ +int Interface::PlayMovie(const char* ResRef) +{ + ResourceHolder mp(ResRef); + if (!mp) { + return -1; + } + + ieDword subtitles = 0; + Font *SubtitleFont = NULL; + Palette *palette = NULL; + ieDword *frames = NULL; + ieDword *strrefs = NULL; + int cnt = 0; + int offset = 0; + + //one of these two should exist (they both mean the same thing) + vars->Lookup("Display Movie Subtitles", subtitles); + if (subtitles) { + //HoW flag + cnt=-3; + offset = 3; + } else { + //ToB flag + vars->Lookup("Display Subtitles", subtitles); + } + AutoTable sttable; + if (subtitles && sttable.load(ResRef)) { + cnt += sttable->GetRowCount(); + if (cnt>0) { + frames = (ieDword *) malloc(cnt * sizeof(ieDword) ); + strrefs = (ieDword *) malloc(cnt * sizeof(ieDword) ); + } else { + cnt = 0; + } + if (frames && strrefs) { + for (int i=0;iQueryField(i+offset, 0) ); + strrefs[i] = atoi (sttable->QueryField(i+offset, 1) ); + } + } + int r = atoi(sttable->QueryField("red", "frame")); + int g = atoi(sttable->QueryField("green", "frame")); + int b = atoi(sttable->QueryField("blue", "frame")); + SubtitleFont = GetFont (MovieFont); //will change + if (r || g || b) { + if (SubtitleFont) { + Color fore = {(unsigned char) r,(unsigned char) g,(unsigned char) b, 0x00}; + Color back = {0x00, 0x00, 0x00, 0x00}; + palette = CreatePalette( fore, back ); + } + } + } + + //shutting down music and ambients before movie + if (music) + music->HardEnd(); + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + video->SetMovieFont(SubtitleFont, palette ); + mp->CallBackAtFrames(cnt, frames, strrefs); + mp->Play(); + gamedata->FreePalette( palette ); + if (frames) + free(frames); + if (strrefs) + free(strrefs); + //restarting music + if (music) + music->Start(); + if (ambim) ambim->activate(); + //this will fix redraw all windows as they looked like + //before the movie + RedrawAll(); + + //Setting the movie name to 1 + vars->SetAt( ResRef, 1 ); + return 0; +} + +int Interface::Roll(int dice, int size, int add) const +{ + if (dice < 1) { + return add; + } + if (size < 1) { + return add; + } + if (dice > 100) { + return add + dice * size / 2; + } + for (int i = 0; i < dice; i++) { + add += rand() % size + 1; + } + return add; +} + +static char bmp_suffix[6]="M.BMP"; +static char png_suffix[6]="M.PNG"; + +int Interface::GetPortraits(TextArea* ta, bool smallorlarge) +{ + int count = 0; + char Path[_MAX_PATH]; + + if (smallorlarge) { + bmp_suffix[0]='S'; + png_suffix[0]='S'; + } else { + bmp_suffix[0]='M'; + png_suffix[0]='M'; + } + PathJoin( Path, GamePath, GamePortraitsPath, NULL ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (dir.IsDirectory()) + continue; + strupr(name); + char *pos = strstr(name,bmp_suffix); + if (!pos && IsAvailable(IE_PNG_CLASS_ID) ) { + pos = strstr(name,png_suffix); + } + if (!pos) continue; + pos[1]=0; + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +int Interface::GetCharSounds(TextArea* ta) +{ + bool hasfolders; + int count = 0; + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameSoundsPath, NULL ); + hasfolders = ( HasFeature( GF_SOUNDFOLDERS ) != 0 ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (hasfolders == !dir.IsDirectory()) + continue; + if (!hasfolders) { + strupr(name); + char *pos = strstr(name,"A.WAV"); + if (!pos) continue; + *pos=0; + } + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +int Interface::GetCharacters(TextArea* ta) +{ + int count = 0; + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameCharactersPath, NULL ); + DirectoryIterator dir(Path); + if (!dir) { + return -1; + } + printf( "Looking in %s\n", Path ); + do { + char *name = dir.GetName(); + if (name[0] == '.') + continue; + if (dir.IsDirectory()) + continue; + strupr(name); + char *pos = strstr(name,".CHR"); + if (!pos) continue; + *pos=0; + count++; + ta->AppendText( name, -1 ); + } while (++dir); + return count; +} + +bool Interface::LoadINI(const char* filename) +{ + FILE* config; + config = fopen( filename, "rb" ); + if (config == NULL) { + return false; + } + char name[65], value[_MAX_PATH + 3]; + while (!feof( config )) { + name[0] = 0; + value[0] = 0; + char rem; + + if (fread( &rem, 1, 1, config ) != 1) + break; + + if (( rem == '#' ) || + ( rem == '[' ) || + ( rem == '\r' ) || + ( rem == '\n' ) || + ( rem == ';' )) { + if (rem == '\r') { + fgetc( config ); + continue; + } else if (rem == '\n') + continue; + + //it should always return zero + if (fscanf( config, "%*[^\r\n]%*[\r\n]" )!=0) + break; + continue; + } + fseek( config, -1, SEEK_CUR ); + //the * element is not counted + if (fscanf( config, "%[^=]=%[^\r\n]%*[\r\n]", name, value )!=2) + continue; + if (( value[0] >= '0' ) && ( value[0] <= '9' )) { + vars->SetAt( name, atoi( value ) ); + } + } + fclose( config ); + return true; +} + +/** Enables/Disables the Cut Scene Mode */ +void Interface::SetCutSceneMode(bool active) +{ + GameControl *gc = GetGameControl(); + if (gc) { + gc->SetCutSceneMode( active ); + } + if (game) { + if (active) { + game->ControlStatus |= CS_HIDEGUI; + } else { + game->ControlStatus &= ~CS_HIDEGUI; + } + SetEventFlag(EF_CONTROL); + } + video->SetMouseEnabled(!active); +} + +bool Interface::InCutSceneMode() const +{ + return (GetGameControl()->GetScreenFlags()&SF_DISABLEMOUSE)!=0; +} + +void Interface::QuitGame(int BackToMain) +{ + SetCutSceneMode(false); + if (timer) { + //clear cutscenes + //clear fade/screenshake effects + timer->Init(); + timer->SetFadeFromColor(0); + } + + DelAllWindows(); //delete all windows, including GameControl + + //shutting down ingame music + //(do it before deleting the game) + if (music) { + music->HardEnd(); + } + // stop any ambients which are still enqueued + if (AudioDriver) { + AmbientMgr *ambim = AudioDriver->GetAmbientMgr(); + if (ambim) ambim->deactivate(); + } + //delete game, worldmap + if (game) { + delete game; + game=NULL; + } + if (worldmap) { + delete worldmap; + worldmap=NULL; + } + if (BackToMain) { + strcpy(NextScript, "Start"); + QuitFlag |= QF_CHANGESCRIPT; + } + GSUpdate(true); +} + +void Interface::SetupLoadGame(Holder sg, int ver_override) +{ + LoadGameIndex = sg; + VersionOverride = ver_override; + QuitFlag |= QF_LOADGAME; +} + +void Interface::LoadGame(SaveGame *sg, int ver_override) +{ + // This function has rather painful error handling, + // as it should swap all the objects or none at all + // and the loading can fail for various reasons + + // Yes, it uses goto. Other ways seemed too awkward for me. + + strings->CloseAux(); + tokens->RemoveAll(NULL); //clearing the token dictionary + + if(calendar) delete calendar; + calendar = new Calendar; + + DataStream* gam_str = NULL; + DataStream* sav_str = NULL; + DataStream* wmp_str1 = NULL; + DataStream* wmp_str2 = NULL; + + Game* new_game = NULL; + WorldMapArray* new_worldmap = NULL; + + LoadProgress(10); + if (!KeepCache) DelTree((const char *) CachePath, true); + LoadProgress(15); + + if (sg == NULL) { + //Load the Default Game + gam_str = gamedata->GetResource( GameNameResRef, IE_GAM_CLASS_ID ); + sav_str = NULL; + wmp_str1 = gamedata->GetResource( WorldMapName[0], IE_WMP_CLASS_ID ); + if (WorldMapName[1][0]) { + wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + } + } else { + gam_str = sg->GetGame(); + sav_str = sg->GetSave(); + wmp_str1 = sg->GetWmap(0); + if (WorldMapName[1][0]) { + wmp_str2 = sg->GetWmap(1); + if (!wmp_str2) { + //upgrade an IWD game to HOW + wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + } + } + } + + // These are here because of the goto + PluginHolder gam_mgr(IE_GAM_CLASS_ID); + PluginHolder wmp_mgr(IE_WMP_CLASS_ID); + + if (!gam_str || !(wmp_str1 || wmp_str2) ) + goto cleanup; + + // Load GAM file + if (!gam_mgr) + goto cleanup; + + if (!gam_mgr->Open( gam_str, true )) + goto cleanup; + + new_game = gam_mgr->LoadGame(new Game(), ver_override); + if (!new_game) + goto cleanup; + + gam_str = NULL; + + // Load WMP (WorldMap) file + if (!wmp_mgr) + goto cleanup; + + if (!wmp_mgr->Open( wmp_str1, wmp_str2, true )) + goto cleanup; + + new_worldmap = wmp_mgr->GetWorldMapArray( ); + + wmp_str1 = NULL; + wmp_str2 = NULL; + + LoadProgress(20); + // Unpack SAV (archive) file to Cache dir + if (sav_str) { + PluginHolder ai(IE_BIF_CLASS_ID); + if (ai) { + if (ai->DecompressSaveGame(sav_str) != GEM_OK) { + goto cleanup; + } + } + delete sav_str; + sav_str = NULL; + } + + // Let's assume that now is everything loaded OK and swap the objects + + delete game; + delete worldmap; + + game = new_game; + worldmap = new_worldmap; + + strings->OpenAux(); + LoadProgress(70); + return; +cleanup: + // Something went wrong, so try to clean after itself + + delete new_game; + delete new_worldmap; + + delete gam_str; + delete wmp_str1; + delete wmp_str2; + delete sav_str; +} + +/* swapping out old resources */ +void Interface::UpdateMasterScript() +{ + if (game) { + game->SetScript( GlobalScript, 0 ); + } + + PluginHolder wmp_mgr(IE_WMP_CLASS_ID); + if (! wmp_mgr) + return; + + if (worldmap) { + DataStream *wmp_str1 = gamedata->GetResource( WorldMapName[0], IE_WMP_CLASS_ID ); + DataStream *wmp_str2 = gamedata->GetResource( WorldMapName[1], IE_WMP_CLASS_ID ); + + if (!wmp_mgr->Open( wmp_str1, wmp_str2, true )) { + delete wmp_str1; + delete wmp_str2; + } + + delete worldmap; + worldmap = wmp_mgr->GetWorldMapArray(); + } +} + +bool Interface::HideGCWindow() +{ + Window *window = GetWindow( 0 ); + // in the beginning, there's no window at all + if (! window) + return false; + + Control* gc = window->GetControl(0); + if (gc->ControlType!=IE_GUI_GAMECONTROL) { + return false; + } + SetVisible(0, WINDOW_INVISIBLE); + return true; +} + +void Interface::UnhideGCWindow() +{ + Window *window = GetWindow( 0 ); + if (!window) + return; + Control* gc = window->GetControl(0); + if (gc->ControlType!=IE_GUI_GAMECONTROL) + return; + SetVisible(0, WINDOW_VISIBLE); +} + +GameControl *Interface::GetGameControl() const +{ + Window *window = GetWindow( 0 ); + // in the beginning, there's no window at all + if (! window) + return NULL; + + Control* gc = window->GetControl(0); + if (gc == NULL) { + return NULL; + } + if (gc->ControlType!=IE_GUI_GAMECONTROL) { + return NULL; + } + return (GameControl *) gc; +} + +bool Interface::InitItemTypes() +{ + if (slotmatrix) { + free(slotmatrix); + } + AutoTable it("itemtype"); + ItemTypes = 0; + if (it) { + ItemTypes = it->GetRowCount(); //number of itemtypes + if (ItemTypes<0) { + ItemTypes = 0; + } + int InvSlotTypes = it->GetColumnCount(); + if (InvSlotTypes > 32) { //bit count limit + InvSlotTypes = 32; + } + //make sure unsigned int is 32 bits + slotmatrix = (ieDword *) malloc(ItemTypes * sizeof(ieDword) ); + for (int i=0;iQueryField(i,j),NULL,0) ) { + value |= k; + } + k <<= 1; + } + //we let any items in the inventory + slotmatrix[i] = (ieDword) value | SLOT_INVENTORY; + } + } + + //slottype describes the inventory structure + Inventory::Init(HasFeature(GF_MAGICBIT)); + AutoTable st("slottype"); + if (slottypes) { + free(slottypes); + slottypes = NULL; + } + SlotTypes = 0; + if (st) { + SlotTypes = st->GetRowCount(); + //make sure unsigned int is 32 bits + slottypes = (SlotType *) malloc(SlotTypes * sizeof(SlotType) ); + memset(slottypes, -1, SlotTypes * sizeof(SlotType) ); + for (unsigned int row = 0; row < SlotTypes; row++) { + bool alias; + unsigned int i = (ieDword) strtol(st->GetRowName(row),NULL,0 ); + if (i>=SlotTypes) continue; + if (slottypes[i].sloteffects!=0xffffffffu) { + slottypes[row].slot = i; + i=row; + alias = true; + } else { + slottypes[row].slot = i; + alias = false; + } + slottypes[i].slottype = (ieDword) strtol(st->QueryField(row,0),NULL,0 ); + slottypes[i].slotid = (ieDword) strtol(st->QueryField(row,1),NULL,0 ); + strnlwrcpy( slottypes[i].slotresref, st->QueryField(row,2), 8 ); + slottypes[i].slottip = (ieDword) strtol(st->QueryField(row,3),NULL,0 ); + slottypes[i].slotflags = (ieDword) strtol(st->QueryField(row,5),NULL,0 ); + //don't fill sloteffects for aliased slots (pst) + if (alias) { + continue; + } + slottypes[i].sloteffects = (ieDword) strtol(st->QueryField(row,4),NULL,0 ); + //setting special slots + if (slottypes[i].slottype&SLOT_ITEM) { + if (slottypes[i].slottype&SLOT_INVENTORY) { + Inventory::SetInventorySlot(i); + } else { + Inventory::SetQuickSlot(i); + } + } + switch (slottypes[i].sloteffects) { + //fist slot, not saved, default weapon + case SLOT_EFFECT_FIST: Inventory::SetFistSlot(i); break; + //magic weapon slot, overrides all weapons + case SLOT_EFFECT_MAGIC: Inventory::SetMagicSlot(i); break; + //weapon slot, Equipping marker is relative to it + case SLOT_EFFECT_MELEE: Inventory::SetWeaponSlot(i); break; + //ranged slot + case SLOT_EFFECT_MISSILE: Inventory::SetRangedSlot(i); break; + //right hand + case SLOT_EFFECT_LEFT: Inventory::SetShieldSlot(i); break; + //head (for averting critical hit) + case SLOT_EFFECT_HEAD: Inventory::SetHeadSlot(i); break; + default:; + } + } + } + return (it && st); +} + +ieDword Interface::FindSlot(unsigned int idx) const +{ + ieDword i; + + for (i=0;i=SlotTypes) { + return 0; + } + return slottypes[idx].slot; +} + +ieDword Interface::QuerySlotType(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slottype; +} + +ieDword Interface::QuerySlotID(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slotid; +} + +ieDword Interface::QuerySlottip(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slottip; +} + +ieDword Interface::QuerySlotEffects(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].sloteffects; +} + +ieDword Interface::QuerySlotFlags(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return 0; + } + return slottypes[idx].slotflags; +} + +const char *Interface::QuerySlotResRef(unsigned int idx) const +{ + if (idx>=SlotTypes) { + return ""; + } + return slottypes[idx].slotresref; +} + +// checks the itemtype vs. slottype, and also checks the usability flags +// vs. Actor's stats (alignment, class, race, kit etc.) +int Interface::CanUseItemType(int slottype, Item *item, Actor *actor, bool feedback, bool equipped) const +{ + //inventory is a special case, we allow any items to enter it + if ( slottype==-1 ) { + return SLOT_INVENTORY; + } + //if we look for ALL slot types, then SLOT_SHIELD shouldn't interfere + //with twohandedness + //As long as this is an Item, use the ITEM constant + //switch for IE_INV_ITEM_* if it is a CREItem + if (item->Flags&IE_ITEM_TWO_HANDED) { + //if the item is twohanded and there are more slots, drop the shield slot + if (slottype&~SLOT_SHIELD) { + slottype&=~SLOT_SHIELD; + } + if (slottype&SLOT_SHIELD) { + //cannot equip twohanded in offhand + if (feedback) displaymsg->DisplayConstantString(STR_NOT_IN_OFFHAND, 0xf0f0f0); + return 0; + } + } + + if ( (unsigned int) item->ItemType>=(unsigned int) ItemTypes) { + //invalid itemtype + if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0); + return 0; + } + + //if actor is supplied, check its usability fields + if (actor) { + //constant strings + int idx = actor->Unusable(item); + if (idx) { + if (feedback) displaymsg->DisplayConstantString(idx, 0xf0f0f0); + return 0; + } + //custom strings + ieStrRef str = actor->Disabled(item->Name, item->ItemType); + if (str && !equipped) { + if (feedback) displaymsg->DisplayString(str, 0xf0f0f0, 0); + return 0; + } + } + + //if any bit is true, the answer counts as true + int ret = (slotmatrix[item->ItemType]&slottype); + + if (!ret) { + if (feedback) displaymsg->DisplayConstantString(STR_WRONGITEMTYPE, 0xf0f0f0); + return 0; + } + + //this warning comes only when feedback is enabled + if (feedback) { + //this was, but that disabled equipping of amber earrings in PST + //if (slotmatrix[item->ItemType]&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) { + if (ret&(SLOT_QUIVER|SLOT_WEAPON|SLOT_ITEM)) { + //don't ruin the return variable, it contains the usable slot bits + int flg = 0; + if (ret&SLOT_QUIVER) { + if (item->GetWeaponHeader(true)) flg = 1; + } + + if (ret&SLOT_WEAPON) { + //melee + if (item->GetWeaponHeader(false)) flg = 1; + //ranged + if (item->GetWeaponHeader(true)) flg = 1; + } + + if (ret&SLOT_ITEM) { + if (item->GetEquipmentHeaderNumber(0)!=0xffff) flg = 1; + } + + if (!flg) { + displaymsg->DisplayConstantString(STR_UNUSABLEITEM, 0xf0f0f0); + return 0; + } + } + } + + return ret; +} + +Label *Interface::GetMessageLabel() const +{ + ieDword WinIndex = (ieDword) -1; + ieDword TAIndex = (ieDword) -1; + + vars->Lookup( "OtherWindow", WinIndex ); + if (( WinIndex != (ieDword) -1 ) && + ( vars->Lookup( "MessageLabel", TAIndex ) )) { + Window* win = GetWindow( (unsigned short) WinIndex ); + if (win) { + Control *ctrl = win->GetControl( (unsigned short) TAIndex ); + if (ctrl && ctrl->ControlType==IE_GUI_LABEL) + return (Label *) ctrl; + } + } + return NULL; +} + +TextArea *Interface::GetMessageTextArea() const +{ + ieDword WinIndex = (ieDword) -1; + ieDword TAIndex = (ieDword) -1; + + vars->Lookup( "MessageWindow", WinIndex ); + if (( WinIndex != (ieDword) -1 ) && + ( vars->Lookup( "MessageTextArea", TAIndex ) )) { + Window* win = GetWindow( (unsigned short) WinIndex ); + if (win) { + Control *ctrl = win->GetControl( (unsigned short) TAIndex ); + if (ctrl && ctrl->ControlType==IE_GUI_TEXTAREA) + return (TextArea *) ctrl; + } + } + return NULL; +} + +static const char *saved_extensions[]={".are",".sto",0}; +static const char *saved_extensions_last[]={".tot",".toh",0}; + +//returns the priority of the file to be saved +//2 - save +//1 - save last +//0 - don't save +int Interface::SavedExtension(const char *filename) +{ + const char *str=strchr(filename,'.'); + if (!str) return 0; + int i=0; + while(saved_extensions[i]) { + if (!stricmp(saved_extensions[i], str) ) return 2; + i++; + } + i=0; + while(saved_extensions_last[i]) { + if (!stricmp(saved_extensions_last[i], str) ) return 1; + i++; + } + return 0; +} + +static const char *protected_extensions[]={".exe",".dll",".so",0}; + +//returns true if file should be saved +bool Interface::ProtectedExtension(const char *filename) +{ + const char *str=strchr(filename,'.'); + if (!str) return false; + int i=0; + while(protected_extensions[i]) { + if (!stricmp(protected_extensions[i], str) ) return true; + i++; + } + return false; +} + +void Interface::RemoveFromCache(const ieResRef resref, SClass_ID ClassID) +{ + char filename[_MAX_PATH]; + + snprintf(filename, _MAX_PATH, "%s%.8s%s", CachePath, resref, TypeExt( ClassID ) ); + unlink ( filename); +} + +//this function checks if the path is eligible as a cache +//if it contains a directory, or suspicious file extensions +//we bail out, because the cache will be purged regularly. +bool Interface::StupidityDetector(const char* Pt) +{ + char Path[_MAX_PATH]; + strcpy( Path, Pt ); + DirectoryIterator dir(Path); + if (!dir) { + printf("\n**cannot open**\n"); + return true; + } + do { + const char *name = dir.GetName(); + if (dir.IsDirectory()) { + if (name[0] == '.') { + if (name[1] == '\0') + continue; + if (name[1] == '.' && name[2] == '\0') + continue; + } + printf("\n**contains another dir**\n"); + return true; //a directory in there??? + } + if (ProtectedExtension(name) ) { + printf("\n**contains alien files**\n"); + return true; //an executable file in there??? + } + } while (++dir); + //ok, we got a good conscience + return false; +} + +void Interface::DelTree(const char* Pt, bool onlysave) +{ + char Path[_MAX_PATH]; + + if (!Pt[0]) return; //Don't delete the root filesystem :) + strcpy( Path, Pt ); + DirectoryIterator dir(Path); + if (!dir) { + return; + } + do { + char *name = dir.GetName(); + if (dir.IsDirectory()) + continue; + if (name[0] == '.') + continue; + if (!onlysave || SavedExtension(name) ) { + char dtmp[_MAX_PATH]; + dir.GetFullPath(dtmp); + unlink( dtmp ); + } + } while (++dir); +} + +void Interface::LoadProgress(int percent) +{ + vars->SetAt("Progress", percent); + RedrawControls("Progress", percent); + RedrawAll(); + DrawWindows(); + video->SwapBuffers(); +} + +void Interface::ReleaseDraggedItem() +{ + DraggedItem=NULL; //shouldn't free this + video->SetDragCursor (NULL); +} + +void Interface::DragItem(CREItem *item, const ieResRef Picture) +{ + //We should drop the dragged item and pick this up, + //we shouldn't have a valid DraggedItem at this point. + //Anyway, if there is still a dragged item, it will be destroyed. + if (DraggedItem) { + printMessage("Core","Forgot to call ReleaseDraggedItem when leaving inventory (item destroyed)!\n",YELLOW); + delete DraggedItem; + } + DraggedItem = item; + if (video) { + Sprite2D* DraggedCursor = NULL; + if (item) { + DraggedCursor = gamedata->GetBAMSprite( Picture, 0, 0 ); + } + video->SetDragCursor (DraggedCursor); + } +} + +void Interface::SetDraggedPortrait(int dp, int idx) +{ + if (idx<0) idx=14; + DraggedPortrait = dp; + if (dp) { + //hmm this might work? + Cursors[idx]->acquire(); + video->SetDragCursor(Cursors[idx]); + } else { + video->SetDragCursor(NULL); + } +} + +bool Interface::ReadItemTable(const ieResRef TableName, const char * Prefix) +{ + ieResRef ItemName; + int i,j; + + AutoTable tab(TableName); + if (!tab) { + return false; + } + i=tab->GetRowCount(); + for(j=0;jGetRowName(j), 8); + } + //Variable elements are free'd, so we have to use malloc + //well, not anymore, we can use ReleaseFunction + int l=tab->GetColumnCount(j); + if (l<1) continue; + int cl = atoi(tab->GetColumnName(0)); + ItemList *itemlist = new ItemList(l, cl); + for(int k=0;kResRefs[k],tab->QueryField(j,k), 8); + } + RtRows->SetAt(ItemName, (void*)itemlist); + } + return true; +} + +bool Interface::ReadRandomItems() +{ + ieResRef RtResRef; + int i; + + ieDword difflev=0; //rt norm or rt fury + vars->Lookup("Nightmare Mode", difflev); + if (RtRows) { + RtRows->RemoveAll(ReleaseItemList); + } + else { + RtRows=new Variables(10, 17); //block size, hash table size + if (!RtRows) { + return false; + } + RtRows->SetType( GEM_VARIABLES_POINTER ); + } + AutoTable tab("randitem"); + if (!tab) { + return false; + } + if (difflev>=tab->GetColumnCount()) { + difflev = tab->GetColumnCount()-1; + } + + //the gold item + strnlwrcpy( GoldResRef, tab->QueryField((unsigned int) 0,(unsigned int) 0), 8); + if ( GoldResRef[0]=='*' ) { + return false; + } + strnlwrcpy( RtResRef, tab->QueryField( 1, difflev ), 8); + i=atoi( RtResRef ); + if (i<1) { + ReadItemTable( RtResRef, 0 ); //reading the table itself + return true; + } + if (i>5) { + i=5; + } + while(i--) { + strnlwrcpy( RtResRef, tab->QueryField(2+i,difflev), 8); + ReadItemTable( RtResRef,tab->GetRowName(2+i) ); + } + return true; +} + +CREItem *Interface::ReadItem(DataStream *str) +{ + CREItem *itm = new CREItem(); + + str->ReadResRef( itm->ItemResRef ); + str->ReadWord( &itm->Expired ); + str->ReadWord( &itm->Usages[0] ); + str->ReadWord( &itm->Usages[1] ); + str->ReadWord( &itm->Usages[2] ); + str->ReadDword( &itm->Flags ); + if (ResolveRandomItem(itm) ) { + return itm; + } + delete itm; + return NULL; +} + +#define MAX_LOOP 10 + +//This function generates random items based on the randitem.2da file +//there could be a loop, but we don't want to freeze, so there is a limit +bool Interface::ResolveRandomItem(CREItem *itm) +{ + if (!RtRows) return true; + for(int loop=0;loopLookup( itm->ItemResRef, lookup ) ) { + return true; + } + ItemList *itemlist = (ItemList*)lookup; + if (itemlist->WeightOdds) { + //instead of 1d19 we calculate with 2d10 (which also has 19 possible values) + i=Roll(2,(itemlist->Count+1)/2,-2); + } else { + i=Roll(1,itemlist->Count,-1); + } + strnlwrcpy( NewItem, itemlist->ResRefs[i], 8); + char *p=(char *) strchr(NewItem,'*'); + if (p) { + *p=0; //doing this so endptr is ok + k=strtol(p+1,NULL,10); + } else { + k=1; + } + j=strtol(NewItem,&endptr,10); + if (j<1) { + j=1; + } + if (*endptr) { + strnlwrcpy(itm->ItemResRef, NewItem, 8); + } else { + strnlwrcpy(itm->ItemResRef, GoldResRef, 8); + } + if ( !memcmp( itm->ItemResRef,"no_drop",8 ) ) { + itm->ItemResRef[0]=0; + } + if (!itm->ItemResRef[0]) { + return false; + } + itm->Usages[0]=(ieWord) Roll(j,k,0); + } + printMessage("Interface"," ",LIGHT_RED); + printf("Loop detected while generating random item:%s\n",itm->ItemResRef); + return false; +} + +//now that we store spell name in spl, i guess, we shouldn't pass 'ieResRef name' +//these functions are needed because Win32 doesn't allow freeing memory from +//another dll. So we allocate all commonly used memories from core +ITMExtHeader *Interface::GetITMExt(int count) +{ + return new ITMExtHeader[count]; +} + +SPLExtHeader *Interface::GetSPLExt(int count) +{ + return new SPLExtHeader[count]; +} + +Effect *Interface::GetEffect(ieDword opcode) +{ + if (opcode==0xffffffff) { + return NULL; + } + Effect *fx = new Effect(); + if (!fx) { + return NULL; + } + memset(fx,0,sizeof(Effect)); + fx->Opcode=opcode; + return fx; +} + +Effect *Interface::GetFeatures(int count) +{ + return new Effect[count]; +} + +/* +void Interface::FreeITMExt(ITMExtHeader *p, Effect *e) +{ + delete [] p; + delete [] e; +} + +void Interface::FreeSPLExt(SPLExtHeader *p, Effect *e) +{ + delete [] p; + delete [] e; +} +*/ + +WorldMapArray *Interface::NewWorldMapArray(int count) +{ + return new WorldMapArray(count); +} + +Container *Interface::GetCurrentContainer() +{ + return CurrentContainer; +} + +int Interface::CloseCurrentContainer() +{ + UseContainer = false; + if ( !CurrentContainer) { + return -1; + } + //remove empty ground piles on closeup + CurrentContainer->GetCurrentArea()->TMap->CleanupContainer(CurrentContainer); + CurrentContainer = NULL; + return 0; +} + +void Interface::SetCurrentContainer(Actor *actor, Container *arg, bool flag) +{ + //abort action if the first selected PC isn't the original actor + if (actor!=GetFirstSelectedPC(false)) { + CurrentContainer = NULL; + return; + } + CurrentContainer = arg; + UseContainer = flag; +} + +Store *Interface::GetCurrentStore() +{ + return CurrentStore; +} + +int Interface::CloseCurrentStore() +{ + if ( !CurrentStore ) { + return -1; + } + PluginHolder sm(IE_STO_CLASS_ID); + if (sm == NULL) { + return -1; + } + int size = sm->GetStoredFileSize (CurrentStore); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( CurrentStore->Name, IE_STO_CLASS_ID ); + int ret = sm->PutStore (&str, CurrentStore); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Store removed: %s\n", CurrentStore->Name); + RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID); + } + } else { + printMessage("Core"," ", YELLOW); + printf("Store removed: %s\n", CurrentStore->Name); + RemoveFromCache(CurrentStore->Name, IE_STO_CLASS_ID); + } + //make sure the stream isn't connected to sm, or it will be double freed + delete CurrentStore; + CurrentStore = NULL; + return 0; +} + +Store *Interface::SetCurrentStore(const ieResRef resname, ieDword owner) +{ + if ( CurrentStore ) { + if ( !strnicmp(CurrentStore->Name, resname, 8) ) { + return CurrentStore; + } + + //not simply delete the old store, but save it + CloseCurrentStore(); + } + + DataStream* str = gamedata->GetResource( resname, IE_STO_CLASS_ID ); + PluginHolder sm(IE_STO_CLASS_ID); + if (sm == NULL) { + delete ( str ); + return NULL; + } + if (!sm->Open( str, true )) { + return NULL; + } + + // FIXME - should use some already allocated in core + // not really, only one store is open at a time, then it is + // unloaded, we don't really have to cache it, it will be saved in + // Cache anyway! + CurrentStore = sm->GetStore( new Store() ); + if (CurrentStore == NULL) { + return NULL; + } + strnlwrcpy(CurrentStore->Name, resname, 8); + if (owner) { + CurrentStore->SetOwnerID(owner); + } + return CurrentStore; +} + +void Interface::SetMouseScrollSpeed(int speed) { + mousescrollspd = (speed+1)*2; +} + +int Interface::GetMouseScrollSpeed() { + return mousescrollspd; +} + +ieStrRef Interface::GetRumour(const ieResRef dlgref) +{ + PluginHolder dm(IE_DLG_CLASS_ID); + dm->Open( gamedata->GetResource( dlgref, IE_DLG_CLASS_ID ), true ); + Dialog *dlg = dm->GetDialog(); + + if (!dlg) { + printMessage("Interface"," ", LIGHT_RED); + printf( "Cannot load dialog: %s\n", dlgref ); + return (ieStrRef) -1; + } + Scriptable *pc=game->GetPC( game->GetSelectedPCSingle(), false ); + + ieStrRef ret = (ieStrRef) -1; + int i = dlg->FindRandomState( pc ); + if (i>=0 ) { + ret = dlg->GetState( i )->StrRef; + } + delete dlg; + return ret; +} + +void Interface::DoTheStoreHack(Store *s) +{ + size_t size = s->PurchasedCategoriesCount * sizeof( ieDword ); + s->purchased_categories=(ieDword *) malloc(size); + + size = s->CuresCount * sizeof( STOCure ); + s->cures=(STOCure *) malloc(size); + + size = s->DrinksCount * sizeof( STODrink ); + s->drinks=(STODrink *) malloc(size); + + for(size=0;sizeItemsCount;size++) { + STOItem *si = new STOItem(); + memset(si, 0, sizeof(STOItem) ); + s->items.push_back( si ); + } +} + +//plays stock sound listed in defsound.2da +void Interface::PlaySound(int index) +{ + if (index<=DSCount) { + AudioDriver->Play(DefSound[index]); + } +} + +Actor *Interface::GetFirstSelectedPC(bool forced) +{ + int partySize = game->GetPartySize( false ); + if (!partySize) return NULL; + for (int i = 0; i < partySize; i++) { + Actor* actor = game->GetPC( i,false ); + if (actor->IsSelected()) { + return actor; + } + } + + if (forced) { + return game->GetPC(0,false); + } + return NULL; +} + +Actor *Interface::GetFirstSelectedActor() +{ + if (game->selected.size()) { + return game->selected[0]; + } + return NULL; +} + +//this is used only for the console +Sprite2D *Interface::GetCursorSprite() +{ + Sprite2D *spr = gamedata->GetBAMSprite(CursorBam, 0, 0); + if (spr) + { + if(HasFeature(GF_OVERRIDE_CURSORPOS)) + { + spr->XPos=1; + spr->YPos=spr->Height-1; + } + } + return spr; +} + +Sprite2D *Interface::GetScrollCursorSprite(int frameNum, int spriteNum) +{ + return gamedata->GetBAMSprite(ScrollCursorBam, frameNum, spriteNum); +} + +/* we should return -1 if it isn't gold, otherwise return the gold value */ +int Interface::CanMoveItem(const CREItem *item) const +{ + //This is an inventory slot, switch to IE_ITEM_* if you use Item + if (!HasFeature(GF_NO_DROP_CAN_MOVE) ) { + if (item->Flags & IE_INV_ITEM_UNDROPPABLE) + return 0; + } + //not gold, we allow only one single coin ResRef, this is good + //for all of the original games + if (strnicmp(item->ItemResRef, GoldResRef, 8 ) ) + return -1; + //gold, returns the gold value (stack size) + return item->Usages[0]; +} + +// dealing with applying effects +void Interface::ApplySpell(const ieResRef resname, Actor *actor, Scriptable *caster, int level) +{ + Spell *spell = gamedata->GetSpell(resname); + if (!spell) { + return; + } + + level = spell->GetHeaderIndexFromLevel(level); + EffectQueue *fxqueue = spell->GetEffectBlock(caster, actor->Pos, level); + + ApplyEffectQueue(fxqueue, actor, caster, actor->Pos); + delete fxqueue; +} + +void Interface::ApplySpellPoint(const ieResRef resname, Map* area, const Point &pos, Scriptable *caster, int level) +{ + Spell *spell = gamedata->GetSpell(resname); + if (!spell) { + return; + } + level = spell->GetHeaderIndexFromLevel(level); + Projectile *pro = spell->GetProjectile(caster, level, pos); + pro->SetCaster(caster->GetGlobalID()); + area->AddProjectile(pro, caster->Pos, pos); +} + +//-1 means the effect was reflected back to the caster +//0 means the effect was resisted and should be removed +//1 means the effect was applied +int Interface::ApplyEffect(Effect *effect, Actor *actor, Scriptable *caster) +{ + if (!effect) { + return 0; + } + + EffectQueue *fxqueue = new EffectQueue(); + //AddEffect now copies the fx data, please delete your effect reference + //if you created it. (Don't delete cached references) + fxqueue->AddEffect( effect ); + int res = ApplyEffectQueue(fxqueue, actor, caster); + delete fxqueue; + return res; +} + +int Interface::ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster) +{ + Point p; + p.empty(); //the effect should have all its coordinates already set + return ApplyEffectQueue(fxqueue, actor, caster, p); +} + +int Interface::ApplyEffectQueue(EffectQueue *fxqueue, Actor *actor, Scriptable *caster, Point p) +{ + int res = fxqueue->CheckImmunity ( actor ); + if (res) { + if (res == -1 ) { + //bounced back at a nonliving caster + if (caster->Type!=ST_ACTOR) { + return 0; + } + actor = (Actor *) caster; + } + fxqueue->SetOwner( caster ); + + if (fxqueue->AddAllEffects( actor, p )==FX_NOT_APPLIED) { + res=0; + } + } + return res; +} + +Effect *Interface::GetEffect(const ieResRef resname, int level, const Point &p) +{ + //Don't free this reference, it is cached! + Effect *effect = gamedata->GetEffect(resname); + if (!effect) { + return NULL; + } + if (!level) { + level = 1; + } + effect->Power = level; + effect->PosX=p.x; + effect->PosY=p.y; + return effect; +} + +// dealing with saved games +int Interface::SwapoutArea(Map *map) +{ + PluginHolder mm(IE_ARE_CLASS_ID); + if (mm == NULL) { + return -1; + } + int size = mm->GetStoredFileSize (map); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( map->GetScriptName(), IE_ARE_CLASS_ID ); + int ret = mm->PutArea (&str, map); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Area removed: %s\n", map->GetScriptName()); + RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID); + } + } else { + printMessage("Core"," ", YELLOW); + printf("Area removed: %s\n", map->GetScriptName()); + RemoveFromCache(map->GetScriptName(), IE_ARE_CLASS_ID); + } + //make sure the stream isn't connected to sm, or it will be double freed + return 0; +} + +int Interface::WriteCharacter(const char *name, Actor *actor) +{ + char Path[_MAX_PATH]; + + PathJoin( Path, GamePath, GameCharactersPath, NULL ); + if (!actor) { + return -1; + } + PluginHolder gm(IE_CRE_CLASS_ID); + if (gm == NULL) { + return -1; + } + + //str is freed + { + FileStream str; + + if (!str.Create( Path, name, IE_CHR_CLASS_ID )) + return -1; + + int ret = gm->PutActor(&str, actor, true); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Character cannot be saved: %s\n", name); + return -1; + } + } + + //write the BIO string + if (!HasFeature(GF_NO_BIOGRAPHY)) { + FileStream str; + + str.Create( Path, name, IE_BIO_CLASS_ID ); + //never write the string reference into this string + char *tmp = GetString(actor->GetVerbalConstant(VB_BIO),IE_STR_STRREFOFF); + str.Write (tmp, strlen(tmp)); + free(tmp); + } + return 0; +} + +int Interface::WriteGame(const char *folder) +{ + PluginHolder gm(IE_GAM_CLASS_ID); + if (gm == NULL) { + return -1; + } + + int size = gm->GetStoredFileSize (game); + if (size > 0) { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str; + + str.Create( folder, GameNameResRef, IE_GAM_CLASS_ID ); + int ret = gm->PutGame (&str, game); + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Game cannot be saved: %s\n", folder); + return -1; + } + } else { + printMessage("Core"," ", YELLOW); + printf("Internal error, game cannot be saved: %s\n", folder); + return -1; + } + return 0; +} + +int Interface::WriteWorldMap(const char *folder) +{ + PluginHolder wmm(IE_WMP_CLASS_ID); + if (wmm == NULL) { + return -1; + } + + if (WorldMapName[1][0]) { + worldmap->SetSingle(false); + } + + int size1 = wmm->GetStoredFileSize (worldmap, 0); + int size2 = 1; //just a dummy value + + //if size is 0 for the first worldmap, then there is a problem + if (!worldmap->IsSingle() && (size1>0) ) { + size2=wmm->GetStoredFileSize (worldmap, 1); + } + + int ret = 0; + if ((size1 < 0) || (size2<0) ) { + ret=-1; + } else { + //created streams are always autofree (close file on destruct) + //this one will be destructed when we return from here + FileStream str1; + FileStream str2; + + str1.Create( folder, WorldMapName[0], IE_WMP_CLASS_ID ); + if (!worldmap->IsSingle()) { + str2.Create( folder, WorldMapName[1], IE_WMP_CLASS_ID ); + } + ret = wmm->PutWorldMap (&str1, &str2, worldmap); + } + if (ret <0) { + printMessage("Core"," ", YELLOW); + printf("Internal error, worldmap cannot be saved: %s\n", folder); + return -1; + } + return 0; +} + +int Interface::CompressSave(const char *folder) +{ + FileStream str; + + str.Create( folder, GameNameResRef, IE_SAV_CLASS_ID ); + DirectoryIterator dir(CachePath); + if (!dir) { + return -1; + } + //BIF and SAV are the same + PluginHolder ai(IE_BIF_CLASS_ID); + ai->CreateArchive( &str); + + //.tot and .toh should be saved last, because they are updated when an .are is saved + int priority=2; + while(priority) { + do { + const char *name = dir.GetName(); + if (dir.IsDirectory()) + continue; + if (name[0] == '.') + continue; + if (SavedExtension(name)==priority) { + char dtmp[_MAX_PATH]; + dir.GetFullPath(dtmp); + FileStream fs; + fs.Open(dtmp, true); + ai->AddToSaveGame(&str, &fs); + } + } while (++dir); + //reopen list for the second round + priority--; + if (priority>0) { + dir.Rewind(); + } + } + return 0; +} + +int Interface::GetMaximumAbility() const { return MaximumAbility; } + +int Interface::GetStrengthBonus(int column, int value, int ex) const +{ + //to hit, damage, open doors, weight allowance + if (column<0 || column>3) + return -9999; + + if (value<0) + value = 0; + else if (value>25) + value = 25; + + if (ex<0) + ex=0; + else if (ex>100) + ex=100; + + return strmod[column*(MaximumAbility+1)+value]+strmodex[column*101+ex]; +} + +// we don't use the stuff maze yet +// IE: bonus skill points are ignored and the plain int mod used! +int Interface::GetIntelligenceBonus(int column, int value) const +{ + if (HasFeature(GF_3ED_RULES)) { + //learn spell, max spell level, max spell number on level, bonus skill points + if (column<0 || column>2) return -9999; + } else { + //learn spell, max spell level, max spell number on level, maze duration dice, maze duration dice size + if (column<0 || column>4) return -9999; + } + + return intmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetDexterityBonus(int column, int value) const +{ + //no dexmod in iwd2 and only one type of modifier + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + //reaction, missile, ac + if (column<0 || column>2) + return -9999; + + return dexmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetConstitutionBonus(int column, int value) const +{ + //no conmod in iwd2 + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + //normal, warrior, minimum, regen hp, regen fatigue + if (column<0 || column>4) + return -9999; + + return conmod[column*(MaximumAbility+1)+value]; +} + +int Interface::GetCharismaBonus(int column, int /*value*/) const +{ + // store price reduction + if (column<0 || column>(MaximumAbility-1)) + return -9999; + + return chrmod[column]; +} + +int Interface::GetLoreBonus(int column, int value) const +{ + //no lorebon in iwd2 - lore is a skill + if (HasFeature(GF_3ED_RULES)) return 0; + + if (column<0 || column>0) + return -9999; + + return lorebon[value]; +} + +int Interface::GetWisdomBonus(int column, int value) const +{ + //no wismod in iwd2 + if (HasFeature(GF_3ED_RULES)) { + return (value-10)/2; + } + + if (!HasFeature(GF_WISDOM_BONUS)) return 0; + + // xp bonus + if (column<0 || column>0) + return -9999; + + return wisbon[value]; +} + +int Interface::GetReputationMod(int column) const +{ + int reputation = game->Reputation / 10 - 1; + + if (column<0 || column>8) { + return -9999; + } + if (reputation > 19) { + reputation = 19; + } + if (reputation < 0) { + reputation = 0; + } + + return reputationmod[reputation][column]; +} + +// -3, -2 if request is illegal or in cutscene +// -1 if pause is already active +// 0 if pause was not allowed +// 1 if autopause happened +int Interface::Autopause(ieDword flag) +{ + GameControl *gc = GetGameControl(); + if (!gc) { + return -3; + } + if (InCutSceneMode()) { + return -2; + } + if (gc->GetDialogueFlags()&DF_FREEZE_SCRIPTS) { + return -1; + } + ieDword autopause_flags = 0; + + vars->Lookup("Auto Pause State", autopause_flags); + if (autopause_flags & (1<DisplayConstantString(STR_AP_UNUSABLE+flag, 0xff0000); + gc->SetDialogueFlags(DF_FREEZE_SCRIPTS, BM_OR); + return 1; + } + return 0; +} + +void Interface::RegisterOpcodes(int count, const EffectRef *opcodes) +{ + EffectQueue_RegisterOpcodes(count, opcodes); +} + +void Interface::SetInfoTextColor(const Color &color) +{ + if (InfoTextPalette) { + gamedata->FreePalette(InfoTextPalette); + } + InfoTextPalette = CreatePalette(color, black); +} + +//todo row? +void Interface::GetResRefFrom2DA(const ieResRef resref, ieResRef resource1, ieResRef resource2, ieResRef resource3) +{ + if (!resource1) { + return; + } + resource1[0]=0; + if (resource2) { + resource2[0]=0; + } + if (resource3) { + resource3[0]=0; + } + AutoTable tab(resref); + if (tab) { + unsigned int cols = tab->GetColumnCount(); + unsigned int row = (unsigned int) Roll(1,tab->GetRowCount(),-1); + strnuprcpy(resource1, tab->QueryField(row,0), 8); + if (resource2 && cols>1) + strnuprcpy(resource2, tab->QueryField(row,1), 8); + if (resource3 && cols>2) + strnuprcpy(resource3, tab->QueryField(row,2), 8); + } +} + +ieDword *Interface::GetListFrom2DAInternal(const ieResRef resref) +{ + ieDword *ret; + + AutoTable tab(resref); + if (tab) { + ieDword cnt = tab->GetRowCount(); + ret = (ieDword *) malloc((1+cnt)*sizeof(ieDword)); + ret[0]=cnt; + while(cnt) { + ret[cnt]=strtol(tab->QueryField(cnt-1, 0),NULL, 0); + cnt--; + } + return ret; + } + ret = (ieDword *) malloc(sizeof(ieDword)); + ret[0]=0; + return ret; +} + +ieDword* Interface::GetListFrom2DA(const ieResRef tablename) +{ + ieDword *list; + + if (!lists->Lookup(tablename, (void *&) list)) { + list = GetListFrom2DAInternal(tablename); + lists->SetAt(tablename, list); + } + + return list; +} + +//returns a numeric value associated with a stat name (symbol) from stats.ids +ieDword Interface::TranslateStat(const char *stat_name) +{ + long tmp; + + if (valid_number(stat_name, tmp)) { + return (ieDword) tmp; + } + + int symbol = LoadSymbol( "stats" ); + Holder sym = GetSymbol( symbol ); + ieDword stat = (ieDword) sym->GetValue( stat_name ); + if (stat==(ieDword) ~0) { + printMessage("Core"," ",YELLOW); + printf("Cannot translate symbol: %s\n", stat_name); + } + return stat; +} + +void Interface::WaitForDisc(int disc_number, const char* path) +{ + GetDictionary()->SetAt( "WaitForDisc", (ieDword) disc_number ); + + GetGUIScriptEngine()->RunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" ); + do { + DrawWindows(); + for (size_t i=0;iRunFunction( "GUICommonWindows", "OpenWaitForDiscWindow" ); + return; + } + } + + } while (video->SwapBuffers() == GEM_OK); +} + +// remove the extraneus EOL newline and carriage return +void Interface::StripLine(char * string, size_t size) { + if (size >= 2 && string[size-2] == '\n') { + string[size-2] = '\0'; + } + if (size >= 3 && string[size-3] == '\r') { + string[size-3] = '\0'; // remove the carriage return too + } +} + +void Interface::SetTickHook(EventHandler hook) +{ + TickHook = hook; +} + +void Interface::SetNextScript(const char *script) +{ + strncpy( NextScript, script, sizeof(NextScript) ); + QuitFlag |= QF_CHANGESCRIPT; +} + +void Interface::SanityCheck(const char *ver) { + if (strcmp(ver, VERSION_GEMRB)) { + printf("version check failed: core version %s doesn't match caller's version %s\n", VERSION_GEMRB, ver); + abort(); + } +} diff --git a/project/jni/application/gemrb/src/core/Interface.h b/project/jni/application/gemrb/src/core/Interface.h new file mode 100644 index 000000000..3965efc37 --- /dev/null +++ b/project/jni/application/gemrb/src/core/Interface.h @@ -0,0 +1,786 @@ +/* GemRB - Infinity Engine Emulator + * Copyright (C) 2003 The GemRB Project + * + * 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 Interface.h + * Declaration of Interface class, central interconnect for various GemRB parts + */ + +#ifndef INTERFACE_H +#define INTERFACE_H + +//skip messy warnings in MSVC6 +#include "win32def.h" + +#include "SClassID.h" +#include "exports.h" + +#include "Cache.h" +#include "Callback.h" +#include "GlobalTimer.h" +#include "Holder.h" + +#include +#include + +#ifdef _MSC_VER // No SFINAE +#include "Audio.h" +#include "DataFileMgr.h" +#include "MusicMgr.h" +#include "SaveGame.h" +#include "ScriptEngine.h" +#include "StringMgr.h" +#include "SymbolMgr.h" +#include "Video.h" +#endif + +class Actor; +class Audio; +class CREItem; +class Calendar; +class Console; +class Container; +class Control; +class DataFileMgr; +struct Effect; +class EffectQueue; +struct EffectRef; +class EventMgr; +class Factory; +class Font; +class Game; +class GameControl; +class ITMExtHeader; +class Image; +class Item; +class Label; +class Map; +class MusicMgr; +class Palette; +class ProjectileServer; +class Resource; +class SPLExtHeader; +class SaveGame; +class SaveGameIterator; +class ScriptEngine; +class ScriptedAnimation; +class Spell; +class Store; +class StringMgr; +class SymbolMgr; +class TableMgr; +class TextArea; +class Variables; +class Video; +class Window; +class WindowMgr; +class WorldMap; +class WorldMapArray; + +struct Symbol { + Holder sm; + char ResRef[8]; +}; + +struct SlotType { + ieDword slot; + ieDword slottype; + ieDword slottip; + ieDword slotid; + ieDword sloteffects; + ieDword slotflags; + ieResRef slotresref; +}; + +struct DamageInfoStruct { + unsigned int strref; + unsigned int resist_stat; + unsigned int value; + int iwd_mod_type; + // maybe also add the ac bonus and/or the DL_ constants +}; + +struct ModalStatesStruct { + ieResRef spell; + char action[16]; + unsigned int entering_str; + unsigned int leaving_str; + unsigned int failed_str; + bool aoe_spell; +}; + +struct TimeStruct { + unsigned int round_sec; + unsigned int turn_sec; + unsigned int round_size; // in ticks + unsigned int rounds_per_turn; +}; + +class ItemList { +public: + ieResRef *ResRefs; + unsigned int Count; + //if count is odd and the column titles start with 2, the random roll should be 2d((c+1)/2)-1 + bool WeightOdds; + + ItemList(unsigned int size, int label) { + ResRefs = (ieResRef *) calloc(size, sizeof(ieResRef) ); + Count = size; + if ((size&1) && (label==2)) { + WeightOdds=true; + } else { + WeightOdds=false; + } + } + ~ItemList() { + if (ResRefs) { + free(ResRefs); + } + } +}; + +// Colors of modal window shadow +// !!! Keep these synchronized with GUIDefines.py !!! +#define MODAL_SHADOW_NONE 0 +#define MODAL_SHADOW_GRAY 1 +#define MODAL_SHADOW_BLACK 2 + +#define WINDOW_INVALID -1 +#define WINDOW_INVISIBLE 0 +#define WINDOW_VISIBLE 1 +#define WINDOW_GRAYED 2 +#define WINDOW_FRONT 3 + +//quitflags +#define QF_NORMAL 0 +#define QF_QUITGAME 1 +#define QF_EXITGAME 2 +#define QF_CHANGESCRIPT 4 +#define QF_LOADGAME 8 +#define QF_ENTERGAME 16 + +//events that are called out of drawwindow +//they wait until the condition is right +#define EF_CONTROL 1 //updates the game window statuses +#define EF_SHOWMAP 2 //starts worldmap +#define EF_PORTRAIT 4 //updates portraits +#define EF_ACTION 8 //updates the actions bar +#define EF_UPDATEANIM 16 //updates avatar animation +#define EF_SEQUENCER 32 //starts sequencer/contingency creation +#define EF_IDENTIFY 64 //starts identify screen +#define EF_SELECTION 128 //selection changed +#define EF_OPENSTORE 256 //open store window +#define EF_EXPANSION 512 //upgrade game request + +//autopause +#define AP_UNUSABLE 0 +#define AP_ATTACKED 1 +#define AP_HIT 2 +#define AP_WOUNDED 3 +#define AP_DEAD 4 +#define AP_NOTARGET 5 +#define AP_ENDROUND 6 +#define AP_ENEMY 7 +#define AP_TRAP 8 +#define AP_SPELLCAST 9 + +/** ea relations (derivated from 2 actor's EA value) */ +#define EAR_FRIEND 0 +#define EAR_NEUTRAL 1 +#define EAR_HOSTILE 2 + +/** Max size of actor's ground circle (PST) */ +#define MAX_CIRCLE_SIZE 3 + +/** Summoning */ +#define EAM_SOURCEALLY 0 +#define EAM_SOURCEENEMY 1 +#define EAM_ENEMY 2 +#define EAM_ALLY 3 +#define EAM_NEUTRAL 4 +#define EAM_DEFAULT 5 +// +#define STAT_CON_HP_NORMAL 0 +#define STAT_CON_HP_WARRIOR 1 +#define STAT_CON_HP_MIN 2 +#define STAT_CON_HP_REGEN 3 +#define STAT_CON_FATIGUE 4 + +#define STAT_DEX_REACTION 0 +#define STAT_DEX_MISSILE 1 +#define STAT_DEX_AC 2 + +#define STAT_INT_LEARN 0 +#define STAT_INT_MAXLEVEL 1 +#define STAT_INT_MAXNUMBER 2 + +//sloteffects (querysloteffect returns it) +#define SLOT_EFFECT_NONE 0 +#define SLOT_EFFECT_ITEM 1 //normal equipped item +#define SLOT_EFFECT_FIST 2 //fist slot +#define SLOT_EFFECT_MAGIC 3 //magic weapon slot +#define SLOT_EFFECT_MELEE 4 //normal weapon slot +#define SLOT_EFFECT_MISSILE 5 //quiver slots +#define SLOT_EFFECT_LEFT 6 //shield (left hand) slot +#define SLOT_EFFECT_HEAD 7 //head slot + +//fog of war bits +#define FOG_DRAWFOG 1 +#define FOG_DRAWSEARCHMAP 2 +#define FOG_DITHERSPRITES 4 + +enum PluginFlagsType { + PLF_NORMAL, + PLF_SKIP, + PLF_DELAY +}; + +/** + * @class Interface + * Central interconnect for all GemRB parts, driving functions and utility functions possibly belonging to a better place + */ + +class GEM_EXPORT Interface +{ +private: + Holder

`S;3A%@YrOI}tUb z^^xRO*=>w#MGB0(W-Wa$>9H&GQ!bRt1+Vu`j%$>pp|ajY3Ctd6tjUKO{V! z;xOgsw?^+Y>7-LDcgbq{{&}~6>rnR!rTgEU4JAVNzv5Q?@kBXN_ta_!m)@-7Otwnd z{YsCYOiAQqT`AE!`S#L7PA>bJgeI(?s=qgD^R|zdHDf#^Q{!t4`OnBI%nsO^?$2=c zN!goS&F!AI-$gv$$8FU%xv}^`{^Q#|qJKYHmwf0A)t~))&eHgosmjwI+iqDueevb* z98X?MTKCZO_sNN+^?UmKw`afaIM-OWGH0iaarXLEo0_s@ceAd4ownI;{_I)1lrmCR zyv?21|9bD5spcZ}b(MSK_@Zs5Xg}yZ!hQR4R;Sz9)j|{F=EcvP&i<|Dw^+z6>B#N1 z*5|MJiC>Q1xz0Oi=Jd1O{V#ThEZkboz2~fr>G@2KRb zum5fvBezlEecgJWocC5Uqxz+nivIc`Uu|F?U-#F)s=;|%>Zhf>Kb-mVYhE~uf6Z@u z-Ro$ye@WNWGu{fJf4%${8vFgaXUu(i>+L@`=cL(p^!^W2k@^w$r|)KB@ag8aE%WYr z3cpVM*1b|^_LSAD*Pre0>B_6r_;_}{+F$9dd*^=HWAbvT+v~rcrYyIY$nNnr zv)+|+_Sw_EXGx{{Of~$W@7Z%#+e_Y!e0%WmlGhRbtfjf!H!ojpo8Rp9MYwp%ztcf$ z6V7^uYP#Hd^kbrj#vc0(x2iY(V?>>;P|mM8s?5s3;KIYeAd97h_slKGFDh}%%t_TN zsN5PBoqx+ir0&@MM;vn3*4)ui@x7I|WtB|Q3RNZ@v$JliLro4aT%I9mA~z}M>+$#I zW(j$F-yF76csTR@o%3_vhpagI{I7JC>%uu4nrFJ0EJY^;8^o)f(RdrOVxgF}?lNQN zyv1J6x87nsYP@gSo6N7B{XM~q2JbTf~rWZH?X zyqL7ZWKN5s+VTCVZO^iF)U>O&Oi5mM@<7+as(F)UrO2;6$B?FaU1#CFq8S~6TaR{} zp5i>~;5J+D^ctVTp=uYl$hl|5#S65g{F!{lz1*Xy(Se z>>zIKu-5h3PH|i(H&31@&e-R9!so4zuE1FyF2VcV9zs$@QOjyJ`PgJIOnZ0uhjY2W z4vmr1*hj%uJb%2-r10JWXVjIlUG={-Zj-dW>)z#dxPvT{%gO} zB+8pK7Nu?XxBL31PR5KacfbE$$NkrPn~WEAL@&?zwQp|pEQjkcijB#6GdwQYJY%0? z+?sxJM%tU1i`OXY&3h=mSkUU(^Q>tamS>e%ws|}>?d0@KwW+$undnzWD2jotG*Y);iwEn0tNIxnBy^EazNSTRwa>?Xlp;lIDL`3$}Z~wJBc$QjL#~bluHn+BKX?+v*`Ne@L?ya)NHB%SPk5snYcihcC&(b^K z=Hl&JXSs))rmWf`{$$~l-TfBbI~-Y`-&uR;NT6L=gKnOgX2I*{SDg!%X(V30!rSa_ z@wLLZ<8j{ejpAmH9PNMXs=xKpuC2UI`@^oi$2B_EzHL4t|1)>?0ZrNPGdWD(@8mhR zME!s3;wwY;A9(*se$UPzoA{djxv_zFTmSk@`&KN~AaI89R#}C-v3>n&+q0LyxP!Cy_4$s zSL}?;n#sR2cf!p%%$aYpHiq7~Y&F5tOvpIp;k2V~Eg~0aS*jbJ$=`ieXMauP!w;-B zE1u}9rQ6=-KN@nJuZh*2_pR5Kmtxi2T8&GdR(RA$Elcwi%-+Vbu=qlZ9{YnTOU7#( z)b}aYK5+a#Ws&EanT9VUqT5cH&t0qYOOPYCC`@cs7&*pk71!^)~&14yL0nnw4Yw}D~j^(5#UoVFMb-h^YYu$--WwF z*InYjAGrDc+asZ?cucJhtXuT@F4tSdv*&%@F4!#RKKsZ&Zq(9smhfKgL(B{evK-h; zSD*aEl;Gr|)YLq^g376(7mHq*iPZG%e>7!}!>fSFTjp+?X(N%;v7ANXw#sC6w*<*6 z5}gyD2K?Kn{rJeqv#HC>{he-YzIC_w$(+kik3M~uVCI$daL(n9{Ka;$K^n%2t(E}_ z*PV*EwxyoyxzxaO<=uj#Rmm%CUtE~_JT`dK!_ucdtLuN9vUtv*CLMUeZ}rx+=tD-A z=L9b_+MGV?{sqUWTaqT3_`4=)Z>$Yk7Bl&vZrkfzN3GR$ zJ!-`<+B?%M&z?%v3o{YZ*5mHWYTNxbZsS8uUH;I67qmLESxrJ7q`GNTI>$VSxvYJi zweGSg$1CqFx9L-snHh&|Q`@>ZRW`#`peR#o_x;j)5Mfy{PWgz-sMTb zkssEai8WyG_D?LbpR4}Ff8NWJ{Yv$dZ0<93=RKUV|C93M8}Eu3lPWIxEK1d4KIkmK z`!4X|_rrxVY@)v&Hkip$=yN#J#qR7M4NmsY8|5$N$emqQctN#mr)cV9>$$qA4{m9( z%qTkS5+`G8zj!X|l&6}xH?-ZO!(;Y1i7ysad-ngUPIytQCCj$X4{a`PL6XzIMJ0Ij z{JP~EV*a-Ns` z{(NKmvICZ3I=$g5ro>k4*fRId(>DQcxifTE87tKPIbV3ea_QNRPc3GKMN6GMY~45g zIQJ@keVOGK9k!bUPO>)MXrw9?@V_Kz-onyn%p30hJZ;Bbo=%S!9JHJiT4ywHOZfE_jxaZw1>od>3v3}^vsQ3PCZBBKw zf7t7{0>|!83;X->;fCgof|qVQG`M~Dc%FNf{Q>R6%m4pfbJ1I%$M$+#S$`GF$GM&x zIW|pb`Jesj=I;mVrQ|ZVFx|b%KILoj^(*UEu5EuNJ161C)kVL2=DS6xGF&PA>AEm% zxl#3D)*qjo+$D4_Y(J_qt52yWO#I-JyvF55JNVzl{JHHjn|puBhd_}SOL@ogvr z14AwY_Rh0!Vs>g8s9}|uds@feP|Hv6l#WJcc_B|>$cE4hm$X+rIn~0wJVlI=HPoc& zb8v%?Z|(Ph^9dVVHrjbiw40vo;xQq4Pj`9&htKBq^#vXNOE&kfUOa#H^8Rdt5Sy9Y zXg4r4JUCtTkd=YKm!E+_0ZT*AH?_DpF+DXrGcP5-98?;GL+)XasXH1o!Ec$7jLO^4 zT`SZrSvo=wh?$*zx^Dq*Ttam9^;@y(59|J(y~UR7aVexrY0td$XEVQ-3g42y^)f7< z<+yyWT2F@2?l!X>HAgKjM@xJ#eJsM}DJHC|()Go4*VLT77Zbil7hHTBx9#J7bq@0_ zEx&_mmV4jLSX8njkt;~^+9Z_*`|wFY0ZB{ecPt6tAS$WD$?mDVY)cCN%w-2m1myls zjP`ap5*A%NEe!Ze5tB5X79VcwIN4Kr7|B z>v?T<jJ z_qadul%03nMRS#uwx-bM8t09hS2P7Za$an>&}*Wp?t%}jyLjdAdmQ?5FwiCZ!N-jX z6R-YSUAV39D6`vowyg{MvQ5qvU7IWDnc=Y2f5|b%Yz2@3p4n zwSF!xYrFR{>~ndg(!F)F$;Z&J^Y3c6RM+3yd%jxn@Op!Hlb`Zu%TGJk@v8Z=7}K6T z)mQEveXJp)y=51NmhbykKc><-9evY-;-+tk>Sdn4Pw>r)&$B)j@Ag~v_}&&jfzr9p zk8i9O6z(d~zxQtLzVBy#R0cD7PkUjmdWz$xli}yZ6PnaIRaGyF-JaxLXxMOR(ud$c z9rct;-?hBD680`y&hxZ!UjKUc&$%mY?AVp&Pv}3*S@oww_xyEz)<3$1Q|$G`Thd;( zd|G4if_vT44$+9MMOzu!kAxItd994oK4upDMseA-xz>9>z7xBD=fGaM*?!hq6Supl zewwgn;`YBbM?2lTwHno|g4ds8cp^OS+~mKJFGS8SPLP!<0hPCGW6+s$@?VO{Zs6YtqI zgf4H|G{I$;$+Pd5zZY=*FuXZyYS5mImzK_LF8f>9VHn!xetqV#+mE7EbH5hJwmvu) zy6gLwD!I0MC5GRA#dF(tSHInNy=b@Sj#yV-{(0r|-be|bbNuZNrgsb`Efo&!x_2UA-lMyJRzEvACx4aTj_8!LI!Ci@^0->% z*|JSio>`iw#?+ZSU472rg!(eelhb1^-}%H^mv7X!JC8kV!?6jaZ~q9N^tkZIc&o{x z588_a!j7FuUB`Bv=bxZU7ssU;I>lU?Q-A1AW=PwF; zUCvQw59fRpE)CAq-z_|a2dbNUbC{>s?@wlC^?KjF<&u}pgflaZjnn&h!#{Y4XG*VA ze5m@^CtvZI@iEOK-Jz~qCY%#>N!mFz$L#t8H{Kmz|EfJH-gREa<0Jpvtmj;fKj&yh zco|MkVf?Xm%9JLdrS}9p-zRE^8K}yr=+3w_$v12k(`AQ>kIQwp2(W5ze|EJ?Jf!KE z)~r*lOo6xBkKYqGU8Lp7+I)3MQt*pSnoqlu%nsgqHh0!#Cbj6<7Yt=ne)BC*o-jQ_ z?T2kZTOnt*mZ;rfU7$(f{j-Q!bu5Qm4!SHxmncg&u zvof58`*>Gt%?OClj9RC~FDbyiSmpmM`DX&(;u1ubW!^X)|4wlAbEd^5zU4kS8`nq( zJ@%PY5HnL_;sZ^;#p`DXZkwDpY5$CO)}HseH}hEi6>*Yi$(-kMIB&+Ggc*FlB5Rl@ z1YVvp@%`~zPMtB_jZ9)8!eJ}#p3&do*E%P3_{NqS-TQ?<{ZsH}_sefw)4)hkVhld6_NBuI*Dj?yqc_Ah)IU z65Br!rXSlKAH=i!+J8ux(ORa(epEE*{h7B@f^;4f&aervkqJ3y_oMx>O{eK%ky3uU-}~6*Yxh6AaxS`N_RD+oob!xU1@2;H6=$CQ zc#f0&%6sfF|Alg9Y8eIR3H5K>S}6B_ZhNLo-{u|C?im;IdLo;6fFLVzB`=w=K4{YsT=d}?7H|#@!TRSr{1qKEf2j|C0Y7+bF0L?kQ}8g z3(KSv%Azi}UrL`K$MxFi$?An0%UKOS_$*m1y*4aCDEpPs=b0heXFeY|5wlysDn?IJ zKjvvH`}_5b+$~<6n&(nFg!hE*zi#{Ar?%O6W!{ICk~MYOKMbqNzpGkY{vpA&uO=(5 zWew|t2aVqh&i~$9DDrL5j@OR$2Je>hKF^gmj4S-*%T>pwc`r#$;*CSl(tFvTp0BL_ zq4;~x<@w8{-q^i=bil^=Xiut}=lg~BQugYRn~b+FsZ!k;yxINV*8J*MS64s%#%FQ& z?ElvmJuAXaZ@+(k^LMVDRN&U=YXBjPy&)1&tK04ZWMc+DxRLul{J@Zzr`pw_9J|-3z_y zA~Z>9=>*1|q4SlP*gtr^IWeQ`=lgP>OBa^iz2Z6flfdK8zb)jqolk6=UBD85;IZXn zpSK5|vOeZ`w``@5>XJgfZNPW_(o?@;Z@36yRT~2D! z3-<4HX^DFB%5q`_uk}TTSp^ejY>e{UmM6j#JmJHe=V2MjM$_JZS>Bee;W)9&Y3EAE zCvTIU>3dFF?4uQ7^15rOLAvHADbBeQw*3p6KI353b*5#v97_rqf*tpCCN;$~8{L}z zp=HxD(-?t+j!zC`G)_oR^SV7vVVl%7_4U>Zc9ons*gmtIGwncycXXF-YOs*v)9+_i zab4r^?X3)sJmRpi;Qi6<2aVW{*EdwF^T^uQ?N}BO%603g`W@qqXSXG}_*kB72|n%O z$5NhWu}`(d_|vnvKMU^q@4ZpJm}8kE2cOOygKx)9?-V|iB&pYQLYBd0h40Hxe~WV! z)z|edU`ksd6n6FZ9QhSw-=PXZEnjBKTBO~_x%gRlT2I=1UHoIR*Utv_d`j16or-<}|xty0c z4@vROwwG-4Ur>F__#=;b!uHde&a0-!`LY}jTztZJw~5gi#RtU`Q_}vMFAfh$@)dop zp=0q#_r(FX^DnmAu}SUXk5zfOxboU8+szMV=SF|Hy6v~y``(4YQ#?B*_X;NVZ`<|b zAkWo^0#(=4xNq_8yhkqVV5N@eC8|dIU#xWZfDt#U8VBU zdn#sksFl7vuu3@Sm#&l5@BJ|9a>ux3u=uI_)bm-Bx^mEO?%+&NPT~+vQ^0xoTpK z{W16Mw=8UTjj*DYoW2v7g^`<=A3wyD#>r#<9;(4 zE{a!vd{le-#tIidw*0vl%HQ2F_Fnz{;r|ogerIlreQ!5W?`80+wCv~4j$PyXcY4M5 z4XZvDD_pNXD0Ai?FKYg8QEllkV_{&(=fS?a!M~s+Gd~a1P}mw88~n&aq}FWzqiOpl ztURH%sL%SE9Rt&3DU}eREj5c~En(g(yldq(GDhuvkpfdE;X<2)#N89(;q z*|Z1!@fOaFRWO)m$jYs`Y|8_KQ1?ySg5mn(i4LizoKal^Hry4cim&zde+!m1@1&3y3PLGqUBniU~GD7{&E ztv_v_w*l)h?pre>63mQMj{J#>oOih8P|(Gknbs#7A}YQ;o_|oI?b!p?d9xF5$Q;rC z-f=W<;nZ~*CyzYqo}*~^Ooj9Kv`!vQIqSsV>QyGub@%(@eZIun%attR*yYIKD`9r{ zMt=XVvjT043nX0TTDe>~lCtXi|D~-wE1xmFo*v{IvF_BA_zknUm`?}Km?(O5o!XSO z20Bcfd}p3VP2;xH6>@pAuIc*$qX$mbAG9RTb%?c}b$K6aEh}?>$-4u+?FxtLpGSQ5 zP?pi$rQ`f7Pp_q9f$9{q569JZgwJ~(SH*k1I6=hJH|T_D$4j~`zqr*I^z>#rPP#v>wN@x-dUaB+Aiv1>9<3V^`y;Qp zecGAOQh#9Unki`lANDC^p8c0N*SFa4;HS`rYmo<|%@=$+Dd3v@JXPR1-<_T7n76OX zj=pwNIkd|sv-^bk)Y|t_O#B63J6@?TyP>jtYkK_$0h|8$e@^6owKj_pF^=cibjjt_ z(?uCUkrJDGZk#mr4cu$1JV|AT@v9Vud9@-{N^IHDE$p6g-dk45p8n3!ZeNl7bIB`f z{?|P0CGjlMr5ZMmDlZ;A^thw1&`Rz9v-G)d&mT{GDD_S5luLiN!SVnDo2koJ`Ky_@ zH)(BgFWoKkZr1PP#ohj=*I7yKoAK}B*0|?}Ilc3(5}&X)FZwr0Cru}C?b2gMtz7@f zS}JeeDDV7U;MU<6?Us@CJ^ya=tcx)G)v2&2o8|wa*PRNVgd0Vs@~({Y`p3DUJYvH3 zLzVaU-q!v-V`p=E?DaqAB+Se2NF6OSka%1!@#tw;LbtrvoHbdj+IOzEZ@YYiwc>P? zZdvP(XDfa-FEIb1`}g;|Hz)Gf?VYWB?D7@Xs?*WAPo#?%7uHsnU#Xc@tzcnVu3N!T zci`I23!7F2Z#aE0cSrfRg&(*0b>36k`2P0UlOYG^74NppDF4OBa=1`HPNwy;dXN~? zQ}3s9zjT^&&s%j$xOmB`rrn#v;*@z38dy8K#M9o|80ohZ?_L!v`E-5AeXh6c8>D~g zpS`<6=E=W0<$WgKdoR>6?B4s+>ub^U``aH@XEw&7XM!INAcO3!JwHk>`2e&{9Zo(;SfN7((+ z^8|{@cX4X&P!y>D=Dj52Xxh6!t~$AYB%cZ_nl|B6C_|21Z49s4ifhZ~R2bayvAxcC zxpakmz!S9<(Q8<}t(sqYAGshd77@+8;Cz4{XY8|hhSI|MhlQUq)-`uAZS&eDl$szj zOGPBDI3fGsHK*u!hQE`F~jafr(QJ?!VsGYI!Qku;f8c+mWwL z5}$3`j2%wzk<>XUd~}1=m0G9W92ONxeBCpT|8Z>*$P5;bYp`aY-)dx2EY9zF;?zZ# zcP8_U=EXmcXbWcT?D+p;;pMdgCj(vX&t3du-*JiQ3lBF1Ot_!HkQ*9!%l(vR^u)xZ zOuU_eE8lr?gx7ku0PiVXRJaASKkwI%uD0ZFGH*>c_pJ4wq`CCW z#;3<4xi=L>f0bIkO)DNvKdm9dbVTrTee6cb+Lz*OjCM*h5w5jmkmc> z-fe8;@O^b6c2BVAN3UQh$=GV6Yq}=;!>qWE20TbQDN*7){ikBO+^yTIxZ>_BcQedt z``1uc@xmvk+M?pdtH0{|tt9Lu%x_KA<9FwumKWZ&*=bFmYvHpEzuWH#zcYCG$06Hr ze^&CvQa)vS!AsT>7Y?8BkB-br+~K=Fu{qe!UOV2adaB*4tmbujT=6`|ohr+lm!_7^ zs9LDxJ>T%bn=hYN3S3XNv-O!_u|cy`>(!IfH-BEg6sJ0E-o5K;%jPzntcWvsd87J} z(qs$u3Nu|vrB{N>9WR_}`@!JnILF{w?Y{Ov0rehN|96SKCxxHwDgU;C@509DV^wb} zUnWhuRpY#I+0_lnQ(wQ({b<&zrRBVvb^7$%n^Y~bcPi#EulWVs7j_XnnoA11Ozy8ZKJ+pTP*P?}P z|4WEZbA0c2yFI~f)sa8aTUICRec7)3l)dR*suz#cRT2N*34vCg!uPwS%PyG*c3S+K zcKEKTlx}HZfRpqR9mjP~Pi3Sl7g(J0)AOrb#IE<~<5SNCr{*8G&~!CAmiF#f-X6jF zw5YgaOV#*ppZYjo`c(X*L({jONb9#SGgr-awb6e#;nKxLUsrjQ&i9!7fpx7|r=MSJ zblD5{;|^1!LhoH!mc-<{XkO`(3)B9d%Q~gn)39Uh^v>yaN;bU?EzIwE&n2fsC#D^~ z60qdO1a;3v9NogF4Yn`4;w&0^_QUK;ee*V7o2HlBk=d}}rpnr{Q{T-wdF-^er_d|o z8%qurn8@5Vx1H0TUEMZ+USeiMec_*#f%+QXx;}aS&Rm^0Kc;qr=x;L#L)krst4>Sm zv_5dx{LFYzU+vPr2djTRSpA;w-_QR7HLssJH?9A3;(<0hoBYO*un9$*&&adlknnQy(yi)Xx+@+}*f8MX?b$d*1*84qb9i1x2x2m7Lf2=U9Re5L8zURAs z#z|b%DDYVQ!+(F1P;1p#{|zI`|CzU^}Lvi<+;Gi9B5mR#TaZQJDswwt#mi2K!@{~+^uM$_^@ z4MWb(#`2E*NprfN{$rGslC=`I-)LEQY4`E{sLkmI`_(-Zm>3v#F=Mab0!uSX+#y}+ zslmSew+uvV%=afP+50A}@laUk(xOwF4!9*XsyTKn_KfR@KJF*WKBfMC`m`?R=#^(r z%71=#c2D6ug9`hMs&L7(-4FPBm%yw;Yu-&MIA)ZRHS6A~>{mrY20-)|K=q4lv|U(Ve_Q>LFy+WK_ctTh2QO4wD4byEFysjd^db9??) zQBjL8)0=jNPlyvu$eR&vk@$ReF58~k%FTRm>&YpJLlJklpQlF8I5ypO!!CsO=^qUfBa_Fv}H5a3%lHK@f7pl zep=&T+U%TbFLOASJaPT`@o()mjhU;>Yz{QdnXsz%^PcAx(P7KDLLRxuWcz(OvWumt z&hY=VnMS`V3Qyd)&7nR)bxWb*xv)>lh0ng5?EEz?{lt57`L-V>X%jBo7v;AtPnUrCq4OJ?|9&(qD!QPBk(x)vpAH>&@f zzWb!i>9mjuB5A)y!wDEV%p)nAAGxSDkyyWe&~|O zG?8zU)P7%aW9eyix8F7YoN|Gh|A!YWjmDkx@(jarE#_THk5bt9Rxg#K$Ch)_!c-f_ zhWBv~?j^Swe>h(EWy#I{KN*i&6?>L3S{M{JohW~vtln_bW`?ixdR~DbU$4{O_idY? zaBbS+2JKZG-l-Qq3*XY57`dy7w{lrVw$DxDPf7xhkJOyoJSF}09_OC5DtlO`IUJkH ztKal+g2umFzGv3`*-}{dcpGp0ZV8u7@|s<8T0HI@g5CmkQ;PI*BnnoR{3vH%aQSta zb^NZse%IS7Up$GGTwCGH(z*5U>p7*2{rws<%8T#seI3bWb)Z!%uVEs`K2{sapgoQu zPL)4y8=iW4Tlc)uCZCliJq(RG&7Yx8TUrnL;*a=kr#&u~)a+d|v;s zZsp{Zz6th*VJ3~4X3tVmqv|DlOHWN-k;j^Qm~|bm=nKt#Y&j-9Zuk774~rQX3;j2k zU3uA8I?qaW!+I;((-~K0?Ks2q`Iu3cgg9@IM3J7O_4=Fp7Hd7wcWuag`D%J?*p8#Z znN@7RqigZH(ks_fu4%524KnpQ8P)W%nmec1 zaEANEl1&_Q7TW9D-M)M?N$SO%aMAVl;qSl4?OjyHCjN4Em(7Hd{$8Wq$&R+#Av;R+ zDyxLk4ZgK~_29qWva9FF{>X;a2hL@lW0|^X-dBefW!3(`r;HOr+!sET-_WveS*8h_ zg}VI93vZ(e)Gwq zo6S?@emx9)UiD@6VdF1u=W1j^b>ibcmaoXN5n9AAVyg?$5ja^{+x8 zyS(uE7q2`MCq1;8(f=>SqBh3n$HM+c2{RUC>2FH1oV)DJ-oGUqtG_(IJoUW0&m8|n zrFVQEeq13F@OOR63;z188EPrs_xF@4tAy~C>_2ij{QbNW%C0SIH`?xIa*dUq^~33_ zSe1F^svqxGEq#3I+U!TM+4YsDPYIn}D88%vfV64%*$?X8V> z;niome=ON5H{U=kXZe?Jy3$#36}3`R@4WsWa_DHe{=cvndlM_RPAxEZ@;=t|ri`cF z=7l`l&)YvExx<#1N&Wxd8zuiW|ET7~Pa!YkQOhdDn`c&Oa4|5j$YGy*3eL{V$pH;1 zU6s6Dbj$0L_P_<#FZ8}^@^C2M$YrbhI7`;5XlGyZ?rPq! z<85J^D|seYPfXxT{<-}_>DIo6$-f;A8ci$TxSipTRevjk>hoV0yN|j@oEH5N$Fgw- ztJm>^B}-XZr1oTdYm?M{qBCRt%M8)8E<92@GX(WTPFVimA$3RLw#Sh;j>A_?cxqm- z@9UW2ki%bjdFC#0;~DEDA5Kz{P!~F|)MeK^zLYP5E)Cxe{|Ib6bpL`A6X)cXMa)KQ zr!T9N@f3Mxn4Q|iw#B=za!c^%n1hlmf|K`7_rCH>j&o66zoW#VkON+wKeb9GFdaIy z{oa$$U(d4KYJUG=t*z8n_Q~(%1;g%p?-8|cStz12EwP~`aQThy3#mnYEc?pb=brDl zX!rhW_2l;|e^c{~Ui2L=sI+Y|iBYn{|;yyJTvN6BjaCV7qN>~?NvKG%qydeLXGQMndD^MwvJTvi6sz&OY13N=z9oWB(ahC4& zT)ADxeXlppE}Xq})wRFX`rm3keUSCeJ+Yeo%M7O1ua;fkX|;9r3ueje$;?+Pb;KLY z#hHpdG(7a;mj}r1j5v|;$k%z{s;h<*e~Gym|Kv!&u*UC*|J}aU>k}vTrJhI^HxhcT z-Mi?MzguF>znhM#pW=mgzDeBm=ZU#Mu3?Y7-N(`Oy~_PdV1_uSv>z$NLeClxU(Gu^w61>IC8x5}f4VCHkXwUYM(Gs(}%Ej|#WxM2a;dKG-%Qon= zh-+#|hXn6caFcns(p1=$C;ynGyT%8HuOX?bY_GZNj_>)@G2JLJ$K{9PBrP59ehFp6 zM4dhCJ^o5wYYa-Ja%r3}5}R1zb3#m!_gWUMy76rEFyA>s)O&?!~I(%fpq6aGjZUnphIe9T|rG3#v z^Q*c0x@xr4Q!Yj>2A@e3$P3_hB%c5u6=kxgb8ty-pY|AE@JeYCymg{ZX%tf`6`4`Q7_Oxxs zOsn55I>Jmjz8fQwqr!ad7JRSPtGJuBBmXVWmap6aW{i%nPdp83`S4@!)VlkTGm7LV z?~FM3{9*i;W15X;Ogvs{CQmj#_0I5_zR{i2OI-GJ{L20*xXX_*e>`t|KWzj8Hr~LH!1~e4zywWy<<~|w$71hsz0}+JuZ42rrmkx z&ic=pZ&pk;i8>;A(!FWJ500JJcRu`)mU8ITT=Qow3org!bLNZ8La&~iTb$QkYCPL8 z`TdV0s|x+&vRwJ%mt{^?dh&MlPRqbAO0{~6uWq~dHdEk|T6xUo`&;<@8D70`6>PiV zq8xu<$^@R(s@G;r;nC{Z?b*@XdTfcIQ{mH!=Jt@jJvaIJKc?H<)O{%Y{NkrGM-%*x zpV=gHEYa-K^LOp_S6=gMRQP0QQOLexirO{R;{r8tx*3lS_BKVQES}1vr!#?R+T;~G zwpcqI){W;rzu%uB0yG$LQUL{ zehM_?kKruXbnwH2WMPr67TJeK*Bw~&xhNv{eq7QzspoRmskdbX4m9T8&grj`-DPhp zRW{-CnK#Kd-)s)%zf`s<;$8Pn=1t*ebT~FWXgU_zHoaZ$YD(F%w+^e;pDmhLA;Ej> z|NI51&z>xD-ne@2QL%sD0@zdbOj`Vp^|L})r*W|F&F`Et?OJ)$|1n1#jBU}@XbUu) z_VcKMfMKZP6y@?`k}@^>e%_LksWFgMnO@+0J1pwC`8mVl)t;a1=I)%c{lybLy~S18 zUwrg+vV@NA%~dy)I+I;?N_^{0Zr^ii`Pa+dXlwf4i#lbx#bP7dw!@c2H*xOy(7SSB zPT`hMK5pz5R~{?*Y*AjY_J=^O-`y#9r=D4D@~COjpLNZ`J3l?Mxv_bD+Epjc`6t)~ zBe!O3ozb=9*_)`{MXJ8S(u?k|mCC@u@2tN+^V@)*HO3qMW& za!0N8g4&;H>yOR7Sh~Odp4`OUFP?tjN?-r!#jf@y zU3T*2xw|Ike-K?DF5;A=x$zJ~_kON_&S#f=>Dn5&b<@Nd*YC$FJq?_7cvwSUyU&I6%JlHPrIy%3g@bBAi>+kJo?`WOc zP~NoW&W9+2DYrK-^i0}4F`wbLQvQ8mukP)e9;r=IuPl_-e8YEM^ryWQnS5L1Cr{H_;rCp1gZkF8*lE`l?vl z<=b`LSLBnm>%m_kF&n$sy?ec_ChZown3;RHDUz|SkM*@zMz3(@r~4-Q2AbZp`rhs4 zIdyhzPQ`{Ioo(OjQ%*0++wjuVK!dD*L#fjw#_&%xm-a1c3XzxT<(q| zS-LlC79Q-aDtPw!vHy%hjD6(eB4DU@Wf1&TbXCo;aSh%XqLB6bqs6QT8|Qdka=3L)D(q0h z)G69sIZf%aO}8!(J#%f^1#`YnYnfkgUs0XKS;s!*;l7q|Ez!Eerd&DvH?^u1RCc&X z-xfM@>O$3vQr;WWnLod3pDNF_rlC-Kw}AGVw8o~0`_kD`GXgV2*WNzn%zETdaZrt{ zKNp9){s)<~NDHmFcLJ-$naW>k-__XobXB58kE!P&L!-dO2Mo?FT7QD${ba|WJrdvl zE|EA|$@jOAO>s$Ln99WzfwT#Jn@Z*!4`;A6UFP$wQoht(e5GyN5Z9S^QSB`N;y0Efw?CHt|V;bx`JEa5TXFn3{ookC&5S1t*Phy$5R>Vp zmAv0%@~WxZ=f}wC?d5e``0;Pdxs#I)O`q3%l4GU0`QepIBMv=Qsa#O8c}3IPXYzf< zBI|qXjN>}Ze@NFXUvR%vL$j0RdXP!k*`yH1G~TWE{amvpJ~t{%z5V-9$+O-WiSGSN zY%DH+eBg1U(CXT?sev~Hf`aObcfTmGsPlTXj(vX3{pS5O`=6Ki>CQ90`#ShUsGsRK z+1U>s9s6C>x3E7X-#q5O?F}u{ly!Gh<}KQqDfmCQB~r%kbA^Dr#ocC!$V}&J-`5_~ zs9%?7<2IY6o_(+5i{=Mk%6IVZ-t_&Tj8$^@op%M3)C0u}pItI2{#hvZ{K{AVO9qQ( zR&*CMY`x7aoUwdy8Thn8zrV6RvGTOztZ`J+X z^Xe}-G#iJQ|ELhIv3h?q@%y)L#RZp^9S-~O^YPi;y0ewuB<-0maZ5}%Fp~`TFQNYqyOB0v$8f=-@AFj-tef;i}NR6 zB>u6f_4<@zJa5vgu%hM3`fu-T+q*r>-lX4b{^X1atM2n36wZw5(ro5@XHalBY@?Cv zj$7P(?ze>dS8r|4^ElQqwQP5wn7xYYJzM2>*;{pLn)|uTl&fO?x0rv^y|psg-pzuy zQuC(Q?z=I)l~RYs48dc!;*ah3%<>|aP4b$fq2SsnZj@4A6tt$ z9m*Y(BMQx)-xKcZkLFtf6U0-NuV8$44p;qMN zwL-D$oVpIp5dN-o$4G3!fz6ihY!wf`h+;lnJNe!3Y1SsP?7zNA@JL7|Uw`Gxb_{eP z(uuM&+6?U;!JBH1XLB=V=}L+fxV5++u9*E02SoEFiC9v`Vv zxS_4mcK1$$6lU?ab_zXYHqSSDsiF zF#ey$3-uhN zKk|$4-?35L`e?gl!Q2m~N%JSq__cLax#T9lpld&7uGL(r>m2>R>6(~B*nysFQ#NOG zT0d&t`e=K*s z>a9ESMDEJVlDyRTlzrc{*Y5VaS8&UHp?SaK)-&aAPJe%Qr1b5>-_1A8)tJMXtH0#+ zFAcl*C+_{O%kNB9ce9&TAKQ@5S2U^BZHkF-ywIW}ht6m8yl3Ps`yi-Z7?XFF>$YSB z|FeiqPph9^T(f+C#hs4iXRCX~#nikf2|jAOc4_I|tmf<0=ZZI-xVq2&@ZXz|_O`0A>#lg4{*CpEo~H%xy(==O_0-u_#y#^Eh3PDN9nV+1 z_#IET<}dAlyl-=pKdJ1@3T85z&90WX_coJecXh_Hf5(Gf{?X{~SKs`7bN2sBWuG5w zRK~Y6G4-UlNltsray9a#baYSnly%letWWXA1%3I_ynW3!w(BodY>cY?pU0=9vYz07 zbh+lY^5ZG(xxRNqVkIPhTVLVseQBcdYPD(2hWPvOI>%q}`5Y-!5|Yc!ul;#&=Doju zd*+n{nas;!4wRwkbg)7bDmH{LsUs*(3f_e-Zt zv)1PAKC;%#Yp%;~ox%remP(nNh`0LS_|V2@k-^@(>6u9_if#ope&J5J9v4OKX}I2= zd!}Cg;Jtsjny;VxefTf<7;TyNW=*#JPe9wf1sNEmu(gE(0uqZ#D#7P@IV`}c3CsqC3jwAwnhb(P9I zN&C-dx{r(QP&~PUGykA>y2;Wa-nk`y!Ax^gMB68QEMkqy+4-zw--Lwit1hg#x=tZt zLhrK)o85&@7O}E6vx;tbl5_4b%ZI{aoQwz4K3P6_B6Tf?HE;%#da#zt^2t+G98_Cg zehJ?jcxYM4wy^nOFQ*ht*wL+GBX}U~*VAS8$8v)Y9hPWY?dhZZ;$p{=rUhjk)vNQ9 z9G4kHX1A_$dXO*RFn!_OfS_q{%nNs%d)Qgf`2KADW zHDP+p=T9r1?`#fUu+nUIg7=rSMz)Ll)1{6ZC~go++14eyrD0RSbJlpa#|(G=+1}Z? zc;apTck2Vg^d#0zQcu#km?9Z$(qs40;OK;nl2SX~_C&=89L}w>)!z~RVt3qy_do1P z*p&+sU-U7pH+{ELS)$MVaO3@*EFU_VmcFQ-89ig++7`veU?pcM?#G{P+kzD~Rd98G z=uC)EcA75`&LNm~rqZxG((1b0q8z`6rWYia_*i{7qp8-J(tgq9t<5~XXZa;B59F7M za9w_HIO(0Yh`VsCmGkGjGYsE3Oy*p9BtNzxYH{59y=&vIzT)et4X+Me_rqlFahA5i z9mh2Io%>jRtAtth9WmDPeKb9Nj%tDNw_7aS?Ho6B{srk}%PuZ*zpPo^c0NFx{YiU? zCFj>}u}sC9mGOT&He2Mi#q6KDJcX@!%ke`iWB-QeolQ0ieDZd7slw)$4tWt>=_M&%WDYi@vo_W0ub{Wxsbd>uS-;m|Kl0U-CM>{EFL`@Vu$|-P`xc*QZ`& zKIuK*qsQMV=fG1Y<@QNC%Da~e*>1eQFlXYz$%o{-4rT4XkGzYn&%HOH-g z*(b;LmpxR^d@fVGvtP@N>;I*S!a2llh8>tD_id?s*u*^FlWX&0&Q3jYr;0aWZ{Xcf z3x=CA?2LyF-!Eu++I@1>Qsa`}ySm(-{9e^(Gu3;oL9a)R+LWr&@Q0-uZdT2k)@Lt> z*_*$|e$$QK!#i&n-zoaN*6+L|k35@l>%Ox=MQUYJ6L$W1ePU`(TXp#P1?%c->Pt72 z@D%U9TzY)30(0!CRcD@bYyC<5cUHQ+wz+Rer zZuGRRzYX&>E7$D_-5PNy`TUzd)$e+mf&;#(_Q_XIeDx(ZW&K*62@w;FO>WKGANX_q zPK(1!lWQboQx}M|6yG_yrm>ZyVB03{*HsIBT}s|O_|?27*W^LN)#JtN(dKN6m;5VJ zd|{Ex8(O}3!-e`%E;;XYxrP$wc240@T&{**0%&Iew z&M=;ME;74+;hTo*{?Y#BH{_OYloKv6wQtV9!gB&vWF^iTv%e>w2=u3&8vaU9d;q7@%sby!%R|tge%sTg6%dudutU&r}`?`$*MFc4s;*Rg_Eqc)hErwDiEn2+!F* zXVadj9C5n0;%1h(;6sTy`;IvszV*x|tc@@4;fi|IJ^C9IFM6@|Q_pn%(96rEgzR4A zrCidzUb(pJ>dCMzlkMXhzUuy+7WI3re*L}^ORqN05-i$uJ~Sj@%1;|n$N0jMrnpN} zUPY{UVpdzDz3lME$szJVv*+82e^ol2W&b+Kdt+57@86iV=^kw@f3lb5pST}Y{khV4 zui-7HYbu7}j@H6O4=y%V`Q5#b+9vL>KDB%k69dB*7VK*gf+2N-kzPS%Z}7pqTMi;N zkJryQbX0?{)n%{9(+3J3YtnzDO$P2HffDnkl?Ya`$WQ|*EdhRC);~RMC{*+?%8$!#XKF;pWS((yY$*? z0|U*J-nkd0m9;*k=XC_UzO#?R*;(eR+{yrhDJO49?YtM=v_(Mc_YIL%C2F4+|K1ud zDH*XY>12p$$vqjlW!`$LPjm=<{<~|6rd+DmDz!B3b#MP&WlfEcmp*jFvg16T!NoP& zYp!H*OsdH*Pze4kX|7YCR}{vxfVU=bZZ7lQ;w^gBwoS!54XWRl)nytqhxFzwEWhJ> zu<6eIZXNG6oWEy%s+HWpl>AXfH9qY$%fph^=LH)%mY>Wz5fPoSZz=QKDJfgERk@4( z-8AH8o{M~txksWl(XB$>*6rk}o%3BzmdG5RaYl^8kYC)>B+9Oh`^@z2pE`2i8)nSW z6u-ZJv+KF2sm~aUH#trFy7IYb|K_yx)HWxXj^nc?*=*2noMQg$e_B}9)=aCEH>!se zQ}~wVHi#=%dan6bJ?qTAGsX}8`c^aA@k*#%IX}s{U-xiil2Y-s`BS21Gko8*jqjfb z)8E+#7u4JCc+1bE^LWF{Q?HK%<$4|3BjP^qQ;Y=@{fOJv%I)0>&yhzLayg%o3M$fp~Z}H1+pOnSJYvEQ_>sDXiSF}g>bJRNvQPIDVX0zOU zS<`;R7&6qeWLPun%Dt6-dN_9X+bQP_dwD+IwsL*{pN;hfo5UW~Cx4kxle)C%n$JG0 z3=EnA3=GP+n?Y{*MY)Nf_3YrD;OU}U9wM=e*Y9W*Z*=+Q@nGZJ9h)XNTC6(cq{_K_ zPm$@0M!%L>S*FL%TtA=N*pzHrZcta>#|8qlN~0#SpQ~$VgHSUb^B(i35qIw z6!AQ2!D@Fx?%iEIx!D1ZNgtN&o$+*S&rT z@eU4CeYL9Eow=E(NEP?QyQn4>&f5K_=cdoa4Hiv;l}l$_wM$)}A=YRob|zD^yVB^4 zjLRFVrtJsVCKRkZ>|?M-@MQVq&Nro2r8@Wjwer=^=1@AhULsk}qh+y6H*df`(PF)l z9gkOrd|>D9nUyK^JngPQ=?k`LMO>!L(|D4(;(VUXNbcw0$?xtiEaM4zx7+VQs3~vo zfz~t84$>TUY&Dmc+;B2!J$m~5tZ5&^zo<`hsbRZ+@WLB&HSYc>7xoLDx8}G6tgO52 zb$G`2kOSX7Yz-=V)ynkze#Mgi+*(i1Y}>?V-nQ`Y)Wiidmol^EXgjc8tUf5tI91}3 zVJnw{%gtKzsS?g1H@o*Q-*iw|vw~;pu8od?QHr&P4U?|zmkPIjpQBSy^YVuNi&Lj# z&3UZfasR8f6Whqg^@elSGV3E@K5Z|}HYV=m-M!ZNc=gL4T4MY+OEP;p6l!xGd?~Tl zdwtdUOOwC3`LnCLcQ`zHaKmi#OO|V~xAXR$wh@w?uD*pUJ#qQXpKQ0aB2QfStJdUK zzOyV?H;5~5R{Z+c*6+WiEy&!yCeK=D$$`WB1X@m-tp7D>mW~gvN6sz9P{S+P(^viT z&b_qwqh^~_{<4Mt&fF4jOPhL*MfP*}n@2T^j(E9;D}R4(GWBN6yso&e=JHC9X_DXg z=k?_-=GsC!?xN$rUP$el*d6Xa;p-f?8(-gsHcbdTbK%uz@!-o{N&PMnnWuhR zlr+say=mQ^)p-tmAvya5RbszYMc?w$G}Qc53P7Yni`u*eBkwG@zV)?jzJFw^^%7yRe80%l;^dh! z2KWB_H$TDNE$}v|W$IN{XZw&!?TP$h^3o!5q0_~9UZ@q;-9CN%fSj)Djp+TXS#k4T z^mH%e{ng~VWLC3K=2P1~kxf6+V}9<{yDu?8J*Fji&iA8ByH_XdWDWTFBPi@o&GYqT ztwp&?64OF&c5N(OYwTG_3!>;x%x|`z(!(Sy4+mn{&Su3LMhfOqSv%~Z)&*dCS-MpsXCjnMB-Gb zMe09Yfv(?-dnPL!DPOU;VeO=~E}{_(rtRFDnG^Dx%KEnDG5p<>9w#=hrv3nHKzvv1 zCUcv!LadK|+O2qgu;+l4%DY>^flNscPfFeAS4%edb^b$2kDFWlpPKH79zU+#-8KqI-{tnOzG^zZgG-+AsP&;pyBR0$wFe)4ZM{F_p_luF$NQ^Z6wg+caV=TV z{m5D_xsl6PtcbDRz+v_4U1bZ+O=4R+b32?D`moMD%_JB5?#I&Ob6RcP3->)@%VE9S zc~xzOo=S`EqwYmF?r{DP{wn^4`JF?sfuY50WBrX^j?en^<@KM?Q{N<}YoBV`VesK- z)Z0JiOFuo@DEUiQF{)L>J5Xz(NzN}v@rpCMPkcD7_wbwAla#M7Sgy>hWXbyW_{+(x z{`cq0r>IW!tesPwA9eS);Uoi<%+T@$*3E&M4zxx5$bcR%)F(xIgY8T%$zI{_+`hn$#Yi0@DFJVcVmDrc?GUxJwRb2WUjt}qJNf-L-U1fRC zqkTjBXp!crjT-Is-P*!RZYyW(J8}K4tP}sTtJ`&>UxjNm>7;b6Q%UXqBRchncz1b` z?zP;luG2!!DfuddblzL`SW2wfmXo`2LeZtJwZbeW(Z5=vHrgG2{dv`sONtM#_e_e> zlHqZD)0|^Ag`>zy{?r{V^(P;%dS45dEZWGq`>v;l*wqz#mPOv+Zwdcw9uZga_tk2> zR}3GyI(``@J$`okt76h$mXFneZEwn?TYql2yKmk7C#Ub^?Z3EV(o}W%({sP9cWvx0 z-@5vc!O>mPRS(OK7sqXK%#r*%?PhSI!5ls}*W;#zJdY)X{_(n7WUk!M%Fis+)@s#% znbq#uv&RKf-IrdK$u@8iopL6^9WTUc^j^gFb@|hlnue1Hb_S;s> zvWwc*I&+$owA?ktx%|~zwd!Uia7M2^ylTZP#)q#D^s}v=(#&@*{N+yG*Ljzwb-q7& z^Uajju=@PuJb^R5n!DD#&y`Bb6c(*}^tk*WlhXUvi|j07_iikTjrQ~QEAujJT(Vl! zGKQ~T-KE}Ot6a~zo&KFdzoh?f{5O043i;%I$4@6N6q~7NB=+bnzaaB`!l8*5x3OK^ zEG55l#@sh7UPq<=MIMq!Jk9r0QaETyiguvZ<7*eBrWGEk3Y|$oYhffpOwS=|Bd*A|5fX>?-x&= zcwfOdi3r6 z)+;)S&*nvS)$pY&@U+G&i1<`Yl-ChF`o)q{MLjvKXo=O61@+J8uWe)c|6%gFn~?!^ zQ#}7ROqEW4mGmmZ^52{{!dd4}^r?F$>^x|ZbmXhXX??>p?(7zc(SKHckUTvpxq4P~ zO6cu35|i&)B!(^W6p>ykDpT=2v2^a`UD~Qgd!IgA73lA}xn!TZeSY1!-fiv9NAkMz zN|gRBeP>!Ky#7$nk;c<}pN{5v9F4gpG}m>FYS#gK)Go=d;9f0LRt5%nJ{-O8(j?HD zZO}yZ(lFoRM;4$(+ZR*y(jF%&%5UC0`&P4|+G8y?4QaKh!m2$yw+zKpPoDhretT+j zFQ@Q|xvONFu2%j1^>zBJ4=(18{C17=kLjdlvB&L7(qzq_tm;yjXmarB?YRl7w=$j1 zJ(#<^nEkTp-TQMm{_*!tx#)8EFGln1AH!Xt174{_4h3+ah~jK}nNiS4t4) zwi6<(%NmxPzF+Jvy)WgXU;U{|s-g=nbT3L$Zan_8fB8k8yURi*u&CWsN)+UYbP4wS zXy$nT*X)?fsybyqTN~2^-bo&4P}U3VS>Ep;Igh(ysnetU9j*;~C)+OKjcIV5vP`!j z|Lazyz^QXK@p?`7#WjKgkE@eCnYg)TE-9RLqoZNlhd2KF`_h98 z-1I-ZVQDm;wa;eJ+NffirR%piB+bq}Bp{J2q;ho1M+wG#m5ske^DS)aU--wH{BE?D ztxV)-E@a^InUnBp_vVGd2U5g*jxD^-#<5au>C@jkw+Sd;Yg^ply^6y-MesBCvS6jn zDV*7#ybRY0rmjEH?a(4x9D7b^-nvg57q?CNAQ-`;8E&?Rv(0tUkKZ|u-fGP^yOw`t z+rx$3ZYqB#E_kufsklXAgTzC2&gX~PUwL3+}JTgwN>r7(3ax)Z`D^Z{hKBoq5Jla$r&!Q|4B0+t`WCbCq7Fm_PXlj zzpa|_f!vpWESQ=xWml-k*42A?#ImQFy`8Yi(JAcGr@fZPEZ(18cKKt5oJDPY%%A*z z_0NS1){8uxvPR&ewcxay%9CC(7q36rk@Ld+;j7};XI?UN)~S2Ct+lpJzsDr|L`Asd z{q|*k594P~_sccdxOwl+FpteU7dV&uOGv98i25b@^w7yhwqFHvPuhH7o65d+(K^%D z9Ba!Yv}9(g3soyGH~;d>)SBOYj@8D)`#l?i*StB{xghA>%HMxqXa4?cmFzQ5S+!rs z`24)s2TxOP9Qn9-`&H?_aG7+&JO&v>qiBw~9f##6t1LUocVd%7rR)1mswVOU35p$Z zktyBEx!MxP`jhqY#j>Yv%(!}lZR6Khhkx~Ma4$S_@3rOkf@N#oe_kawiS_E{hZmTi z@xGg}XvTh_o#M}nrrwyqymj4+$w{ zP9(M&DFFWwEZXwUUEe~$2yRzta!TrB?tZc2OCtjD_dGCXPN3UD%_POP&#C=l3 ze(m}F=lFE{`?9rbYVUrN*}u=cAnkCb>xaKicISiI>yx5`{%XXA>d&gxFX=8?tzEn` z$nC|`0JX0&H8uXX<<4KIjo9TgBccH`?-Ex7112o|8?FJ{o;~nzCbB=A`{=`TUhiR;ltUAKdopz`GY4 z9_5rrG5AVy&iTi4e|wdn%(UaH)yk_@*Er4DU0L(iVv^DS{ZgIX`f)W2?=QS|Rzopp zTHQZp)Y8`cT339m0t3U6Yz78V)Bzx$)Uwo^&;q@J$|Ew_#gE)R8UN|B{FA6SP4>X+ zulrVSIA|uC6Wp-*=B=EaY$g&%ogS$qzWM$2dcS0*iov$Yt9Qk0YIifXwf*~U-nDlf zd*i=}^`F$?dEj2@K4<062N7>N^9;T+=~s%(eQ=F?%8N78_xT*+ST?7n`}m37b5$2r z1qq+6`kTQ2uR`0+c0Zf;|QDJbrSZMnu3octS}TrD@yX51yAE-4ywQm{keS9HaBlmjn)sfn z5y!H2eUWWoC_cLV-HSi3s#Kz;-#@zjTEc~0GPVC%iW^fugx%F{dvaEMAA7??pNTvT z`42@HVoKQ5pKn-HdgpDa`+dRRm*2hEWGvHld39q(XI-|zt9dcsqnI!5m;185ZEme$ zNQZFH`^-ySnhmqs9qhwZFMUq7`&9lQji=-1x)~>{S!3pEyqCK$DTyiQv(dDli3Q&5 zEjuMy?K$(cnhXk$Xnk^Un^Z7&<_4?e<;+GlH_MMCGOL|@aE^Tzzwit`1;4$T4fbCT z8x$QWlu)<$yGZrbS+~Xeu3M}ymnpQ(dAN`vjeo_Pc>Qk=8LE}$x!hCgEd1{&p%VB) zSkCU8;OW*Gdd-}9`n6Z&1d=Vh9<;6A!K1^qxFvLnP4A(d7Q5eV6MZqWtF`VY_x=x8 zeCM1ql$Lc1K^SM3PcXCJap$!Bxn z=CWCgx9q2XeDP{W)b9wrrJv7LujO6s_*GHxouKBX*>7BY9{iXIXCs}R(JX4)sv`(T?BL3xkac}#NFTN*Da`bub zWuyJqX)@ETLjAw0IRU-S8<}Len#~hF#fUG_68ZczB}_a1=ex@PPIeDg1!cO|{PW>{ z{^{Us{pU%~RJpCh{bZWfDQ>fWWnXW3=Gifep7-v?mYm!T2VVKS>S6bLxlr$U`VWgK zei@T5%!uvVl)0|FpHJ!8b?yZAmrUz^PK*Am7`^$^KV{|eTHXygkL?N$hK`TDmhnu5Mq&ZaQPy=H_&UMbh%~O*<#6n%J*wJ$3N5 zeQ>8?$W^z`8>Y?P%g@wsra7G5{p;ed(`7%r_^|Ev&c?m@ee2y9uRbmPV>|z{x)(Fn z7Wm57IQCRLTl4htjw{*ULbxX$YcD^$wOs!&^YsiHuGj4k_J&@l+ z_Viv)EBA}OzE`kddwsF#fywV0?iNp7G3QJQixS_jGl?v}L@&&pCo|dc{N)EMUsMAB ztc$A8xbktn@`UzXhySmRiHnyhXA@l>R4BE_m*I}y^b^xI7byI0obl%9l-u@_RR`Dd zv$rgkcbJOAGrJ+-aNKb-vNvN(AC`??jgMJ2Pp+|zpb`%|Fn6Mum(lP}#|an0a_ z_P!T&+2#>>iu<}0@@6jxJMj3lp^Zx9R54ecuoIa_xJ1?zUzUoA;q_CnlX&z+wyW`B zJg1?xl$oTh(}(x^mJZI!7LNLhElyron7l@)s#Q^7U%Q6UNBRFpZ@-WFz5SS+TjcS? z4Ji*Z%l{R)T)x?*cxGkiEX!)QY>sn^TikEmT7O8O=N8kHCo4YA>tQS& z|63`$ePPu-*{|z&O<$at%N>?)yhg#ILN-kPA77aGTKl+$8wW2|hb^^WSvXgxu{*)v z;+^L7H7}kmjkaVe*_`5Pd|RwmD@6PhFswKuGM{E#s1p~LPl5FSRJCBK!8&(IrhOUr`DO>II2#wAo8z`>>WokQT%RAF^ZsIIWpRH8@0?tL zbp>vwhdIrfG&svQyySH5F`Qjfnz(e0d5Xr< zNuQEk{q(uQUtGK>{C8`Q;#Hx8MRs9=yA>vIeioBo9>)?KGc)1hq4;ynHWj}e4DNh> z#M1B5vO?(a>l!JWLo&%plkyE}Cmye?&^#?}9C#^Nr%~@SQ{;rFi|oHlk?)SN$@NMW>)NbFoi(})Xt)G%olT#1=;96OF(yP>aXU&Wh zJ)a*l__*g37@p88+8hjUF|FvsGv3>jO7k%H zcQVK*Fms%X$TavcDZ|%;k-cZdts9s9A4qj)@#Wfb_Z?;wS@_^W-lVfPtp2byU3l*$ z){uTgEJ@wj$~|Dt^he2j`*`Q1nwsv&S*OfbS|g=4?zF}D; zS;csgTP!b3Xo9zs&P}Ds3KIbIf8+*LE zvTa-U>85TBTWGK&T=4jDVO{qaX`YxZQ)6bNc$VK+c&^i`t0l&}`F>Te|Ct}r|08eb zR67y~y{&cR;xvg9)9i5|`g)Yo#*lIRs(W86X zVtb|<)lG~%m?|!LnEQUZs_7AhuRfAre2(1sx%cvF$*Z+*52dZ0Q~pPbcXEhffL2wF zP{!s6Zug@P<>O!9Op-J7-h1QetK&Vn@l?&I=^awS2)G#+&KOz6XG+&Qv z&p!p;aY{3rxR3ve%H;hF$ulbiq6_xkV=WC5d7gcdr95!O+?Nx#*=~x9U9!V@{qG2e%$@IuL*M3>(Wo>!rODb)GW1?buP3 zy_0)V{@f2gVr&l2p7SPE=w0tsxdjh2_sAUVD)&@Ad+wE@;fc(Ji`E<#c@elyFOhGr z73M)$0_lC{18&bRl9Yv!E1^tGO;tGCT@ z>%^OV^$n41>wo$`Oq{9wn1A=#tfL?2c=1Z4Dy;QYjE|bJT<`)T!#$3qfSeNMzse`A zLeI6%Nj@qTEA~J{^;mrLisuPU>D|-jWnbD?`tfn~a$de8zdN_u^IX2jxRCMBYzHZ+ z6Rmqf>xEr{mvei_FSWSxVE1K92Gepbl~XtM{{K?a+8@$!KBe(OfeA~1(WF_^UWU4h z?YXeJQ@yt1Pr*}z+9YkCY8zp-psL~!Z|AcjsrML^{P)~(JLBnI6m3#)Vp)h{p!n4< zslEQs+8-V|!n@{ojm6so%=;cHIUn4lES4x!&B5KQzl#596UF+-sdG#0^$sMg za-Yt&C~Eb_N$K~V9*N$4p@2R8mhwIkcON2G7xpyj;Avd(=ifWPPMY)*r+lT9xU%y<+c=@Q~*3M5-2TXr% zixUnmW{ha|mt4Gh%ErEUbB?vFDYI{E%blpbrgGdFWp0QnWTlQ`1i6=gpFm-Ox z=zL|6bMS-nsy=B|zYyai!JU_5PX1o)bLUh~_sV$>RJ^x+UVSY^t2+8}!Zno)-&#D-({uMJX82Y!ghcvC8$VUWrla7Fu`ia8J_?S(Iq9&Ztw)_k*Jn?}2%HrhlA% zut@Lt)Tn)4!dcEUmcDRGs_i-TE#>h~vA_`F+MPA*$D)#w=S9s_nJ{x&=zY(S;v}{i zoW-+0EiNj&D_yF+dqw!`qla(l#q$4|DHXg=m*dDUU7x5^cX!=Cc6;{L?&C3vi&CVu z-|oHC#K8%Eo6e*K=<7++Ze$4ck)R-xm5WuCsme z|B_F?qI_#Mdbi!TGVNLN`P0eryw_j$wJWpS?oGDU)|gSd{D;)1c+b`Ymo(#eX6(7# zu`adja_+%*^UBx{3#y+HeY_&!wMq0}9ixn%lDLfz<`*t`5*w-SBb%vser;|;OKJj( z%Xg8GzgrFB^*-%(jLAE^E$v;_7UNyFy3JHRoQiqS{QbxGOWm91y_%`Nxy&r~_SSQ= z|NSWyj$A9Y_S6E?R}xj5j!Hb*;}N3G=AoMXepPP%rKU{%R~M_6uWe)WKH?#sUj9Dc zUS{9gwR@{0my75a_6A8FEG{tn?(nj2WsI=MakFP#CfBA*@~&z47n7gxA#{Oi!(!`s zd-TP*S^Ko6_U_#ie$VSh(q{eLGE&B}B~PlmE+-@ulv?ePtbPAF>2m4)lbhL;c%HgV z+AW;?chkg5C+FS&#koSPtft!4@iZKrpLD-4DlU29(z`!X{e6YsIWaJ+O!xHIn{(xb zi*(@~2CHKSnR;I?cDT3i(9GhjCU$enu-7$pr;Yd>D%Y;4I=^Y-h1q-e@7ua8sZgos z&$YK-|GBUE^MB#Qjgu>9zL(H9xLh^Us93u8`tNJ=H#uE(Yu4&)6+QMPBym|Bmqn7t zq`>#0v-jlY-EEHQE3~`Tvi@CY$@`U|if4`}-+pprhSk=~%v%GDEI;l&IoB*LeQx*c zgZan0CJOG(iB0;SE_YIq`DC!46fJF>P{nIHZf_WRZCvo{xfotqn{*WKyt;iV+n zc~xi0`Su%=kMCl>(WQDfK%DpSm*32vwtYMLSp4FT0Ohx3XvI}>JtyOiOBX#bj7svOwem)vu;~qSFNk0EIPcISGY0pi& zE6*jLDdjLxcx7~-JHcBn{@A)St%kG8A-u14&pUHL{lV9_LYp+!usz+i@=pYErut6#aS6jBMTpb2ai<;ATL3NJoM4iP8Iy^h>TkO$uJ99Mq_EL_O3!A1e>6ta8 zs2s@pBW<(ieY9T-vm{x>Mg}nMgSkn}GMV12e_5_f#DWXPN80c;zwneeW(edn~lr za7~W!6WgzUM;q0 zuUPVfanj^#9Wr}%F|D}LE}p&AIV7ixU$XO6;^z;{X-D(_@%-btH=(0(nr_X{4kOp_ z)gkZaG#=`odT4!?YR6eyu84(`HBMW+{@Js+PjdOr(%e5PE|)vwFBf{n&pY-xc5%b= z#Lp`je{YGo{&>CeL9P?50eZ&Xa^Jc?*iIJ>eEnMOR#y4rIQK2#PZxW5bC@+S$^DRQ z`d3lBZ^1O)0R1I0WgJ1O`fJTXzn;}vbvfdogN5l=yK6>k9xv40Z)NXnxM0h_2)}m+ zSo$}HEQnUw{&o6|P?=}4^_5MvqUXy}Y%fp$xT;UDdY_(3|JT454?ksH`KjTs`H%I4 z;7MFFdTVblQ2P~SXvg;@8yqrbp!mIl{zt#6PH!s^4_*rRB zdE|nV%+i%iZs9(*N9F`%e$~HirZwSslk26>^I8ae7e z+YL@Fcoeb!`cWo1j$h&XDzU$j|wYGL=I`O$fyHLq^wHi(&RNmSqx*tX+W(z0Kt{JP3bYg+>^hquOk zc)wJ<+H=KI(f|J#UUo^=8q2&EU`ai8FM0`Y%;r?4Ym!RZ28#>@zQ-xeO=>&)p=AGI zr(c&`Z3_IWjdH@iLhTjeQCeDu zYlZfHnDZ}<@mI&3e_@TA#a;$&zUa``R1>s(70-+AQZ~P-)twQ)`zt;lnI6D!DnzN6 zwcaaXQ-Ab_U$ZB_efKmq`^v&UI~Q$V(@;})a7kxZXM*^AF=-3ixpR&l+q5w4s_>&v+9Om40W z&N{VD?xg&J*^hqoT|IkveQj*n0d@Nh}J`+x8nORNS5y{ zx+YxikfV8~^1jaPv&s49XMbMM$@Wf5{2m!G-zD|p0X6ZtjoKDj+=u3|M(=)OekEw) zr8SNR8|$_?H<@Sl6b9I{{FIox-S5>5FJ1MJ1FLTH)p^#|&*$u{`K)SUvpPKCzU_r*?K^dWI~3PD>@A2l z5y<Q%<%?OKtw|vu^zXhE?phuidPyS#e@TbV78muRv0to;7LY5nOQk+T}H+;#W0t>)X!+0m(xTV)%~^5=3<=mGAQ#I&Ai z+s-^l+qg?_b(qp_*-5gF`bkFvPw+|v3kVflGm)IhaqP*=kEXucA2Pq2u{3MaO>MX7 zwy75*zSNd=#Tk}HPk-ln^#<>I4vE_*J9#|~*YHhyuiB?twXbVJXmp&_^CX9^ptO|! zPr9cYx93c&^IaY!DWMuAm2o@oW9}ZoJ^Duv%Fc3J5uvgtS<=|#Sn&b&_`bvEnwGwB z4VWa@Qu0mx7~lL$6BA^P{j_Zj+f?7VuPgDy0}bVGx)aQ%e%xWQwD4m@Z;h51-@S?+ zcYEVA^{iJdJg005{G?MRdPDqe#?xCr^7^Erb7vo_-9JBR%jd_Uc>Y3b6?B4H05M}&YRPIO_|%Y^Y4+G;Lt^@^~|4jXYnpueC&^n{tb<0%O%C} zo;q_owN9q*lWSu^4Kv-w~0G+s2{XD;6vIxYU`eaCD~i%;2U9P2$)o4&+F$tLBUkStttaPN`u z{mU;mbl2UT8y~sm(WJPyYHzL09=*tPdK|~Aev>OL@)+0oE19(Au8Ym~H&35@)#EyFMdokd z1B(g4Pu+@UGCMhncU;-4b^3+Elt5ug6|I(LFU2`?zVV$u=gRS88&BYDgVPH%Sg+q! zU_G<6Rd4P!#+?ytCky*#yh?as6K3l0&L{uIuGX+MK8Gf(5)&z%!k>Jd{jtT_yHf;N zY&#m&Q@P%pI=9k9X2Or9UaS9o^x9+4wsvzw(S&u))}E40YbAnmu;Q(b~>kruBqx8%>!R@%ZrPH+b;zDTamNu z!gJQI57p&8-; z;V#L?yr;jUy7_(E^j6|GmsZP@`G#`uk0rB)2D8?>yLR#h+D5d#Qxd=YYmwU{b4k~En|_{< zQR+HTDdH)%BjT2~VZ18qEA6>k+WvQ0hJ=)v?Ub|Bf3`R@y^TZM;jO3dgDVdXeys5E zie2Nm@qzq5yVTe+;U7-hPd>e#x9D(w4ln!1T`SgWtzQ?Yvgm8!vy7f}jg%j64ayw% zHfOl}w0Rk2Gh>Ey=##z;#&0`59hZvDX~}xG_qD9t-rt|!{=2!;aOwG9$CtA&vppXw zob@O%rRD05^xgH#dD*Y5%`{$YO zrn`RsE;5aO`X}hD&@eF%O0xFu6~{D+9WTdHaBIq zxzmNc3Qpg*#7+(1wV$&rWwEJtq}k&%m7lNJzMHT4&zQ0}YkJj7@lRXVN0&-PyivT& z67Sq8e&V?F*{JggRhw!K>Rr2Y{pG5|S0ax&zgpG4r9}TjT#Q5QUf24YyDk*v7e$|wVRFQ#V+>z4%w9<(M^@NR1F)##jF;Yrkqh~tbgH9V|k$V%68LvvdPAJ zFW2psw|SDgpk-OyjsrJjwiVsfJt*A0e{t|y<>I~HT$y9z|AfsyEEgZ>?XMT^nYOrUkK~iH+LTFdR745g;k+%}NBA+aO->Ojb)%Kf{qKEm6Cm{j*&mFYV4|v}AN~HGceS6zi zvU}@wP1e3NKPlalwmIim!RFKKp4*;&P&smb(vMWTG5NK_O4l+e>z|Wi1gxH)kd-J=;b8M+kM! z&&|yT-4OaVI=A?ihY)B_E?<2_$3e#DpB_%K2yJ;VBPuDlVYBDcnCMLsXV$bXx!Rrk z@2~A$W9829$JY7pR_TSVG@7)ly6V@{m+vMPDM;x{Jye>>YF9Uf|A@M!e4POA#m~!6 zg`adP`78a@`{LZ?^Cq(hdRRH`=(uuLNARkjuZ;QZ{{oL^n#^g|_~E?E^Cv^g zCFO|;Q(fFXN3aC%`%zNYnOZApIZtBA^?60E@jEZ|tL~6p@=UI5$Kd@mSNXAQY;uxK&xOaUH+fmE%tAF&+lG5(PFPe*EWeQ z?T_Q0Z@Mqnb?|9Hf#EATPQyt{L}upi&OIu!=@}!B#l+6Vdm`Tdl)fYBY@Dx@c5ZG< zbnMD^3#Kc_xSXl8`^@yet(@c5otFI8SzPuWuO8V2aZFN)_^!62xc-{p{&#_~d-57j ze9X6%sGZUvqvUs0R5luj=%0GpImgMTNKbx}-P#Ma9IjLAueu&DezfD;vY1sNTFx_<2%0FJ zwkV#Vq&T@=*5RmquxO@izKn34-I2`R{sYl^yv@~xo0eP%-1bOt+VRhFlRhw7p3k1< zUBGEC`o3&KDU<%(-?G=1aY-)8)=ctc4Pv^qZBt$0o)DwpSp5*;twlGD%(o_5^_@Sj zcX-ufrg>qxf{rbh8jqisEU38sLFoOS7%KsDp>0lKySJDgk{7J%(|&%bU0vgr{qnd) zbu&LK?qB)%;d1L@r4E;C)4g+qX7um%-NkQ{uDDpxMsrIX$BB3E=hju5ta|#yyZZU; zAJwb=bl&`x{rZ7>#FpxFb9bKEdsu3+m)WzvD#QDieJ1;?7wgFM*4|Q*&Af8!H0C`@ zi#KU*J=tV^D$`%Vrg6!pm_z;UzMo1Pp8k$H(fQ*aN9q3GOK-2gDldF0<@nZL_JY~J zC+u4ld-2#Yae4iF9i9vUlX%P3?nbjI_I=?^3R_wuv#t91*6(MXT9iFHXTJTnw^H?9 z_ciIpU2fOcndx=h^y7bcy5|kU*GGQ75}haKw0yb8^i^@^bhgi?<|W#J6RsrmR)D`{JHvLgdN` zGROMXYB#qVhMcXFi!bEimfl{Jc{$+oEtS4e{X zJ8PEPOTmuA6N}qhk8jr4(eG+n()&vNd80mWPJElj{Yej87MJQIr5)0n@%omch1cN& z5r0?Y95`$ym-j$@+aXELJSo=$g{K2v-Q3jrt!di2;Qy{?HKPo&7L~l;*m_rl=L?VE z%G8Vg**)*F6`!43`$Wn{{8FcA=~~8LDl9^tX%-I3i$fLu+cnjuEaUkh^}>havRQM~ z#4kTMx2$)lcI6Z}o!YvNXV;4jjiJU%c`r^BZWGd5Z{+Gco&D5A3x_#}BVW9I$6>;y zTJ+qkwDHI;=aBokM-N}%yFFJ=|Ao&#kH<-IMb%y$UxU;QxJviqt7u-sJZ@Kxs1_qd*$~_Urb$3-ZnVpu*Xs3%lRifGc5XoD@;yHi?2>NvG|ryRLLE-)P>Q> zF%ezAmM@UrM-sF9bOPusidTQ>xYP{l&kJ^fkbxqpqneA1Tl?55qQ>ujHyu%h$ zYj4f?Tey&C-zx51#}hp48S*D(&a9kWaQn)mU3yxJzfI3usyA!3(gD8=d(UKYJH5v~^4gIb7r*@7 zH6gRKY@+Fk3i%d^l^e9opK#9ZZ`U?u_Y)812({tJyDaeU3`^9VM+rY2>W=eGW|KGWyaSX~;1(R$OToxwt zTsnJzZEd67k`1S_r`^41%apm(@?p-ti9h%C*p=OWyxDna_le|%kyZ{<+V3zdH8+m+ z|72+Tv`zDJ;O-j>6l4!=U}#rkw_4P7cJ412u+s#_?!l#R;LaQ5J z?%z;*?+j;=pZ5MFsl$hLf3Y^9j)^ZWl`qWFDS+TOlla@CY zeCIIQXYsn%_rb~P(;q8N|C_(fwKAN=r+l_q*EhW?{~4ky`W>Dpf8X|o-+8`n-16&6 zKPSFST%9&+zK_$jf{&`Z7bffsTc~>~%w&y--yFYVb~_!9tTNTG=)GXxzs2fU^HI}q zE17j&wOR`+1OKd8cKL4WMdh~VE4pu1&8}crEE0F>M8B@>@xXJI>N@+TiG5DuVp2Kf zYjnBw`X0Mw+fV%p5S-4v?~neJ_)8Y6D@rz|OWxbh@jkvOoV#FIpY9=HqxEYKhJ9wr z4eRdw@RQ51_3<|=pN-!)^ZZ|6l*7%VF17bdy7v{%**>k;de>;4_t!in{YX>$T+LMR zjM|$q`|gCUn3=RR;(g>4iCfpYCp8ARow^&kx!&;2D}%~4KDXwneYF+6+|KpKv$f}C z^wb~g*Hj&nh^ zwvAO=DO>4>A^(xae=#io=e&qgohYn(?MeBsyNT6k z9C}*)^83vUe$P2uXXWO1`MBE=@A(}Uw=cZqlk&Ibc16sIykBj(S9Q+cx0uKKrr?{$ zE79n+n;lbUmgjCT?*CG~;7>~Lq>29)R0OLZ+LGgOXy&27S&91lwp~llkWhcG8(3C$ zIU-Bc({QU>^116e=1u1WHukPG53<|8MK@@#@x;i8Et%5KW3HU?jP;u>du3zD>M5eL zv}c~&vOUb@*_77B_IIOipLgapxNu(D_5SMWv_z)-D)YBqPXj-z)Rws@_UFCm6;C~1 zGH0>4JUbX+v8?IcBG%J@o!ySCheLsh8o%64woi1CQ8W+YqxaHLDyp&CT&!sD^ zPwbzp|MX_|KgE{;sXM>E{bhd9=A&Bnqoo@T6l%AU~muh3{Eb}EGQ|~_X!Os%1EPq=R1IK7rv@4##Cr9qmFZWTg`fl`T1SO5NaJ+FPq#FpO4yU)g6 z%`G#(W>fX+uI=5&wwG!y#^gQvZO|aUs`|+!J-6jcJbyJFXA;Zz(4VonVbZ;%r`ClM z2PbVV*x7mGkDc1J$hljh*M|H4xUV?#PVm;hYFzf0BV+D1U!7ci?4%Lb^NE`kJ}edw z^s0DqG+TX9{t301i8F;KbnNOmE;xH#l2?S!oCCF*e$g#@pUo_`aK%VXl<@1!-10!> z%aP+xABgqwW<2@vB+c~e&7PQzlDrnJclqY@CjXhYD`l6Uw4Olo#zPG;EezK?BPI4I zPB~cPBA0RG;p{Dp4OT9WITH+&rU@(G66Bn|V=do~JPrf(yOjZ6m0kH5eMl+W3hd3p5 zFFns-a6!b9@r3Rdh6APfCEu*yyqNcG{jbU2JN~-X8@=c~{v^plDYodctnaV%?csL? zckYzE%X54$v(d5+;i7!&>`wLtVu~NmFWq>!M$fa3|I(9&8Sf`mKW}UbNuOfN5XCpM z@!A_TBYU1_O%4&Wi$3rs%5fgr;do7uS+?`=B7?+ot#fQIKD=4r*q0OWgeAV_!B2~} zrwospDjEJA;y=8?(a%}#@u$~%nOk#rU)>VF^$puj*>csu@a+rE%gG1Jmq;z;ZZSDo z`l89zrR8;^3SYt38$A~p{yn~`#i81hlqnLb&n5ok)3$oQgPSzevp%)XN%$h%Ki9%| zjrsj==MNoK;`!3>XO=~9BDsHcfjC0MGIaIq%-^=JDb0|{q>y9*=`K~#V&2N7lxUo|p zH1xyEKGwV)ZL+#QYuU7yoqN+QvGUzPalvDcE9WrnUE#s;{$OXs4yHSEHT-g_brU=- zoHr)!iCVJY>4leCY(H=Qe5jr7_4m&M3$}?*kL4+ysoIUW?AslW8;)X45h+{c=j)foAbyhLRUph@{iiXP5kpd zWT&;%_3jodHL~IVdsXSgnghj?&Kqx-7?G&_cjb&}LXQ5davnQYyxenFVGgVDDp@h! zm_OCdVtv|wOmuv&@*h~pT2PYN$imRp5_iUZ)1}1;xkoOrHQi{mnD|Iz=1e8Kd9DlJ z&*y66Kh&UfEa&Ylea1~qs&}5(zStcj!|+RV9pm3CBIZwWKg-Ts7{c)OsnC_sQ!o0g zyc5=@^|k01G8%ii#k^|t?+HJ>rYib$q;o{Hvu39A3?{8A<`uOI_TBw?A?v{W{#U#2 z{!B1`z+b~1Gvjuq>4lz#1Lstt7*&o~9&S75`FDHP(y-OM(;rB)mPW1$In zL-3)uRkGZt@B*3k1M`edH+sL=>G<$j{)snsEcZ_zonXu>|H5&r65}(5m5v+gd4jw@ zY`W6Ab=I3ZS^1(TE+_o;k*H;Gw>&tf=#GkI!>MBz^IIGDE56fm3V*atCjQ*wcuvt6 z$=%Gmrroh-*v7E8*nUoufT4^_v&i0NL$#i(5{|R;1TvSW2DJ7ESO+$$!1f9{>F|Igoj|7e|`hWzE`#& zCClUY-aBr}Yi5s`cVl7#1F#!v2mmhFNoG&kgsr zht@b<+t$w&+{vHy_i50Yly|oM9VUD0?6j_^=;{c+PRyPi(I2(6D0gc2*MjcVdACyD ze|@dHsMaOoxccpsui4eRSKL}ZUvbsr!~at_4rotM^!k0G)LTFy8fod(_t3J&hAsZ~N4Z^~;p0CSC0drmaZ~^xd3t+As3g ziZ^FhuDN~UlP80Fn09R)->YkAXO$R@_y46F_q2LOoYHzSQ$9+*{6rOA zTw9#)cwxxBo~-_ajVH9CR`6ffwokKc6-Z8%TDZ)i;EC+Ui5ypW+)7#*F15R*UEHU8 zwsup*D(7HpBNc7)4gJ2+a;i3M_r6Y3$>7dl6U~|^VSX~z*ZS(DH`9V-Shl{ZIQYu) zn!^VFV)=C^nSQ%;?S7#X@O7iN(u_j~LthuMdF@=9x9l9xZ?B%ony$q&y4P`*P0+cz zCg^(jq8{y!i`$%U_35s1ZN8%SYw<@Hwd*v zZ~lF_YT_odaaoi_`ToUP^W}|v{{ApZO%4wA+I~h^PKJB^r0f~u-DL-ZuCgsRi3?VU zoi6orvYwc&{GmxEN$#E}6}EP4{nET?#~p#Mxnj%1W|U6u^iz5I?T}FFRUa@eI$JDG2I~Q~Xw{Mo5naC+(z~uSjUOT7G%m#_uIw9#N?b7yI ztSW!~^=!?p&Cf4MS_CaLS(zVtjM>U&vL)yBn-1b8m2xU~XP!!tUG(e4nuQHZ7j3_` z?{#T@8*|U2WuLd0WRS+>|<2p@}{6JzlJhfmUTy-h|V;+s&VA;3Ohb)u2;+cx3#?Bx!x1Z z`s_28ljL-(JqMNvXlbi$+Z@`mc2%m-xoy+ZLRUII=-PYpxxvPzkI!E#(QVZHck4J0 z^Z9r4zg9PI{=WR~$0gQQ^7nb&pYOPFq$+z>(}q{}+xKnj+nlyV-Gq70^2h7v@`^9J zd(DwUWV!E_%a1=ETh8@aXqQWhQHaQ2@7yc0F-}%Z3xu3)9(d0`8um8WH!wS>Vfy_^ zS7u%LRw)y?=AOdLuU|hMUA|&owAli&0*6jb`{n-`fa@wo@?|UR=W~EoKeZqu& z8R`9D4 z{$Ix9_G6Q-V`JsC$TWdjo70ckUOh73DsW+FfJI4-nN(ct+%oqHy?tkQy)i9%`_N@> z*Mf5^pF5OI6|-IQsdN^$t5>IvcI=wCxy@Ug=fx}zyGjE!Td|KjE(g6Ae3J3c zm8pm+{{4*p@(-a-_m!C#?jtCzxLDPG|$FJshJyBBs8m;-k*Yj|E@YM7L zUh9snZq)Tadj$LA@iEB<;t3+6{1sySv`T8(<>x9WuE9cLiP|lGn8(n%Zr@Pcj>Erf> zvkMme_!D&7Ggh_rl-fdenX`WHV_rFQTL8@%?j5sgnX02D- zacS<+L)B^T4F%Tsa5Mbk4+@`p@cc?;+e61>LDL`y2R+KkbA@D^vREJ z+zZe#$rP#WH%gPPJJgWz=IGKN--BZWyIb1yKJprG&syMQE}nkF5K2($-)5JkM@ukM>& zGt$)}w=a7YBfh{TH7qWw_xXB-Nd^m4&-fQ_&XUYMuk`lj6x-vwV>d6z$=$eTp?ON| zq392{Bw{x+OC>zxT$0$zsv}iZepxRwmQ+I6cvSn$?LpqM#u+g#?pKlv3?KgQNn@O3{}(JH)orUvKf zOH0?pJX$MREj`U{Lu~yMMXohx6)vx{J73ZJ;m$=ByZfKMoz_|ZQ#@*3hLHOHa~CwV z8>h2KrHd(lo_vlWcJ@?;)M=g;b!n%Ue&JgCOR6CG=&i;b&tJ983y@@HOf~%bIeq$D z`L(yC4IZ|ubA3M%vot|f&u(R0_mLmB=XWS43H`Br?EUJy4S)77rxlMBo*GR(_2qo8 z_cXim?C8K}{m%E#22aYj*Ld(GeMi6^tBH~6rxTKC=Gpi6t9f3=Hz91X8rx|zh=#2=Dd0D)|+FRvtL&J{dG6of9{s}t=o71(EISveXF0|i?lMY zTPaW0&p7``R`OT&vtXk;D)VI76&rY(-s+{i-aW&FP3&cSX}hmgy-K9 zznHbmUmo?jrbx5BzHm?A$4)Ly)xZs1@^2^9DeP8I?0Y+T!Y-XI=WRYoUJ*Vy_iNAk z-)7N^ep{gVF;;34Xy9Wp>^O2m$9v<$mhPTg_T6{+ZKdm< z_a$W2*zRY)dS)fNS??`Yw_7Sf{Y{!~;VMoWRz$JJvLx!%EZoJ_%)4`k+}ywd4JpB& zpQctEe32`1d*jA6X7T-O2h)UqWI5XCzFF%2uw$3-j|~_57i&%Uemlcv-hDkg-}dc~ zAHF~Q@#_?s$$9U1B@VqwvS>8lduR6M*Z(RE-ZclxOy2%-_vwG~9Dmj^A8#!Y)tu<= zcVe4i#aecy*kaYtoh^&{&y+X2-?%c7o4FzK);ZhwRo9xYYAxF6_9dZR$HU=pnM83p zTgE50kp2jfbK3jTr7me033Ok*tipD-l>4=CwKI43<%Y%`T&F*$B(L$E)axMSIVq8! zw{}CYN*G7HLtl-!kJv%G`yHx<2WG5GVt%M}gGuG@=@=&yHQ9OAygrB69teDAHh5ye zU3$zZvxv>;SNI+NmAN{9k9}f#;Vu~c``3*uS*DXICOa$ildiZ2n9q#Sy-+H@Gp&V7 z;nvT4eK-1=52b$;jATjpZe!JuecY2Ht~pJ?=kJTAWBYFl$Y)y?UePH~F?#f_x?QcA zQJ!h;*^`U%yxr{^r<5FcDaj(SLAcN&?~sP}<93Dq91Rvnai@OnbLQ=9Z}&)=ZS(6s zvRl_B_h`TJ+l{U^9dqKo|0rJ0TA%l%`2COK?X2|=Zho9#^?k=>r)qwi_Z5O_SE}OI zuGyQw`7J^2s#0We`1!WCY15ZHOcM{xwEVK{XYbJ4P zI<;oqtKJl@@LIV!WRst^POZ$1brG3YT&}E-6fzQL)k->CGe3Ka zhx=QWMkklnsO5_nzF-Qm?();G;_cg|lT}_l z_s(pSqVoDPdxaQ(`qo9ZZ86{bM^bCe{@TU&Qx^S*-kWp8>+%kRJYFVyHhp#86aU5X z9@y1$X;pJ=`)qt5=)Gm*TVe0rzxOLm-EJ$w_2H(#!3o*Fq&Ypq<$oXflDB`#y=h)A zmlr8M%?&vH;(5N}B02FuF}b^P_2#?mzuo)y<*3q|zX_=cg1S>CmpwZA^53z`LDR0@ zx0C!n&*11EZN(>h!j?>^zoGu*o%zRe^Z)ZX^<|i~ty64Fv_Fx}b|^+pp?2O`_luEC zl^<_(Oi#*e*l!t57+H?$x2vc6S*OQ+(AKyOe(@rBh(p2v272GKJh*!{H|-Q;d1TF z`~{Zc3s zQvHigVg=oi5(hjiM_WDg;BO^a{5`%s;tr){#9a zBI+yVaRqDiDM8bWw2qe zRJt!a`PS-LJ&tNudBUd&?0)CUx$Me*w$PhJGcG(}7W*F;-afNU*wZLrofKBC-A>5J zo@!|ITrzs?5{LQ5i%Xi1EdKM`cz0&Em2}<7FUs?SdG9W`T^jVxIM&IsdF$tkIr{`m zQ`TPaDqYLtJ9F_Zw{50;Sxt|pAKI_AbV1rzTNuujgg-*@BHrbPB0`J=N4KvyLwC@O9zH{X9oOku{@51$qbR;L9`*v&o!-TCD zX9`St$GiXb^;Z*q&3gDCTq9HR2WQ(23x&X(c0>7pE8Z;a6kzwVnsSTBDK9jr%ll`l((ydsq^9*W)-1pB zJot8tljW9#*N(0WFZl*tdhy2St@onc=H?In{0h-lZ918?UZOL#bzYuQ?j`LO$wiBW zGKFj`j&iBnEt$A%%PGT#jVD?f9vX>H)|=9FW8u3W8gd%^DMn=rgwyPdPP%{mqsO4_ zW;&&P8mD`5sn3p@=F^LGk3>Z7cCzg3jx%+p?evCIZzq;X5#FvbP$1cy$d88GwnR~N%Fx!Lm zHRo2lzb`s-THD$F{5Q|UdH?p!l6K*d-=LiDFq84@y|*<6yQTUSEAGVk7HnWPbUl{1 zxAT1IglD_FWecu%bXkklt(^6;sWn7N=*J1(r?uK&GE|cOFPbLsH`zhReo4jAx03|c zKP!5|czS{f)AFA8e?z_Jy)L^xLwZU#*GWy^j8f}P=7qeayLTihZeYwilyd2(!=59J z^49lQkCs;y_sEKE@N>0J*^Hcim?!<8J-z(Dkoi%of`g{n9OX<)rUX+NU?ZDCMchTYSpJf4w-j zf!~jME9rC3gJS-BD3ykM6_ASmY7}y6=917_!$}xEkvx;rdA}}ko0xn`dF`a4Fhky~LQ}iwYZy<}#a)kaPZ_;CjpC;*()%o1ckkeuzm3nQCm*_W%JSFLoj1G+`2#8^ zUw{9LE!HAk^s;o-x3Y5>c;&84YyS78%*^I_=W9vH z$+b>yQ+$^hYA{OgRV<%q^|bMasR+xSdCvb_&rIIRxJ7Hm4c$pEOBOGeJiC$QK=}7| zlhrc|T`Nyrn0M{(tQVJmUs|BxyvK+&G( z^Y-=?ZJ50J_Qh0rt@hO~-(J<}|GPnU*Vd}G_J>ar#JU!+7aF|u=X$|^XYt)Ib`#|( z4^^%_EahLX6`|s@J*e{D>Ti})IlK<7{nb}t@?5O7kmZJbT+)*{6WlGg-ZxssZ|<=n z^iI&LuwEm@&sz^0Uer{Oy_Ie^SwqM4r%&Kx@B4E~j%N6a`xx(9T6Tu9g-fq(`u5HD z^Zr-fwVxg9fBf*z5(}Gb!CbdrMN4|FOu1A$zqsb)!JzN0A}h5;{uwrNTvwg7GV*NB zjcfBepXjM_H~zZtq1J2;zsnyzWnU3ood&hn?>vp!9`5W;Zn^@sjK_4RhKPH(5| zQayKle}YQ-wEEt8p+CA-ZHc*3`r&_j@6;ZTt8;sTes-U^V6tlRn~?9UX-f)C_%|>9 zcK>+aF7{i2Wg3A7%R>G}{eNk`SvAXJcERh;Wy}4PemA=95}z)bK38Ymxpnu1pZ9HN z_;GA=dBsB=d)?IjciB2DDR=eUwomwNUiRkBN&fi7H(soKX7TvY>2FNYi!Mz$I&b5& zS1%j-xwxI1_kI?cqqwYj!{NNfAFrq1W&F7GP^x!YjOKb5oyBni(pOANv#P4~GcUzX zyLoWge2;(IkLv2*J;lE5S5Qa2PXycK-8QdMc7(iDmHBXWZS=b0$CJ;j&#dFi50AUm zP!k(oT9N(blG*duum3zU_<0|-m%Hrt3-?SB28JF-EOY$91*thXD4pEs=<-`9rq#La zU)1+kz~cb#4L_@we&-L(uvl|!I_r*NBT0SzND+l-j~Iek^o%O>ZdsyM&( zTXJ1u%ig};UY`55``YPizcgdr6#qPY(`~#{YJSHwiMC?3kWq>ScIgiT>UzH`^~>UOK;{R7N+cT=l{EN}Z)1 zizqeyPc^ERbv#`d1c|MMn$JzRc>H+}zwyww@c&d>jFQEkr2h$S1mwH}8m zEwZe&*uA4I*I9CRP01djY5RfryWp7Jn-$ZAJHXpMMmbZ}TqJ$NoQCvSZ4J zwSTlKRqMC%)kH{~6WOhJLU-0-rsvIt|CP_Yn9JjS_+|Q%e}@aI3}^TpX+OR3Md#{! z6;^NJ`>T=*OKtbd358I}H3c&J>@mZ|0tVIb`NeKpI< zs!RX5Qg$9MT3Fa;|KYN=64%-&3izG~^zELnmWlQ!&s^f4y>h<7y~Ufqecd3xHZ1VP z0nUaQH)1_LUhS>@t~a4`^S2|_7t8i6mDYE-^W*#cx~g|-{9nwqx@I%*Pu|MH>y^T= zjh&}J?T6*hC+(YG2)bX(`u*tp{qDmJS1lUc|4X{{KNQ)%Rn6vs^E|dk45s`JZ{w%%wnX;bj-AIQBi8?YmgqJ!5Vj z&ob`#j|V1eI0@G5yr{kSqiy4=_5|UD{8?`fse}ZpON9JXM0X{?8w}B?)H(5 zJi$lPHb|R~SUnoy_TCKOXar5D` zulOXGve#zH8D5oa@izaWvLY+5_!0L8RmXl^haDSNFBNjp+}WwpW&7dULW$VL>#|Jy z4n6SXd~xc#Tn_J|rIEQ4!gb;q-E5e%H_e>*W#Y00Cf64quhKqVm{z2xcirgBOqaV~ zgGv~d9QREt7ryhQ=AHed5$4)_WM^n9-OTjzFvU#<0vb9b1RALCkZrE|O1OE$YD z#Rn=>Z0%*{d(3#yc-$?#<-F2@mSu-|ES9OA@G0R+`{(Pf}xMuUfi*SJ@D|ffxQ%)2AG!`TBo}{mdDiKKc$%)kHWv@BH0-y_#1>vH8?iE79zU3$@ig z74TXc$Xpiqv$*f|F{8Uj)^IQuG?lnLY&;ZcRJQSI?9O}5N`>n+(itVw;1r*6YIub!EPOds1b!ch3&lm@j^A&6nR!W=mL}nJDyUH&cntMZM|I zV;YOSw&}Mhtu5!fACaWp`%_>?w~I*G>+gEMngfGl;_k-vN%u{fb0YnxYP6D*3Z?2h=bes^@-_v`Y8 zd#k>G-Je>yf78>yQy!My-!^-0tnT`4_ifu=oVc|h$G17Q!@F=(YWUF&Ro^+xY+6h{ ze|RBblP>;WKeA+Yv*nK$rj^fhRL?LSi?3+dDb^uS%;q8Een*-*c5NY`b>sPmbAxbGOagm##mN*B!!R_hp@XaPs!8vw~l< z9=Lz=a!Th;txfCN9(@o@bb7?_v*fSt$|hb#xutGlW_uo{sG5uULb@6Qf&9(jaMU~cQ&RVj#pvP??hnnfJ-|fe`Pdz^* zX?y>ujM>6wyYD@@r1AN?J+<3kYjy~i`5v6&sCy-w z<$Tk&fW<6v`b$=IUGWwBBr|jG#Sd>D)f5=A@;cZiSf$Ory32zjQdm+kv?t-t(X>*o zQ0^1zw-PVuH%>cxH5hw1s8r^T6L);!}p<)Zet@V_vN zeQ0cGO8S++fav6}7AO5P9`E06$~(nyiJJQXdO(-&X#%EsA_`IJ<^Q%&$uEIJ+g@Y~g*Sol{hz<2@%x0+QHU9o_mtvJleMQb0 z@9T^L4rga7|F;j36P|k9QpJ;oBehrG8tG)xsXCDRSNXmb%7z{X>eOVuw0Y z_8omx@7VJ}bN-CyYoz)gsN0G9+VSf9>D%A^XF7+6QQS=C^_8EN8@^VW6dx3v6F={p z+xE%*wsR^9D-CZNZ1%3!E$+o5RJP?plH z!!;N;s^55!Noug!f>KX+DpEB7xq(Z18btfH2KJsQFb)V@v)|S$xM}fg_HP@v>`KlGQ2uj_YwFXlhR?R%`dWT%Nb~JJ+NI+bt()8(xrCd(~K@8vbid&5CW#ZeKnh+z@l} z=kG-i>t_W2<1btzT<>Z>D!Dj3F?wyX=aP;cY=WZu27MV`X)W1@v_dq44LfvyjQ_g6NgbJyT3U@i{}?x{ad|e&yp-byPW#|m1nml zOltikANb->%c)1Nk3N0-VNd&wpPycp8O<>BTFNBtQIq~XXzLeuv;8k(=d3zX_U^K+ zEobb_oms9IufNh-|487c-RZfZXV!F0U;oZ9X%$mI`zJvMOSmcXqwRUT#H{Le`N$k!Ql2v**v{#>n)kcaCHo4xE~ftXU;0sE%N@yY(Py>P zw6k6vN%_0@_ObW_B|5d`vt%D9&0oH*+2PWMhv{pIKI*7y z+vH&WJ1S>a)&0I>IDN^N;Q8^}kGRf0Dzy8RfgbC=?;Be0c-=Ed|G~fb#G!SbT~_73 z0WUfEnr}AS&29SM9l@kn-TQ`br{>n3HmD-K_ap*TmcQFE{%5Eo9fU-F_PcTR&z*%P`FPKW$CW z^_q`fB4^fLRr;;A@`CJI{mO-|w}d#?RtcsGjQos^n*s#FyU--JE z68~fvF0C%{-aa|<@0z0AuHDm4GcNzF6nc06?PmrXPCuO#;UB!CX;z1%Vc_%UY)R5; zv(Dv3Cg^eI+%=z*wW71dR7>c|qDvd?YOgHvUy%`cAzZXw{?y7(TqRS|ZuFk+}uEdw)1Ic6%JMZ_@y}i>Y}@50Zcn3PW+QAv~tn?{jEi6YoJs4gB@~R6W{;4 zde~RYL89dLH@5q$bB=tmYqGB`GqJKu{_vxI+QZcbEgHSff`UOA{U_&WA28ggET{cg zclG^K9_2b4e;i(Pb}zR>-HwR&YzO^99XsA_o3Qgp$d-EhHSc)Scn{vvyE?N#eUsqn z=c$#Nm!0QwNAYiuf0a}^Gv%Sp>^n(ePOck5^xmGjtt3>wE?v99XyqqS_V9}I7P z2dS>S-oX8R`j1@`UVA>bPSp+FaVm84gTi;84|cBJwdd6}U#=%N9u$9HH+^kJ=d@t4 z_44m#uCBjP$|V-RRB&1QnHSFalihT$c`oJ1nE&+M7i;n8J<1`|8*c5`x%TeL*hO(K z{=L`T`zYKfZrAH0Sz_n5hMO8*S--et-NJeMlQkn9uf1FKYgLdR-^ZBf-Ob;>D1Hl= z+LdZ!ENZK3v9oT=LGH)L%HNr488TJN95-=#aYbQ!`>QJ-KY#aVnKv(c(}|n^t(mK} zgBbI^E_=rEC3b^M5tlW0(FS#KwUs=@8zPxMuq@zU-{hY#&z@aj=eY%pE1nkQe_88& zuGaXh6hCvx+OE^}#$RgZec}22j;YQ#a&{SC@%qzizg_AsUA^bb-u~dzjF+mJJ<1vP z>e`0o$Jf4Gm?wK;L*V@RjPtA}Yd-UrJm|YNy~nD1yJqXQ-_K9DfBVPLbFBH9Z1mES zo7?Al)$E*d|LyGCuS8D&zxzXn`>LMD&);6lURNSsX{KdL$g@Z)j-};NU=W0FP@+rXn;?C#(vC;X5?;nboujiQZJ^kvD zG+U*_5WQWZow_ere}6nxcB%fggp_T_+p8NB)Yk_ada-ujIry;VAlt^Ss#|Hf*JteZ zI=+*~V)^#wl1YtgolCU4LQlPU>wJPS_=9`4o z9j|_VKk=h1dJl`*)+5=he4DQ|%c{v8KJu7{b=Ae48`p>~eUsVEt+Vga$;uATsV3${ zCDYz5lD3T14O;$X%cG#UD<73_$*=ps+$-uLa6WXU{c01gRaXt==U;JO-Inm(H>H?w z_wk#|b4ssl)!(tH@$UAwe-4}z+&6u4cEFCVhMQSl!8dOOAO4AUm5;;6R?7wk28QJf z7>7CdCT6FmW#**nCFXMTl@@*E;R@Rj>fvS1lBC>v=97qkS&)W?-or;;`e$|g^iJt$ zbe0$L6tZYtzNEdvrDIu&7~|0plcLYT4L-iL-viDkY;f6VH$B_MV?y$t?(_nVkl9U} z^OMR}`KJ|nZjAGE?QEK6w~Ar<`uc(n|0SFIS1+DFdwGAhK}fujxHtpq!0PUKQp_)y z85pd&7#Ji`mk;@-mLxjoBo-H^f-WCA8|t0^$U|V?@9-A$jILk2!NfIW;FQFnoM0YoCTL)Aj=?-*#&UujqLmovxYM zCZ%(A!wPO@t?&JxS4V4U1sXG&KXv*bqF2#%DM7$!)f=C*R!N<0c6D!F`M>^U5XN#^ zC;Su3gvs%Tg|nRVEw~Qta_JRsymTSuTH&n)zNxEnaSEfb< zYil1`{LCzdBSK*c+vgw`M~{UC;^+6p-aogXzLdZG@5v?grn~JI2HX;={K%=T5VwX^ zJtoV;*mHMKr9fPtT66xC3-;amk8bW=Y}c0_zWAlcvFpMw4*S*{b+d05|I2yjj$huo z<%`XHE*dU6SZfn^1N|Hk?Gira5x%KpD7QQk54+igdImbf#lTW4^k@kmAW1xOdnS}2>^=iau{nsaMG zkoi@uU^N!i=SzKLr8h^Hm5Ql63tBK=qGz7uTi)s~FTD@^?9p9iu5!}*sY+w;d1bkK zncLzPmocV9tUNQ}b^dpjj5&+{PVN+v-n%MnHjmteXl3!Q{p)}Gc!sWgR(Lv^?a0j2 zOFwTdH#pnec}vEnz#!mJ!y&E4Aa$V+lEIe@Z{E|qVzX?Hd=Rsgu*e%hVRPv#oe4*k zl09v5;mMt|q8zzbFy!nHCXkG%srY2Jb@q!YRR5``WkiFIeX%+}(c9<%ZtPv$9g2NiCbU zEOg7i*U{eFe&VLq8n^uKq8IPPYMqQ&kW(e6Yhl~^Zk^ZBgHyWuGaQcA-K)&l|1|3O zl{3$(+4zrqvc9ps(E6KR!M_K8t7G@_WZhfQ+pBft?tkUHxf`#;hm^{?J)S~vHv+WJ)F(m(#xD+#*wfBX`dRo8|!)m?b8 z&Ah85-*mBjLu%xLEmywEPG7?NYjM%59}Z7VPb^>h&QSSB#5d~$^L2jhdYAn+Tz6jB zyZ`kfc55BeKVDw{SoeN^e~SEX?mMTJ`=@Sy5&u8#AAD~m1H+fKjkXL(fFHGZC@3z` zM=eW^vVAxb&cMJ>z`(#Di>cTdR0#TH7MB$3l~j}zfBsT8W~1iE&VL@; z{%vxWsjU&&@`X!F)MV$51vR`KT&z>GN~Uh*(pz^>ab8`Y;=F%#4wd4^1&&vUpI;CK zDhwYyS#+jpQqte1M?wn{(Mq?2&sSfu=VM^lY{I~xi0LA?oczQR-_*R)@XWlF{Bpg5 z%D2*)#kV{_%Om?%Mt)$iPI%+_=t#6`^MP3n7nB71&7Uq}F}aYeXgI0hklg>;`>(IH z3QP~4Ig8(Vs+WuW`*-D~{*80~)@+OTX?(n`zov1^i#_Gu=Q@)7XPo^bI`OUMX3dGu zy-$CU6`yux?$J}JmuLEWeGM)OTzRF#aPdLEv!!**mF?pjT95TqraZI?zCK6z$DAEg zgwD_W5$`*_PC+}}(`}h=j8)pa_nRJGTHf;W$d=>z7K?Tor%e{+(|v1L+Q}bL^Xi=K zBcC7r({8bEF)Q`$&N#jBsN0gWZdbl(yfI@m@2fPnQadbq|AGtGmj=E3znj(Msu&S8F zNANH(`>)GvJ~fAv;l+u)zhvJ`$eX$?$wuL;@6mwNSoO1c{T?optJ;FzZ(k~R@t5t| z)u*4kfR{z?&N#{9a8`E0{TaX1HrpKh#GG-N`Oq4}nKRFeY_VGOM)HKwNe-T822&&Z zY@hNRx)sqR&oNuz_Q%stR-ZoLR+An)bAxqQy5yPik~p>S(1?^<{$eL$z9(8XWNhMW z$dBrKJniJ#$3J)MQJ#3pb!(L6>T6n8jb6SMGHNx@J^sb|O58o;>NhpE>~7}*W^xuSe<*ZiX_iuCw;&{80b}sLEJtOw<0VeNl|+{mCT4+{*rNm-sak(M~ags z8#ywaVo`ij%p~X@Z*KcxOLySR5c|5G>kLNIe|cZ;>j>*%QqzcUiYyRy)Z#BkG)hj8~SPlz-%D zDXoRw6LwjfEh})CaIc1Mj~|!Pf?GzjysNwR@iHf$tEkf3v?*rYg^;?}C$2By=VO{< z?{z+*_|>5h9e>{C9xF4WB-#SRH|p5W7MSX1!Ne%T^K#9p=}Y|V*NI6n@<+~}`E5$s zv}xzxDcp+^IN;?G%(FZH{G?3|+l)m@iko-uU`VQZ-S>8PAL|dE%X5SFH>+<+@z^^p zr$c#FS*T>7>n-2I;_X(It9CAb(81LGbkfpm`)V8SbCz>$wk=ry!R29CofQ8a$(^gu zo)tVTblpYz(T=0F!G10~YuR5~MY{?Jro|5Icd_wApgMmR(9e<-|u1PW% z&U1*_KX=Zw&Bx^Z3gT7z)V;)Q3LiAix+%xe|GzHni?-XI@bz&|iy8eEcof?NIjwQ% zSY)>Ad-JX}QbqlL)c@4pe!H4?r{V?$mg_Akd_m^&t~ICs_qsG}VO4LQ@t;}xeM0Tye&$7QZGklqHZLVNbr_7g!9res&FWrp^+273Fn8@4k(BeVA*pe^ne(lrZm{cR*^QQNP(~*aFeJ>v^zo7D< zI=XY0fXl~@GmF3F1!diL**F=OGxOZpSOhgV*>?`mk>E9SEB_4L)NZ-vhMP|xz{m1t5OfBnUu zhvEzWI3Aqn7Tr2md}{K?w5T~(!lE2Cnl8*VEx+9=Klg;f#4FJ&AIE;?xG+(H-PlCo z*Cwt#EXi$w|3!FW4^6m|dGb}R>7PqS-Q!=GCIs?|?bV%p>u4%(SM{AD#{YY&zrWLs zFKb^pi|2J(f|8i|tPF`_%6>kFL+VCH7d{)Gki6q-06Mm2Pdr zn1_dVU3+%=@Y`LAl8?449Li{abLCTw!{m_A8WX!(A^Edu-ZdI)**{)uN4 zt~0!Ge7BR`6fu@ArZ?5CsZ3MW*gbIk6MQ{V&xPyQ+I=2>GGAe?8XC*{0`P{4hJ9Yh??>2)2zKWTpYy}|F8RCU*Pf}=W5^QzgFd!HP>H! zcg;RAtF~wHJx-1N+mDEZ9et}=8nv$F;_@j=jH|N(1CMcr@7Qnf3L@#tEGuxIT>_WM>C=yz0nGm4qoTyT`N(oy9>$69xFy{~CD$$7@-pDR>u zep0e{{Q-YF^|bZ1&%Q}cTazSp^+wZ^(zlCU-wG`+N??p!{`+E_T<13jV^Lopy%)_( z_FUVeAza!D;rN_SN^#WE#GsEQ_6krf(_D#_qYAIr1?VI z%Q5)l`c(^k)*sE0{^5N`j^n?f@!rUpNA|hgIoyEtufw5b&FkNZDduOM3}x*MPK?F%yZP-T-|;?$MVUY zf8u_sNATzSalg5FdW+nTQstXsv3_eqE%)q_4f;|T6X0yo`h4+GrTN{g%n$zky1TXLfK?`O-JJnjFhwXV{zIqpdM3N) zvK?O@H8_R;sb)%I@II0DF1LLr<657(=jGBbCjV$!zs%j|^wMV)t>qt+H^n?CxiDjX zb$r*JL%McxlLaNrHc$T}#xDLqh5gcExf{_R1vPtAk9T@|d^&yXm%BpEcbl-Noh?n@ zn^voZE!=kQ?9rxMkAv7Y?ykKRwMy*Cl-;(`2g_XM@a=vZQFwDopW})Z2N*bxO|t(W zWpGB;aneDNO&xnX8Q%w~OzARjGjx348r`|1=)kmYuMN))>O&7$H@(<)<8dd`49gYQ zMYhUI&MLNWJbiI;`z68m@-^)}vNcN{uKOcdFPCu3`|_r+Lz5GZO)Z=+_C5OApJc-~ zK_bHMHVUPkZTk2ow{fz6?xDx>Cc95c?q}Y(VR>%X3f42Cl?ob84loi!D-Qd=Bn9B+;(n{gtwb$Wu@zBy-_c6G1j6cn~P=AGHMj%UWt-5EJK z0*`*aliIP5edmO5#)##f_49oHw@G~PIGVEZ#*xlv4ICSGZ{{^}I(Gd#XWFI@>FfO; zSuW6DQO54&p4I8LMQdg6SKI2U)gQke`L*r*GVRKF#!NpSESaIvRbzAQMVRqPkxypA zo>gaN%-gEYDVc7w|8%~aUglpr#;8W#6>tFFm)6 z|6Ix5nLA9E=6$L5yu$eIy7W?=pBytet)_pCG80_AScg%4f6BoMA6DiF9UdFT)W2sq zg6)51);QI9JrU{r+Hd3ks5rYYRHM-P!G|3m<~p7e+&xXc`Ubz=yUj`&Dkcw8H`l(u zc3~rXsq;nWJ12!FRi4-1)9Ef;bpPKGuE&P!_9#w${X}@d8tM8?|D;sgbQ6?ZTD}UO z+@p0MWs17Ut~=9SA6uZFox8HHzlzJ{tyDm`LC$x3YySxyIoUIrCF+u^HO>CTA;r%^iObpVR;h33HvDLjl&@g<>g-g-t@d;O zF+?4<`Q*M@;YId-7xuulw@dB_s;cEDe|Ww8v5SUeTgd+{uNS3HUvR|sOTUdUftUr+e=MGIw5D%rgdeAC-F zx$u~^cg)1E=Y)>S8^zVRtq)*1@PaedKhL}EQ{=e{7s-j+e*bU(A&=T0{&{3;Mu;#2 z!)qr71|`hir+cU;Vhe8UOa~`H`J8Q;u`R+m^rwch)O= z&FPpi@2|GS-n&8K4WA*+cNJhU-!O_NO=))gvC}r@ zN9zkBxGQQE6{hE;EH8O>lO^|21<%_<@dtCn8U&twf3rhx=kgTUOFwVrGP5$|mcT}g-EMD;0gTQg3|1nAaInD486xzO3} zss4hq0usf!n+)b_WL#ZzPIyDA5I4`WQmx|J_l>>+3C5iN+~!z*{IkM$9y5E6$|v)w zoQsW1w;ilm>o}sIKA#aPuWJJ zW{sIO8I1f4TmRR8waU23?vpe1>hs3^F86+kglY16-uXGL+@eIxEiL3yj?uQw-&i!y zTrxd8p)HAfe$br*_jx|wJKNA!UMX>vd2);GBh@^%!ip)k5|}fL)Mi+4B{F@gI(?M$ z^!?KJzOM~^!j87AT|J?k`^=wOD><(9%APel1>>%Kbbpw;Nb=BZGtszZT`}wVmtFFh znfb{lysaxoqqfL(SLA9%-+Yl~fsD6TU$<>uVt7A1_hOB58eoPYKPK6p6AL+g*F@-9V5E1S8(%O*1yIc{0G>V?HC zTgJxKlMQX^6oT(vdoA>M^TI2y&E^{4eSS?ibDQ4ltr1Ey8*Zxz>#jK$vf{&>uFY}l zpPC#Bb4!0QyX?-DH``uqozrFJSj8~u@=di9hD{w_cUUYl8I3dR+xVjn#?HEz=(6m} z7O^M&_WxE~-|>ZU0&{6zhRW|akso^LGryOYNWI+9rJ1?v6c0~XS#7$+E9&l}+M?d0QIV6q5x*c28Y>xt=$C(y53HLpiq( zDRu#yK83%&T2@x|UrAH)zVfVskoAmzs;0g)TE-`K`o5%3T0_Ub#llt_?3r4u-uc^| zwOh4|%jU_N0_zyJJocEZ{@V|4ESel^zvSv_z3qz*T zbu^1CX<&8d{&TNVZ;i}@hQ`zt0zLxiaypacn>a6>X-<8V>|&glme9KOOu5UHOM0b` zMU45kA9g4XOaHM?+EP2hZSUv2eEH@l|JF3`PV=(dY8PYttZ!;j%*hiUZ_exKQ8~sw z;Y{%dhwYmSg3ca#xliZL5gnTgE$j)5)9ZK4ZQsl#r?*SIdJ{t+^W#J>UHc8rfg<(o zdkz@ieLH7PxcB1}|F8>xjpC9HDd|T&xL|PW+$qXOV*OsWKbA<a zqu#$jpY2c2*YIMyj}un*AGjPZWYqCzZaM3}bJcfZjlMmaVd_8Yx1SvIy&2w8lMh(5 zq?R*ZuVGNAHJChg<3bDSeen>%J}Da@|fZNgU0d{aVr;yQ`hr{9aJ)j6M4m=b%uYDt-C)5Rx#;b}gKhDIL$ z!_9Q`B+pKfmU_o1KHf4XGiCoxycx_Jxtbb7XOF1wV|HOkb_~ zVOrYbsJSmRd~02G)88$aCzb!^*zBO>Ln|9pU(Qlm%^cd6D0GB1&EVMDmt86?EX{3s&%b`P>f#O`*7BbajV|D3MANo>O{j@9SwX1`P3*HHbj=DkMc(tDGI zf7}!LmiBCcvy|bkV)cex&7SGH7gN?O8@t5S725;ZN&sz|6Gwn#wZ>jbFAE(?9dVIL*!wlZ1&YSLX zCq%933Qm;Zb`)gX%KSOAdmvL=gt>K~u$aZ@Xg@6vwnHZz=W&Gqn&;&XMwjPv&{VZ6O%U)gPy@4v&&XB}uw zDi`kOTw@_4dL&R|;%BRx;QYt?I(Jk4q*Yvb2rIL&vl;oXvbBp zRc=pY1-d(=ghpB;O9K81Oo6}m8G&ByiU%*&#}))(eS#9UtcX|7>s@?_Jw ztt+HCKJ61Lp7%q>^r2c$Z0;`ZoE(Yzv%l^y6ra+coXhqn(vImALn-$>o(D74Z%!eN%=fgMb2+?~`Ty*bi3sk|Jo3Pc@vAGV$&t-Pa-a4v6)k!- zxlr(JA=93SzvrHQdM(-P!Qdy!aDL{cV|hCK-~RosoXPqwa^cMv7uqg~pWygC`C`9Q zCv(kVUNK9T)jpPgx_4@*hDEJON;~tUQQ=Y8p5~c6#?v*gA9`AXU|;=Y$9Ck5Lx72os!th*OD zX9D9#t0krRISM?Hs+G)Zwj_&1xz;xx*!2F<-sKB?2aRsZ_m@35`>)Kk!i8;O zQaodC|J2haom19u{Fs(}T>sJCX&2*)ZlCp7t@z`-iY9x^8L#hldMEhUy@FyKZmYR% zSeARpH^VG6;r`T*N9+yvzB)3)cfERfMu?XRu9Z63uGIZu|341QgDNS95^_2)8$ z!o~$rNo$loycZ0R@+p29a&-09>l}QGszqY2=1F=lTD5zh@)BEbr(3epMRoRmEm3!R z|Ia#Wp&nuTDSXoFXHRCj{fyjdzD=M^PrztF#N$ogiiKV4^A|;aVPRsAP(FD2(3_m& zhpZ<)sdPNbbz!sQv8{^MdY^J6xU?_xUOBJ)bdrwG>y6G=EH~Z^_1)bo`d;+NTGiGi z;cMDtfBH1nV0AI)-5t%p^N}fC;`8DiHO-ey36fJ7By8bJE|A-^e>~4vfmHmYt-;FbGFxI#4xODWq?u?zsf0xYmDZJY4 zdvYP;yro>y9Z^LET!MNIwYF#c{P=I4T$voKU-az9&r_lg6?FV8{b>(fA((qay^bExWQ&$z}*Kw%OzMjH(BpFC{pGq zCHhss?$L({&zGFco7~C!M?t;!|D%tqW_@F1f3U94tinSt^2zbS)+vPneEOFXodi_& z=w1^JnjNcL)auh?!mau{mNoZcSiEV$s(W{qwQJA*HZT2WsJcmH(iVB4hbe5f9P^GY z;n-A>`Q6FlRj8!#tdNzn{QLF&ZcN=Vn_>Toq-UY}<|pT}<^(?ftv#W3+lKv*CMEuU zmDOL^RP|%UmyFNCZ$2~I3+&r{lC>z)_zB0!z56!>_FDLPOK-aHMl8&}VDh}4=ZRT2 zS65iq9j<=xysz>;yQlYZWtoLp8>Vht7&>pgo{;yUoh_HW zafK_^`h1J^`sgA4lkwcgpc;AQ^)hc|=QHNc-TT5Q_Vc`_3QZ#1)8%e1)!-I>boNI; zfO1jA0fq3iEjPGUF)}oDUud6iA1}9PN5R_8>oXQLg!=hcEq|Xlb)PctkN>iXa;gut zv-Kp)%Kj<7KCj_)y1sI$v!DH!bA7@mQ%=iEKK5E+z~|E>IsHjT#)J~*Ka7>@T|1Y> zOZ6Br#=XBem9?p6?|Ft~k2T^(Fa903^E_b3An~NO?19b29dDVmAI@j`Q!(YCZrS$* zw-dI1RjB;iDcB*z*0gR#s{M2LW<^PwdmTPqwy6NO*Q`!nUZX8(GyzcW>4D zOVJyjr^l8FM(ntz8PuWezuG`)o4@m)Hv;wN2?kfxWQ+Yfu+U?Uz+y@ji^ z4?CJ(w3{1z{$J*z+SCO-zu)qDSue}dk@>}<_h9);rw`xLXXnh$R%QRx6xHwi%jNEF z(YP0PeKxsv9J9V5Ah2d`-Q;@{ef8g^nm0v%{SRNz!U0~;a&4|AXEi?q!#fKG1_`WV za-M!+dIgndA;*Mo{wcb7N0wvu8-`pzpF>Rw7PY+d?t;?~zX zF}1D_9^Y9yrQ+P3`fIVj8uwU8C%rH$e0k;qcwuQOmXoXFI11&oSteU zR^a9{dt$6r_JlM}7RP<*mp7f5GO5@7x%6r_5r%0sVOxLyi%>3l&;3(}zrD!+_b0}L zgHZ#;ny9=4kd=m)Cr9j-^0FK-=5u0~7MEtXoy<_%1(m?eZSCC)?~*j3iGu z`{wrV{xpH-*kQ-|C37YUy3Pxzz8&iQ%pvP(!@smE*L_bvR;p*rt~xNoRN4ElH0x@q zD}UH>ULIj+zj;1oTC)C(1_tTu56p(okCgq9sx0&UAb9_zl<uJM-TGa^ z#xzIhe8JhR?;rkZOa8qsxzX?-*9P6VhV1HRHj5`)uV&yrer?xB(HAoRg0)(wScz;? z`g-A9-|NZoKUzH1=dJ%;d3c&KAG`dGZ$0~Zu5Q~LWm#r%V1MSXD~saTUKoq(%7i=5 zWENVQ(ZD<@`%-aER8Z}%N#O}1_LleB*BUX&9e&UAA@2J$gAeonPm5@Icq`Oe^u2A( z`sPpTIcghyU(EV;Xtf~MzH|1Cd^N`pD&A)ZNOS)5`<`t3T~?CUkE%$bY^1%@OMv6K>CZs4Wq< z?(*U3rDi^1zrLRdVO|z?Ps#Y6OJa$*)A2(a(`LRbo?)hQ_j7{#nHO`uT)*OXdym#$ zeOXHfooximC(ThU(Pt5-_>XLSovz#zuuiiu1pCX3={3=ZPh)# zxR&R}!&jC%iq7tHUhK78+`#K|NFcz=QRzWo)|;H1=mT8tHy=Gv{3-a={^bOjQ%v4Z zr7UwjA}6rFs{GYd_EmFh(u^y9&L0G~D)c)UWb2n!JHA)cj+&6RYDbYzd5*s8&6rpD zF*hb!79D@}w3|o4fYIJ2Xey(EcFGsUUhh@U1&`_;T7Gq9*(M3iSe6OpSNOlxo0c5@ zJo5<0l)k>!j?;4%m$bY*ab(S_Me{tiUQL>||E5FC24zi_O-r)81R08ovtM%ehlHjw z-I{Ty$8_!e*(YMZt}~ZhRsLOS<-~spm*kgiXprK#C~bd7<8Yd#B`4FVdCw&n8VZ~J z8+ukw&6g49o}IN&l!sp5Y?FA(`|vQDen=k6rEmDwg<6nYWCG z*;DbiWk9*cw)2NS<-J*_R39|yMl6@*{N%+Gk4%_p7_fNXZ3p$W+oMHKb&9Y2s&DY( z{^6jNSE|LL`?Q}1+se*a(lCC>wf_q`jx{u2J`xG(s`d=*Q>fZMk!Rv47aXht;!1obj9 z{dvjr)cVJ2dj_58dq4UoLPw!!9uaU{RdqqtF?H2><0M%ym#;`)5`PNSG0 zLskmoRaud!4ad{=#NEwqjZ9)Kc)l&!GEa0dC+B~!<_5#lH(OR)W{X|isv>c3#>_r{!%sVUm0dK_vrZ<>X# z(SK62ws*ynoy^U5TAeQ6TwvR@j49i{#o7DCh02|V7Uul&Yu5EjWqSN(Jz=)Z(LjEo zeOQ2pSxjZpQ{CTxHa1S|tKE~gF5pb?(lpIVYqrv&gcf{LB)~kX_*Ll7D}J@ex_6 zXX`D*4c{qR7G#;eXwP2Lyh?D%qMg#SCQFngw@aTq-2QLF-Kp-%osRQ49(=mO!d|(v z@C~oOm+Vu1jwxH6ni!M?Uz%!at}`rgKJ`rHM~u#?y{mYCW%*0L-TABJ^sCcbr<_SR zZTe?(@<;Zc{m$3Tx2@wp-D+)Q7jUCn?XHvY#@TEhU-&W(nQ>j?ymQpO=k*cCYmw5+ zSE#p4P_iuuS6Xg!WXiY4Rr8)oSvr1EWi>0m5&N=CMI(~?<|OwEOn1KTj>vbM_aJ4x zr!4=e?#FA?vQM!{rGGMhIN!SKPT~%UpG+GKHqV%MvM3J(d%W ze_I^8^}R>t_v(yp56j&KQ|9s9dLv?*zVm7AF6-B~Pv~uuDz851m=!iRMrqnbKc~H{ zhm7aU|Dh=MSpGEQM2_RG264-HR|YaSQ17#b z;2X@#*IW@2?_Tv?C26LyD3g{M+w>nc4L{dB;MvXDHAiIfy5(0kp3P))PBpo8KJnXv zW9K}!U5W{|wrdPrxVy8eu{NXs-PPM2X4a=wjz{gkTz`Ju#ur>0CwX^-b1eN6yZy3v zmC^l;mD@5+@6YQlw$Ez$(C{t5RlZ?*Xi}$3`u@UhRk5wEq1tWl;^KqvCFe6dmA}{P z^ns7b_Pyw;FFA#73m#mW(%6!CeM{-#1#<12On(gH63%U~Y}k{nm$SlY{>s-IUAC6V z&M%)PXMe8W>%aTwm&)EAa{Z~#Je4_eq)Nn-V!Dp4iYo{>*}?K~wcPU64U={p(e6|` z@My*>R`?%DZUto*Y~k81r|r=&HRsmK4$gw;U0ok0 zd0N{4eY$bFzweWz@EfApTes!)+jfQgNONVMd19}=>87JBH)7Pdc0J+hRoJlH^1OrT zk|l4;sZY|y z1Q!ciatbpnYuWF@vvBF%@U9K{V%tnk-;0nr_T1==QMPaTn~i=E2fVIdU$cn0)2QK7 z*4B-_b-Uyp->Ej2t>+75+E^f#ccsEm?8()pEM2Wv-)w*14L!uiBA0{l_Nf?>7!yY0_WD?yYa8|ASAE+slEC(NR&X;rfl> z!|76X_mca+GF&a;oO@pOIzLC?`Se=}cVZ{2ZH~@4kn_)V#itzR)L!GIsSi#0ZanUb zeI0W8Q^JB1&O5J%o!a^-c6+^Y`RT1{TA8o+2AzsYT=MkTjq5BcwM42HZHV!evkZNd z&L;YD<(7W?f}fMGE?qHwZKyW;1SQ*}i&|yfeJw5>I3uZMp?NR(jM~>+Yl{I|^=Ob;h{MP5rvq>xKJNW|!TjgF4NJn(W|rBr*KPeTm-%OkP3$ezT^2P` z0voD*1s=Zn&RZH^5vZ2>IDFT0uP?JNs(z`vm$Rvyy#dKRVf4md5J;Ey@tzb2~d#o>k@g znG*-^-~D^FZl`sI*m0k4mzdl?=t)gU%CTPLP=4rU?D4jl%dKuM*Abk%W8%pTi;Y9w ze&?OeOPe^c&#I-xys-OCPU*ybC5uDtr+=%QEfRA5AzC@_gu%kmzOL*+y3)vIc`-ybw~EOJhMIb zQv&%E?LW(k8ml+Iu>5l0IX!OUzC&C#LAj5fD1`UVy#Mf~^JdATRSSc-)|IcEy<-S!^}{e`xO#%Dvkfe8--Ly!&w`UW-74ixuLPp=?9AH9Ogy-^Q~ zZ|xBJ-}9eK_9w%Z7AKF(CbhN3Ef$*PEd`9XUZ)sICbC!EuS$8wwN$%Ok9yOxobHm`?wyRy7e$`AMH-|F8^wKQeBP0uAUL6z|I4Kd-|qOg z+`RB^vimdk4?cVjjdNVG)(D+fPnA;2nP1^86UMkEo2ksx?V3^eJe>lkoaw9QR(nUB zl@U4gYhKQA#bmcI#@XxUXk|3)dlH@g<7t+O%G!J7pVpSv-hIWpzrJbThUg2Y%rHpCgv-cb3GAb0SrTEE9=Bz&Y#{Gb6 zCu`5m-19Q$=YAA*x__i)euu5Qfm_C zvrNm^Kg_@wF;Du}Jm+_fe0rt#d~;r{XWV7|ao&Vi_mrf%4ykUH6UkNou$k|dm`H;s zZ;{RxlLb$Ycd;4->R2<}oDt}Ijb-NJvs+dsPMZGTu)%4bJePQ%yxZJu+aoI8wLO>} zu|!b!Q;*}C1jdPf@9p6`{QeM+ljTpJs(Gh-)|p>sU4Dmc*Bk#l?O3*FjMw=mmWtGL zvo+clH}*WOVvRlc=|HB$uH>D86OWmt^_jQTPVg^cx0@_oR2qL>w%>2Zl9)ev+t#rk z?FtOIe(-jdxXvH1xuPXfJc{@Dtf!}K^y-R_zB{o~xrfPR+l`G`x$N5>aBWwOI&f@O zfvbhaw;thr?|y8I5q!I9afjY2l~+r5^}Sf?RiXbwJM7QVc?Jz$N}4>^Cn#=cQd`*@ z!+z!n`y1UC&AbO?nZF0!ifhsNl)-K`YuY&lnMWln9fFNxP0~(p6)(8rwduNU2hMZ`_21i(fg7kJDnVFe{`86*)C$-b7yB3$Na|gJNF!3HdAO7M`^7E8^cl? z)(H$RZ(dTqAm00S^%dWHmWTgka?iWz*vHv>XkJQVphawJffN&0tm=dvy8hOEZY9nZ z&fkPL9cccQ7(9LUgxL`XclG*u+_zsTS?;@APNS$z* zcs^p1d&w35cDdIqA1bEqHJJML@#EG_6^t+5-8?Y+#_^gKA0EH`)0Ex1c^19b-MHzX zh;T=6VDaDS$L-}#8C~cqc&4paqd3|dRN;^m;Ph7ZTlWJ=i86JAFH<%zMHC^ z9v;MfF}7m+)Fa9NmMyKRssE68I!J0K%VY18Io6);A9I#_9y_qLF>!yFS8GI?$h%hG zjX9s0Py652Xk8wc(SFL6-RI)V(jz(b8%#CX7xv#-bw6y?k|V#FmIUpcGN)T6B%5<0 zTi*VqwM=inTdtk6BF59T%xTN_C%UuxZfg`p-{|k`m9*Bh58Ce)aAs9RyI|6m*;_xo z-yu^{aQ35+-ixNfOzQ14EZ~I8FOKs(BbQ$pX z@di)euPbvM8>U4qo^5o0o|XUermH1flc#!p5YV+2iYj(^_VdJzu$4?KDJynQxKg;o zTwEtxeapoS-{wT}J91umXmZGZ!qbkMVFu<0R*7wzw^)zC`?GAoIfjzHFOTZ)Ylfve%;#nrlqIWO`hlA5|=d!8r#}1A-Cn-M1c=Rr{bXx~ZbPKs+xkH#W)9Ikd z_p)VjS66O7Xn8c^<9Tsmy+_g752NK4XVk3i`XS;eWaa3rBJ0e5IciSW<-`V~#6VUVzaO(FNBdrSAY_rgcdE4A;hjSJ>|K0nbmS-c zTi%NiTrMid{?gfHn)bqpyXUnq_cMIj=9X#{xNY@~*5!L|ye)O@*uk}MZ}yF#)_~ux z{)@OKPTBSJ{aaDawP~{hj~z8#$+T5z;@6}n@~b?SEV~sn(V}9V(!#ZD%a6u!eH?DGC{+e>!AcIl?YGAzf}J6*V}u*6Gnr-WTfLPe-!(q^GqS{_fF zEY27nUO!z`DEwB`v=#qrQ#tLD(;}0WuVRs(7ow_i&1~O{t5&6&8_i%-+ z&6BG+UzbM23F^JuyfY~CLfvv(nb_G+_Pb2~zV4mXbltwK{Kq7YT|2verp$*GOXdhA znQvLtxyezfBb9|u()`Qn-AOBCxs;N^9j;6K>exQfJThNqaZ|^{(lV|!Cwa?aCO%KM zwX;a>Uc#mMM_{dh&9o>+vs?nUVRf+xEtu=*9f;ZkcnWCj;sI_v&6r0&yngNePdis5rCe4c7 zRBSZ!sMp&KeSQbF7S|=0l_{<7TKIO|htSH0jzP)Nt5gD?tN9CvJ`vhENoDuu)S^Wa z@2`K_{p9f^-IK9*H0^V}L+_XUtd~zX#e^d4muBya{W7~_(u6~zrg&OKRfdFs67bS_kFXQ z)1P&$?UPsc-B}t%Qo5-w#$x%g^q$ zs#{c2a^puu!|VAE(^T%?-@_VxSHv*nVe&_@^_L5$%xQh!*U$IbV+xb`R2^@#gc~X@ zFXJ@q9z9G7*vtRP{G-H=?%ne3-yiipwVe0=r_8@AueTrF{xOWtX~(tmr)2V;=1vs3 z|EMOv+x-2Hj_40-o=MAHH)(Cy+tPGNH~;z48NHs}fwvoXymq_!>{@teq~xvDO)umO zWA~*7ORIU=O)~k%j=Emor2DHQ3mXFihX}5&hL69a3#h9Ro}2&3P2iu*|1QCaT011V zJgZKbo(*N=NuSEhJ-hqTDyL&Q8!R8YzEd#@{PSnt7{B&j zv2XP{v!sJeH351Hy{8>k3pvP>X0jlO+09@%BV+Q;Z?Ok0y0+ZNI{dOmalxHc4vXXV zv!y!<`F3wsf5iDzh;=S2Z{pFLb9Q?lI4-Hq;$$YQucuzVAu;>I9R`b;mjVi`BDtAD z8l-*d`($))PB~{*lqu`O$=(>p^6<344V|i&-`roADct#bFv`E?m1BRE3I}J_?ibPP zTDgr{PpSpIIKlBoh3SRCme*XTnyeK>zFXP|%RRC-x!~|fW#bB_#{p%nJhPdjJ-2cl z+r@asds(D)hYhm@tCUNccus4EScbsIy#e>zIU7qo(z4@Sxiz@?mwnDOC~|mjf2i3` z=2h+Uk9?^cqjq@D^UUeg3%7GU+|jw4aazyW3kTAZZ~SVJs=EAU=DEc^`;x6{4{uv& zEYZX_&93CkwH3?x4@&W_ZOpxLK+Q;P=HACETP0^j8J)R5g=6}!M4q35t0j{X{)vda zGd{Sgs>LojLa*Y6!SktS(hu4&b!>`cd*5(QA;GCt+;5{h%WZwNtn{ZL8*BH@;@Y2~ z@q{hD_RLP*02$ws7d&UnYmV*U@)hIK{1@3?<$U>7=-XZERvK(7<@8#XnDULSYIhsM zstd{QM4r8yl+bZ-%H9O$&8b>S*TdF*7u~w=?{bX@mt(Fq9hbykp8sa>#=+BeE2o^d zkdx9!zrZCIoMq10UJ-5I!!=iU`%$KYGv(Ko8a@%2e@c|~tl|CqT{gD@mZh*YD;x@4 z_h(hinOd`hhf>T!Zy2s{O7U%7tLnP5W6`?QD6waZr?jJCIaMl>xwFyz z&$Flm*LR!GMosS!@%j1eZ(qV$H7B=@=v(d1v)See-7)PobTiKDlS#iI|9HOXk;x03 zbn*&?+SsR9@3ZDFpBi1YKlRI%MpHQ+?dT60e_1z2>76`r_GPx`+S9(3VJmDt-DoRr z3OuC~e?-ne*gYZ3JbcThsizIh^6uZiBWCXRU@~9I!JrmP-)M#Ww^`eKqw~M@lm&`R ze3^J+kByDi!?|un)iR!cXHVGD>Cf_0e`AbN?~W6ePi9V<{z0Z-_YJ}+SX1fAmRauJGp+>mZI=A}k?Yg$qQ=E1)EzB@R@Cg7_OtZH zsUzH{wACzbuezrpeuV#IpF?fG!KJg4X4$<>IQUtdfQQ4l)6!e;BYzk=HY zzxQ{3_*T-AA8)W_bCvq4YhL$UrQd#%lhK~1@bpyUx(TzyzJ558xYYYo#~jal$C>PW z?z%s==XWSNblP9Ah39afKqa4MjAatn!p$*Hmn@Z;5%}Qv_4AWUineZ^-1ola^`qHE zy#9jXH>*=Db{CvFAap=T_fzee9D7HNuS#v+PkR~mzh07)qRel&yE{QPZHhkk_U9*T zwng=Qy}#gu#Fwydyc0h9y|JDfS*a5^FW6cBwb6NXci+(JYPor)otxyWZ8{CssQ5>+ zi->*l{a~A{9p3+fZU5U{|4-+xyFOV>!y?G1(q2qx$*tIOYat+oA>^+j_Ix+Pm?~+T36A-zSwPhj_uBc+(nOF zCE8zVTYfa^k+giR^C?)*;nLID;evYMnpb!8W<_69t<;`Y+{gNV^4Z|UY)<*2YFh+r zH2;`BEL4={Fy%jVbal+BM-7}_ zgr|3Hi7juz>KhYq}boU?PrZ_sjP#G@ct&Z=yK+*ta9ayEix5F8r8d zBY<( zX@dRJPdQx>)BEG`uqkQYtS>rFDxRnM7jwQ-ny2=u{GQ3nRjf+(|FoMfFV{SwxJX`4 zVV``Vp3km_o?0j0=WM#Qc3SDhU0b5m&Mw<#a%w`|jGg6W!oT7JjKm+R`9AqrEVsPL zeakoV0CT<{n|<>4U3+5G`t(y#4fb)^T_4 z?@{8(tG)Xd_Cz{a-qmj_e4cM2Si4BEe`eu%R}q=DeU+cs=js0Zut4tL#9xfj7cve% za;n?=@!$#_<$o5PUwbvc8CN9%4OE^F|*lq92H8Lvi9c! zKK=DO3z_83E~w-x+7n)ue>PI4(Eh>{cfMO%2B|N_<>xNkwLI_YlI}3Q_~5gTuI^jZ zv~*@i;@6FWRjCclTh|yCv0n;}3*ESX{fT!ECv?7=ZXKAf5i#TCH>nFtmc^=H?>*%^ zZIMoyx=DWIt+U4a@=Uk=4%%`&&Vc<^*z&*8nRgw_54F9var%6G_RQzKZT&xF{Vz10 z5l*~lb;FV2`?5ECxYi15l|;S2y);!KSDkbI;ZNc>W@k5b)y%QF_qgDRebm%9egf|bOIQ!KP zMb&Ogm&o+pl4M`^#O+UAh%s6HB}k3MWaGn{DdsiSU)A1y2(!Af zVnxQ@z*K!*@4qpoMZ1)~eww^7Q2T93rHY}~Z?4ETk5f)-1Erh2{Z>lITrYZP6kov= z{Ac&Im&K0PuV2>^uU@rt?&^K3g|A+J6*1jD>VNgp{l{1}Yi4Fmak*K< zX^`I94{{1`Cj|fPuIEA16&q*@F{QGy8W$d?|i$tFaF7Yd;Y<^ z`OEyBb<^#uW$yP?`#NUSGB#CVig+xR;m zBv29-s zRzkU3?=3D}HQ~LY^U9yI3sz5lFX!`jkI0gz84Ar8S&ka)+-zvFcKWu^ny6RB8-K)U z2B$wLv0eT>|LFpg{X2rc`+Dx%s_y9?T_+g4{@(pw+pE4=#a}|zI93<;u2RwZR;*|J z!`S=MOBuUEx%HhDv!*dc-e=DAyPH|$(QI_->XY)!Ln||FYNql`_YH|)k{&YUY2bY$9&$D=<{pKa# zvo>D7$QkRN&)7WI{lJ$48^r4$cA1qV>V412NZ0RRmdpCsvAM8O@27LQf#X)QLLP3n z-7{0qElfYx`BrF7?t(nCXX{QfrB&N4Jgjq}hI@gt;PnqaS@o<*kE+@4bv%A#Y$A9d z{E>24qg8;JP1-i*lGDpVFI=--5Wi^V)-zMxxnmkKi*K0TeUx>LqvLh8xh{7aOPOBe zyER%$3v_H&{yJQ+sM)5XO})})8vC!$;Zu%gJdFP2#xiN=%_udd$FpP(ADeJdmFdgW z!q<%HC%$L-U!6bmt8A#e#w@nE4<%A0PA13Z`c{3CaGU%wV}+(EL$AT&8NWZ(a(f@S z6;Se^siG}&XVTg85`N;ko5ePm-Q#F0^*dv}<#dB(+L_}?X@S*8!#g%jjvorpy?kGNfr%Er_e)+cN0o-QCxPq&^bxyl(e#=^$ zp6O?vBqe=I>T~!J%m0&9cyvl_0wW$CcFDbXw(ZuzhKftQGhOdYxiHzllglSdcum%$ zCjIZ8dkjuq`O2ejrt`HhHp%GD!G>Ec{TVq6Pw<#L59Keubm(H0iOEKDmg1v-&NI&0 zqTnr&*C~5BWZfOF)~T-PGo&tVP!Tow^>9*_ZuhQ~6RS*TOlwJ0V12jwR`X5;3&F-h z4o4+Lwcrl9Y~kCBjyZ8GyDs-2^GLPH1lBIYuSd0F%x;*dl|Cppb5s`lr1&f)JNJh6 zr-nCaim_893sha^-FUrYE#JqozO#;(wFQ-Jw}h{dOl(`|YxB74hh1WQMX?3yY5)66Q^LSTKqv7n661NgJIxY2` z=lja${3^8?w#|>a4GzCsd3f8ZqP<)V{~yMeT;K6gUi!?k;-Iumu4T#_eWn|2ejd=n z`>E)3#YCMQbq^#0&+f?N4F7j`+50jJwN4Rl?#12v_IFG_Dfy^1gk!Q{*xlB*HfPSQ z+o*Uq;ctpzR_3z3n>ga9Za@C_6Q9AOTOKdJF8lS_W2W88|5J|Vu1xrOWO9nq$_<8f zK8Ck0?|kd()$x4xIg82OyLIna8qD4HSY}a}{8=dQ`yg+h?DM5ASz!_vgI;z_e_eBZ@0q<9 z6yyFy@6ju$F7Vm1ULw8d@?O?2@i`ho-JKh{J6V<1=lEMa*`Klam&JF}*Xldg7m7}p zKi_L%mfW3i^V?0u8gIU)2i}OhbLs!%nNNQ>*&ot5x!L1{*73&TNLKSF3Of?F{)-pY zUY25c&m_q?b;r7ISxKA*`|YK!@lF2o;K<^{W{pq6Q@2-bS{AzM!kRmGMeRkk-#k9F zzlquFn~3l9($ub8mOqk5YNIFKK2u%y*I@qab(YD;_o|m(khAmMo%}O0F;dFHD*j90 z)hoZ}J(4fEeAo5W3TbOqFUM?&*NOkxzDDmC^(ooL&OH5s!^$(vx1y$EmN55mw4{;GC%3LHIh5&?x&A=-?T2C{k8nd>R;1~XWfZ0J1TIK`;)TYvO}LN zCUdF@G~Jh2z3~Hw)|boLwMxIw9gSbq} z*vR)USyOw{$^Qx`p7O2nnLl4b%YJQYQSw73t5^2FPU-&s#3y^_np~D~vU2K%v{%k) zYUOfXbJH$} zxYsQAa=oha?~_|JCG*OrJ+#*2k+irSe!ZsZn`dQ!;@PUbuFuYgINaNL&i>^k{dtED z{M`I<;@a0XJCC&;n4dj8=#gdi|Je^O$)8xgS=sxrnqKV8dGBZSThAA|tlqZw*F3>n z7TU#syL_#yU1J|Hi_YIA-EWco?eVW~awl$GG1V#BUcKdvs^o0%tZI=3ybF4B?-|EV zJRZ(g&?<9=h5gAX#yxHMR^RwrW?gtcN&HpShuJ2KjS5NzVV#dBZF;DDTd!~%M;K#V zJm(43rZt)Aa=rzwKC>6rmTfbbo$kQ#`FC2BSmlzG=NG@8TgAjC;ceIZOkVwa@umHL zo$S6Yzx?m*pVRyXVqx4>FS(!2ICJ)z5u@RmlcqTlM(Mm9d2N>ux;MOt?>V;kr}&e9 zStk2s&!s)dGE%KAx<3D$d-LzYdarlymX}Yyxxwv3k5aJ4zLJ}hOds5`P&gp^mG|GK`w}D>s%*i|6#(#>6ab@Zn%gpt~eztnZ6k6&m`p zPp@grV*cE?Z`!TvBDHcQ+peCQcI9T{xqo@7SsY~xU*EcMVQuOjwd(2%)vhmJ398M0 zW1F5)o1Wb;Y4u%&*@ypYHa)UlcV5>2$KyJa0yF6ejki8@uAgwl?zKHn_d&1Npn{4k z%8P#QejG9DLuFyq6Xut&hDrR^4U(uQvTx z=P~K$N0)GY@&6K3Uur_LjzXwhB`g^iC1f*ctR*a`7xtop1|}B2LSWv`l7!x3`Kk{%&G#xK;YR zp=xT!j4Ll)4y*`^XSy)sP(jBIK33*ruOsZozG_KGXbQ@!YK9uPT(h;)Sk{{(@#nP8 z{%?X}>|xd{edh&lac!J-Yj5sBmLCOuiEe+{nHT-;T%xKT`f1(4HVwWdW?OpQyT9cY zGdI?&Mp;^|J?XjT-tF^7oAthyuRF`8viwkVz%`k>vb(N+;^bFUH*ioD`?t_(QjzVE z=|)#~w75;Th@1O;>C2_Qdm=1UgUuv+dRH7ddWf}KLot49_A7pu3n_m-)TLzj)wjlM zy7_ur)y8j%DHazDrSux6A3gfF#%^cqf&JydCz4iq@&{=u$*(A}jL3Kq@nTLn-&2KU zkLNH?so~?BRmC@}y-2{={oFww;SD)_QC57f?q2(;sMFyP((XlnP|JT7bY>@Kri1$_rN?XB8& zH!UgIZTfaO&$M$v5m~aQf*i~hv_BuI{a5F9&a`JsRj=Q6Cl1S_vHXm)v*b@qmC3cx z_gC1J&>yb4)2D8*Yp|{3Prt2n7J2daXZ4b8l z7O7`@44A}OS2{E`$gs8UKcytTCvY%1!n-S%9XrBZ~P?==}6VwJ74ni>&2g;&T1MuF%iq&H)l^eQOfAZe&TBN z^SxTZ>$oiE`s=B`kF2|t^i8t--m71?{AYDzmDKxe zywUW*JJl`R`%+T>{hoEx)6z9j>wfvFkAx;wvY-p(oY z$=f>jr$-z(rYs`yzA-aNT3ToO!ab(1KYl)~zp$Vwv^LSx#iv);vqeYRv@^r()khVj z)D5#-mhikbEPHZc9ghxYWYNwSLY-yX*@Fi!f z@Cn6v=Cfpe&U7>8J8=GsVx#>DNrrm;l^N{|++%$f@E5T<)X%#T&ANBi^c5;>dZsm> zTls#^FSWc{DLcI)WB-hG$@vMj%(j(k#{M%p!ZDLMyws*}-n+Im z-_ZJIH`CT#YaY+w4TKlSnNUMgjl{;c}f<=KzhJggQpu!{H*a{lYaiEJ?@(G-Y~Av z-g$oBLf)+%lVqZ2S@%7iecPMstQXUf$JPA*Yy=lCx}un|V$#nPqj~-B#HJrE{D1n< zD*GA!%clBlGUwB{k@M1O8tXBe+fAFaRtoa}x8<3aWAJb>r?m8^gC7%u&CV|PGTUpL zY7mn`x29vnqH95X8=orIWv;vatSJ5GdM~l7sVwIW&UPM~U|4(bZs4MfB}aSIvuj_^ z{`g8$^NM7+-Wi5U`-jbwZ??0&H+|zarDo;vkBP_H_uBvak*w!4=UYkoX>T6x+boUc zvvb@0Kd?79MRsfL5nKG|&XYOv7e1Z&yC(ePk;T7u{b*Zq^73cnZJL$EzFxc;T-HkI zK{1{-n>o9j3#CJ}qBpr7=Z<-B&B=M)gp?V5&S{%2cWoA)x#-Y@l-G-*6TOn}TYWRJ zQ|6!kRbsl~=BE#qD=shOUzi^DUUb6!Ev?sn&$U^TeJXQO|B1#0OPvLFcFSnYn$@Io zt4=O}_v;1Gl!J@oFQ_c)didN>N>;q<=5xo(c2@IsS~yd0xf=&L?YvfhK}~&1l+M)+ zmOdJ#@s%Q%Hy^8&R9#knYhFav_V30Ud%ooPw@sb(S4&>!R4ve(mKe;j&3waEvw2AthSPKA6fe2E$K&}q_sjF0-hB2t$)&23tFNy# z;mYEN{cpA|)^ufZ|GK9`fhqdhfe%xPuPu1Pv0|3Y)!9P(U6_Q8mEL_1?N?xx-B9Ms zv_Qg}PiKwDqOg=LY!h2~);b5@y7aP|FT~b&q2I-4GMis#ZOGVa!LR4LNmw(Vk44sZ zlb~j<9Luh)lct#RbCtdMxa?5)EA?F)nf?B5|N7C{Pj+6`m3eblF9})oZQ8~kE0?eH zw)}bL`@>)J4dXqZR`?$(npYdKa_W>55klTeEiZWAbvCcqyI`%{tEJ2(!s^v8lg}U7 zzWj>1ep~jV?T6m15-^>1K1K3Jee%wdJEF7t-U~G6@LoUuSD53`#V;RU2`y0Y;VAcL z|2a45;Nkz?bMogsFgSEf(CmYYpq{*-V$_}6*53q;PwYHX_)mQ0|ETNf3eos{Eqsn4|mUS+ieLC zPO;^?H|>D12J~IjewJ&m=(lZgevZ_V5HH}^Q_t6I|VJG>Y#B8~J`K_2p z_;l|6cRo>NF_jbMyuY2#vTGvu#I$I!a_yZPUYJQQ>_5@!xcPPmkDYkiw~f`U=5O_{ zaM!*+EbF|+-ByVI8_P%SCY}3!TOP?NTodyCyLEd|eK&K4%mStRljm&TH0kpXkspnl zcYG|p(!2eeqxroFJb6(Si);nrRphEpuf1#WUe3w%X`iH7qE!Be-G*;Cn?=7iywhHJ zHS1{8jky^+oD8*h)!$1y9-hycw&HAK%MPK}?_Zh>*~_CFsRKGm7b z`=ali|G&s}FNd^b#G2(2a&?+wV*8_|BO>;Q>grq*+HCMv^!*;OvRSD~THkEduAMX5 z_(R4?`0(o1<1=qKhFeYaNlB`I5PQi%{`2i^@BY0o+_v^k!qkTKI*(5VH?se|eXxLM zK{@xIO)NW)K98RvTw!KYv%J|RNXt&jJ-nbrzkye2|K4`{51c(q6N`^9t$moUlwJ8c zoMDoF#qR43bIyMCRubm8@^bqp_lnEgN=j@$YS+d_ENj>PSiF&eW1^2|%)gbJ#I9GU zL?7Rx$a?tiDf@Nady?PT*e2Shr$^qq)cQ1aZ}_6=@twz&`X6qtJ!k)Hk^7-Od-uZ< zmSH<(Zmw9|*B$?8iks@n=jTin7YKwEq-MN-a!>kkr&q1<#pA~5BC6SYr8~;vlGJSs zkE~N^-`|i>FI6{F_ua4ShmYobx$it7WV_y0UbXMri|+_e)qSu)hGPqp)U6d0-h0aI z`ec5oqp9KFs%#x87k|YT-)RqjaxY2V@R>27YN={Xr}O+dp6hj43Z1hKEfDKom??<$WgJipCh#iNnuBKWw}|HH`|cE?Gr8aw7q^4?`uR&n>;h1Fl)ey(D7 zKAf3Xenw=Xa4M7imqRs+WB;GcOGrQ3c=}=J|2ms~)+U=o@A94hW^5?cx!?JNb=ebL zn}F?ale+bm#yscNe|hNP^^F%_2zPv}&t-pWdnNGXqnRx43l{WF-}sKXrB$ zxh<<$wsFaVyZ27?iEIp2{jaq!U2CuUgeB}}1PTI03);4SH@{)YT_rj_{O9%@%i`zS zRrY=BW;raEmU^`Q)yHt5>5nBZ=cs-VICw-md|l#V2~qWKzcc0WGxawMxQnXtf8yBl z`Fzv%_afhU&dl1+Ao91=b9)^N|AM7L{~l$uyw~pFUl*yIpQ=#t=%B2j%YM<)n||iU z50y@+QI(PP-4l{@zwEi=rMxAIiNc;`JTlz#Hy&QEZ6*87zDI1UjonOhL%vC!=Ev7o z+}SVDm#Vz&w@+TqkGbBq`h2`ncfQ-CpS1t0=X9ygzZyQ{fct+n zUQdm=;q>*?k!7cE`#k$}`+2Y5^R@5GHQpz??ouva^0Z^)^KDH!hSxf}^vsVKx!(0I z{Hdq?f8NfAXP+dRxX3JjlJ(_h+K+iUx>+C8jejl|tYG?^@$hobD-k|##mB+joti#M z?>|WAT`T>uY~R&Y+aKMG3#&>|wsiCTp*d~NL+90X$EvM%*@(TEFZ<1Cx^9N&mH#_h z%zjp!XN%@o#+0z4=Kt&<`$P9qo>|D(&HKLm@V_K88GiQ9cISULZJc;tyxM11lFk$t z*0&RQ8`|m@&MDq@c$(>??Nj^%F6y^$X8imiYn{l!M={IKZExJ{%%-K2YxPvpFX@7= zw#$YMI~UzEGJL|g{`6sncj`M;enmf!wP9jPxS}ohbK||EJM4eFydlKSzpU`B`0|-a znL>&|ktXX54<&CXuQ|GSTSQ%zS;3^xPj4gl?3E4@;$JaIy-TcZ{)U{`bnn>LqVoS; zcdp&He2v$pRhy32`p#b?bLw=e8TY+xp^rZpL!R$_@{%vG>Cin_-VB$c-!k}S9Q?Sw zuy6X_oS&bL3U{75nP9a?=4|2ff5+#RZq!n>i`%n#`k&uB(z9|6SvJTTPvWfoxbFN#XUF5^tmvgrp>jd=i=1Bg?J}5BD zz_8+BMAP@y(=qE4t{<^4Sa&r`)a{CbP9yKR4~z8|G_K>V2|Iad`w#j1N6Oh`jviv+ zP1$fMeuH}P%UxbPB`MwFhu2Ojov@JU!KH+(>jLsJ=V~4>yuZV-?83_rne3a>;$^$; z@h>ubmg5{};P6@A^LvHy&iTCJhj0HZ(27Z&dO`fK)_f23HR=-@J6DEnn%LX^xBX#V zic>=ePfk(pe)UX^^yCdYHFxFLWc{w{aB|+=bh|5?ZyArfez{ z)K>eRwOV%jBEAoe?_{1_^#A;tfB(1sESSjo_UZl4?Ym|NM24FF zXJ_=>xNzo9qi^l^CMSyL#Pa-`&vTHM>rv9~6(WH@|E%WkUBI%JOJ11MQ%&9~ zbH45I;yYmzc@oz@agfSC#HaQrf2YWQX4Dbq6ZtW712`BMZsMHi4hRBIbXVs;@(}tj zUEgJ;q-)@8aabiXSFKo`-6u(wvBkIOqJ|UOtOvb?%C}V}|NRnu_w9G)L-+1+t4?~k zF8uoA>!HtB{OsrE-T!M^`oYnvQF_zNn8TVG=S~&r{Fe=RezS;U#q7Dczpl7W(!0BF z_qETr+N-uR%0~WUIyQ@CPV)4BUSj)7np%$C`jqhcQu_5f40h{coDMA3{(rXQWO&7L z>xdbAJ5n+$_hp|gw_`lbemU~rRP9?AmTs*|_qI;=jQJ4Q?SH42z2xZrJ3Px*TvJ?^ zdAP!S`&w3>HLQg;O?T8@rDflnrIYyYoaC=FOtu>seu+Gtwa<0#dIz05e3iYRZ(w#ofkGd^?Z8vFg$G)vjA8Tg^ZRfmx<+J3^ zxlBdN?@sf-9si_Ec-gK)e5!`_K3hL7Y5g|8X?wxiweJnTa=-eaxAme!cZFR=bkc1( zsdqXrrM*jb3HIb2>QdFVpWb@8R5wKKC~cijw7j}f@fQ=BKP@BT%>XOh$8 zDwfx?Dg-Xn3Q3F2tlalfTxT}-v-1V9FQqoGTO>Wnt64bpK>l+dM`fGrV?rpzUcAxU1ncT zbX+YvF+=nyOX!aF=})l^35n(q;JKh}xaccelQ@nH+t@McQ%MITmxJlfmh8x7<+ucdm5Dg+G-?PBG|; zWgVL_HADFPC9QYqegd{BKNtK{ofKBIY0C61am>j@dp~bHr?hje^rP}-p;f1Q3+8i% z2wwY;cJYejEVJG7q>g1+9NzztWuf*I+p?3|=ap(c>~ZG0bzECwl6+m%LC&Pb`)@nV zSbBQHi{~+$)I%k7qImAbiW!C6ez7cn&TM?Uh|?!nc!$R{ zH)j*AlUCc#mOuIw>UXZFG~wLNe~Y4iUYvGw`R8Jf<4$=A+my@}2rPS~{6_qwl-UdJ zZ9-pfIqVH*->R0do~iU<%lV9exZvN@yXRZmFrJ<(=(72W=>LyJ4?FS>in5p$I~BoP-%fno!lsm*8B_79B2`K4*4edzNwIen3Ib)k zCokks+Hik8+tO|MxeL$TWcCVUV~myDv*nC#)MgtMx%XyAKfhqoxA0SWHz7-lH>5qZ zR!La+VsrkD#|D>9yiR00W-y8Gm|*9m6HeEJmBT}KUuRcdJZmN6PL&VsrM+tMT*U$p zHkexK=@!KV`ebjljhklqYtM$+irw}fOFtIAd3l0;!}40OCVzoL9;W55y2Eo08VY>+ zl#fb)BjMUH`RlLxQ0-Wmf4kM*Pne{v*yph9 z`AdB=zBc>MA(*v)&V7Z59ax@C0_Nv+VbK2&S$L~+veE%hOcAK z@}INdWANVEQc=EMUb|VYO&eIg-&`QwweVa)Rl|G-o9`h@k1rCMqRzeSX=L%Wi?$hu z@4ZMYGY*ss4P!}UIPLk~G}hT!`e03@Ntno%ZJlueUF=7W1pGFipuf1#!1T?tX?_cP zr>LGSJGA19#wYilT}_|+rM~`L5V-VfTw0`$qUhPBk&}g){wuM)JpIIA-a=2Gr!(g^ zecbcptV^EXd*#LP2FvAV>^wc?jpWkvpB9%yRerq4cm7KH%8BMLZk>Okf86I#^#s}G zXF4bQRW@+Ho;*YARjfdqVNL2mp#?HagBg^HAW!+Q1cWm#N>V9tJ%g2tqr_DXBmVap# zygF}Rx!(MUl~Zq5&)a1FLL-^??3D)9UF#QBaWIzq-hcN(sBuA^`76!lnRTmM7-She z1OF>JJZW~W`8j7_{+|C4@4BXN-BT;t)id|!u|ChIxhb4AVSWqN|NZQ@H)Z|Lh5hAk za(VwW(GHi_&I9_U^scH1+ZE_;YMw+v|7!5YJoHWG>I% z^Iq%lIxCMG|I`zsk99BV;1|DebzW4YaN)NI~!&y!AO zR}vOX=$Jgqb7xCNkDAjWmBiWGYrdEJZ7=Io^?P>DT;FuE$KB=iwZC#|s~p{*Jo$Sv z>#=SQw_KO|lRmxU=1*F_tl60;d9UrnwaJNrO8VEAJ-f*_!Q)%jV+num{wYhjR$kBI zU8J|-e$kT~^+9*FHI&rGcL0pJ0lsHab9H0s^G_xH!gMXu@%oa<#W3#iJ9e=N7H_RWIvtTt0##c zXORB*W{uIp(k17#W~{G^Qq^VbFnK+j(eq ztMkGN&QPO37nu;jrv@*3Q(ua(6*$G*H4K~A{rB~@rBlWp45rUxswZPJ}SXWpR$CoDtGTwB0+ z_e#w4hW?5tkB|2U6gT>8JTx^eP}oba@BxpiPL}e;L)#9DO*^1o7;X}$du2oC-j=SV zY-PMQp`8t4yqk_yDK6jl?)q+zhD9@_R{Xx2)SSbq!T7?>+~ksbA-UI)7)w!p<`)L()CED{XFMA5#Sst}L z-LF!&o?WHlx>r3lq*!zg@0=n|9qW6`q@tK-`MnKYvC&@2WYZQ^3$F;1Rwdtl(HB=c zNT0i|(b`OupbIXn2EhT1)?`{4U&tv{g&tXZ&R`##2ET();1!3(}ROz-8E5U@M^Sk;KHU%v6f@Bbo-tC}YLSR=O2 zr%LGajRg(+SKhk9ve;X=FuJhY(d0$IjG&d?HM_p=Ikcc(r^Dr_;JL#Eci!*m{cCsY zK+4fdh3hE^(OWE@6>3Ft7~9_9>z;kGYxV)=$w%{((%qh?>{t{wsk!Lss)!1sx0)@B zTzn%|HDt`F=sxQ7gJ+Jx);ZsDbB}qQQqAq!xFtkWtc!Jqu9NBg*51n_ZYMMLk(Pj4TT6vvuT6gQ>9eiD_(q`T*vUBeR`hR-<=##7J zisN!29jBMvSz{jEnIw5hP2>H^euIwVvpZMuvG;$tpTV;-&@_JcnN@ua_F}2-5~=0I zMKjNPey+CQ{PTaxZSAtNReTXneKV#{KAB~1{hW7q!Ncrp8viH`$j?FpRkWOe?m@02E;yzLhz^;Im`!zX-w;iX@EiK=VdOA<1b z6YDmLKT%k@@W!sihmVxa6pgN`X+P2XCz|ED>7I$al@I(U-~R4XFj-XRPexIYzF3i0 zVUYGS-{zxArw_^HMjW@7Hks4?_ss7?M(37Ij~^$^SaSUIJktnfhsL(wBG;en?{n|l zn37gf;~F4)oNL2G4F@i1z-QaMcTyv@VrVpZ9lf;_1Gr0FO?0Xb7ZS9YZf91n8 z&$u~wYTKSsdY5pwNIYZyIk!nnYZdr)ehV8%upMq>HOS*}d99;7&uYPXR@2yZD?fQX zpXeJE@K-uH)$yiVK+DDpG5Z!RGJf%(rcCGe_YmUK$?L@2vMdZ;`0>hxyH8yDN#j zlhyuR@0UK?pgV!PHkT_hbY0!zrzJ<1cL!^9Z^zLP{3&s*Ym?4zX05lKla3;B{6oqG=~Tv5Hv_*44<_OdlA z7Oj%`efV<0t0&%{n_Vo7r$;7`ZI87?yTv~FzNPzCXiW85v@^H4be+O_(Ocqk zWcSq;N@)L*j(GX)%Eb-l0p$fnl0{9@8tjEe)y(SOA3bNDky)H}gQsDq^`SOKGx6}i z;|ylD9UHp3gO1eP_1CTsJHDvDM#J)O`-0$SwSTxQdF4bte1Dz&aMIFG{}yYvcfr;V(F&}mT}66tM9)1r`fMy3AV6WR+Wy0e_u90C{x$v) zYyLjAdctt;2G2AO_DQ*qEJOBQ?FyUlFlPHyqaUk-be_0PUtn)PY4hLkm>nIL^26Wk zY0tl2UDLsLHKr_!X)>d2nWSYJ2a{&`b?>vQLL6n*v46Ih_sb-D$r+7>#rC!-)<3W8 zReGP+vh|pH$)>pz6I8ryBN;!$h9n10GGvkSRph@SQ#R*WF+Vr=bQ{NOr_N1##%7w% zaw$B0Z_}Jj5ebWL2=~;8o!}I72-xwNU-!}M1Iv>g5-&ehKO56*s-P8q+3{n1M%rWh zvd8OH3YBF(McDA(-6Y&KyZF-r<$lpSb3a@5Z0*>%t$2w5U%{07hpV#HU)Lo~P&b|y zthQv^<&8Jr$QzYQYb3Mm)R|4jDS;nlVs=iMT(FU^k^RI~t4&I_eNUr(u5%T$Zw}@t z|c4O>&+j&U;Ceaxo!G(vQg}EGg+fqea%O|yfJrPcr;{Bf5eJ6 zm*4&5&z>%y{4eF}FXrExr&4co)Cjl8&S`Jo)p};z1zGWf1{#Ug9IR81Z`~vDxWqU=GhAHED|XvXUC?-m%qGUquRNMomS;t0bIc93+a1dJ^-FNwjcLX{ z`-HdTdZ(ZMQGe=ykj&AN?XwrZ6Z^dWdW%$d&{F#~_G^S}j>xtdt1jA8wy9S{@b!)B zdTonqA8!As6JvI}_f-7h=pA2wm0g=vFPNRkQMY?pG^g+K?Hct=m)*_Vdd;n*ri)J( zKeFlL#=C3GKTMkJ{&&0D8X?`>hx@ml@cI3%H}=GV>%~U%?SGyZXOW26{qMHw154xT zm;1M!{On|`pnqO4v><6)bWrv2?!Gm8E4(@53Y#Ao92WTGV!O@sRQP}PcWafd%-z3r z){XGi1CM{*acZ#15Kqj{lFj25;a>leMbM=<|NNnotKU32!gy?gv&w$Al)6WIEgAeQ zc+TB#so1+Ot$N4$>(W1}i(`|Qez{&8_9~A1?V6hlXUA@P{7qwB*o5iRcV^aq@IU6L zc7jc!!g9}hf#+MoR_+Tk7i}uK^{VvY%9s8dRL=y?VBBEC`b@S=`|^dG%dBQxx+X2T zA&BkLrTu4=(hn}^ZatTHTQ+lx0OPzLvG&tmDPHsR61?ENwU1+G*1jK(rpi^!qOs1d z`PYm8nEZV&Xx1Zf{HW9W4R1H=2JQRfsOkJZbYalix08+b&-n0t!h7z=qCagigbm|2 zy}iZnC051${)Ya|AG&?@=_{hvl-zC#`4um|=F5!}-K;M+ti8gM5K}XA!qiKgUka5z z$X<+@%Wc1+_`rHI{*K6lU+3&^w|{B3eZk_^vJ=-z=9S#udaR$Td|_zxPrj{BL#(Yn z`OBxEvPrMtrQ6Ry8FZmvI?{C&cdm)3m?S)B8ad<6 zahmVH`LVQ};n+8BdqF-u@zfc-`W*^BFKbuM@E49#QP`UF&atLR-eK3R+^f67#{n_vPFF+B`Zg^1Je=@ay>pGya~PW2eO389Y_~@sf{aepmAR z_pN<0!||6N>+LC@xL0WIU*}P2efg@a)s3WSC--H2)K*gFjOBRG#CxQAr@r>7#{~+{ zH~*R2_AShsSv&cnhJU4$6FU${@c-K zm0qFSJeB3(j<)=Tv2HE*4lVf1lA3%vV4uBTuH?D+w7TDK^W)PEr>0tUpKRcN%fXUp z8{B^W-=FqW6kz3{9E3SRZ5%lYNbg!wmR?F`?} zpZV|nO_$#LkE}hWS}*<5+||>#R3wt;^429GH-e`YJdFRiTk}uU|N044y?0ePq?c@! z2um^6DO+=1x;8=lf|Iq-a{;4QOeOnldEeB(y51T%U)D{u;gjFtw>Lvh=PVcScz@)g z;qFw2`PRp3_|{qoPf*!bS-7|Dv0aVI(yHE#g43NiJ~#4C`ub&Ff!pG_hC*hYE%PmR zF;3jG;c58Ga@l;JnERXTfxOY67!%jNxO$m~h4LMj!vZ=tGLXl=`xEh`!y~ za!TJPas6_s=!p|Pq9@Lr*LbzbS_W`!Sj9|&6RUtxOt$M5;lbr0q&OZi&ywo72a z+Uv7Ce!rO~#M+a}7?b)dE##_0+@d94T$kRKzH^tq@J7s4#wcwEX~`=Ww=dk9l(Bf8 z&9rR`=CWr@+I;Wa?MfbBDgSMs$^;kOm0p)ULozxkLELb|?|F;9{3?>sj(-*N?&~d8 z$>8e@QXOwBo7kHqtaFVHm>gHx-ZuHXxAl`$9c^3Jy>EL{pFD}JiE!$@lkMNOPtLCBWlUkXpulLq0o24le`0DA0%a4*zyJkG))#fggvyv9H^}oD7wWoFA+~pUT zj-O_VYx%cL_=6Iw#E&?!4@MHm!pZf;io;>xqTaRk_dme0a-J7;I?y&x? zBl~nFE^_6XKVPkC)h*GbQEi-SxGDqM@6MaWq3ru?+v}%OtSYhu`Hn`fTO^rkuyg;! z`$GV#W{eS5$eywJ;|54!C6mvc!fr*cs z=Xcd^ot=5F!6WVLxnm`n_Q8v+ZfQ-Qe&XfTjTS*h2EGNHf2Ac4My#0LEwIzNFQw)C zqboc<+?EOU7N?stj?Uy*Dwto-7knt|?N9rh(@IBbYfKtrSo9T_8R&I%WiRP8J=}e( zX66)T(aP;slKg7TeL7`7#A3PLPx`Wzx3Pz%e(Fo1!p!v_-EzeYi(2>nNVRy;)!e6a zk6*ocOL#-|{RQe3x2BYs?313>+P&vDo2;{FzLnCuhxhz{W#wOKPiWb&{M) zMqBIVk{mnxYKD(b8BK#6_%*hh2;5L_^~~MJxbMu_OJ!j@eN2NR7AOAqmRjYSCnFqF18^K15fu*}^0!P`W* zd#z#}_=C1{O?ySk(yz0bX#R%tZ3j9m-&6Q?6-Lcq) z=XlPm{}UXtg)6_Y*yK($RzB(cmY==+=LB8GIR!hH_{l|hpI{E&b$H{f=4(0IZksCY zd$vRF+7rjl`A>D+d6j0q&eXH-wGsH5*wN^CPic!-g!(+;=LfcadQc}ETBzV=uv_Q# zq>kXjN3tEF#jHJqXO_=iVfOOTdaFvu#bRvQ`wz@#u-^7|M`EJvm$P<@)MqXGyC|~C z<-Wq3ZJPIgJoITPJ*)9}=HH!Vw*NM__9UA=@?Jehw7sA0^@E2D`~44CS$FGAbtzY&zKYznqc-zwX(~9qA@5O8+Y_8vyQP$EyLygE&XhZNX}*_znK3s zI8NQzCwu!_a_RpaYR9)G*NL^rFEL(Y`n9w2cD6}*9Dh$s(#f8#^6tl0ZDKb%_8kAG z^=FS}pSkruCHFb==FW|FF;1K{BbVb#!t5Uh?WZqHtNLnv(rI6vdUyDfuM+=KrBzR) zs8(u~S@kbl)7Yx_<5V&S4j?PV~{eedk<_ z%BmT^m#*0{Wr5vS(~bXi-+9cw<+f@5$MpdPOE)w#Z&RGGq-e@v!OhYJsWXx`Z(cbi zN!zh?W9+oKJfB~Gm%Y38dXRS>^GeTs5}tp5)uiul{+y8-+%wU<&-%UCIfL(4PAv_c zaYARh|7!`Uv&KKSe7d>OrD%=UQO><8H$~%ErhJ<-wiWWcB5*{bo(k;WyYb{&{DKG|qZ^9SEM9wo-DtqZ`T**3u(bj z`Ip!ej_e4$D4>4I-1U64+Jc)Kb-ulwZeXgD{oq}ID zxL8`PmmxFipXG+N*SM$YXT9N*)X`A(W2)J;kJUq*VfH>X_w{V^HcHOlvv*nBfnLTW zi91a8uAeP5La$Fgs`GI9;ScK^Z&p^P&$naOzY_kZ$Vc9;(rM%65?A%~0Ow=5$7CO+ zW*PL+FOs_^#wvRP-l6uPEQmKNyBmX^0(?%qE4ytwqMOHv1~zwYDTz1RNNFXdM&u9yNc7M;7)!A_cbMK3fay71inl3<{tX%qAD#k zWo}b#c3xHAi^J8ajVr}dcn#hM&Z*iJ*z^1K+2ybIeZF!2T=UKjQ{l5&UC$QX=+3tK z68W;_M|W!Qt&B&}`!)BLF8E<$w&_~e9d zJWOV?uJTDZ-p{e-YK>2VW#==+1GnZK=xCL`xzA>Y4EqZYzpUjGzIgb}3RI2gu~7NA zf6eb@i+^9!Gwfl1-V;0N^v4CoWd&b4KNWoa#hS?(+HmCB(XUUgzf9qp?&FxfkkG%FKeOg@D1ox`WuxHo$xxQ}s z^Zco|n#t}zKfJzJ9csMPm_Gf{lY+=Cr?yu-TL1J%Oa2|>ZA}TA#hmAamaAS7=iMQg zQ)BURqxacbrwyV0x6US7&f6x>>0g|Z1|Zq+YecuX4$_@AFo!qS#W&4;$4N4 zWxH?W8A-)OOR{+h|C;(ed0#fm-Ov4rHhpcgS?+I^*N%;^4F44Do`1E%=0Hl!#8vMV zS0&sDJUc~0Dk)3*N9QY+dsAZX`7Tz^k6fz%CbI61Lb&B$mLo}jCm$EOapl;eM(3u9 z7H_8je<5TttJ+)1Vx7REFoP46py6){1=FXg*udh#%bx*iz zYWUDw$l}o5&-^9pKFU3{k})af_VxeS%#;6kwTa22$K8(>x84zt)R)uu+|6V%kD1}5 z!_UB}T1R-*1&{kVeZD9wms31%vc%cr*GsY{B^->?=B!%Fax&JaKkqd2Le*y0Rom`G zJuf)htucr91fwv&lDeRTZr<@D`G!9&6>j=lU6^rRcvedO%(x4&lV&bG#Gc)`W`WVJ z*r?5WEYro%c`F;r3(z$2!raU`l@oLWE&nK3( z9M(A7GvT86`~8^@W@tDZ753oFS#noXFT?n}krHp-ABM?GT@SN)7`<5UT>iWH;v;D> zw>vY~Co>*!